almost 3 years ago

這是今天在做一些小程式練習的時候,使用原生的 Win32 API CreateWindowEx() 建立視窗中間碰到一個問題。

狀況是這樣,所有傳遞給 CreateWindowEx() 的參數都正確,但是執行程式後,不管怎麼弄視窗都沒有被正確創建出來。CreateWindowEx() 調用後,沒有如我預期的回傳新視窗的 handle,而是回傳了 NULL 表示視窗建立失敗。接著呼叫 GetLastError() 拿到 0,得不到任何出錯原因。

如果你懶的看原因,那麼簡短的解法就是記得在 Msg Handler 的最後呼叫 DefWindowProc ()

CreateWindowEx 的線上文件 寫了幾種可能失敗的原因,我檢查後發現原因是出在訊息回呼函數。在呼叫 CreateWindowEx() 之前,會先 RegisterClassEx 註冊訊息回呼函數(WindowProc callback function),而我為了方便,暫時沒有實作回呼函數,只留了一個空殼:

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return 0; // 不論收到什麼 message 都回傳 0
}

而實際上,作業系統會在嘗試建立視窗時發送 WM_NCCREATE 訊息,並希望視窗做出正確的響應,顯然的我的回呼函數沒有響應 WM_NCCREATE 訊息,創建視窗就失敗了。

最簡單的修正方法,是轉呼叫 Windows 內置的訊息回呼函數 DefWindowProc(),加上這行後視窗就正確顯示出來了。

LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return DefWindowProc ( hWnd , uMsg , wParam , lParam );
}

如果加上自己攔截的一些 Windows message,那麼最終這個函數應該長的像這樣,反正所有自己不想處理的 message,都應該要轉丟給 DefWindowProc

LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch( uMsg )
    {
    case WM_XXXX:
        // my custom message handler
        break;
    default:
        return DefWindowProc ( hWnd , uMsg , wParam , lParam ); // 最終一定要導到 DefWindowProc
    }
}

參考連結: StackOverflow - CreateWindowEx function (WinAPI) fails but GetLastError() throws 0

← 無法驗證此版本的『安裝 OS X Yosemite』 Mac 生態系的優點 →