BG MVC Model View Controller eğitim serisi yayında...

Ana page > Programlama > Windows API Programlama > Subclassing

Subclassing

Kontrol nedir?

Kontrol bir programın ana penceresi içinde veya ana pencere içinde yer alan diğer bir kontrol penceresi içinde oluşturulan bir penceredir.

Programın ana penceresi de dahil olmak üzere tüm kontroller bir pencereden oluşur.

Kontroller CreateWindowEx() fonksiyonu ile oluşturulur.

Detaylı bilgi için: Kontroller

Kontrollerin haberleşme yöntemi

Bir ana pencere ve bu pencere içinde oluşturulan bir butondan oluşan programın main.c dosyası içeriği aşağıdaki şekilde olacaktır:


#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

#include <tchar.h>
#include <windows.h>

#define IDC_BUTTON 1001

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

HWND hwndButton;

int WINAPI WinMain(HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl)) return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           _T("Ana pencere"),       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           400,                 /* The programs width */
           300,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}

/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
         case WM_CREATE:
         {
              hwndButton = CreateWindowEx(0, "BUTTON", "Button",
                                 WS_CHILD | WS_VISIBLE, 140, 100, 120, 25,
                                 hwnd, (HMENU) IDC_BUTTON, NULL, NULL);
         }
         break;

         case WM_COMMAND:
         {
              if (HIWORD(wParam) == BN_CLICKED) {
                  switch(LOWORD(wParam)) {
                    case IDC_BUTTON:
                         MessageBox(NULL, "Button tıklandı.", "Buton tıklama", MB_OK);
                         break;
                  }
              }
         }
         break;

         case WM_DESTROY:
              PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
              break;

         default:                      /* for messages that we don't deal with */
              return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

Bir Windows API program oluşturulurken önce WinMain() fonksiyonu çağrılır. Bu fonksiyon içinde program ana penceresi oluşturulmadan önce WNDCLASSEX veri yapısından wincl değişkeni oluşturulur.


WNDCLASSEX wincl;

szClassName değeri wincl değişkeninin lpszClassName değerine atanır. Böylece oluşturulan WNDCLASSEX veri yapı değerleri program ana penceresi ile ilişkilendirilir.


wincl.lpszClassName = szClassName;

Ana program penceresi için düzenlenen WindowProcedure fonksiyonu wincl değişkeninin lpfnWndProc değerine atanır. Böylece oluşturulan WindowProcedure fonksiyonu program ana penceresi ile ilişkilendirilir.


wincl.lpfnWndProc = WindowProcedure;

RegisterClassEx() fonksiyonu ile wincl değişkenini kaydederek, CreateWindowEx() fonksiyonlarında kullanılmasını sağlar.


if (!RegisterClassEx (&wincl)) return 0;

Programın çalışması devam ettiği sürece tüm mesaj işlemleri WindowProcedure adlı program ana pencere fonksiyonu içinde işlem görür.

Program ana penceresi içinde bir button oluşturduğumuzda, ana pencere bu butona SendMessage fonksiyonunu kullanarak mesaj gönderebilir.


LRESULT SendMessage(
  HWND   hWnd,
  UINT   Msg,
  WPARAM wParam,
  LPARAM lParam
);

Kullanıcı buton ile herhangi bir işlem gerçekleştirdiğinde (tıklama veya fareyi üzerine getirmek gibi), bu işlemlerle ilgili bilgiler buton tarafından program ana fonksiyonuna WM_COMMAND veya WM_NOTIFY mesajı olarak gönderilir. Ana fonksiyon her iki mesajın taşıdığı wParam ve lParam değerlerine uygun olarak işlem yapar.

Örneğin, ana pencere SendMessage fonksiyonu ile BM_CLICK mesajı gönderildiğinde kullanıcı butona tıklamış gibi işlem yapılır. Bu mesaj butonun WM_LBUTTONDOWN ve WM_LBUTTONUP mesajlarını almasını ve butonun içinde bulunduğu ana pencere fonksiyonunun (WindowProcedure) WM_COMMAND mesajı yoluyla BN_CLICKED bildirim kodunu almasını sağlar.

Detaylı bilgi için: Mesaj işlemleri

Sınıf (Class) hakkında

Pencere sınıfı, sistemin pencere oluşturmak için şablon olarak kullandığı değişkenlerden oluşan WNDCLASSEX adlı bir yapıdır. Her pencere, bir pencere sınıfının üyesidir.


typedef struct tagWNDCLASSEXA {
  UINT      cbSize;          // Bu değişkene sizeof(WNDCLASSEX) değeri atanmalıdır (GetClassInfoEx çağrısından önce).
  UINT      style;           // Pencere sınıf stillerinden bir veya daha fazlası atanabilir. 
  WNDPROC   lpfnWndProc;     // Pencere fonksiyonunu gösteren bir işaretçidir. CallWindowProc() fonksiyonu ile çağrılır.
  int       cbClsExtra;      // Bu sınıf yapısı için ayrılacak ekstra byte sayısını gösterir. Sistem bu değere sıfır değeri verir.
  int       cbWndExtra;      // Bu pencere Instance değeri için ayrılacak ekstra byte sayısını gösterir. Sistem bu değere sıfır değeri verir.
  HINSTANCE hInstance;       // Bu sınıfın pencere fonksiyonunu içeren Instance'a ait handle değeridir.
  HICON     hIcon;           // Sınıf ikon handle değeridir. Eğer bu değer NULL ise, sistem ön tanımlı ikonu atar.
  HCURSOR   hCursor;         // Sınıf imleç handle değeridir. Eğer bu değer NULL ise, program mouse bu pencereye girdiğinde imleç şeklini belirlemelidir.
  HBRUSH    hbrBackground;   // Sınıf arka plan rengini gösteren handle değeridir.
  LPCSTR    lpszMenuName;    // Sınıf menüsünü gösteren bir işaretçidir. Eğer bu değer NULL ise, sınıfa bir menü atanmaz.
  LPCSTR    lpszClassName;   // Pencere sınıf adını gösterir. Sistem tanımlı kontrol sınıfı veya RegisterClassEx() fonksiyonu ile kullanıcı tarafından tanımlanmış bir sınıf olabilir. Maksimum uzunluğu 256 karakterdir.
  HICON     hIconSm;         // Sınıf ikon handle değeridir. Eğer bu değer NULL ise, sistem hIcon ile gösterilen ikon değerini küçülterek kullanır.
} WNDCLASSEXA, *PWNDCLASSEXA, *NPWNDCLASSEXA, *LPWNDCLASSEXA;

Her pencere sınıfına ait bir pencere fonksiyonu vardır. Bu fonksiyon aynı sınıf türünden tanımlanmış tüm pencereler tarafından paylaşılır. Belirli bir sınıfa ait ve ortaklaşa kullanılan bu fonksiyon, aynı sınıftan tanımlanmış (örneğin "BUTTON" sınıfı) tüm pencerelerin mesajlarına işlem yapar. Böylece, aynı sınıftan tanımlı kontrol pencerelerinin çalışmasını ve görünnüşünü belirler. Örneğin, bir programda "BUTTON" sınıfı ile 3 farklı buton tanımladığımızda, butonların tamamı aynı fonksiyonu ortaklaşa kullanır.


hwndButton01 = CreateWindowEx(0, "BUTTON", "Button",
				 WS_CHILD | WS_VISIBLE, 10, 10, 120, 25,
				 hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);

hwndButton02 = CreateWindowEx(0, "BUTTON", "Button",
				 WS_CHILD | WS_VISIBLE, 10, 40, 120, 25,
				 hwnd, (HMENU) IDC_BUTTON02, NULL, NULL);

hwndButton03 = CreateWindowEx(0, "BUTTON", "Button",
				 WS_CHILD | WS_VISIBLE, 10, 70, 120, 25,
				 hwnd, (HMENU) IDC_BUTTON03, NULL, NULL);

Programda sistem tarafından kaydedilen Button, ComboBox, Edit vb. sistem sınıfları kullanmak istediğimizde, bu sınıfları doğrudan kullanmak mümkündür. Ancak, kendi tanımlayacağımız bir sınıf kullanmak istediğimizde, program o sınıfın bir penceresini oluşturmadan önce, RegisterClassEx() fonksiyonu ile pencere sınıfını kaydetmelidir. Bir pencere sınıfının kaydedildiğinde, bir pencere fonksiyonu, sınıf stilleri ve diğer sınıf değişkenler bir sınıf adıyla ilişkilendirir. CreateWindowEx() fonksiyonu kullanıldığında belirtilen sınıf adı, yeni oluşturulan kontrol penceresinin o sınıfa ait özeliklere sahip olmasını sağlar.

Sınıf fonksiyonları (Procedure) hakkında

Her pencerenin kendisine ait bir fonksiyonu vardır. Bu fonksiyon aynı sınıftan tanımlanmış olan tüm kontrollerin pencerelerine gönderilen tüm mesajlara işlem yapar. Bir pencerenin görünüm ve davranışları, pencere fonksiyonunun bu mesajlara verdiği cevaplara bağlıdır.

Her pencere belirli bir pencere sınıfının üyesidir. Pencere sınıfı, tek bir pencerenin mesajlara işlem yapmak için kullandığı varsayılan pencere fonksiyonunu belirler. Aynı sınıfa ait tüm pencereler aynı varsayılan pencere fonksiyonunu kullanır. Örneğin, sistem BUTTON sınıfı için bir pencere fonksiyonu tanımlar; tüm button pencereleri bu pencere fonksiyonunu kullanır.

Bir uygulama genellikle en az bir yeni pencere sınıfı ve onunla ilişkili pencere fonksiyonunu kaydeder. Bir sınıfı kaydettikten sonra, uygulama o sınıftan, tümü aynı pencere yordamını kullanan, birçok pencere oluşturabilir.

Pencere fonksiyonu dört parametreli olup signed bir değer döndüren bir fonksiyondur. Parametreler bir pencere handle değeri, bir UINT mesaj tanımlayıcısı ile WPARAM ve LPARAM veri türleriyle bildirilen iki mesaj parametresinden oluşur.

WPARAM parametresi unsigned int bir değer ve LPARAM parametresi ise long int bir değer olup windef.h başlık dosyasında tanımlanmıştır. Mesaj parametrelerinin sisteme bağlı olarak değişen boyutlarına göre içerdiği birden fazla değer LOWORD(), HIWORD(), LOBYTE() ve HIBYTE() fonksiyonları ile okunur.

Aşağıdaki fonksiyon yeni bir WinAPI programı çalıştırdığımızda, program ana penceresi oluşturulmadan önce, yeni oluşturulan bir sınıf yoluyla ana pencereye bağlanır.


/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
         case WM_DESTROY:
              PostQuitMessage (0);    /* send a WM_QUIT to the message queue */
              break;

         default:                     /* for messages that we don't deal with */
              return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

Programcı bu fonksiyon içinde işlem yapılacak mesajları ekleyebilir (WM_CREATE, WM_COMMAND gibi). Bu fonksiyon içinde işlem yapılmamış mesajlar için, DefWindowProc() fonksiyonu ön tanımlı pencere fonksiyonunu çağırır. Her mesaja işlem yapılmasını sağlayan bu fonksiyon ana pencere fonksiyonuna geçirilen parametrelerle çağrılır.


LRESULT LRESULT DefWindowProcA(
  HWND   hWnd,
  UINT   Msg,
  WPARAM wParam,
  LPARAM lParam
);

Subclassing hakkında

Bir uygulama bir pencere oluşturduğunda, sistem, pencere için mesajlara pencere prosedürünün adresi de dahil olmak üzere, pencereye özgü bilgileri depolamak için bir bellek bloğu tahsis eder. Sistem pencereye bir mesaj iletmesi gerektiğinde, pencere fonksiyonunun adresi için pencereye özgü bilgileri arar ve mesajı bu fonksiyona iletir.

Subclassing işlemi belirli bir pencereye gönderilen mesajlara ilgili pencere işlem yapmadan önce, araya girerek mesajlara işlem yapma olanağı sağlar. Bir pencereye subclassing işlemi uyguladığımızda, pencerenin yaptığı işlemleri artırabilir, değiştirebilir veya izleyebiliriz. Bir WinAPI programında Button, ComboBox, Edit gibi sisteme ait global sınıflara ait pencerelere subclassing işlemi uygulayarak bazı özelliklerini kolaylıkla değiştirebiliriz.

Program, pencerenin orijinal pencere fonksiyonunun adresini subclassing fonksiyonu adresiyle değiştirerek subclassing işlemi uygular. Böylece, subclassing fonksiyonu pencereye gönderilen mesajları alır.

Subclassing fonksiyonu bir mesaj aldıktan sonra üç işlem gerçekleştirebilir:

1. Mesajı işlem yapmadan doğrudan orijinal pencere fonksiyonuna geçirebilir,

2. Mesajı değiştirerek orijinal pencere fonksiyonuna geçirebilir,

3. Mesaja işlem yapar ve orijinal pencere fonksiyonuna geçirmez.

Eğer subclass fonksiyonu bir mesaja işlem yaparsa, bu işlemi mesajı orjinal pencere fonksiyonuna geçirmeden önce, sonra veya hem önce hem de sonra yapabilir.

Bir pencere için subclassing işlemi uyguladığımızda, pencere için yeni bir fonksiyon oluşturmuş oluruz.

Subclassing işlemini kullanma nedeni

Bir WinAPI programında ana pencere altında bir button oluşturduğumuzda, bu butonun görünümünü düzenlemek istersek, ana pencere içinde 3 farklı mesaj yoluyla bu işlemi gerçekleştirebiliriz:

1. WM_NOTIFY mesajı ve lParam parametresi yoluyla erişilen NM_CUSTOMDRAW kodunu kullanarak, buton penceresini tanımlayan hwndFrom veya idFrom yoluyla,


case WM_NOTIFY:
{
	LPNMHDR lpnmhdr = (LPNMHDR)lParam;

	switch(lpnmhdr->code) {
	  case NM_CUSTOMDRAW:
		 LPNMCUSTOMDRAW lpnmcd = (LPNMCUSTOMDRAW)lpnmhdr;

		 if (lpnmhdr->hwndFrom == hwndButton) {
		 	 .
			 .
			 .
			 return CDRF_SKIPDEFAULT;
		 }
	}
}
break;	  

2. Butonu BS_OWNERDRAW özelliği ile tanımlayarak, WM_DRAWITEM mesajı ve lParam parametresi yoluyla erişilen DRAWITEMSTRUCT yapısını kullanarak, buton penceresi ID'sini gösteren wParam yoluyla,


case WM_DRAWITEM:
	 switch ((UINT)wParam) {
		case IDC_BUTTON:
		{
			LPDRAWITEMSTRUCT lpdis = (DRAWITEMSTRUCT*)lParam;
			.
			.
			.
			return TRUE;
		}
		break;
	 }
     break;	

3. Butonu BS_OWNERDRAW özelliği ile tanımlayarak, WM_CTLCOLORBTN mesajı ve buton penceresini tanımlayan lParam parametresi ile, wParam parametresi yoluyla erişilen HDC yoluyla.


case WM_CTLCOLORBTN: // BS_OWNERDRAW olması gerekiyor.
 {
   if ((HWND)lParam==hwndButton) {
		RECT rc;

		SetTextColor((HDC)wParam, RGB(236, 236, 236));
		SetBkMode((HDC)wParam, TRANSPARENT);
		GetClientRect((HWND)lParam, &rc);
		DrawText((HDC)wParam, "Button08", -1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

		return (LRESULT)CreateSolidBrush(RGB(40, 90, 155));
   }
}
break;

Bu yöntemlerin hepsini içeren örneğe burada ulaşabilirsiniz: Button

WM_NOTIFY, WM_DRAWITEM ve WM_CTLCOLORBTN mesajlarının hepsi butonun içinde bulunduğu ana pencereye gönderildiğinden ve burada kullanılan her 3 yöntemde de, ana pencerede işlem yapılan mesajların parametre değerleri (wParam ve lParam) içinde buton penceresini tanımlayan bir değer yer aldığından buton için işlem yapmak mümkündür.

Ancak, WM_PAINT ve WM_LBUTTONDOWN gibi mesajları kullanmak istediğimizde, ana pencerede parametre değerleri (wParam ve lParam) içinde buton penceresini tanımlayan bir değer yer almadığından, bu mesajlara işlemlerin buton sınıfına ait standart pencere fonksiyonunun arasına girerek mesajlara işlem yapan bir sistem oluşturulması gerekir. Bu işlem subclassing yöntemi ile yapılabilir.

Subclassing işlemi bir kontrolün orjinal pencere fonksiyonunda yer alan işlemlerinin değiştirilmesi veya yeni işlemler eklenmesi ile yapılır.

Subclassing işlemi bir kontrol oluşturulduktan sonra, her kontrol için ayrı ayrı olmak üzere, tanımlanmalıdır. Ancak, subclassing işlemi yapılan tüm kontroller aynı ortak pencere fonksiyonunu kullanabilir.

Subclassing işleminde kullanılan fonksiyonlar

ComCtl32.dll dosyasının 6.sürümü, subclassing işleminde dört fonksiyon kullanır:

SetWindowSubclass

BOOL SetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

Bu fonksiyon bir pencere subclassing işlemi uygulamak için kullanılır. Her bir subclassing işlemi, pfnSubclass ve uIdSubclass değerleri ile benzersiz bir şekilde tanımlanır. Birden fazla pencere pfnSubclass parametresi ile tanımlanan fonksiyonu kullanabilir.


LRESULT CALLBACK SubclassingProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

Aşağıda örnek bir subclassing fonksiyonu yer almaktadır:


LRESULT CALLBACK ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (message) {
     case WM_LBUTTONDOWN:
     {
          char cdizi[50];
          sprintf(cdizi, "WM_LBUTTONDOWN: xPos: %d yPos: %d wParam: %d", LOWORD(lParam), HIWORD(lParam), wParam);
          SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM) cdizi);

          return 0; // Üst pencerenin (WindowProcedure) işlem yapmaması için
     }
     break;
  }

  return DefSubclassProc(hwnd, message, wParam, lParam);
}

Aşağıdaki kod satırı ile bir button penceresi için subclassing işlemi uygulanır:


SetWindowSubclass(hwndButton, ButtonProc, 0, 0);

GetWindowSubclass

BOOL GetWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass, DWORD_PTR *pdwRefData);

Bu fonksiyon, bir subclassing işlemi hakkında bilgi alır.

RemoveWindowSubclass

BOOL RemoveWindowSubclass (HWND hWnd, SUBCLASSPROC pfnSubclass, UINT_PTR uIdSubclass);

Bu fonksiyon, bir subclassing işlemini sona erdirir.

DefSubclassProc

LRESULT DefSubclassProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

Bu fonksiyon, bir subclassing zincirindeki bir sonraki fonksiyonu çağırır.

Button Subclassing örneği

Aşağıdaki main.c ile oluşturulan proje ile, ana pencerede yer alan bir buton için subclassing işlemi uygulanarak, subclassing fonksiyonu içinde WM_PAINT mesajı içinde buton görünümü özelleştirilerek çizilir.

Projeye resource.rc dosyası yoluyla manifest.xml dosyası dahil edilmelidir.

Projenin tamamına buradan ulaşabilirsiniz.


#if defined(UNICODE) && !defined(_UNICODE)
    #define _UNICODE
#elif defined(_UNICODE) && !defined(UNICODE)
    #define UNICODE
#endif

#define _WIN32_WINNT 0x0501
#define _WIN32_IE 0x0501

#include <tchar.h>
#include <windows.h>
#include <commctrl.h>

#define IDC_BUTTON 1001

/*  Declare Windows procedure  */
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
/* Subclassing fonksiyonu */
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData);

/*  Make the class name into a global variable  */
TCHAR szClassName[ ] = _T("CodeBlocksWindowsApp");

HWND hwndButton;

int WINAPI WinMain(HINSTANCE hThisInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR lpszArgument,
                     int nCmdShow)
{
    HWND hwnd;               /* This is the handle for our window */
    MSG messages;            /* Here messages to the application are saved */
    WNDCLASSEX wincl;        /* Data structure for the windowclass */

    /* The Window structure */
    wincl.hInstance = hThisInstance;
    wincl.lpszClassName = szClassName;
    wincl.lpfnWndProc = WindowProcedure;      /* This function is called by windows */
    wincl.style = CS_DBLCLKS;                 /* Catch double-clicks */
    wincl.cbSize = sizeof (WNDCLASSEX);

    /* Use default icon and mouse-pointer */
    wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
    wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
    wincl.lpszMenuName = NULL;                 /* No menu */
    wincl.cbClsExtra = 0;                      /* No extra bytes after the window class */
    wincl.cbWndExtra = 0;                      /* structure or the window instance */
    /* Use Windows's default colour as the background of the window */
    wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND;

    /* Register the window class, and if it fails quit the program */
    if (!RegisterClassEx (&wincl)) return 0;

    /* The class is registered, let's create the program*/
    hwnd = CreateWindowEx (
           0,                   /* Extended possibilites for variation */
           szClassName,         /* Classname */
           _T("Subclassing Button"),       /* Title Text */
           WS_OVERLAPPEDWINDOW, /* default window */
           CW_USEDEFAULT,       /* Windows decides the position */
           CW_USEDEFAULT,       /* where the window ends up on the screen */
           400,                 /* The programs width */
           300,                 /* and height in pixels */
           HWND_DESKTOP,        /* The window is a child-window to desktop */
           NULL,                /* No menu */
           hThisInstance,       /* Program Instance handler */
           NULL                 /* No Window Creation data */
           );

    /* Make the window visible on the screen */
    ShowWindow (hwnd, nCmdShow);

    /* Run the message loop. It will run until GetMessage() returns 0 */
    while (GetMessage (&messages, NULL, 0, 0))
    {
        /* Translate virtual-key messages into character messages */
        TranslateMessage(&messages);
        /* Send message to WindowProcedure */
        DispatchMessage(&messages);
    }

    /* The program return-value is 0 - The value that PostQuitMessage() gave */
    return messages.wParam;
}

/* This function is called by the Windows function DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)                  /* handle the messages */
    {
         case WM_CREATE:
         {
              // Subclassed buton ve WM_PAINT yöntemi
              hwndButton = CreateWindowEx(0, "BUTTON", "Button",
                                 WS_CHILD | WS_VISIBLE, 20, 20, 120, 25,
                                 hwnd, (HMENU) IDC_BUTTON, NULL, NULL);

              SetWindowSubclass(hwndButton, ButtonProc, 0, 0);
         }
         break;

         case WM_COMMAND:
         {
              if (HIWORD(wParam) == BN_CLICKED) {
                  switch(LOWORD(wParam)) {
                    case IDC_BUTTON:
                         MessageBox(NULL, "Button01 tıklandı.", "Buton tıklama", MB_OK);
                         break;
                  }
              }
         }
         break;

         case WM_DESTROY:
             PostQuitMessage (0);       /* send a WM_QUIT to the message queue */
             break;

         default:                      /* for messages that we don't deal with */
             return DefWindowProc (hwnd, message, wParam, lParam);
    }

    return 0;
}

/* Subclassing fonksiyonu */
LRESULT CALLBACK ButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
  switch (message) {

    case WM_PAINT:
    {
         if (hwnd==hwndButton) {
             PAINTSTRUCT ps;
             BeginPaint(hwnd, &ps);

             SetTextColor(ps.hdc, RGB(236, 236, 236));
             SetBkMode(ps.hdc, TRANSPARENT);
             FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)CreateSolidBrush(RGB(230, 30, 90)));
             DrawText(ps.hdc, "Button01", -1, &ps.rcPaint, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

             EndPaint(hwnd, &ps);

             return 0; // Sistem Button sınıf penceresinin işlem yapmaması için
         }
    }
    break;
  }

  return DefSubclassProc(hwnd, message, wParam, lParam);
}