剖析WINX的Hello程式

來源:互聯網
上載者:User
概述

我們已經介紹了Windows SDK的Hello程式,它的流程主要分為三個步驟:

  1. 註冊視窗類別(RegisterClass)。並且我們詳細解釋了為何要有視窗類別,為何要RegisterClass。
  2. 建立並顯示視窗(CreateWindow and ShowWindow)。
  3. 訊息迴圈(MessageLoop)。即:取得訊息 -> 指派訊息 -> 處理訊息。

這裡,我們就要結合WINX的Hello程式,把整個流程串一遍。

作為比較,我想溫習一下ATL/WTL的Hello程式。我們在此提供了幾篇剖析ATL/WTL的Hello程式的好文章:

  • Win32 vs. ATL Windows Programming
  • MFC程式員的WTL開發指南之ATL介面類
WINX的Hello程式

 

#define WINX_USE_APPMODULE
#include <winx.h>

class CHelloMainFrame : public winx::MainFrame<CHelloMainFrame>
{
    WINX_CLASS("CHelloMainFrame");
public:
    void OnPaint(HWND hWnd)
    {
        winx::PaintDC dc(hWnd);
        dc.TextOut(1, 1, _T("Hello, WINX!"));
    }
};

winx::CAppModule _Module;

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    CAppModuleInit module;

    CHelloMainFrame::RegisterClass();
    CHelloMainFrame wndMain;
    wndMain.Create(NULL, _T("Hello"));
    
    return module.Run();
}

 

WINX的編程模型1. 註冊視窗類別(RegisterClass)

WINX中RegisterClass是需要主動調用的,這倒省了象ATL/WTL那樣解釋半天:-)

區別於已知的所有C++介面庫(MFC、ATL/WTL、SmartWin、wxWidgets等等,甚至包括我早期寫的SW系統),WINX傾向於把RegisterClass概念告訴使用者。並且,為此我專門寫了一篇“Windows精解:視窗類別釋疑”來解釋相關概念的重要性。這一切與WINX的可視化策略有關,我們在“WINX如何做到可視化介面開發”中詳述這一點。

以下這些宏與WINX的RegisterClass有關:

  • WINX_CLASS / WINX_CLASS_EX
  • WINX_CLASS_STYLE
  • WINX_DEFAULT_BKGND / WINX_DEFAULT_COLOR / WINX_DEFAULT_BRUSH
  • WINX_DEFAULT_CURSOR / WINX_DEFAULT_SYSCURSOR

它們分別對應Windows視窗類別(WNDCLASSEX)中的成員:

  • lpszClassName
  • style
  • hbrBackground
  • hCursor

大家已經熟悉用WINX_CLASS指定視窗類別的名稱,其他宏的用法完全一致。例如,預設滑鼠游標是箭頭(IDC_ARROW),要改為象Edit控制項一樣使用IDC_IBEAM,很容易:

class CHelloMainFrame : public winx::MainFrame<CHelloMainFrame>
{
    WINX_DEFAULT_SYSCURSOR(IDC_IBEAM);
    ...
};

 

2. 初始化類(InitClass)

WINX引入了許多小巧的初始化類。大致有:

  • CComAppInit - COM初始化類,即CoInitialize/CoUninitialize對。 
  • COleAppInit - OLE初始化類,即OleInitialize/OleUninitialize對。
  • CDebugAppInit - 啟動記憶體流失調試(僅Debug版本,Release版本為空白類)。
  • CComModuleInit - CComModule Init/Term。
  • CAppModuleInit - CAppModule Init/Term。
  • GdiplusAppInit - GdiplusStartup/GdiplusShutdown。

這些初始化類代碼簡單,但是抽象得恰到好處。在WINX之前,我曾經試圖把這些初始化過程封裝起來不讓使用者看到,但是最終不得不放棄。

 

3. 訊息迴圈(MessageLoop)

目前,WINX並未提供自己的訊息迴圈。我們借用WTL的CMessageLoop::Run。你沒有在WINX的例子中見到CMessageLoop,是因為它被CAppModuleInit 類隱藏起來了。

class CAppModuleInit : public WTL::CMessageLoop
{
public:
    CAppModuleInit(
        _ATL_OBJMAP_ENTRY* p = NULL,
        HINSTANCE hInst = GetThisModule(),
        const GUID* plibid = NULL)
    {
        _Module.Init(p, hInst, plibid);
        _Module.AddMessageLoop(this);
    }
    ~CAppModuleInit()
    {
        _Module.Term();
    }
};

 

4. 視窗過程(WindowProc)

訊息迴圈中,訊息最終被Windows發送到視窗過程(WindowProc)中。那麼WINX的視窗過程在哪?

template <class WindowClass, class HandleClass = DefaultWindowHandle>
class Window
{
public:
    static LRESULT CALLBACK WindowProc(
        HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
        WindowClass* pWnd = (WindowClass*)WindowMap::GetWindow(hWnd);
        if (pWnd == NULL)
        {
            if (message != WM_NCCREATE)
                return pWnd->InternalDefault(hWnd, message, wParam, lParam);
            LPCREATESTRUCT lpCS = (LPCREATESTRUCT)lParam;
            if (lpCS->lpCreateParams) {
                pWnd = (WindowClass*)lpCS->lpCreateParams;
                lpCS->lpCreateParams = NULL;
            }
            else {
                if (WindowClass::StackWindowObject) {
                    WINX_ASSERT("WindowClass::StackWindowObject - unexpected!");
                    return FALSE;
                }
                else {
                    pWnd = WINX_NEW(WindowClass);
                }
            }
            WindowMap::SetWindow(hWnd, pWnd);
        }
        return pWnd->ProcessMessage(hWnd, message, wParam, lParam);
    }
};

這裡面有幾個細節需要解釋:

  • WindowMap::GetWindow/SetWindow是什嗎?在介紹“SW系統的視窗類別”時,我們提到:
       - MFC通過一個全域的HashMap建立視窗控制代碼(hWnd)到視窗對象(pWnd)的映射。
       - SW系統通過視窗的UserData建立視窗控制代碼(hWnd)到視窗對象(pWnd)的映射。

    在WINX中,建立兩者映射的策略是任選的。除了以上兩者中外,還有第三種選擇:
      - 使用SetProp/GetProp建立映射。並且這是WINX預設的選擇。
      - 你自己實現的其他方式。

    我們簡單分析一下,這些方式的利弊。
      - 通過HashMap建立映射,問題在於這個HashMap對象如何在其他的DLL中取到?這導致bug或者強耦合的結構。
      - 通過視窗的UserData建立映射,問題在於如果UserData已經被佔用怎麼辦?這導致機制上不安全的隱患。
      - 使用SetProp/GetProp建立映射,效能比UserData方式慢,但極其安全。
     

  • WindowClass::StackWindowObject是什嗎?我們知道,對象(當然包括C++視窗對象)有兩種建立方式:
      - 建立在棧(Stack)上。即以局部自動變數方式建立。
      - 建立在堆(Heap)上。即通過new/delete建立。
    WINX允許你為視窗類別選擇其中一種。詳細我們在以後由專文敘述。
     
  • 最後,視窗訊息被pWnd->ProcessMessage(hWnd, message, wParam, lParam)處理。ProcessMessage進行了最終的訊息指派。這一塊是WINX訊息機制的核心,前面我們我們已經仔仔細細作了講解:
      - WINX的訊息指派機制
      - WINX的訊息指派機制(續)
      - WINX的訊息指派機制(續2)
      - WINX的訊息指派機制(終結篇)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.