Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Pencere yordamı yalnızca her ileti için çağrılan bir işlevdir, dolayısıyla doğal olarak durum bilgisi yoktur. Bu nedenle, bir işlev çağrısından sonrakine uygulamanızın durumunu izlemek için bir yol gerekir.
En basit yaklaşım, her şeyi genel değişkenlere yerleştirmektir. Bu, küçük programlar için yeterlidir ve SDK örneklerinin çoğu bu yaklaşımı kullanır. Ancak büyük bir programda genel değişkenlerin çoğalmasına yol açar. Ayrıca, her biri kendi pencere yordamına sahip olan birkaç pencereniz olabilir. Hangi pencerenin hangi değişkenlere erişmesi gerektiğini takip etmek kafa karıştırıcı ve hataya eğilimli hale geliyor.
CreateWindowEx işlevi, herhangi bir veri yapısını bir pencereye geçirmenin bir yolunu sağlar. Bu işlev çağrıldığında pencere yordamınıza aşağıdaki iki iletiyi gönderir:
Bu iletiler listelenen sırayla gönderilir. (Bunlar CreateWindowExsırasında gönderilen yalnızca iki ileti değildir, ancak bu tartışma için diğerlerini yoksayabiliriz.)
pencere görünür hale gelmeden önce WM_NCCREATE ve WM_CREATE iletileri gönderilir. Bu, onları kullanıcı arabiriminizi başlatmak için iyi bir yer haline getirir; örneğin, pencerenin ilk düzenini belirlemek için.
CreateWindowEx son parametresi, void*türünde bir işaretçidir. Bu parametrede istediğiniz işaretçi değerini geçirebilirsiniz. Pencere yordamı WM_NCCREATE veya WM_CREATE iletisini işlediğinde, ileti verilerinden bu değeri ayıklayabilir.
Şimdi bu parametreyi uygulama verilerini pencerenize geçirmek için nasıl kullanacağınızı görelim. İlk olarak, durum bilgilerini tutan bir sınıf veya yapı tanımlayın.
// Define a structure to hold some state information.
struct StateInfo {
// ... (struct members not shown)
};
CreateWindowExçağırdığınızda, son void* parametresinde bu yapıya bir işaretçi geçirin.
StateInfo *pState = new (std::nothrow) StateInfo;
if (pState == NULL)
{
return 0;
}
// Initialize the structure members (not shown).
HWND hwnd = CreateWindowEx(
0, // Optional window styles.
CLASS_NAME, // Window class
L"Learn to Program Windows", // Window text
WS_OVERLAPPEDWINDOW, // Window style
// Size and position
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, // Parent window
NULL, // Menu
hInstance, // Instance handle
pState // Additional application data
);
WM_NCCREATE ve WM_CREATE iletilerini aldığınızda, her iletinin lParam parametresi CREATESTRUCT yapısının işaretçisidir. CREATESTRUCT yapısı, CreateWindowExiçine iletmiş olduğunuz işaretçiyi içerir.
Oluşturma yapısının düzenini gösteren diyagram 
İşaretçiyi veri yapınıza nasıl ayıkladığınız aşağıda anlatılır. İlk olarak, lParam parametresini dönüştürerek CREATESTRUCT yapısını alın.
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
CREATESTRUCT yapısının lpCreateParams üyesi, CreateWindowExiçinde belirttiğiniz özgün void işaretçisidir. lpCreateParamsoluşturarak kendi veri yapınıza yönelik bir işaretçi alın.
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);
Ardından SetWindowLongPtrişlevini çağırın ve işaretçiyi veri yapınıza geçirin.
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);
Bu son işlev çağrısının amacı, StateInfo işaretçisini pencerenin örnek verilerinde depolamaktır. Bunu yaptıktan sonra, GetWindowLongPtrçağırarak işaretçiyi pencereden geri alabilirsiniz:
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
Her pencerenin kendi örnek verileri vardır, bu nedenle birden çok pencere oluşturabilir ve her pencereye veri yapısının kendi örneğini verebilirsiniz. Bu yaklaşım özellikle bir windows sınıfı tanımlar ve bu sınıfın birden fazla penceresini oluşturursanız (örneğin, özel bir denetim sınıfı oluşturursanız) kullanışlıdır. "GetWindowLongPtr çağrısını küçük bir yardımcı işlevde sarmak uygun olur."
inline StateInfo* GetAppState(HWND hwnd)
{
LONG_PTR ptr = GetWindowLongPtr(hwnd, GWLP_USERDATA);
StateInfo *pState = reinterpret_cast<StateInfo*>(ptr);
return pState;
}
Artık pencere yordamınızı aşağıdaki gibi yazabilirsiniz.
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
StateInfo *pState;
if (uMsg == WM_CREATE)
{
CREATESTRUCT *pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
pState = reinterpret_cast<StateInfo*>(pCreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pState);
}
else
{
pState = GetAppState(hwnd);
}
switch (uMsg)
{
// Remainder of the window procedure not shown ...
}
return TRUE;
}
Bir Object-Oriented Yaklaşımı
Bu yaklaşımı daha da genişletebiliriz. Pencere hakkındaki durum bilgilerini tutmak için zaten bir veri yapısı tanımladık. Bu veri yapısının veriler üzerinde çalışan üye işlevleri (yöntemler) ile sağlanması mantıklıdır. Bu doğal olarak yapının (veya sınıfın) penceredeki tüm işlemlerden sorumlu olduğu bir tasarıma yol açar. Pencere işlemi daha sonra sınıfın bir parçası olur.
Başka bir deyişle, bundan devam etmek istiyoruz:
// pseudocode
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
StateInfo *pState;
/* Get pState from the HWND. */
switch (uMsg)
{
case WM_SIZE:
HandleResize(pState, ...);
break;
case WM_PAINT:
HandlePaint(pState, ...);
break;
// And so forth.
}
}
Bunun için:
// pseudocode
LRESULT MyWindow::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_SIZE:
this->HandleResize(...);
break;
case WM_PAINT:
this->HandlePaint(...);
break;
}
}
Tek sorun, MyWindow::WindowProc yönteminin nasıl bağlanacağıdır.
RegisterClass işlevi, pencere yordamının bir işlev işaretçisi olmasını bekler. Bu bağlamda bir işaretçiyi (statik olmayan) üye işlevine geçiremezsiniz. Ancak, bir işaretçiyi statik üye işlevine geçirebilir ve ardından üye işlevine temsilci atayabilirsiniz. Bu yaklaşımı gösteren bir sınıf şablonu aşağıdadır:
template <class DERIVED_TYPE>
class BaseWindow
{
public:
static LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
DERIVED_TYPE *pThis = NULL;
if (uMsg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = (CREATESTRUCT*)lParam;
pThis = (DERIVED_TYPE*)pCreate->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pThis);
pThis->m_hwnd = hwnd;
}
else
{
pThis = (DERIVED_TYPE*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
}
if (pThis)
{
return pThis->HandleMessage(uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
BaseWindow() : m_hwnd(NULL) { }
BOOL Create(
PCWSTR lpWindowName,
DWORD dwStyle,
DWORD dwExStyle = 0,
int x = CW_USEDEFAULT,
int y = CW_USEDEFAULT,
int nWidth = CW_USEDEFAULT,
int nHeight = CW_USEDEFAULT,
HWND hWndParent = 0,
HMENU hMenu = 0
)
{
WNDCLASS wc = {0};
wc.lpfnWndProc = DERIVED_TYPE::WindowProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = ClassName();
RegisterClass(&wc);
m_hwnd = CreateWindowEx(
dwExStyle, ClassName(), lpWindowName, dwStyle, x, y,
nWidth, nHeight, hWndParent, hMenu, GetModuleHandle(NULL), this
);
return (m_hwnd ? TRUE : FALSE);
}
HWND Window() const { return m_hwnd; }
protected:
virtual PCWSTR ClassName() const = 0;
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) = 0;
HWND m_hwnd;
};
BaseWindow sınıfı, belirli pencere sınıflarının türetildiği soyut bir temel sınıftır. Örneğin, BaseWindowtüretilen basit bir sınıfın bildirimi aşağıda verilmiştir:
class MainWindow : public BaseWindow<MainWindow>
{
public:
PCWSTR ClassName() const { return L"Sample Window Class"; }
LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam);
};
Pencereyi oluşturmak için BaseWindow::Createçağrısı yapın:
int WINAPI wWinMain(HINSTANCE, HINSTANCE, PWSTR, int nCmdShow)
{
MainWindow win;
if (!win.Create(L"Learn to Program Windows", WS_OVERLAPPEDWINDOW))
{
return 0;
}
ShowWindow(win.Window(), nCmdShow);
// Run the message loop.
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
Saf "sanal" BaseWindow::HandleMessage metodu, pencere yordamını uygulamak için kullanılır. Örneğin, aşağıdaki uygulama Modül 1başında gösterilen pencere yordamıyla eşdeğerdir.
LRESULT MainWindow::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(m_hwnd, &ps);
FillRect(hdc, &ps.rcPaint, (HBRUSH) (COLOR_WINDOW+1));
EndPaint(m_hwnd, &ps);
}
return 0;
default:
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
return TRUE;
}
Pencere tanıtıcısının bir üye değişkeninde (m_hwnd) depolandığını fark edin, bu yüzden bunu HandleMessage'e parametre olarak geçirmemiz gerekmez.
Microsoft Foundation Sınıfları (MFC) ve Etkin Şablon Kitaplığı (ATL) gibi mevcut Windows programlama çerçevelerinin çoğu, temelde burada gösterilene benzer yaklaşımlar kullanır. Elbette MFC gibi tamamen genelleştirilmiş bir çerçeve, bu nispeten basit örnekten daha karmaşıktır.
Sonraki
Modülü 2: Windows Program'nizde COM Kullanma