在 Windows 程式設計中,視窗處理函數必須是靜態函數或全域函數,因為系統呼叫時不會傳遞 this 指標。若想在類別中使用非靜態成員函數處理訊息,需透過「靜態橋接函數 + 實例指標儲存」的模式實現。
WNDPROC 簽名。this 指標:呼叫 SetWindowLong(hWnd, GWL_USERDATA, (LONG)this) 將類別實例指標關聯至視窗。GetWindowLong(hWnd, GWL_USERDATA) 取得實例指標,再呼叫非靜態成員函數處理具體邏輯。class SArea {
protected:
HWND h_Wnd;
public:
virtual HWND CreateSArea(LPCTSTR wndName, DWORD dwStyle, int x, int y, int w, int h, HWND hParent);
static LRESULT CALLBACK SAreaProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
virtual void SProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
};
HWND SArea::CreateSArea(LPCTSTR wndName, DWORD dwStyle, int x, int y, int w, int h, HWND hParent) {
WNDCLASS wc;
// 初始化 wc,設定 lpfnWndProc = SArea::SAreaProc
::RegisterClass(&wc);
h_Wnd = ::CreateWindow(/* 參數省略 */);
if (h_Wnd)
::SetWindowLong(h_Wnd, GWL_USERDATA, (LONG)this); // 關鍵:儲存 this 指標
return h_Wnd;
}
LRESULT CALLBACK SArea::SAreaProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
SArea* pArea = NULL;
// WM_NCCREATE/WM_CREATE 時 GWL_USERDATA 尚未設定,需跳過
if (message != WM_NCCREATE && message != WM_CREATE)
pArea = (SArea*)::GetWindowLong(hWnd, GWL_USERDATA);
if (pArea)
pArea->SProc(hWnd, message, wParam, lParam); // 轉發給非靜態函數
return ::DefWindowProc(hWnd, message, wParam, lParam);
}
void SArea::SProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
// 具體訊息處理邏輯
}
GWL_USERDATA 是視窗結構預留的 32 位空間,專門用於儲存應用程式自訂資料(這裡存放 this)。WM_NCCREATE 和 WM_CREATE 訊息到達時,SetWindowLong 可能尚未執行,因此必須在靜態函數開頭判斷並略過取指標動作,避免空指標呼叫。CWnd::WindowProc 機制,也是物件導向封裝 Win32 API 的基石。自訂視窗類別、封裝 ActiveX 控制項、實作不依賴 MFC 的輕量級視窗封裝庫等。