Windows sistemlerinde
kullanıcı ile etkileşimin sağlanması ve sistem kaynaklarının yönetilmesi gibi
işlemler için mesajlar üzerine kurulu bir sistem kullanılmaktadır. Mesajlaşmalar
sayesinde kullanıcının uygulama içerisindeki davranışları tespit edilebilir,
bu davranışlara göre programcı tarafından belirlenen yanıtlar verilebilir. Sistemde
önceden tanımlı halde bulunan bazı kaynaklar, mesajlaşma yolu ile yönetilebilir,
belli işlemlerin yapılması sağlanabilir. Hatırlayacağınız üzere bir önceki yazımızda
Windows sistemlerinde bir pencerenin nasıl oluşturulacağına değinmiştik. Bu
yazı kapsamında geliştirdiğimiz uygulamada öncelikle oluşturmak istediğimiz
pencerenin özelliklerini sisteme tanıtmak amacıyla WNDCLASS yapısı türünden
bir nesne tanımlayıp, bu yapı nesnesinin veri elemanlarına penceremizin davranışını
ve görünümünü belirleyen değerler atamıştık. Bu pencere sınıfını sisteme ’RegisterClass’
fonksiyonu ile tanıtmıştık. Tüm bu aşamalardan geçtikten sonra penceremizi oluşturmak
için ’CreateWindow’ fonksiyonunu çağırmıştık. WNDCLASS yapısı türünden nesnemizin
’lpfnWndProc’ veri elemanına, mesajları işlemek için kullanacağımızı belirttiğimiz
bir fonksiyonun adresini atamıştık. Bu fonksiyonu yazma amacımız, uygulama içerisinde,
sistemdeki mesajları dinleyerek belli mesajlara karşılık belli işlemlerin gerçekleştirilmesini
sağlamaktı. Örneğin programın çalışma zamanında kullanıcı bir kontrole bastığında,
bir menüyü açtığında, klavyeden bir tuşa bastığında veya fare ile bir yere tıkladığında
yapmak istediğimiz işlemleri pencere fonksiyonunda kodlayacaktık. Kullanıcının
yapabileceği işlemlerin hepsi, Windows tarafından bize mesaj olarak gönderilir.
Biz mesajları işleyen fonksiyonumuz içerisinde, bu mesajları anlamlarına ve
kaynaklarına göre ayırarak belli işlemlerin yapılmasını sağlarız. Ya da kendimiz
uygulama içerisinde özel mesajlar tanımlayarak, bu mesajları uygulama içerisinde
kendimiz göndeririz ve mesajları işlediğimiz fonksiyon içerisinde bu mesajlar
oluştuğu takdirde yapılması gereken işlemleri kodlarız.
Windows sistemlerinde
mesajlar iki başlık altında ele alınabilir :
1. Sistem-tanımlı
mesajlar (system-defined messages) 2. Uygulama-tanımlı mesajlar (application-defined
messages)
Şimdi bu mesaj
türlerini sırasıyla ele alalım :
1.
SİSTEM-TANIMLI MESAJLAR
Sistem-tanımlı
mesajlar, sistemin bir uygulama ile etkileşime geçeceği zaman gönderdiği mesajlardır.
Bu mesajlar genellikle, uygulamanın kullanıcı ile ve sistem ile etkileşimleri
sırasında sistem tarafından gönderilirler. Uygulamanın çalışma zamanında, kullanıcının
gösterdiği davranışlar sistem tarafından bir mesaj olarak ifade edilir ve uygulamalara
iletilir. Bu şekilde uygulama, kullanıcısının ne yapmak istediği hakkında bilgi
edinir. Sistem-tanımlı mesajlar, uygulamanın kendisi tarafından da gönderilebilir.
Sistemde önceden tanımlı pencere sınıflarına sahip olan kontroller (düğme kontrolü,
menüler gibi) ile uygulama arasındaki etkileşim de uygulamanın bu kontrollere
sistem-tanımlı mesajlar göndermesi yoluyla gerçekleştirilir. Örneğin uygulamanın
çalışma zamanı sırasında, uygulama içerisinde yer alan bir düğme kontrolünün
basılmış duruma geçmesi için uygulama bu düğme kontrolüne bir mesaj gönderir.
Sistem-tanımlı
mesajların her biri için, sistem genelinde tekil bir değere sahip mesaj tanımlayıcıları
sağlanmıştır. Uygulama içerisinde mesajlara erişim, bu mesaj tanımlayıcıları
ile gerçekleştirilir. Mesaj tanımlayıcılarının isimlendirilmeleri genellikle
onların hangi kontrole ait olduğunu belirten, ne amaçla oluştuğunu açıklayan
birtakım kurallara göre gerçekleştirilmiştir. Mesajları işlediğimiz fonksiyon
içerisinde hangi mesajın geldiğini anlamak için bu tanımlayıcı sembolik sabitleri
kullanacağız. Mesaj tanımlayıcılarının değerleri, Windows tarafından 0x0000 -
0x03FF ile 0x8000 - 0xBFFF arasında sınırlandırılmıştır. Bu aralıkta kalan değerler
uygulamalarda tanımlanan özel mesajlar için kullanılmamalıdır. Kullanıldığı
takdirde bu özel mesajlar ile sistem-tanımlı mesajlar arasında çakışma durumu
oluşur.
Sistem-tanımlı
mesajların önekleri, mesajların hangi kontrole ilişkin olduğunu belirtir. Bu
önekler ve anlamları aşağıdaki tabloda verilmiştir :
ÖNEK
|
HANGİ
KONTROLE İLİŞKİN?
|
ÖNEK |
HANGİ
KONTROLE İLİŞKİN?
|
ABM |
Application desktop toolbar |
PSM |
Property
sheet |
BM |
Button
control |
SB |
Status
bar window |
CB |
Combo
box control |
SBM |
Scrool
bar control |
CDM |
Common
dialog box |
STM |
Static
control |
DBT |
Device |
TB |
Toolbar |
DL |
Drag
List Box |
TBM |
Trackbar |
DM |
Default
push button control |
TCM |
Tab
control |
EM |
Edit
control |
TTM |
Tooltip
control |
HDM |
Header
control |
TVM |
Tree
view control |
LB |
List
box control |
UDM |
Up-down
control |
LVM |
List
view control |
WM |
General
window |
2.
UYGULAMA-TANIMLI MESAJLAR
Uygulama-tanımlı
mesajlar, bir uygulama içerisinde, uygulamaya ait pencereler üzerinde veya diğer
uygulamalara ait pencereler ile iletişim kurulması sırasında kullanılmak üzere
oluşturulan özel mesajlardır. Uygulama içerisinde oluşturulan mesajlar, mesajların
işlendiği fonksiyon içerisinde doğru anlamında yorumlanmalıdır. Uygulama-tanımlı
mesajlar için mesaj tanımlayıcıları belirtilirken, sistem-tanımlı mesajların
son mesaj tanımlayıcı değeri olan WM_USER-1 değerinden sonraki değerler kullanılmalıdır.
(Yani WM_USER değerinden başlamak suretiyle numaralandırma yapılmalıdır.) Uygulama
içerisinde oluşturulan özel mesajlar, sisteme tanıtılmalıdır. Bu amaçla RegisterWindowMessage
fonksiyonu kullanılır. Bu fonksiyon, mesaj için tüm sistemde tekil olması garanti
altına alınmış bir değer de döndürür. Bu değer, mesaj tanımlayıcısı olarak kullanılabilir.
Böylece diğer uygulamalarda da tanımlanmış olabilecek özel mesajlar arasında
mesaj tanımlayıcısı çakışması önlenmiş olur. RegisterWindowMessage fonksiyonunun
prototipi şöyledir :
UINT RegisterWindowMessage(     LPCTSTR
lpString // Mesaj yazısının adresi
); |
RegisterWindowMessage
fonksiyonu, sistem genelinde tekil olması garanti altına alınmış yeni bir pencere
mesajı oluşturur. Fonksiyonun lpString isimli parametre değişkeni, sisteme kaydedilecek
mesajı belirtir. Bu yazı karakterinin sonuna null karakter eklenmiş olmalıdır.
Fonksiyon başarılı olması durumunda geriye 0xC000 - 0xFFFF aralığında bir mesaj
tanımlayıcısı değeri döndürür. Bu geri dönüş değeri, yaratılacak mesajı parametre
olarak geçeceğimiz fonksiyonlarda kullanılır. (SendMessage, PostMessage gibi
fonksiyonlar) Fonksiyon başarısız olması durumunda sıfır değerine geri döner.
Bu fonksiyon ile sisteme kaydedeceğimiz bir mesaj, genellikle birbiriyle etkileşimli
olarak çalışan iki uygulama arasındaki mesajlaşmalarda kullanılır. Aynı isme
sahip birden fazla mesaj sisteme kaydedilmek istenirse, bu durumda bu mesajlar
aynı mesaj tanımlayıcısı değerine sahip olurlar. Bu mesajların sistemdeki yaşam
süreci, geçerli Windows oturumu sonlanana kadardır. Bu fonksiyon, iki ayrı uygulamanın,
aynı mesajı kullanması gerektiği durumlarda kullanılmalıdır.
MESAJ
KUYRUĞU NEDİR ?
Wİn32 sistemlerinde,
sistemdeki her kanal için otomatik olarak bir mesaj kuyruğu (mesaj queue) oluşturulur.
Bir veya daha fazla sayıda pencere oluşturan kanal içerisinde bir mesaj döngüsü
oluşturularak bu mesajların kuyruktan alınması ve işlenmesi sağlanır. Windows,
mesajları doğrudan uygulamalara ilişkin pencerelere gönderdiği için, mesaj döngüsünü
oluşturduğumuz bir kanalın en az bir tane pencere oluşturması gereklidir. Mesajlar,
kuyruk sonuna bırakılır ve kuyruğun başından alınır.
MESAJ
KUYRUĞU NASIL DİNLENİR ?
GetMessage fonksiyonu,
mesaj kuyruğundan bir mesajın alınabilmesi için kullanılır. Şimdi bu fonksiyonu
inceleyelim.
BOOL GetMessage(
    LPMSG
lpMsg, // Mesaj yapısı göstericisi
    HWND hWnd, // pencere tutamacı
    UINT wMsgFilterMin, // İlk mesaj
    UINT wMsgFilterMax // son mesaj
); |
GetMessage fonksiyonu,
kendisini çağıran kanala ilişkin mesaj kuyruğundan bir mesaj alarak, bu mesajı
ilk parametresine geçilen mesaj yapısı adresine yerleştirir. Bu fonksiyon, hem
belli bir pencereye ilişkin mesajların alınmasında hem de PostThreadMessage
fonksiyonu ile gönderilmiş kanala ilişkin mesajların alınmasında kullanılabilir.
Ancak başka kanallara ve uygulamalara ait mesajların alınmasında kullanılamaz.
İlk parametresi MSG yapısı türünden bir adrestir. İkinci parametresi, hangi
pencereye ilişkin mesajların alınacağını belirten pencere tutamaç değeridir.
Bu parametre değişkeni için NULL değeri geçilirse, belli bir pencereye ait mesajlar
değil, o kanala ilişkin tüm mesajların alınması sağlanır. Bu fonksiyon, mesaj
tanımlayıcısı belli değerler arasında kalan mesajların alınması için de kullanılabilir.
Eğer böyle bir aralık söz konusu ise bu durumda wMsgFilterMin isimli parametresi
için aralığın alt sınır değeri, wMsgFilterMax isimli parametre değişkeni için
ise aralığın üst sınır değeri verilmelidir. Eğer bu değerler için sıfır verilirse,
hiçbir filtreleme yapılmaksızın kuyruktaki tüm mesajlar dinlenir. Klavye girişine
ilişkin mesajların alınması için filtreleme yapılacaksa sınır değerler olarak
WM_KEYFIRST ve WM_KEYLAST sabitleri, fare girişine ilişkin mesajların alınması
için filtreleme yapılacaksa WM_MOUSEFIRST ve WM_MOUSELAST sabitleri kullanılabilir.
Fonksiyonun geri dönüş değeri, kuyruktan WM_QUIT mesajını almadığı sürece sıfır
dışı bir değerdir. Eğer kuyruktan WM_QUIT mesajı alırsa sıfır değerine geri
döner. Herhangi bir hata durumunda ise -1 değerine geri döner. Bu fonksiyonu,
’Pencere Oluşturmak’ isimli uygulamamızda mesaj döngüsü oluşturmak için kullanmıştık
:
while (GetMessage(&msg,0,0,0))
{
    TranslateMessage(&msg);
    DispatchMessage(&msg);
}
|
Bu döngüye göre,
kuyruktan WM_QUIT haricinde bir mesaj alındığı sürece kuyruktaki mesajlar dinlenir.
Eğer kuyruktan WM_QUIT mesajı alınırsa, bu durumda fonksiyon sıfır değerine
geri döner ve while ifadesindeki koşul sağlanmadığı için döngüden çıkılır. PeekMessage
fonksiyonu da yine kuyruktan mesaj almak amacıyla kullanılır. Ancak kuyrukta
hiç mesaj yoksa fonksiyon bloke olmadan geri döner ve mesaj döngüsü işlemeye
devam eder. Şimdi bu fonksiyonun prototip bildirimine bakalım :
BOOL PeekMessage(
    LPMSG lpMsg, // mesaj yapısı göstericisi
    HWND hWnd, // pencere tutamacı
    UINT wMsgFilterMin, // ilk mesaj
    UINT wMsgFilterMax, // son mesaj
    UINT wRemoveMsg // mesajın silinip silinmeyeceğini belirten
bayrak parametre
); |
PeekMessage fonksiyonunun
ilk dört parametresi, GetMessage fonksiyonunun parametreleri ile aynı anlamı
taşımaktadır. Tek farklılık PeekMessage fonksiyonunun son parametresidir. Bu
parametre, mesaj kuyruktan alındıktan sonra, mesajın silinip silinmeyeceğini
ifade eder. Bu parametre değişkeni için geçilebilecek sabitler ve anlamları
şunlardır :
SABİT
|
ANLAMI
|
PM_NOREMOVE |
Fonksiyonun mesajı aldıktan sonra, kuyruktan silmeyeceğini belirtir. |
PM_REMOVE |
Fonksiyonun
mesajı aldıktan sonra, kuyruktan sileceğini belirtir. |
PeekMessage fonksiyonunun
geri dönüş değeri, eğer kuyruktan bir mesaj almışsa sıfır dışı bir değer, almamışsa
sıfır değeridir. Bu fonksiyon, geri dönmek için kuyrukta bir mesaj olması durumunu
beklemez. Kuyrukta hiç mesaj olmasa bile, geri döner.
MESAJ
GÖNDERMEK
Windows sistemlerinde,
bir mesajı kuyruğa bırakabilecek veya bir pencere fonksiyonuna iletebilecek
fonksiyonlar mevcuttur. SendMessage fonksiyonu, mesaj göndermek için kullanılan
bir fonksiyondur. Bu fonksiyon, parametre olarak aldığı mesajı, ilgili pencerenin
pencere fonksiyonuna doğrudan iletir. Mesajın kuyruğa bırakılıp alınmasını beklemektense
doğrudan işlenmesini sağlar. Prototip bildirimi şöyledir :
LRESULT SendMessage(
    HWND hWnd, // Mesajın gönderileceği pencerenin tutamaç değeri
    UINT Msg, // Gönderilecek mesaj
    WPARAM wParam, // İlk mesaj parametresi
    LPARAM lParam // İkinci mesaj parametresi
); |
Fonksiyonun ilk
parametresi, mesajı hangi pencereye ilişkin pencere fonksiyonunun alacağını
belirtir. Bu parametre değişkeni için HWND_BROADCAST sabiti geçilirse bu durumda
mesaj sistemdeki tüm üst seviyeli pencerelere gönderilir. (Çocuk (child) pencereler
hariç tüm pencereler) İkinci parametre değişkeni, gönderilecek mesajı belirtir.
Üçüncü ve dördüncü parametre değişkenleri mesaj ile ilgili ayrıntılı bilgi içerirler.
Fonksiyonun geri dönüş değeri, mesajın işlenmesinden sonra elde edilen değerdir.
Bu değer, mesajın işlendiği pencere fonksiyonundan alınan geri dönüş değeridir.
PostMessage fonksiyonu, bir pencereyi oluşturan kanala ilişkin mesaj kuyruğuna,
kendisine parametre olarak geçilen mesajı bırakır. Mesajın işlenmesini beklemeden
geri döner. Mesaj kuyruğuna bırakılan bu mesajların alınması için de yukarıda
açıkladığımız GetMessage ve PeekMessage fonksiyonları kullanılır. PostMessage
fonksiyonunun prototip bildirimi şöyle yapılmıştır :
BOOL PostMessage(     HWND
hWnd, // Hedef pencerenin tutamaç değeri
    UINT Msg, // Gönderilecek mesaj
    WPARAM wParam, // İlk mesaj parametresi
    LPARAM lParam // İkinci mesaj parametresi
); |
Fonksiyonun parametre
değişkenleri, SendMessage fonksiyonun parametre değişkenleri ile aynıdır. Bu
fonksiyonlar arasındaki tek fark, mesajın bırakılması ile ilgilidir. Pencereler
arası mesajlaşmalarda ve sistemin standart kontroller ile mesajlaşmasında genellikle
SendMessage fonksiyonu kullanılır.
Önümüzdeki yazılarda
standart kontrollere gönderilen mesajları, bu kontrollerden pencerelere gönderilen
mesajları ve bunların işlenişlerini ele almaya devam edeceğiz. Herkese mutlu
günler dilerim.
KAYNAKLAR
- Win32 Programlama
Yardım Sayfaları
Makale:
Windows API - Mesajlar ve Mesajlaşmalar C ve Sistem Programlama Çiğdem Çavdaroğlu
|