首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

SDK 視窗程式物件化封裝:以 C++ 面向對象技術重構 Win32 註冊/建立/訊息迴圈

     編輯:關於VC++

動機

純 SDK 寫視窗程式需重複:WNDCLASSEX 註冊、CreateWindow、訊息迴圈、龐大 switch(msg) 分派。MFC/OWL/WTL 等框架雖解決此問題,但理解其底層封裝原理有助於深入掌握 Windows 機制。本文示範從零用 C++ 面向對象封裝一個極簡視窗基類 CWindow,支援繼承、虛函式訊息處理、this 指針回調。

三步設計法

  1. 封裝對象:視窗
  2. 屬性:HINSTANCE, HWND, WNDCLASSEX, 類別名稱
  3. 方法:建立、顯示、訊息迴圈、各訊息對應虛函式 (OnCreate, OnPaint, OnDestroy...)

基類 CWindow 定義

CWindow 類別圖

class CWindow
{
protected:
    HWND m_hWnd;
    HINSTANCE m_hInst;
    TCHAR m_szClassName[64];

    // 靜態全域訊息處理函式 (回調入口)
    static LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

    // 虛函式:派生類覆寫處理特定訊息
    virtual LRESULT OnCreate(LPCREATESTRUCT lpCS) { return 0; }
    virtual void OnPaint(HDC hdc) {}
    virtual void OnDestroy() { PostQuitMessage(0); }
    // ... 其他訊息虛函式 ...

public:
    CWindow(HINSTANCE hInst, LPCTSTR className);
    BOOL Create(LPCTSTR title, DWORD style, int x, int y, int w, int h, HWND hParent, HMENU hMenu);
    void Show(int nCmdShow);
    void RunMessageLoop();
    HWND GetHWND() const { return m_hWnd; }
};

關鍵技術:this 指針與靜態回調綁定

1. CreateWindowEx 傳遞 this

BOOL CWindow::Create(...)
{
    // 註冊類別 (略)
    m_hWnd = CreateWindowEx(0, m_szClassName, title, style, x, y, w, h, hParent, hMenu, m_hInst, this);
    //                                                                     ↑ lpCreateParams = this
    return m_hWnd != NULL;
}

2. WM_NCCREATE 取出 this 並 SetWindowLongPtr(GWLP_USERDATA)

LRESULT CALLBACK CWindow::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    CWindow* pThis = (CWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA);

    if (msg == WM_NCCREATE)
    {
        LPCREATESTRUCT pCS = (LPCREATESTRUCT)lParam;
        pThis = (CWindow*)pCS->lpCreateParams;
        SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)pThis);
        // 呼叫虛函式 OnCreate
        return pThis->OnCreate(pCS);
    }

    if (pThis) return pThis->HandleMessage(msg, wParam, lParam);
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

3. 實例訊息分派 HandleMessage

LRESULT CWindow::HandleMessage(UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
        case WM_PAINT:
        {
            PAINTSTRUCT ps; HDC hdc = BeginPaint(m_hWnd, &ps);
            OnPaint(hdc); // 虛函式
            EndPaint(m_hWnd, &ps);
            return 0;
        }
        case WM_DESTROY: OnDestroy(); return 0;
        // ... 其他訊息映射到虛函式 ...
    }
    return DefWindowProc(m_hWnd, msg, wParam, lParam);
}

派生類範例:帶選單與繪圖的視窗

派生類程式碼

class CMyWindow : public CWindow
{
public:
    CMyWindow(HINSTANCE hInst) : CWindow(hInst, _T("MyWindowClass")) {}

protected:
    virtual LRESULT OnCreate(LPCREATESTRUCT lpCS) override
    {
        // 載入選單
        HMENU hMenu = LoadMenu(m_hInst, MAKEINTRESOURCE(IDR_MAINMENU));
        SetMenu(m_hWnd, hMenu);
        return 0;
    }

    virtual void OnPaint(HDC hdc) override
    {
        TextOut(hdc, 50, 50, _T("Hello, OOP SDK Window!"), 24);
        // 繪製圖形...
    }

    virtual void OnCommand(WPARAM wParam, LPARAM lParam) override
    {
        switch (LOWORD(wParam))
        {
            case ID_FILE_EXIT: DestroyWindow(m_hWnd); break;
            // ...
        }
    }
};

主程式入口:幾行代碼啟動

主程式碼

int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE, LPSTR, int nCmdShow)
{
    CMyWindow win(hInst);
    if (win.Create(_T("OOP SDK Demo"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL))
    {
        win.Show(nCmdShow);
        win.RunMessageLoop();
    }
    return 0;
}

虛函式表 vs 訊息映射表:設計權衡

方案優點缺點
虛函式 (本文)語法簡潔、繼承自然、IDE 導航友善每個類別維護 vtable,未使用訊息佔空間;難以動態增減訊息處理
訊息映射表 (MFC/WTL)僅存儲實際處理的訊息、支援動態鏈接、無 vtable 開銷巨集語法複雜、需預處理器/模板支援

本文採虛函式純粹為教學演示;生產級框架建議採用訊息映射表。

擴充方向

  • 加入 CWinApp 封裝 WinMain、單例應用程式物件。
  • 實作 CDialog、CFrameWnd 等進階視窗類別。
  • 支援子視窗/控制項封裝 (CButton, CEdit...)。
  • 引入訊息映射表宏替代虛函式。
  • 封裝 GDI 物件 (CDC, CBitmap, CFont) 實現 RAII。

結語

「SDK 物件化」不僅減少重複代碼,更能讓開發者透過親手構建框架,深刻理解 HWND/this 綁定、訊息分派、DC 管理等 Windows 核心機制。本文提供的 CWindow 雖簡陋,但包含了框架設計的靈魂:封裝平台差異、暴露領域介面、以繼承實現擴充。

本文配套原始碼可供下載。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved