Jump to content

Windows Programlama 1 - Windows Programlama ve Mesajlar


Muallim-i Ali

Recommended Posts

Yazan : Osman ÇELİK

Bu makalede Windows Programlamanın temelleri ile ilgileneceğiz. Makale ana yapı olurak 5 bölüme ayrılmıştır. Bölümler sırasıyla

* Windows Programlama ve Mesajlar

* Pencere Sınıfları

* Pencereler

* Temel Pencere Özellikleri

* Pencere Fonksiyonu

Şeklinde anlatılmıştır.

Windows İşletim Sistemi tam anlamıyla mesajlar sistemidir. Sistem içerisinde gerçekleşen tüm işlemler için tanımlanmış olaylar ve bu olaylara ait delegeler harekete geçerek yapılacak işleme göre bir mesaj oluştururlar. Bu noktada sistemin temel amacı bu mesajların doğru ve sıralı biçimde işlenmesidir.

DOS İşletim Sistemi Windows İşletim Sistemine göre temelden farklı bir yapı ile çalışır. DOS programları, kullanıcı girişi (input) sağlamak için açık şekilde fonksiyon çağrıları yaparlar. Yapılan bu çağrı sonucunda program kullanıcı girişi için hazır hale getirilir. Bu kullanıcı girişi genelde klavye üzerinden gerçekleşir. Klavye üzerinde basılan tuşlar sistem üzerindeki klavye sürücüsü (keyboard driver) tarafından uygun şekle dönüştürülerek direk olarak programa aktarılır. DOS programı kendi içersindeki yazılıma göre basılan tuşları değerlendirmeye alır ve programın yürütülmesini sağlar. DOS programlarından klavyeye ek olarak mouse.com dosyası kullanılarak fare hareketleri de değerlendirilebilir. Ancak yapılan tüm işlemler -Windows programlarından farklı olarak- açık şekilde fonksiyon çağrıları ile kullanıcı girişi alma üzerine kurulmuştur.

Windows programları, DOS programlarından (Console Application) farklı olarak kullanıcı hareketleri ve bu hareketlere yanıt vermek için delegeler ve olayları kullanır. Kullanıcı girişi almak ya da aktif hale gelmek için açıkça fonksiyon çağrıları yapmak yerine sistemin, birçok Windows uygulaması arasından kullanıcı girişini kendilerine vermesini beklerler. Sistem kullanıcı girişini bir programa aktardığında program aktif hale gelir ve kullanıcı girişi için bekler. Elbette ki bir Windows programı teknik olarak en azından bir Windows formu (penceresi) içermelidir. Ayrıca bir Windows programı birden fazla forma da sahip olabilir. Sistem kullanıcı girişini bir Windows programına aktardığında o programa ait tüm formlar kullanıcı girişini almış olarak değerlendirirler. Ancak Windows üzerinde birden fazla form (ya da pencere) görünüyor olsa bile bunlardan sadece bir tanesi aktif durumda olduğundan kullanıcı girişine sadece bir tanesi hazırdır.

Bunlara ek olarak Windows üzerinde tüm formlar kendilerine ait bir “Window Procedure” (Pencere Fonksiyonu) sahiptir. Bir form aktif hale geldiğinde sistem otomatik olarak bu fonksiyona çağrıda bulunarak, formun kullanıcı girişini aldığını ve yapılan kullanıcı girişlerini işlemesini ister. Form üzerinde kullanıcı herhangi bir giriş yaptığında –ki bu giriş işlemi, klavyeden bir tuşa basılması ya da bir fare hareketi olabilir- o forma ait Window Procedure yapılan giriş için gerekli işlemi gerçekleştirerek kontrolü tekrar sisteme geri gönderir. Daha sonra sistem kullanıcı girişini başka bir programa ya da aynı programa gönderebilir. Tüm bu olaylar kullanıcının sistem üzerinde yapmış olduğu hareketlere göre değişir.

Not: Bir forma sahip olmayan ve Windows üzerinde çalıştığı için adı Windows programı olan programlarda vardır. Bunlar sistem içersinde geri planda çalışan servisler olabilir. Ancak bu makalenin konusu temel anlamda Windows programlama olduğu için aksi belirtilmediği sürece anlatılanlar ve yazılan kodların tamamı bizzat bir formu olan Windows programları içindir.

Eğer Windows işletim sistemi mesajlar sistemi ise öncelikle bu sistemin mesajlarını, mesajları nasıl yolladığı, aldığı ve işlediğini anlamak gerekir. Bu nedenle Windows uygulamalarında kullanılan formlara girmeden önce sistemin iç işleyişinin detaylı şekilde incelenmesi gerekir.

MESAJLAR VE MESAJ KUYRUKLARI

Sistem, kullanıcı girişinin kendisine verilmesi bekleyen bir programa gerekli girişi “mesaj” biçiminde gönderir. Aslında sistem programın kendisi ile ilgilenmez. İlgilendiği alan program içersindeki formlardır. Çünkü her Windows programı minimum bir forma sahiptir. Belirtilen bu form aslında daha önceden tanımlanmış olan bir sınıftan türetilen ve sistem tarafından görüntülen bir nesnedir. Tasarlanan bu pencere sınıfı, sistem tarafından kendisine gönderilen mesajları işlemek için bir fonksiyona ihtiyaç duyar. Bu fonksiyon sistem içerisinde pencere fonksiyonu (Window Procedure – WndProc()) olarak adlandırılır. Tanımlanmış olan bu fonksiyonun çalışması için 4 adet parametreye ihtiyaç duyar. Sistem bu parametreleri belirtilen metoda eksiksiz olarak gönderir.

İlk parametre, bir pencere tutamacıdır (Window handle). Pencere tutamacı, sistem tarafından pencere yaratıldığında pencere için atanan bir işaretçi değişkendir. Bu tutamaç forma ait kimlik bilgisini oluşturur. Açık formlar içerisinde aynı handle numarasına sahip iki form olamaz. Sonuçta ayrı ve tek(unique) numara olarak belirlenen handle, aslında pencereyi diğer pencerelerden ayırmak için kullanılır.

İkinci parametre, mesaj kimliğidir. Bu mesaj kimliği genelde yapılmak istenen olayı belirtir. Pencere fonksiyonu, bir mesaj aldığında parametre olarak gönderilen bu mesaj kimliğine bakarak mesajı nasıl işlemesini gerektiğini belirler. Mesaj kimlikleri aslında kullanıcı hareketlerinin sistem içersindeki karşılıklarıdır. Bu bir fare hareketi (WM_MOUSEMOVE), bir klavye hareketi (WM_KEYDOWN) ya da programdan çıkmak için (WM_QUIT) kullanılan bir mesaj olabilir. Bu mesajlar aslında önceden yapılabilecek tüm kullanıcı hareketleri sistem için düşünülerek birer sabit (constant) olarak “winuser.h” header dosyası içersinde tanımlanmıştır. Bu mesaj kimlikleri birazdan incelenecektir.

Son iki parametre, (WParam ve LParam) gönderilen mesaj kimliği için gerekli verileri taşırlar. Bu iki parametrenin içeriği gönderilen mesaja göre değişir. Her iki parametre klavye hareketleri için farklı veri taşırken fare hareketleri için farklı veri taşırlar. Bu veriler bir tam sayı ya da bir işaretçi değişken olabilir. Bu iki parametre içindeki verinin anlamı, mesaj kimliğinde mesaj için gönderilen sabite (constant) göre değişir. Hatta kimi zamanlarda mesaj kimliği bu iki parametreye ihtiyaç duymaz ve her iki parametre null değerine sahip olarak gönderilir.

Not: winuser.h dosyası sisteminizde .Net Framework 2.0 Platform SDK kurulu ise hali hazırda vardır ve incelemeye açıktır. Aynı zamanda bu sabitlerin C# implemantasyonu için Mike Hars’ın yazmış olduğu WindowsConstans dosyası incelenebilir. http://www.windowsforms.net/Samples/download.aspx?PageId=1&ItemId=182&tabindex=4

Ayrıca .Net içersinde de bir mesaj yapı (struct) olarak tanımlanmıştır. Tanımlanan yapı System.Windows.Form ad-alanı içersindedir ve yine incelemeye açıktır.

Mesaj Tipleri:

Mesajlar hem sistem hem de programın kendisi tarafından üretilebilirler. Bu nedenle sistem içersindeki tüm mesajlar; sistem-tanımlı (system-defined) ve program-tanımlı (program-defined) olarak ikiye ayrılır.

Sistem-tanımlı mesajlar:

Bu mesaj tipi genelde sistem bir program ile haberleştiğinde kullanılır. Sistem bir programın işlemlerini kontrol etmek, kullanıcı girişi sağlamak gibi önemli konuları sistem-tanımlı mesajlar ile gerçekleştirir. Aynı zamanda programın kendiside sistem-tanımlı mesaj oluşturabilir. Tüm sistem-tanımlı mesajların önceden tanımlı bir kimliği vardır ve bu mesajlara ait kimlikler sembolik sabitler (symbolic constants) ile tanımlanmıştır. Bu tanımlanmış olan sabitler genelde header dosyaları içindedir. Örnek olarak WM_PAINT verilebilir. Tüm sistem-tanımlı mesajlar aslında kategorilere ayrılmıştır. Bir mesajın sembolik sabitinin önekine bakarak neyle ilgili olduğu anlaşılabilir.

	BM_	  Buton kontrol

CB_ Combobox kontrol

DBT_ Aygıtlar

EM_ Edit kontrol

HKM_ Hot Key kontrol

IPM_ IP adres

LB_ Liste kutusu kontol

TB_ Toolbar

SB_ Durum çubuğu kontol

SBM_ Kayıdırma çubukları

TCM_ Tab kontrol

WM_ Windows Mesaj
[/CODE]

Bu örnek sabitlerin dışında tanımlanmış birçok sabit daha vardır. Bu sabitler winuser.h dosyası içersinde bulunabilir.

Belirtilen sabitler içersinde tabii ki en önemlisi WM_ sabitidir. WM_ öneki ile başlayan sabitler oldukça geniş bir tanımlama alanı bulur ve en çok kullanılan sabitler WM_ öneki ile başlayan sabitlerdir.

Bundan başka bu sabitlerin tamamı semboliktir ve kendilerine ait bir değerleri vardır. Bu değerler 0x000’dan başlayarak 0x03FF sayısına kadar gider. Belirtilen 0x03FF Hexadecimal sayı WM_USER sabitinin bir eksiğidir ve tüm sistem tanımlı mesajlar bu aralıktadır. WM_USER (0X4000) ile 0x07FF arası program tanımlı mesajlar için ayrılmıştır.

[b]Program-tanımlı mesajlar:

[/b]

Programlar kendi mesajlarını tanımlayabilirler. Bu tanımlamalar genelde bir programın kendi içersindeki pencerelerle ve diğer programlarla haberleşmesi için kullanılır. Eğer bir program kendi mesajını üretirse gerekli pencere fonksiyonu bu mesajı işleyerek doğru yanıtı vermelidir. Sistem bu noktada gerçekleşen işlem için dışarıdadır. Tüm sorumluluk mesajı üreten programa ve o programa bağlı olan pencere fonksiyonuna aittir. Program tanımlı mesajlar için ayrıca WM_APP (0x8000) ile 0xBFFF arasıda ayrılmıştır. Bu sabit aralığı da program-tanımlı mesajlar için kullanılabilir.

Aşağıda winuser.h dosyası içersinde tanımlanmış bazı sabitleri aynen dosya içersinde tanımlandığı gibi vermeye çalıştık. Bu winuser.h dosyası ve içeriği Microsoft Firmasına aittir. Tanımlanan sabitlerin isim ve öneklerine ayrıca her bir sabite karşılık gelen Hexadecimal değerlere dikkatlice bakın.

[CODE]/*

* Window Messages

*/


#define WM_NULL 0x0000

#define WM_CREATE 0x0001

#define WM_DESTROY 0x0002

#define WM_MOVE 0x0003

#define WM_SIZE 0x0005

#define WM_ACTIVATE 0x0006


/.........


#define WM_SETFOCUS 0x0007

#define WM_KILLFOCUS 0x0008

#define WM_ENABLE 0x000A

#define WM_SETREDRAW 0x000B

#define WM_SETTEXT 0x000C

#define WM_GETTEXT 0x000D

#define WM_GETTEXTLENGTH 0x000E

#define WM_PAINT 0x000F

#define WM_CLOSE 0x0010

#ifndef _WIN32_WCE

#define WM_QUERYENDSESSION 0x0011

#define WM_QUERYOPEN 0x0013

#define WM_ENDSESSION 0x0016

#endif

#define WM_QUIT 0x0012

#define WM_ERASEBKGND 0x0014

#define WM_SYSCOLORCHANGE 0x0015

#define WM_SHOWWINDOW 0x0018

#define WM_WININICHANGE 0x001A

#if(WINVER >= 0x0400)

#define WM_SETTINGCHANGE WM_WININICHANGE

#endif /* WINVER >= 0x0400 */[/CODE]

[b]Mesaj Routing:[/b]

İlk adım bir mesajın oluşturulması ise ikinci adımda mesajın bir pencere fonksiyonuna gönderilmesidir. Oluşturulan sistem-tanımlı mesajlar uygun pencereye gönderilir. Bu gönderilme olayına mesaj routing denir. Sistem, mesajın gönderilmesi için iki yol (route) kullanır.

İlk yol; Mesaj Kuyruğu (Message Queue) denilen sistem tarafından üretilmiş, ilk-giren ilk-çıkar (FIFO) temeline dayanan bellek nesnesine mesajın yollanmasıdır. Bu mesaj kuyruğu koleksiyonlar konusunda belirtilen Queue nesnesi ile aynı temele dayanır. Sistem içersinde birçok mesaj oluşur ve oluşan bu mesajlar belirtilen mesaj kuyruğuna sırasıyla gönderilir ve gönderildikleri sıra ile bu kuyruktan sistem tarafından çıkartılarak gerekli pencere fonksiyonuna direk olarak yollanırlar. Mesajı alan pencere fonksiyonuna belirtilen mesaj için gerekli işlemi yapar. Mesaj Kuyruğu’na gönderilen mesajlar “queued messages” (sıralı mesajlar) olarak adlandırılır. Bu mesaj tipi genelde bir kullanıcı girişi neticesinde oluşan, WM_PAINT, WM_MOUSEMOVE, WM_CHAR veya WM_QUIT tiplerinde olabilir.

İkinci yol ise, mesajın Mesaj Kuyruğuna gönderilmeden direk olarak gerekli Window Procedure gönderilmesidir. Bu nedenle bu mesajlar sırasız mesajlar (non-queued messages) adı verilir.

Queued Messages (Sıralı Mesajlar): Sistem aynı zaman zarfında birden fazla pencere (form) açabilir. Kullanıcı hareketlerini doğru pencereye yönlendirmek için mesaj kuyruğunu kullanır. Bu mesaj kuyruğu sistem içersinde tektir. Ancak aynı zamanda sistem kendisine ait bu mesaj kuyruğu dışında açılan tüm pencereler için ayrı ayrı mesaj kuyrukları oluşturur. Sonuçta sistem içersinde 5 adet pencere açılmışsa açılan tüm pencereler için bu pencerelere özel mesaj kuyrukları da oluşturulur. Ancak bu pencerelere özel mesaj kuyruklarının oluşturulmasında sıkıntı yaşanmaması için sistem, o pencere için bir kullanıcı hareketi gerçekleşene kadar belirtilen pencere için mesaj kuyruğu oluşturmaz.

Kullanıcı o pencere ile ilgili ilk girişini yaptığında –ki bu giriş fare tıklaması, bir tuşa basılması olabilir- sistem o pencere için özel mesaj kuyruğu oluşturur. Ve yapılan girişe göre, girişi yapan aygıtın (klavye ya da fare) sürücüsü yapılan işlemi bir mesaj’a dönüştürerek sistem içersinde tanımlı ve sistemin kendisine ait olan Mesaj Kuyruğuna (pencerenin kendisine ait olan kuyruğua değil!) yerleştirir. Sistem’e ait mesaj kuyruğu genelde sürekli mesaj aldığı için doludur ve birçok mesaj içerir. Kuyruk ilk-giren, ilk-çıkar temeline dayandığı için sistem yukarıda belirtilen mesajdan önce sistem kuyruğuna girmiş mesajları sırasıyla işletirken belirtilen mesaj, sıranın kendisine gelmesini bekler. Bu bekleme esnasında sistem, mesaj kuyruğuna başka mesajlarda yerleştirilebilir. Yukarıda belirtilen mesajdan önceki tüm mesajlar işletilip sıra kendisine geldiğinde sistem mesajı inceler. Sarmalanmış olan bu mesaj daha önce belirtildiği gibi 4 adet parametre içerir. 4 adet parametre içerdiği için mesaj aslında bir yapı (struct) olarak tanımlanmıştır. Mesajı inceleyen sistem, mesaj içersinde parametre olarak gönderilen Window Handle (pencere tutamacı) ile mesajın hangi pencereye ait olduğunu bulur ve bu pencere için yaratılmış bir mesaj kuyruğu varsa mesajı bu kuyruğa yerleştirir. Eğer yoksa pencere için bir mesaj kuyruğu oluşturarak mesajı kuyruğa yerleştirir ve mesajı kendi Mesaj Kuyruğu’ndan siler. Sistem belirtilen işlemi yaptıktan sonra kendi mesaj kuyruğunda bulunan sıradaki mesajı aynı şekilde işleme koyar.

Sistem uygun sürücüden gelen mesajı MSG yapısına (struct) dönüştürerek mesajın gönderildiği tarihi ve sistem üzerinde bulunan fare işaretçisinin (cursor) konumunu da ayrıca mesaja ekler.

Her pencere için ayrı ayrı oluşturulmuş olan mesaj kuyrukları bir kanala bağlı (thread-specific) olarak çalışır ve mesaj kuyruğuna bir mesaj yerleştirildiği anda çalışarak mesajı kuyruktan silerek incelemeye alır. Mesaj içeriğine göre uygun pencere fonksiyonunun çalıştırılması için sisteme mesajı geri yollar.

Pencereye ait mesaj, mesaj kuyruğundan GetMessage() fonksiyonu ile çıkartılır. Ayrıca pencere PeekMessage() fonksiyonu kullanarak sadece mesajın içeriğini denetleyebilir. GetMessage() fonksiyonu ile alınan ve kuyruktan silinen mesaj, DispatchMessage() fonksiyonu ile sisteme gönderilir. DispatchMessage() fonksiyonu MSG yapısındaki mesaj için bir işaretçi alır ve mesaj içerisindeki Windows handle, mesaj kimliği ve LParam ve WParam değerlerini sisteme yollar. MSG yapısı içersinde bulunan mesaj zamanı ve fare işaretçisinin konumu sisteme geri gönderilmez. Ancak pencere isterse bu zaman ve konumu GetMessageTime() ve GetMessagePos() fonksiyonlarını kullanarak alabilir.

Ayrıca bir pencere, kendisi için oluşturulmuş olan mesaj kuyruğu kanalını (thread) isterse WaitMessage() fonksiyonu kullanarak bekleyen konuma getirebilir. Tabii bu işlem sadece belirtilen kuyruk içersinde hiç mesaj olmadığı takdirde kullanılır.

Mesaj kuyrukları ilk-giren ilk-çıkar temeline dayandığı için kuyruğa gönderilen tüm mesajlar kuyruğun en sonuna eklenir. Ancak bu durumun genelde üç mesaj için istisnası vardır. WM_PAINT (pencerenin boyanması), WM_TIMER (zamanlayıcı) ve WM_QUIT (çıkış) mesajları mesaj kuyruğunun en sonuna eklenmez. Hatta belirtilen mesajlar, mesaj kuyruğuna gönderilmeden önce belirtilen mesaj kuyruğu içersinde hiçbir mesaj kalmayana kadar bekletilir. Bunlara ek olarak da aynı kuyruk içersinde bulunan birden fazla WM_PAINT mesajı tek bir mesaja dönüştürülerek yapılır. Bu bir formun tekrar tekrar boyanması olayını en aza indirir ve ciddi performans artışı sağlar.

Nonqueued Messages (Sırasız Mesajlar): sırasız mesajlar, sistem mesaj kuyruğunu ve pencereye ait mesaj kuyruğunu atlayarak işlenmesi için direk olarak pencere fonksiyonuna yollanan mesajlardır. Bu mesajlar genelde pencerenin kendisi üzerinde oluşan değişiklikleri belirtmek için kullanılır. Bu değişiklikler, pencerenin aktif duruma gelmesi (WM_ACTIVE) ya da pencerenin hareket ettirilip yerinin değiştirilmesi (WM_WINDOWPOSCHANGED) şeklinde olabilir.

Genellikle sırasız mesajlar direk olarak pencerenin kendisini ilgilendiren ve hemen sonuç alınması gereken mesajlardır. Bu nedenle sistem üzerinde performans artışı sağlamak için bu mesajlar direk olarak işlenirler.

[b]Not: [/b]Windows XP işletim sistemi diğer işletim sistemlerinden farklı olarak, bir pencere belli bir süre boyunca kullanıcı girişlerine yanıt vermezse sistem, pencereyi yanıt vermeyen pencere olarak değerlendirir ve asıl pencereyi yok ederek aynı özelliklere sahip yeni bir pencere (ghost window) tanımlayarak asıl pencere ile değiştirir. Bu sayede kullanıcı pencere üzerinde, pencereyi kapatma veya hareket ettirme gibi belli işlemleri yapabilir. Belirtilen yeni pencere açılması program debug modunda iken yapılmaz.

[b]Mesajların İşlenmesi (Message Handling):[/b]

Bir program temelde kendi mesaj kuyruğuna gönderilen mesajları işlemek ve bu mesajları kuyruktan silmekten sorumludur. Genelde single-thread (tek-kanallı) olarak yazılan programlar WinMain() fonksiyonu içersinde mesaj döngüsü tanımlarlar. Mesaj döngüsü içersinde gönderilen mesajlar GetMessage() fonksiyonu ile alınıp işlenmesi için uygun pencere fonksiyonuna yollanır. Multi-thread (çok kanallı) programlar ise tanımlanan her kanal için ayrı bir mesaj döngüsü belirtebilirler. Mesajların işlenmesi işlemi mesaj döngülerinin anlaşılmasından geçer ve bu döngüler dil içersinde bulunan while döngüsü ile oluşturulur.

Mesaj Döngüsü (Message Loop): Kendi içerisinde üç adet fonksiyona yanıt verecek şekilde tasarlanır. GetMessage() fonksiyonu mesajı mesaj kuyruğundan çıkartarak MSG yapısına dönüştürür. Bu fonksiyon WM_QUIT mesajını almadığı sürece sıfır olmayan bir değer döndürerek mesajı –eğer klavye sürücüsü tarafından gönderilen bir mesaj ise- işlenmesi için TranslateMessage() fonksiyonuna gönderir.

WM_QUIT mesajı sonuç olarak false değerini türetir ve mesaj döngüsünden çıkılmasını sağlar. Teknik olarak program Single-thread olarak yazılmışsa döngüden çıkılması kanaldan çıkılmasını bu da programdan çıkılmasını sağlar. Program multi-thread olarak yazılmışsa WM_QUIT mesajı belirtilen kanaldan çıkılmasını bu da kanalın kapatılmasını sağlar. Bu durum en fazla belirtilen kanal için açılmış olan (varsa) pencerenin kapatılmasına neden olur. Ancak bu durumda program hala çalışıyor durumdadır. Programın kapatılması her iki programlama modeli içinde programın ana kanalının durdurulması ile olur. Bu durum ana kanal üzerinde tanımlı bulunan while döngüsü sonlandığı zaman olur. Döngünün sonlanması için WM_QUIT mesajından sonra program PostQuitMessage() fonksiyonu çağırarak WM_DESTROY (yok et) mesajına neden olur. Bu mesajda tüm programdan çıkılmasına sağlar.

TranslateMessage() fonksiyonu, program klavyeden bir giriş aldığında kullanılır. Klavyeden yapılan girişler sistem içersinde WM_KEYDOWN ve WM_KEYUP mesajlarına neden olur. Bu mesajlar basılan tuş için tuş koduna sahip olarak yollanır. Ancak bu tuş kodu hangi tuşun basıldığını belirten sanal bir tuş kodudur (virtual-key code). TranslateMessage() fonksiyonu kullanılarak gönderilen mesaj WM_CHAR yapısına dönüştürülerek gerçek karakter koduna dönüştürülür. Bu fonksiyon, belirtilen dönüştürme işlemini gerçekleştirdikten sonra mesajı tekrar programın kendi mesaj kuyruğuna yerleştirir. Kuyruğa yerleştirilen bu mesaj tekrar GetMessage() fonksiyonu ile kuyruktan alınarak ana döngüye sokulur. WM_CHAR yapısında olduğu için bu kez TranslateMessage() fonksiyonu pas geçilerek mesaj DispatchMessage() fonksiyonuna gönderilir.

İster klavye hareketi olsun ister fare hareketi olsun tüm mesajlar DispatchMessage() fonksiyonu tarafından sistem mesaj kuyruğuna gönderilir. Bu metot sistem tarafından programa gönderilerek döngü içersinde işlenmiş mesajları tekrar sisteme geri göndermekten sorumludur. Sistemde işlenerek gönderilen bu mesaj için gerekli pencere fonksiyonunu çağırarak mesajın yerine getirilmesini sağlar.

Bir programa ait mesaj döngüsü, programın ana kanalı yaratılıp bir pencere oluşturulduğunda çalıştırılır. Bu döngü sonuç olarak, WM_QUIT mesajını alana kadar kendi kuyruğuna gelen mesajları okumaya ve bu mesajları uygun pencere fonksiyonlarına göndererek çalıştırılmasını sağlar.

Kimi programlar single-thread olarak tasarlanmış olsa bile birden fazla pencereye sahip olabilir. Ancak bu durumda her pencere için ayrı döngü oluşturulmaz. Programın ana kanalı üzerindeki döngü uygun mesajı uygun pencereye Window Handle işaretçisi ile yönlendirir. Window Handle, çalışan programlar ve gösterilen pencereler için tektir. Bu sayı bir pencerenin kimliği (Identity) olarak düşünüldüğünde aynı kimlik numarası o an için açık pencerelere özeldir ve iki pencere için aynı olamaz. Bu sayede DispatchMessage() fonksiyonu uygun pencereye yönlendirme yapar.

Normal şartlar altında yukarıdaki şekilde çalışan mesaj döngüsü istenirse değiştirilebilir durumdadır. Mesaj kuyruğu içersinden sırası gelmediği halde bir mesaj diğer mesajlardan önce işlenmek için çıkartılabilir. Hook (Askıya alma) denilen yöntem sayesinde, herhangi bir program sistem içerisindeki mesaj kuyruğunu gözeterek, sistem tarafından uygun pencere fonksiyonuna gönderilmeden önce mesajı alıp istediği fonksiyona gönderebilir. Sanırım bu uygulamanın en güzel örneği Win98 kullandığımız yıllarda WindowsBlind isimli programdır. Bu program sayesinde tüm sistem içerisindeki GDI mesajları incelenerek gerekli fonksiyonlar sayesinde Win98’in arayüzü tamamıyla değiştirilebiliyordu. Uygun “skinler” kullanarak sistem bambaşka bir görünüme sahip oluyordu.

Window Procedure (Pencere Fonksiyonu): daha sonra detaylı şekilde anlatılacak olmasına rağmen konusu geldiği için burada kısaca anlatılacaktır. Sistem üzerinde yaratılan her pencere için bir window procedure yaratılır yalnız aynı sınıftan yaratılan pencereler için tek bir tane yaratılır ve hepsi aynı procedure’ü kullanır. Bu procedure, yaratılma sebebi olan pencereden gelen mesajları işlemek için vardır. Mesaj, DispatchMessage() metodu ile bu fonksiyona gönderildiğinde işlenmesi gerekip gerekmediğini kontrol eder. Mesajı kendisi işleyecekse gerekli işlemi yapar. Eğer mesajı kendisi işlemeyecekse sistem içersinde tanımlanmış DefWindowProc() metodunu (Varsayılan Window Procedure) çağırarak işlemi bu metodun yapmasını sağlar. DefWindowProc() işlemi gerçekleştir ve pencereye ait Window Procedure’e bir yanıt gönderir. Aslında pencere fonksiyonları genelde mesajları işlemeyerek o mesaj için varsayılan işlemi gerçekleştirmek adına mesajı DefWindowProc() fonksiyonuna gönderir.

[b]Mesaj Filtreleme:

[/b]

Bir program mesaj kuyruğunda bulunan belirli bir mesajı diğerlerinden önce işlemek için kuyruktan çıkarabilir. Bu işlem yine aynı şekilde GetMessage() fonksiyonu ile olur ama fonksiyon çağrısı sırasında belli bir filtre kullanılarak istenen özel mesaj diğerlerinden ayrılır. Aslında bu işlem mesaj kuyruğuna sonradan giren ama diğer mesajlardan önce işlenmesi gereken mesajlar olduğunda gerekli hale gelir. Filtre ise belli bir aralığa sahip mesajları kapsar. Örnek olarak WM_KEYFIRST ve WM_KEYLAST mesajları tüm klavye hareketleri komple almak için filtre olarak kullanılabilir. Bu sayede arada gerçekleşebilecek diğer olaylar -WM_MOUSEMOVE gibi hareketler- tüm yazım işlemi gerçekleştikten sonra işleme alınır.

[b]Post ve Send:[/b]

Bir program aynı sistem gibi mesaj gönderebilir. Bu genellikle pencere fonksiyonuna mesaj yapısını taşımak ile olur. Bir program için mesaj iki şekilde gönderilebilir.

İlki “post” yöntemidir –ki bu yöntem PostMessage() fonksiyonu kullanır. Program kendi içersindeki bir pencereye post yöntemi ile mesaj gönderdiğinde mesaj, mesaj kuyruğuna yerleştirilir. Burada asıl amaç mesaj ile belirtilen pencerenin herhangi bir işlemi yapmasını sağlamaktır. Aynı zamanda program içersindeki tüm pencerelerin belli bir işlemi yapması içinde bu yöntem kullanılır. post yöntemi ile gönderilen mesaj içersinde bulunan Window Handle null olarak belirlenir ve herhangi bir pencereye özel olmaması sağlanır. Program içerisinde bulunan döngü hiçbir pencere belirtilmemiş bu mesajı tüm pencerelere uygular. Aynı zamanda post yöntemi ile gönderilen mesajlar mesaj kuyruğunun dolu olması durumunda işlenmezler. Bu nedenle programın PostMessage() fonksiyonundan dönen değeri kontrol etmesi gerekir.

İkinci yöntem “send” yöntemidir. Bu yöntemde mesajlar, SendMessage(), BroadcastSystemMessage(), SendMessageCallback, SendMessageTimeOut(), SendNotifyMessage() ve SenDlgItemMessage() fonksiyonları ile gönderilir. Bir program bu yöntemi genellikle çok acil bir işlemi gerçekleştirmesi gerektiğinde kullanır. Send yöntemi ile gönderilecek mesajlarda özel bir durum oluşmadığı sürece sadece SendMessage() fonksiyonu kullanılır. post yönteminden farklı olarak mesajı uygun pencere fonksiyonuna gönderen program, mesajın işletilmesinden emin olmak için mesajın result değeri ile geri dönmesini bekler ve bu bekleme sırasında başka işlem gerçekleştirmez. Send yöntemi genellikle MDI programlarında ana form (parent window) ve bu forma bağlı alt formlar (child windows) arasında haberleşme gerektiğinde kullanılır. Ayrıca bu yöntem sistem üzerinde bir değişiklik olduğunda açık tüm pencereleri bu değişiklikten haberdar etmek için de kullanılır. Örnek olarak program sistem saatini değiştirebilir ve bu değişiklikten tüm pencerelerin haberdar olması için bir mesaj gönderir. Gönderilen mesajın window handle değişkeni, HWND_TOPMOST ve mesaj kimliği de WM_TIMECHANGE olarak belirlenebilir. Aynı şekilde bir program çalışan tüm programlar bir mesaj göndermek istediğinde de send yöntemini kullanır. Bu işlemi gerçekleştirmek için çağrılan fonksiyon BroadcastSystemMessage() fonksiyonudur.

[b]Mesaj Yayımlama (Broadcasting Messages) :[/b]

Mesaj yayımlama işlemini sistem üzerinde çalışan programlardan çok sistemin kendisi üretir. Genellikle sistem düzeyinde değişiklikleri tüm programlara bildirmek için sistem bu yöntemi kullanır. Mesajı yayımlamak genelde birden fazla alıcı olduğunda önemlidir. Bu nedenle bir mesaj göndermek için programlar bu yöntemi kullanabiliyor olsalar bile tercihen SendMessage() fonksiyonu kullanarak mesaj gönderirler. Ancak mesaj birden fazla alıcıyı ilgilendiriyorsa mesaj BroadcastMessage() fonksiyonu ile gönderilir. Ve bu gönderme işleminde çağrılan bu fonksiyona alıcı listesi de eklenir.

Mesaj yayımlama işlemi genellikle sistem üzerinde bulunan aygıtların sürücüleri tarafından yapılır. Bu sürücüler, kendileri üzerinde ya da bağlı bulundukları aygıt üzerinde bir değişiklik olduğunda mesajı yayımlarlar. Bir CD-ROM’a CD takıldığında CD-ROM’a ait sürücü değişikliği algılayarak sisteme değişikliği haber vermek için mesaj yayımlar. Yayımlanan mesaj sistem üzerinde şu sıra ile alınır. Sistem-düzeyindeki sürücüler, ağ sürücüleri, kurulabilen sürücüler ve en son olarak da çalışan programlar. Bu sıra önemlidir. Önce sistemin kendi içinde yayımlanan mesaja uygun işlem gerçekleştirip kendini güncellemesi daha sonra diğer aygıtlara ait sürücülerin güncellenmesi ve en son olarak da programların güncellenmesi gerekmektedir.

Bunlara ek olarak sistem içersinde sadece açık programların pencerelerine bir mesaj gönderilmek istendiğinde window handle olarak HWND_BROADCAST tutamacı SendMessage() fonksiyonuna aktarılabilir. Bu sayede BroadcastMessage() fonksiyonu kullanılmadan mesaj sadece sistem üzerinde çalışan programlara gönderilebilir aynı zamanda sistem düzeyindeki sürücülerin bu mesajı alması engellenebilir.

[b]Mesaj Kilitlenmeleri (Message Deadlocks):[/b]

Send yöntemi ile mesaj göndermede, mesajı gönderen döngü mesaj sonucu dönene kadar başka bir işlem yapmadan bekler. Zaten mesaj işlenene kadar kontrol, mesajı gönderen programa geri verilmez. Ancak program mesajın işlenmesi bitmeden kontrolü geri alsa bile send yöntemi ile gönderdiği mesajın yanıtını bekler ve başka işlem gerçekleştirmez. Ancak olası bir durumda programın mesajı göndermiş olduğu mesaj kuyruğu kendi mesaj kuyruğu ise ne olur? Yani gönderen ve alan aynı kuyruğa bağlı ise ne olur? Yanıt basittir; sürekli bir bekleme hali oluşur ve bu olaya windows programlama içersinde mesaj kilitlenmesi denir. Bu ve benzeri durumlardan kurtuluş yolu olarak SendNotifyMessage() ve SendMessageTimeOut() fonksiyonlarının kullanılması tavsiye edilir.

[b]Mesaj Sorguları (Query messages):[/b]

Programcı kendi yazdığı programlar arasında direk haberleşme sağlamak için bu yöntemi kullanılabilir. Programcılar yazdıkları programlar arası ilişkiyi sağlamak amacı ile kendi özel mesajlarını geliştirebilirler. Bu genellikle bir aygıt için sürücü geliştirildiğinde faydalıdır. Bu sayede belirtilen sürücüyü kullanan tüm programlar arası haberleşme ve koordinasyon sağlanabilir. Belirtilen sürücüler mesajı almak için DriverProc() fonksiyonunu, mesajı yayımlamak ve kendisini kullanan tüm programları haberdar etmek için BroadcastMessage() fonksiyonunu kullanırlar. Sürücüler ve çalışma sistemleri noktasında daha detaylı bilgi kitabın kapsamı dışındadır.

Mesajlar ve Mesaj Kuyrukları aslında aynı zamanda sistem içersinde bulunan Message Queing (MSMQ) bileşeni ile sistem yöneticileri tarafından da kullanılır. Kullanılan bu bileşenle uygun çalışan programlarda üretilebilir ancak bu olay kitabın kapsamı dışındadır. Burada anlatılmak istenen sadece ve sadece windows programlarında kullanılan formlara mesaj gönderilmesi, alınması ve işlenmesi olayıdır.

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...