|
Bir oyun programcısı için windows programlama , içinde grafiklerimizi çizdireceğimiz bir pencereyi programlamakla yetinebileceğimiz bir konu. Pek fazla derine dalmaya gerek yok.Yalnız Windows un temeli olan event-driven(olay-güdümlü) programlamadan biraz bahsetmemiz gerekecek. Diğer programlama metodlarından farkını anlatmak için , programlama metodlarından kısaca bahsedeceğim. İlk yazılan programlar goto deyimini kullanılarak , program içinde çok fazla dallanma yapıyormuş.goto deyimleri program büyüdükçe hangi işin ne zaman , hangi sırada yapılacağı sorunu, zamanla programlama dünyasına hediye ediyor! Bu sorunu çözmek için , C veya Pascal programlama dilinde olduğu gibi , top-down veya structured programlama denilen ,her işin sırayla yapıldığı programlama metodu kullanılmaya başlanmış.Bu metod yapılacak işi belli bir düzene sokmuş ve karmaşıklığı gidermiş.Zamanla , programların büyüklüğü arttıkça , değişken sayısı artıyor ve bu değişkenlerin yönetilebilirliği de zorlaşıyor.Demişler ki: bizim için gerekli olan fonksiyonları ve bu fonksiyonların kullandığı değişkenleri belli bir alana hapsedelim , böylelikle daha kolay yönetilebilen daha büyük parçalar(nesneler) elde ederiz .Bu mantık da bize NYP (nesne yönelimli programlama) modelini getirmiş . Her ne kadar nesne yönetimi önemli bir sorun olsa da NYP işleri fazlasıyla kolaylaştırmış . Şimdi size bahsedeceğim event-driven programlamada ise olayları tetikleyen “if-else-while-for…” un varlığı veya sırası değil , kullanıcının ulaştığı arayüzden ,örneğimizde pencereler ,yaptığı isteklerdir.Bu metodun tercih edilmesinin başlıca gayesi programların kullanımını kolaylaştırmaktır.Event-driven(olay güdümlü ) programlama olmasaydı bugün hala daha DOS ta komut yazıyorduk . Windows event-driven programlama metodunu kullandığı için çok meşhur olmuştur..Nesne yönelimli programlamada structured programlama kullanılabildiği gibi (örneğin C++) ; olay güdümlü programlamada her ikisi de ( NYP veya structured ) kullanılabilir.Structured programlamada (örneğin pascal) programın akışını değiştiren şey , “ if ” ile kontrol edilebilen bir değişken olabilir . Olay güdümlü programlama da ise , programın akışını değiştirebilecek olan bir butona basma veya mouse tıklama işidir. Olay güdümlü programlamada her yaptığınız fiil(tuşa basma , Mouse a tıklama ) bir olaydır ve program tarafından işlenmelidir.Bu fiillerin program tarafından doğru şekilde anlaşılabilmesi için kendine has bilgileri barındırmaları gerekir.Bu bilgileri barındıran yapılara mesaj diyoruz.Kullanılan pencereye ait mesajlar yine o pencereye ait mesaj kuyruğuna eklenir ve işlenmeyi bekler.Her mesajın öncelik sırası vardır ve bu sıraya göre mesaj döngüsüne girerek işlem görürler.Mesela tarayıcı sayfasında, sayfa yenile tuşuna bastınız ve birden fikrinizi değiştirip aniden tarayıcınızı kapattınız.Burada kapatma olayının önceliği yenileme olayından yüksek olduğu için tarayıcınız kapatılır ve ilk önce istediğiniz yineleme işlemi yapılmaz. Aranızda ,“Olay-güdümlü programlamayı anlattın , windows un temeli olan multi-programming ve multi-threading de nedir? “ diyenleriniz olabilir.Kısaca multi-programming, tek işlemcide aynı anda(aslında aynı anda değil) birden fazla programın çalışabilmesi işidir.Programlar ihtiyacı olan işlemleri çok kısa zaman aralıklarında(time quantum) sırayla işlemciye yaptırırlar ve işlenmiş veriyi saklarlar. Diğer programlar işlemlerini yaptırırken sakladıkları işlenmiş veriyi bize sunarlar.Biz de depolanmış veriyi kullanırız , sanırız ki 10 tane program birden çalışıyor halbuki çalışan bir tane programdır ve işlenmiş veriyi kullandığımız 9 tane program vardır(Bir bilgisayarda 10 programın birden çalıştığı durumda) .”Multi-threading hakkında çok çeşitli şeyler var , şimdi anlatıp kafanızı karıştırmak istemiyorum” demeyi isterdim çünkü ben de bir şey bilmiyorum J Şu ana kadar size olay güdümlü programlamadan bahsettim.Belki fark etmişsinizdir , olay güdümlü programlamada üç ana yapı vardır.Olayın gerçekleşeceği görsel bir birim , olayı bildirecek bir mesaj ve mesajı işleyecek başka bir fonksiyon.Bunlar olay-güdümlü programlamada mutlaka olmalıdır.Windows ta bu yapılara karşılık gelenler : pencere ,mesaj ve mesaj işleme fonksiyonudur. İlk yapıyı , windows’ta penceremizi , üretebilmemiz için WinMain() isimli ana bir fonksiyona ihtiyacımız var.Bu fonksiyon C deki main()fonksiyonunun karşılığıdır , çalışacak her programın mutlaka bir ana fonksiyona ihtiyacı vardır.WinMain() içinde penceremizi üretip yine bu pencereden mesaj beklemeye başlayacağız.Sonrasında bu mesajları işleyecek WndProc() isimli fonksiyonu yazacağız.Bu fonksiyonun başında CALLBACK isimli bir niteleyici vardır ki bu fonksiyonun , bizim çağırmamızdan ziyade , ihtiyaç duyulduğu zaman işletim sistemi tarafından çağrılacağını belirtir.Bu da işletim sistemi tarafından olayların algılanması ve cevaplanması için gerekli olan bir durumdur. Şimdi sırasıyla ihtiyacımız olan fonksiyonları yazalım.Birincisi pencereyi üretecek ve mesaj döngüsünü başlatacak WinMain fonksiyonudur ; ikincisi üretilen mesajları işleyecek WndProc fonksiyonudur.Mesaj döngüsünü başlatmamız pencereyi çalıştırmakla eşdeğerdir. WinMain fonksiyonu programın girişidir ve her programda bir tane olur , diğer fonksiyon çağrımları bu ana fonksiyondan yapılır. Herhangi bir program birden fazla pencereye ihtiyaç duyabilir. Bu pencerelerin her birinin ayrı ayrı tiplerinin belirlenmesi, bu tiplerin işletim sistemine bildirilmesi ve en sonunda bu tiplerden pencere üretilmesi gerekir. Bütün bu dediklerimi WinMain fonksiyonu yapar. Aşağıda WinMain fonksiyonunun görevleri gösterilmiştir.
WinMain() 1-Pencere tipini belirle 2-Bu pencere tipini işletim sistemine kaydet 3-İşletim sistemi tarafından tipi bilinen pencereyi üret 4-Üretilen pencereyi göster 5-Bu pencereden mesaj almaya başla(pencereyi başlat, mesaj döngüsüne gir.) Bu dediklerimin hepsini sırayla yapacağız yalnız size kod göstermeden önce windows’ ta tanımlı bazı veri tiplerinden bahsetmek istiyorum, yazı boyunca göreceğiniz diğer değişkenleri yeri gelince açıklayacağım. Bu tipler Windows.h dosyasında tanımlıdır.Bu tanımlı değerleri her windows programımıza ,yazdığımız programın ilk satırına #include <windows.h> yazarak katarız.İlk windows programınızı yazarken çok fazla tanımadığınız kelime ile karşılaşacaksınız , bu tanımlı tipler aslında C – C++ programlama dillerinden bildiğiniz tiplerin hemen hemen karşılığıdır ama windowsa uyarlanmış oldukları için ilk başta biraz karmaşık gelebilir. HANDLE : 32 bitlik bir integer veriye karşılık gelir .Programımızda integer yerine kullanabiliriz. HWND : Handle window diye okunabilir , 8 bit değerindedir.Mesaj gönderdiğimiz pencereyi sistemdeki diğer pencerelerden ayırt eden bir değerdir.Handle değeri aslında bir pointer pointeri dir ve pencere bilgisini tutan hafızadaki adresi gösterir.Böylesine iki katmanlı bir yapının(pointer pointeri) kullanılmasının sebebi windowsun hafıza yönetimiyle alakalıdır.Windows kendi içinde yaptığı yer değişimlerini bizim kullanımımızdan soyutlamak için böylesine iki katmanlı bir yolu tercih eder.Ben HWND ile tanımlanmış pencereleri tanımlayan değişkenlere , kulp kelimesinin kısaltılmışı olan ‘k’ önekini koyacağım. Handle kelimesinin yerine kulp kelimesi Türkçe’de aynı anlama gelir.Ayrıca ilerde göreceğiniz gibi nesnelerin kulpundan tutup işlem yapacağız veya parametre olarak yollayacağız. BYTE : 8 bitlik değerdir. WORD :16 bitlik unsigned integer değerdir. DWORD : Double word diye okunabilir.32 bitlik unsigned integer değerindedir. LONG : 32 bit long değerindedir. BOOL : Integer değerdedir.False veya true değerini alabilir. LPSTR : Pointer to string .String bir değişkeni işaret eder.LP long pointer dan gelir , long pointer nitelemesi 16 bit programlama döneminden kalmıştır. LPCSTR : Const pointer to string .String bir değişkeni işaret eden sabit bir pointer dir. UINT : 32 bit unsigned integer değerindedir. Şimdi oyunlarda kullanacağımız pencereyi üretecek programımızı yazalım.Programda sıklıkla :: ve sonrasında windows ta tanımlı fonksiyonları kullandım , aslında bu iki tane :: üst üste noktalar kullanılmasa da olur . İki nokta ‘ :: ‘kullanılan fonksiyonların windowsun kendi ad uzayında (namespace) tanımlı olduklarını belirtmek için kullanılmıştır. #include<windows.h> // her windows programında olmalıdır. // Pencerenin tipinin belirlenmesi ,tanıtılması ve üretilmesini yapacak fonksiyon , Winmain //içinden çağrılacak.Doğru şekilde üretmişse true değerini döndürecek. bool PencereUret( HINSTANCE kProgram , int Goster) ; // Ana penceremizi sistemdeki diğer pencerelerden ayıran kulpumuz HWND kAnaPencere = 0 ; //Ana pencere kulpu,tutucusu : ilk değer olarak 0 atanır. // Penceremizi mesaj dongusune sokarak çalıştıracak, WinMain içinden çağrılacak int PencereCalistir(); // Mesajlarımızı işleyecek fonksiyon , işletim sistemi(Windows) tarafından çağrılır. //Gönderdiğimiz 4 parametreyi ilerde açıklayacağım LRESULT CALLBACK WndProc( HWND kPen , UINT msg ,WPARAM wParam, LPARAM lParam ); // Programın başladığı ana fonksiyonumuz int WINAPI WinMain(HINSTANCE kProgram , HINSTANCE kOncekiProgram ,LPSTR lpEmirSatiri ,int nGosterEmri ) { // Penceremiz üretilebiliyor mu? if(!PencereUret(kProgram , nGosterEmri )) { // message box ile ilgili açıklama aşağıda ::MessageBox(0,”pencere üretilemedi”,”Hata”,MB_OK); Return 0 ; } //Üretildiyse mesaj döngüsüne gir return PencereCalistir(); // WM_QUIT dönerse ana program düzgün şekilde kapatılmış demektir , şayet hata //oluşmuşsa 0 ile biter ki pencere mesaj döngüsüne girememiştir demektir. } bool PencereUret( HINSTANCE kProgram , int goster) { // 1-Ilk aşama olarak pencere tipimizi belirleyelim , window class “ c++ class “ıyla alakalı değildir, //pencere tipi anlamında kullanılır WNDCLASS penSinifi ; // pencere sinifi anlamında penSinifi.style = CS_VREDRAW | CS_HREDRAW ; //ClassStyle vertical or //horizontal redraw anlamına geliyor , penceremiz yatay veya dikey olarak //şekillendirildiğinde penceremizin içi yeniden çizilecek penSinifi.lpfnWndProc = WndProc ; // long pointer function Window procedure, //penceremizin mesajlarını işleyecek fonksiyonun belirtilmesi lazım. penSinifi.cbClsExtra = 0 ;//count byte class Extra anlamında ,class ın extradan veri //depolamasına gerek yok çünkü fazladan iş yapmayacak penSinifi.cbWndExtra = 0 ;// count byte window Extra anlamında ,pencerenin //extradan veri depolamasına gerek yok penSinifi.hInstance = kProgram ; // Handle instance , çalışan programı sistemde diğer // programlardan ayıran Handle değeri belirtilmelidir. penSinifi.hIcon = ::LoadIcon(0 , IDI_APPLICATION );// Handle ıcon ,Pencerenin en //üst soldaki imgesini yükle penSinifi.hCursor = ::LoadCursor(0 , IDI_ARROW );// metni gireceğiniz zaman //ekranda yazı yazılacak yerde beliren şekili yükle penSinifi.hbrBackground =(HBRUSH)::GetStockObject(WHITE_BRUSH) ; penSinifi.lpszMenuName = 0 ; // long pointer string zero , Penceremizin menüsü //olmayacak, sol üst köşede görünen , dosya , düzen ,görünüm vs.vs. penSinifi.lpszClassName = “SinifIsmi”;// sonu 0 ile sonlandırılmış bir string , //penceremizin ismi veriliyor. // 2-Penceremizin tipini belirledik , şimdi işletim sistemine bu pencere tipini kaydedelim if(!::RegisterClass(&penSinifi)) { ::MessageBox(0,“pencere işletim sistemine kaydedilemedi”,”Hata”,MB_OK); return false ; } //3- Penceremizi üretelim , global olarak tanımlı kAnaPencere değişkenine üretilen pencerenin // kulpunu atar kAnaPencere = ::CreateWindow( “SinifIsmi”, //Sınıf ismi “PencIsmi” , // pencere ismi WS_OVERLAPPEDWINDOW ,// statndart bir pencere //stiline sahip olsun CW_USEDEFAULT ,// açıklama aşağıda CW_USEDEFAULT , CW_USEDEFAULT , CW_USEDEFAULT , 0, 0, kProgram , 0 ) ; if( kAnaPencere == 0 ) { ::MessageBox(0,“Pencere üretilemedi ”,”Hata”,MB_OK); return false ; } //aşağıdaki fonksiyonda goster değeri pencerenin ilk defa nasıl gösterileceğini belirler , // bazı atamalar :SW_MINIMIZE , SW_MAXIMIZE ,show window minimize gibi.Bizim //göndereceğimiz SW_SHOW olacak ve bu fonksiyon pencereyi göstermekle yetinecek. ::ShowWindow(kAnaPencere , goster ) ; ::UpdateWindow(kAnaPencere); return true ; // herşey düzgün çalıştı ise true değerini döndürecek } // Bu fonksiyon ile alakalı açıklamalar aşağıda int PencereCalistir() { MSG mesaj ; ::ZeroMemory(&mesaj , sizeof(MSG)); while( ::GetMessage(&mesaj , 0 , 0 , 0 ) ) { ::TranslateMessage( &mesaj ); ::DispatchMessage( &mesaj ); } return mesaj.wParam ; } LRESULT CALLBACK WndProc( HWND penKulpu , UINT msj , WPARAM wParam , LPARAM lParam ) { switch(msj) { case WM_LBUTTONDOWN : ::MessageBox(0,”farenin sol tarafına bastınız ”,”Uyarı”,MB_OK); return 0 ; case WM_KEYDOWN : if(wParam == VK_ESCAPE ) ::DestroyWindow(kAnaPencere); return 0 ; case WM_DESTROY : ::PostQuitMessage(0); return 0 ; } return DefWindowProc( penKulpu,msj,wParam ,lParam); } Yukarıdaki programı word ile yazdım , kendisi otomatikman bazı düzenlemeler yaptı ,kopyala-yapıştır yaparsanız kod çalışmayabilir , o yüzden ekteki kodu kullanın . Şimdi kodu açıklayalım. Programımızda tam 4 tane fonksiyon var.Bunlardan iki tanesini yardımcı fonksiyon olarak kullandım. Bunlar pencerenin üretilmesi ve çalıştırılması işlerini toplu olarak yapan PencereUret ve PencereCalistir fonksiyonlarıdır. Bu fonksiyonların yaptıkları işler WinMain içine de yazılabilirdi , yapılan işleri daha iyi anlayabilmeniz için böyle bütün halinde yazdım.Zaten bu fonksiyonlar WinMain içinden çağrıldığı için yaptıkları işler WinMain fonksiyonu sınırları içinde kalıyor. #include<windows.h> dosyası windows programlamada kullanılan veri tiplerinin ve fonksiyonların çoğunun tanımlı olduğu dosyadır.Her windows programının başında mutlaka olmalıdır. Bundan sonraki kısım, WinMain fonksiyonuna kadar, programda kullanılacak fonksiyon prototiplerini tanımlıyor. Birincisi pencerenin tipinin belirlenmesi, tanıtılması ve üretilmesini yapacak, Winmain içinden çağrılacak olan PencereUret fonksiyonudur.Pencere doğru şekilde üretmişse true değerini döndürür .Bu fonksiyona parametre olarak programımızı sistemdeki diğer programlardan ayıran kProgram(program kulpu) değeri atanır.Ayrıca goster isimli parametre üretilen fonksiyonun ilk olarak nasıl gösterileceğini belirler. İkincisi ,penceremizi mesaj dongusune sokarak çalıştıracak, WinMain içinden çağrılacak olan PencereCalistir fonksiyonudur. Üçüncüsü , WndProc fonksiyonudur.Çalışması için pencerenin mesaj alması(Mouse tıklaması , tuşa basılması) gerekir.Mesaj aldığı zaman windows tarafından otomatik olarak çağrılır , bunu fonksiyonu tanımlayan CALLBACK nitelendiricisinden anlıyoruz.Başındaki LRESULT değeri ise fonksiyonun döneceği değeri gösterir ki C programlarındaki long değerindedir(4 byte uzunluğunda ).Bu LRESULT değeri bu fonksiyonun doğru olarak çalışıp çalışmadığını Windows a döndürür.Fonksiyonun parametre olarak aldığı değerler : 1-HWND tipinde kPen(Pencere Kulpu) değeridir.WndProc hangi pencerenin mesajlarını işleyeceğini bilmelidir. 2-MSG tipinde msj değeridir. Windows’ta tanımlı mesaj “MSG “ yapısı ,alınan her mesaj için işletim sistemi tarafından doldurulur ve pencerenin ilgili mesaj işleyicisine(WndProc tanımladık) gönderilir. typedef struct tagMSG { HWND hwnd; UINT message; WPARAM wParam; LPARAM lParam; DWORD time; POINT pt; } MSG; Bu yapıda : hwnd : Mesajın ait olduğu pencereyi belirtir. message : mesajın tipini belirtir. Mouse mesajı , veya tuşa basma mesajı gibi. wParam : 32 bitlik parametrik bir değerdir.Mesajın özelliklerini tam belirlemek için kullanılır.Mesela bu mesaj tipi tuş basma mesajıysa wParam değeri hangi tuşa basıldığını gösteren bir değer alır.Mesela wParam = 64 ise A harfine basıldı gibi… lParam : 32 bitlik parametrik bir değerdir.Mesajın özelliklerini tam belirlemek için wParam ile birlikte kullanılır. time : 32 bitlik (WORD değerde ) olayın olduğu zamanı tutan veri yapısıdır.. Pt : POINT yapısındadır . x ve y değerine sahip olup olayın olduğu anda mouse un hangi konumda olduğu bilgisini tutar.
Yandaki şekil windowsun x ve y değerlerinin artma yönlerini gösterir.Hesaplarınızda bunu göz önüne alın. Bir ekranın 640x 480 çözünürlükte olması demek x değerinin en fazla 640 ; y değerinin en fazla 480 olacağı anlamına gelir. 3 ve 4. parametreler yukarında açılandı. WndProc fonksiyonuna gönderilen wParam , lParam ve kPen parametreleri , ilerde açıklayacağımız şekilde DispatchMessage fonksiyonu tarafından doldurulur. Gelelim WinMain fonksiyonumuza : int WINAPI WinMain(HINSTANCE kProgram , HINSTANCE kOncekiProgram , LPSTR lpEmirSatiri , int nGosterEmri ) Başındaki int değeri , WinMain fonksiyonu mesaj döngüsüne girmişse WM_QUIT mesajının wParam değerini alır ki pencerenin doğru şekilde çalışıp sonlandırıldığını gösterir.Şayet mesaj döngüsüne girmemişse WinMain fonksiyonu sisteme 0 değerini döndürür.WINAPI ise fonksiyonun sistem tarafından standart biçimde çağrılacağını gösterir. windows.h dosyasında WINAPI şöyle tanımlıdır: #define WINAPI _stdcall Bu konu şu aşamada o kadar önemli değil. İlk yollanan parametre WinMain fonksiyonun sistemde çalıştırdığı programın tanımlayıcısıdır , bu değer sistem tarafından(işletim sistemi) verilir.2. parametre artık kullanılmamakta(32 bit Windows programlamada ). WinMain fonksiyonuna yollanan 3. parametre CommandLine (WinMain Dos tan çalıştırılırsa) dan gönderilen değerleri tutar.Tipi LPSTR dir ki “long pointer to string” anlamına gelir.32 bit windows programlamada *char tipine karşılık gelir.Son parametre ise sistem tarafından varsayılan olarak SW_SHOW olarak atanır.Ana fonksiyonun(WinMain) çalıştırdığı programda üretilen pencerenin ilk üretildiği zaman nasıl gösterileceğini tanımlar. WinMain fonksiyonunun 5 görevi olduğunu söylemiştik. 1-Pencere tipini belirle \ 2-Bu pencere tipini işletim sistemine kaydet \ 3-İşletim sistemi tarafından tipi bilinen pencereyi üret / Bu dördünü PencereUret yapar 4-Üretilen pencereyi göster / 5-Bu pencereden mesaj almaya başla(pencereyi başlat, mesaj döngüsüne gir.) || Bunu Pencere Calistir fonksiyonu yapar. Bu iki fonksiyonu çağırdığımız zaman WinMain fonksiyonun görevleri tamamlanmış oluyor. if(!PencereUret(kProgram , nGosterEmri ))// if sorgusunun içinde ! var. { ::MessageBox(0,”pencere üretilemedi”,”Hata”,MB_OK); return 0 ; } PencereUret fonksiyonu çalışamadıysa ,pencere üretememişse, false değeri döndürür , bu durumdan haberdar olmak için , yanlışlığı MessageBox ile bize bildiren hata mesajı gösterdik. MessageBox şöyle tanımlanmıştır: <span style="font-size: 8pt; font-family: Verdana">int <span style="background: #316ac5 0% 50%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">MessageBox</span>( HWND <em>hWnd</em>,</span> <span style="font-size: 8pt; font-family: Verdana"> LPCTSTR <em>lpText</em>,</span> <span style="font-size: 8pt; font-family: Verdana"> LPCTSTR <em>lpCaption</em>,</span> <span style="font-size: 8pt; font-family: Verdana"> UINT <em>uType</em></span> <span style="font-size: 8pt; font-family: Verdana">);</span> İlk parametre hata mesajının hangi pencereye ait olduğunu gösterir , biz 0 yazdık , herhangi bir pencereye ait olmasını istemedik. 2.parametre mesaj kutusunda gösterilecek olan hata yazısını alır.Long pointer to string tipindedir. 3.parametre mesaj kutusunun tepesinde gösterilecek yazıyı tutar Son parametre ise mesaj kutusunun davranışını belirler.Biz MB_OK kullandık ki Message Box OK anlamındadır ve OK butonuna sahip bir mesaj kutusu gösterir. Gördüğünüz gibi PencereUret fonksiyonu pencere üretememişse pencere üretilemedi mesajını gösteren MessageBox görünecek , sonrasında WinMain fonksiyonunu bitiren 0 değeri geri döndürülecektir. WinMain içinden çağrılan PencereCalistir fonksiyonun döneceği değere göre WinMain sonlanır.PencereCalistir fonksiyonu 0 ya da WM_QUIT mesajının wParam değerini geri döndürür. PencereUret fonksiyonunda kullanılan windows ta tanımlı fonksiyonlar ve yapıların açıklamaları : Bu fonksiyonda yukarıda söylediğimiz 4 görev yapılır: 1-Pencere tipinin tanımlanması.Windows ta pencere tipi aşağıdaki yapı doldurularak tanımlanır.WNDCLASS yapısı aşağıdaki gibidir. <span style="font-size: 8pt; font-family: Verdana">typedef struct {</span><span style="font-size: 8pt; font-family: Verdana"><span> </span>UINT style; </span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>WNDPROC lpfnWndProc;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>int cbClsExtra;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>int cbWndExtra;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>HINSTANCE hInstance;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>HICON hIcon;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>HCURSOR hCursor;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>HBRUSH hbrBackground;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>LPCTSTR lpszMenuName;</span> <span style="font-size: 8pt; font-family: Verdana"><span> </span>LPCTSTR lpszClassName;</span> }WNDCLASS, *PWNDCLASS; Yukarıdaki yapı ile ilgili açıklamayı programda yaptım.Yalnızca HBURSH hbrBackground değerini açıklamamıştım. penSinifi.hbrBackground =(HBRUSH)::GetStockObject(WHITE_BRUSH) ; burada yaptığımız iş ,tipi tanımlanan pencerenin arka planını boyayacak fırçayı belirlemekten ibaret .Bunu yaparken windows ta tanımlanmış fırça(brush) nesnelerini kulanıyoruz.İstediğimiz fırça nesnesini üreten ise GetStockObject isimli windows ta tanımlanmış bir fonksiyondur. Bu fonksiyona istediğimiz türde fırçanın ismini parametre olarak yolluyoruz .O da bize istediğimiz fırçayı üretip geri döndürüyor, üretilen fırça bizim penceremizin arka planını boyuyor. PencereUret fonksiyonunda yaptığımız 2. görev ise tanımladığımız pencere tipini windows a kaydetmektir.Bu işi yine windows ta tanımlanmış olan RegisterWİndow fonksiyonu yapar parametre olarak WNDCLASS tipinde bir yapı alır.tanımladığımız yapıyı bu fonksiyona yolluyoruz. İf(!::RegisterClass(&penSinifi)) { ::MessageBox(0,“pencere isletim sistemine kaydedilemedi”,”Hata olustu”,MB_OK); return false ; } Gördüğünüz gibi pencere kaydedilememişse WinMain fonksiyonunu bitiren false(0) değeri geri döndürülür.Durum message box ile bize bildirilir. PencereUret fonksiyonunun 3. görevi pencerenin üretilmesidir. Bu görev için windowsta tanımlı CreateWindow fonksiyonu kullanılır.Bu fonksiyon başarılı lursa bize üretilen pncereyi sistemde bulunan diğer pencerelerden ayırmamıza yarayacak WNDHANDLE tipide kAnaPencere değerini döndürür. kAnaPencere = ::CreateWindow( “SinifIsmi”, //Sınıf ismi “PencIsmi” , // pencere ismi WS_OVERLAPPEDWINDOW , // varsayılan değeri kullan ,Create Window Use Default , CW_USEDEFAULT ,//pencerenin gösterilmeye başlanacağı x değeri. CW_USEDEFAULT , // pencerenin gösterilmeye başlanacağı y değeri. CW_USEDEFAULT ,//pencerenin genişliği CW_USEDEFAULT ,//pencerenin yüksekliği 0, // üretilecek penceremiz herhangi bir pencerenin alt pencersi olmayacak 0, //penceremizin menüsü olmayacak , dosya, düzen , görünüm …. kProgram , //penceremiz bu programa ailt olacak 0 // Kendi tanımladığımız veriyi de burada pencereye ekleyebiliriz ama //bunlara gerenk yok , olmadığı için de zaten 0 yolluyoruz. ) ; CreateWindow fonksiyonu asağıdaki parametreleri alır.Yukarıdaki kısa açıklamaları aşağıdaki yapıya bakarak inceleyin. [code:1] <span style="font-size: 8pt; font-family: Verdana">HWND <span style="background: #316ac5 0% 50%; color: white; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial">CreateWindow</span>(
LPCTSTR <em>lpClassName</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> LPCTSTR <em>lpWindowName</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> DWORD <em>dwStyle</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> int <em>x</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> int <em>y</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> int <em>nWidth</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> int <em>nHeight</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> HWND <em>hWndParent</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> HMENU <em>hMenu</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> HINSTANCE <em>hInstance</em>,</span>
<span style="font-size: 8pt; font-family: Verdana"> LPVOID <em>lpParam</em></span>
<span style="font-size: 8pt; font-family: Verdana">);</span>
<span style="font-size: 12pt; color: #231f20; font-family: CourierNew">PencereUret fonksiyonunun 4. görevi<span> </span>pencerenin gösterilmesidir.</span>
<span style="font-size: 12pt; color: #231f20; font-family: CourierNew">Bu da ShowWindow fonksiyonu ile yapılır.Bu fonksiyonun ilk parametresi gösterilecek pencerenin kulpudur.</span>
<span style="font-size: 12pt; color: #231f20; font-family: CourierNew">2. parametre ise pencerenin ne şekilde gösterileceğidir , mesela SW_MINIMIZE </span>
<span style="font-size: 12pt; color: #231f20; font-family: CourierNew">parametresi yollanırsa pencere kendini gösterir göstermez simge durumuna <span> </span>küçültülür.</span>
<span style="font-size: 12pt; color: #231f20; font-family: CourierNew">Şimdi mesaj döngüsüne gireceğimiz fonksiyonu yazalım.</span>
<span style="font-size: 12pt; color: #231f20; font-family: CourierNew">PencereCalistir fonksiyonu parametre almaz.</span>
Bu fonksiyonda MSG yapısında bir değişken tanımlanır.
MSG msj ;
ZeroMemory(&msj,sizeof(MSG)) ; [/code:1] Bu mesaj yapısı yukarıda inceleyebilirsiniz.Bu değişkeni az sonra kullanacağız yalnız kullanmadan evvel bütün bitlerini sıfırlamak gerekir.ZeroMemory bunu yapar , msj değişkeninin adresini ve bu adresten itibaren ne kadar bitin sıfırlanacağı sizeof(MSG) ile gönderilir.Bu da MSG yapısı kaç byte ise o kadar byte temizle demektir. Flag yapılarında tanımlayıcı bilgiler genellikle bitlerden elde edilir.Örnek olarak yukarıda yaptığımız CS_VREDRAW | CS_HREDRAW ı gösterebiliriz.Bunlardan CS_VREDRAW 00000001 ile tanımlı olsun diyelim , aslında bu değerler 4Byte uzunluğunda bir flag dir , kolaylık olsun diye bir byte lık değer aldım.. CS_HREDRAW ise 00000010 ile tanımlı olsun. CS_VREDRAW | CS_HREDRAW yaptığımız zaman bu iki değeri birleştirmiş ve işimizi gören bir flag elde etmiş oluruz.Bu iki değer birleştiği zaman 00000011 değerini alır. Birleştirme işi or ile yapılır ve operator olarak | işaretini kulanır. Gördüğünüz gibi bu bit değerlerinden biri temizlenmese programın yanlış çalışmasına sebep olacak.O yüzden bir fonksiyona flag yollarken mutlaka bütün bitlerini sıfırlayın. while( ::GetMessage(&mesaj , 0 , 0 , 0 ) ) de GetMessage fonksiyonu mesaj kuyruğundan bir mesaj alıncaya kadar bekler , gelince de mesaj yapısını doldurur.Bu fonksiyon WM_QUIT mesajı gelinceye kadar çalışır , bu mesaj geldiği zaman 0 değerini döndürür ve while döngüsünden çıkar. GetMessage fonksiyonunun aldığı mesaj TranslateMessage fonksiyonu tarafından karakter mesajlarına döndürülür ,bu virtual key message yapısından bizim anlayacağımız karakter mesajlarına dönme işidir .Böyle bir şey yapılmasa idi her dile ayrı bir program yazmak gerekirdi.Klavyeyi programdan soyutlamak için böyle bir yola girilmiş olmalı.TranslateMessage fonksiyonu tarafından çevrilen karakterler en son olarak DispatchMessage fonksiyonu yoluyla mesajı işleyecek fonksiyona(WndProc) yollanır. PencereCalistir fonksiyonu en son olarak msj.wParam değerini (tabii ki WM_QUIT mesajını ) döndürür ki bu da WinMain fonksiyonuna yollanır ve programı düzgün şekilde bitirir. Gelelim en son ve en zor fonksiyonumuza dermişim. WndProc fonksiyonuyla alakalı bazı açıklamaları(mesela parametrelerini ) yukarıda yaptım.O yüzden doğrudan fonksiyonu anlatıyorum. LRESULT CALLBACK WndProc( HWND penKulpu , UINT msj , WPARAM wParam , LPARAM lParam ) Bu parametrelerden msj yapısına switch yapıyoruz çünkü mesaja göre işlem yapacağız. switch(msj) { Case WM_LBUTTONDOWN : // Window Message Left Buton Down demek // Farenin sol tuşuna basıldıysa bu durum aktif olacak ve nssage box ı gösterecek ::MessageBox(0,”farenin sol tarafına bastınız ”,”Uyarı”,MB_OK); return 0 ;// return 0 yaparak WndProc fonksiyonu mesajı işlemiş olarak sisteme döndü. } case WM_KEYDOWN :// Window Message Key Down , tuşa basıldı mı? if(wParam == VK_ESCAPE ) //Tuşa baıldıysa wParam değeri tuşun değerini tutar ::DestroyWindow(kAnaPencere); // şayet esc tuşu ise pencereyi //kapat.DestroyWindow hangi pencereyi kapatacağını gönderilen parametre ile anlar. return 0 ; // Mesaj doğru şekilde işlenildi, WndProc bitsin case WM_DESTROY : // Sağ üstteki x işaretine basıldıysa ::PostQuitMessage(0); // WM_QUIT mesajını yolla , GetMessage bu mesajı alınca //döngüden çıkar , bu da programı bitirir. return 0 ; // mesaj doğru şekilde işlendi. WndProc fonksiyonun en son yaptığı iş kendisinin işlemediği mesajları sistemde tanımlı genel bir mesaj işleticiye göndermektir.Gördüğünüz gibi biz pencerede olabilecek bütün mesajları işleyen kodları yazmadık , bazıları varsayılan olarak işlenmesini istedik.Kendimizin işlemeyeceği mesajları DefWindowProc( penKulpu,msj,wParam ,lParam); Fonksiyonu ile işlettiririz.Bu fonksiyon WndProc ile aynı parametreleri alır.Bu fonksiyondan alınan değer return ile geri döndürülerek WndProc fonksiyonu bitirilir. Bu makale burada bitiyor.İnşallah yardımcı olmuşumdur.Bazı yerleri 3 defa anlattığımdan makalenin sayfa sayısı fazla oldu.Tekrarlı anlatımı konuyu daha iyi anlayabilmeniz için tercih ettim..Makale size başta çok karmaşık gelebilir.Bu kullanılan Api lerin(mesela ShowWindow) ve tanımlanmış değerlerin (mesela HWND) çokluğundan kaynaklanıyor.Zamanla alışacak ve bütün bu kodların çoğunun her windows programında değişmeden kaldığını görüp şaşıracaksınız.Bu makale ile ilgili kodu ayrı olarak yolluyorum.Kodun nasıl çalıştırılacağını ise isterseniz ayrıntılı olarak anlatırım.Yazılım geliştirme ortamı olarak VC6++ kullandım.Boş bir Win32 projesi yaratın ve kodları bu projede yarattığınız bir .cpp uzantılı dosyaya kaydedin.Derleyip çalıştırın. 
Çalışan kod yukarıda ki gibi olacak.Alıştırmalar kolay olsa da yapmadan geçmeyin. Kendiniz de bir sürü alıştırma yapabilirsiniz.Yanınızda MSDN bulunması büyük fayda sağlar.Tanımlı fonksiyonları ve gönderilen parametreleri MSDN den araştırıp inceleyin. Alıştırmalar : Soru 1 : Pencerenin üstünde farenin sag tarafına basınca program mesaj kutusu ile bize “farenin sag tarafına bastınız “ mesajını versin.İpucu :WM_RBUTTONDOWN mesajını işleyin. Soru 2: Pencere ilk gösterildiği zaman tam ekran olsun.İpucu SW_MAXIMIZE parametresini ilgili fonksiyona yollayın. Soru 3 : WS_OVERLAPPEDWINDOW ne anlama geliyor ?Hangi Windows Style parametrelerin birleşimi olduğunu MSDN ye bakarak öğrenin. Soru 4 – Pencere boyutlarını ayarlayın , daha küçük bir pencere olsun. Soru 5 –Pencere istediğimiz yerde gösterilsin ,CreateWindow fonksiyonuna yollanan x ve y değerleriyle oynayın. Yazan Üye: Alparslan
|