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

Ana page > Programlama > Windows API Programlama > Superclassing

Superclassing

Superclassing işlemi ile, sistemde kayıtlı bir sınıfın temel işlevleriyle yeni bir pencere sınıfı oluşturulur ve bu sınıfa ek özellikler kazandırılır. Superclassing işlemi temel sınıf adı verilen ve sistemde kayıtlı mevcut bir pencere sınıfını temel alır. Temel sınıf, Button, ComboBox, Edit vb. gibi herhangi bir kontrol pencere sınıfı olabilir.

Yeni oluşturulan sınıf RegisterClassEx() fonksiyonu ile sisteme kaydedildikten sonra, yeni sınıf adıyla, aynı özelliklere sahip olan ve aynı pencere fonksiyonunu kullanan birden fazla buton oluşturabiliriz.

Superclassing işlemi ile oluşturulan yeni sınıfın kendina ait bir pencere fonksiyonu tanımlanır. Superclassing 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 superclass 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 subclass fonksiyonundan farklı olarak, bir superclass fonksiyonu WM_NCCREATE ve WM_CREATE gibi pencere oluşturma mesajlarına işlem yapabilir. Ancak orjinal temel sınıf pencere fonksiyonuna aktarması gerekir.

Bir pencere sınıfına superclass işlemi uygulamak için:

1. Önce ana sınıf hakkında bilgi almak için GetClassInfoEx() fonksiyonu çağrılır. GetClassInfoEx() fonksiyonu WNDCLASSEX yapısının değişkenlerini ana sınıfın WNDCLASSEX yapısından gelen değerlerle doldurur.

2. Program kendine ait HINSTANCE değerini WNDCLASSEX yapısının hInstance değişkenine kopyalar.

3. Program yeni oluşturulan sınıf adını WNDCLASSEX yapısının lpszClassName değişkenine kopyalar.

4. Eğer ana sınıfa ait bir menü varsa, program aynı menü tanımlayıcılarına sahip yeni bir menü oluşturmalı ve menü adını WNDCLASSEx yapısının lpszMenuName değişkenine kopyalamalıdır.

5. Program GetClassInfoEx() fonksiyonu ile ana sınıfa ait orjinal pencere fonksiyonunun adresinin atandığı WNDCLASSEX yapısının lpfnWndProc değişken değerini oluşturduğu WNDPROC cinsinden bir değişkenine kaydeder. Program bu değişkeni orjinal mesaj penceresine iletmek için kullamır.

6. Program yeni oluşturulan sınıf adına ait fonksiyon adını WNDCLASSEX yapısının lpfnWndProc değişkenine atar.

7. Program gerektiğinde, WNDCLASSEX yapısının diğer değişkenlerini de ihtiyaca bağlı olarak değiştirebilir.

8. Program WNDCLASSEX yapısının değişken değerlerini atadıktan sonra, WNDCLASSEX yapısının adresini geçirerek çağırdığı RegisterClassEx() fonksiyonu ile yeni oluşturduğu sınıfı kaydeder.

9. Bundan sonra program tarafından bu sınıf adını kullanarak oluşturulan pencereler bu sınıf penceresinin görünümünü ve özelliklerini taşıyacaktır.


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;

RegisterClassEx() fonksiyonuyla kullanılan WNDCLASSEX yapısının cbWndExtra değişken değerine sıfır olmayan bir değer vererek ekstra pencere belleği ayrılmalıdır.

GetClassInfoEx() fonksiyonu kullanılmadan önce, WNDCLASSEX yapısının cbSize değişkenine sizeof(WNDCLASSEX) değeri atanmalıdır.

Button Superclassing örneği

Aşağıdaki main.c ile oluşturulan proje ile, sistemde kayıtlı BUTTON sınıfından yeni bir sınıf oluşturularak sisteme kaydedilir. Sonra, program ana penceresi altında yeni tanımlanan sınıf cinsinden 2 adet button oluşturulur.

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_BUTTON01 1001
#define IDC_BUTTON02 1002

/* Declare Windows procedure */
LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);
/* Yeni oluşturulan button sınıf cinsinden super sınıfı sisteme kaydeden fonksiyon bildirimi */
void RegisterSuperButton(void);
/* Yeni oluşturulan button sınıf cinsinden super sınıfın pencere fonksiyon bildirimi */
LRESULT CALLBACK SuperButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);

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

HWND hwndButton01, hwndButton02;
/* Orjinal sınıf fonksiyonunun kaydedileceği değişken tanımı */
WNDPROC BtnOrigProc;

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("Superclassing 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 lmoop. 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:
         {
              RegisterSuperButton();

              hwndButton01 = CreateWindowEx(0, "MYBUTTON", "Button01",
                                 WS_CHILD | WS_VISIBLE, 20, 20, 120, 25,
                                 hwnd, (HMENU) IDC_BUTTON01, NULL, NULL);

              hwndButton02 = CreateWindowEx(0, "MYBUTTON", "Button02",
                                 WS_CHILD | WS_VISIBLE, 20, 60, 120, 25,
                                 hwnd, (HMENU) IDC_BUTTON02, NULL, NULL);
         }
         break;

         case WM_COMMAND:
         {
              if (HIWORD(wParam) == BN_CLICKED) {
                  switch(LOWORD(wParam)) {
                    case IDC_BUTTON01:
                         MessageBox(NULL, "Button01 tıklandı.", "Buton tıklama", MB_OK);
                         break;
                    case IDC_BUTTON02:
                         MessageBox(NULL, "Button02 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;
}

/* Yeni oluşturulan button sınıf cinsinden super sınıfı sisteme kaydeden fonksiyon */
void RegisterSuperButton(void)
{
  WNDCLASSEX wndSupButton = {0};

  // GetClassInfoEx() fonksiyonu çağrılmadan mutlaka değer atanmalıdır.
  wndSupButton.cbSize = sizeof (WNDCLASSEX);
  // Sistem BUTTON sınıf bilgisini alma
  GetClassInfoEx(0, "BUTTON", &wndSupButton);
  // Program Instance değerini sınıf Instance değişkenine atama
  wndSupButton.hInstance = (HINSTANCE) GetModuleHandle(NULL);
  // Sistem button sınıfı temel alınarak oluşturulan yeni superclassing adını değişkenine atama
  wndSupButton.lpszClassName = TEXT("MYBUTTON");
  // Sistem button sınıfına ait pencere fonksiyonunu bir WNDPROC değişkenine atama
  BtnOrigProc = wndSupButton.lpfnWndProc;
  // Yeni oluşturulan sınıf için kullanılacak fonksiyonunu orjinal fonksiyon yerine atama
  wndSupButton.lpfnWndProc = SuperButtonProc;
  // Bu sınıf yapısı için ayrılacak ekstra byte sayısıdır.
  wndSupButton.cbClsExtra = 0;
  // Bu pencere Instance değeri için ayrılacak ekstra byte sayısıdır. Sıfırdan farklı bir değer olmalıdır.
  wndSupButton.cbWndExtra = 100;
  // Yeni oluşturulan sınıfı sisteme kaydetme
  RegisterClassEx (&wndSupButton);
}

/* Yeni oluşturulan button sınıf cinsinden super sınıfın pencere fonksiyonu */
LRESULT CALLBACK SuperButtonProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
  static int inButton;
  static HWND hwndButton;

  switch (message) {

    case WM_PAINT:
    {
         PAINTSTRUCT ps;
         char cdizi[40];

         BeginPaint(hwnd, &ps);

         SetTextColor(ps.hdc, RGB(236, 236, 236));
         SetBkMode(ps.hdc, TRANSPARENT);
         if (inButton && (hwndButton == hwnd)) FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)CreateSolidBrush(RGB(100, 205, 22)));
         else FillRect(ps.hdc, &ps.rcPaint, (HBRUSH)CreateSolidBrush(RGB(90, 180, 20)));
         GetWindowText(hwnd, cdizi, sizeof(cdizi));
         DrawText(ps.hdc, cdizi, -1, &ps.rcPaint, DT_SINGLELINE | DT_CENTER | DT_VCENTER);

         EndPaint(hwnd, &ps);

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

    case WM_MOUSEMOVE:
     {
         hwndButton = hwnd;

         if (GetCapture() != hwnd) {
            // Mouse button penceresine henüz girdi.
            inButton=1;
            SetCapture(hwnd);

         }
         else {
            RECT rect;
            POINT pt = { LOWORD(lParam), HIWORD(lParam) };

            GetWindowRect(hwnd, &rect);
            ClientToScreen(hwnd, &pt);

            if (!PtInRect(&rect, pt)) {
                // Mouse button penceresinden ayrıldı.
                inButton=0;
                ReleaseCapture();
            }
         }
     }
     break;
  }

  return CallWindowProc(BtnOrigProc, hwnd, message, wParam, lParam);
}