如何封裝窗口過程是所有UI庫最核心的設計之一,像MFC/WTL/VCL都各有自己的實現代碼,最終的目標卻是一樣的:將窗口過程轉化為對象的方法,從而把面向過程的Windows轉化為面向對象的方式。這裡要介紹我自己想到兩種實現方式,這些實現都很簡單,但都可以達到同樣的目的。
一、利用窗口的屬性表
基本原理是將窗口句柄和窗口類實例綁定在一起,每個窗口都有自己的屬性表,通過GetProp和SetProp等API操作之,屬性表本質上就是哈希表,通過一個Key快速查找到Value,在這裡Key就是窗口句柄,Value就是窗口類。
另外,我們需要一個自己的標准窗口過程,用來找到與句柄關聯的窗口類,然後再調用窗口類的窗口過程。
先看看實現的代碼:
view plaincopy to clipboardprint?
#ifndef WndHandler_h__
#define WndHandler_h__
#include <crtdbg.h>
#define _WINDOW_CLSATOM "wndatom"
class WndHandler
{
public:
WndHandler(): mDefWndProc(NULL), mHwnd(NULL)
{
}
virtual ~WndHandler()
{
Unsubclass();
}
bool Subclass(HWND hwnd)
{
Unsubclass();
_ASSERT(::IsWindow(hwnd));
_ASSERT(::GetPropA(hwnd, _WINDOW_CLSATOM) == NULL);
mDefWndProc = (WNDPROC)::GetWindowLongW(hwnd, GWL_WNDPROC);
if (mDefWndProc == WndHandler::StdWndProc)
mDefWndProc = DefWindowProcW;
::SetWindowLongW(hwnd, GWL_WNDPROC, (long)StdWndProc);
::SetPropA(hwnd, _WINDOW_CLSATOM, (HANDLE)this);
mHwnd = hwnd;
return true;
}
void Unsubclass()
{
if (!::IsWindow(mHwnd) || !mDefWndProc)
return;
::SetWindowLongW(mHwnd, (LONG)mDefWndProc, GWL_WNDPROC);
::RemovePropA(mHwnd, _WINDOW_CLSATOM);
mDefWndProc = NULL;
mHwnd = NULL;
}
protected:
static LRESULT CALLBACK StdWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
WndHandler* handler = (WndHandler*)GetPropA(hwnd, _WINDOW_CLSATOM);
_ASSERT(handler);
LRESULT ret = 0;
BOOL done = handler->WndProc(hwnd, msg, wparam, lparam, ret);
if (done)
return ret;
else
return CallWindowProcW(handler->mDefWndProc, hwnd, msg, wparam, lparam);
}
virtual BOOL WndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam, LRESULT& ret)
{
switch (msg)
{
case WM_NCDESTROY:
{
mHwnd = NULL;
break;
}
}
return FALSE;
}
protected:
WNDPROC mDefWndProc;
HWND mHwnd;
};
#endif // WndHandler_h__
#ifndef WndHandler_h__
#define WndHandler_h__
#include <crtdbg.h>
#define _WINDOW_CLSATOM "wndatom"
class WndHandler
{
public:
WndHandler(): mDefWndProc(NULL), mHwnd(NULL)
{
}
virtual ~WndHandler()
{
Unsubclass();
}
bool Subclass(HWND hwnd)
{
Unsubclass();
_ASSERT(::IsWindow(hwnd));
_ASSERT(::GetPropA(hwnd, _WINDOW_CLSATOM) == NULL);
mDefWndProc = (WNDPROC)::GetWindowLongW(hwnd, GWL_WNDPROC);
if (mDefWndProc == WndHandler::StdWndProc)
mDefWndProc = DefWindowProcW;
::SetWindowLongW(hwnd, GWL_WNDPROC, (long)StdWndProc);
::SetPropA(hwnd, _WINDOW_CLSATOM, (HANDLE)this);
mHwnd = hwnd;
return true;
}
void Unsubclass()
{
if (!::IsWindow(mHwnd) || !mDefWndProc)
return;
::SetWindowLongW(mHwnd, (LONG)mDefWndProc, GWL_WNDPROC);
::RemovePropA(mHwnd, _WINDOW_CLSATOM);
mDefWndProc = NULL;
mHwnd = NULL;
}
protected:
static LRESULT CALLBACK StdWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
WndHandler* handler = (WndHandler*)GetPropA(hwnd, _WINDOW_CLSATOM);
_ASSERT(handler);
LRESULT ret = 0;
BOOL done = handler->WndProc(hwnd, msg, wparam, lparam, ret);
if (done)
return ret;
else
return CallWindowProcW(handler->mDefWndProc, hwnd, msg, wparam