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

ATL GUI 程式設計(二):最簡視窗程式的 ATL 轉型實戰

     編輯:關於VC++

章節導讀

上一章的「Hello, World!」ATL 版可能讓許多朋友不屑一顧——它只多了一個 CComModule,連視窗類註冊、訊息迴路都沒有。但我堅持:它幾乎已擁有 ATL GUI 程式的所有組成:入口、初始化、程式主體、卸載。

「等一下!」你可能打斷:「註冊視窗類、訊息迴路呢?」沒錯,對完整 GUI 程式來說這也是必要的。

基礎回顧:標準 Win32 SDK 視窗程式

為照顧熟悉 MFC 卻不諳 Win32 基礎的讀者,特別準備此節(SDK 愛好者可跳過)。

//////////////////////////////////////////////////////////////////////////
// ATL GUI 程式設計配套原始碼
// 第二章 一個最簡單視窗程式的轉型
// 專案名稱:HelloSDK
// 作者:李馬
// https://www.titilima.cn
//////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <tchar.h>
LRESULT CALLBACK HelloWndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
    switch ( uMsg )
    {
    case WM_DESTROY:
        { PostQuitMessage( 0 ); }
        break;
    case WM_PAINT:
        {
            HDC hdc; PAINTSTRUCT ps;
            hdc = BeginPaint( hWnd, &ps );
            DrawText( hdc, _T("Hello, SDK!"), -1, &ps.rcPaint, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
            EndPaint( hWnd, &ps );
        }
        break;
    default:
        return DefWindowProc( hWnd, uMsg, wParam, lParam );
    }
    return 0;
}
BOOL InitApplication( HINSTANCE hInstance )
{
    WNDCLASS wc;
    wc.cbClsExtra = 0; wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
    wc.hCursor = LoadCursor( NULL, IDC_ARROW );
    wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    wc.hInstance = hInstance;
    wc.lpfnWndProc = HelloWndProc;
    wc.lpszClassName = _T("HelloSDK");
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW | CS_VREDRAW;
    return RegisterClass( &wc );
}
int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd )
{
    InitApplication( hInstance ); // 註冊視窗類
    HWND hWnd = CreateWindow( _T("HelloSDK"), _T("Hello SDK"), WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
    ShowWindow( hWnd, nShowCmd );
    UpdateWindow( hWnd );
    MSG msg;
    while ( GetMessage( &msg, NULL, 0, 0 ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    return msg.wParam;
}

這段代碼雖顯冗長,實則條理分明,體現 Win32 GUI 完整流程:

  • 註冊視窗類:InitApplication 完成。視窗類似 OOP 中的「類」,但包含的是「屬性與響應」而非「屬性與方法」。
  • 建立視窗:通常封裝在 InitInstance,負責建立並顯示視窗。
  • 訊息迴路:Windows 以訊息機制通訊,迴路提取佇列訊息並分發給目標視窗。HelloWndProc 為回呼函數,處理 WM_DESTROY(發送 WM_QUIT 結束迴路)、WM_PAINT(繪製文字)、其他訊息交給 DefWindowProc。

ATL 等效實現

先展示 ATL 版 WinMain:

int WINAPI _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nShowCmd )
{
    _Module.Init( NULL, hInstance );
    CHelloATLWnd wnd;
    wnd.Create( NULL, CHelloATLWnd::rcDefault, _T("Hello ATL") );
    wnd.ShowWindow( nShowCmd );
    wnd.UpdateWindow();
    MSG msg;
    while ( GetMessage( &msg, NULL, 0, 0 ) )
    {
        TranslateMessage( &msg );
        DispatchMessage( &msg );
    }
    _Module.Term();
    return msg.wParam;
}

_Module 依舊只有 Init/Term,訊息迴路也未變。關鍵差異在 CHelloATLWnd——你可能猜測:

  1. 封裝 Win32 視窗類的 C++ 類
  2. 封裝 CreateWindow、ShowWindow 等 API
  3. 視窗類註冊也在類內完成

揭開 CHelloATLWnd 面紗:

class CHelloATLWnd : public CWindowImpl< CHelloATLWnd, CWindow, CWinTraits< WS_OVERLAPPEDWINDOW > >
{
public:
    CHelloATLWnd()
    {
        CWndClassInfo& wci = GetWndClassInfo();
        wci.m_bSystemCursor = TRUE;
        wci.m_lpszCursorID = IDC_ARROW;
        wci.m_wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
        wci.m_wc.hIcon = LoadIcon( NULL, IDI_APPLICATION );
    }
public:
    DECLARE_WND_CLASS( _T("HelloATL") )
public:
    BEGIN_MSG_MAP( CHelloATLWnd )
        MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
        MESSAGE_HANDLER( WM_PAINT, OnPaint )
    END_MSG_MAP()
public:
    LRESULT OnDestroy( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& hHandled )
    { ::PostQuitMessage( 0 ); return 0; }
    LRESULT OnPaint( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& hHandled )
    {
        HDC hdc; PAINTSTRUCT ps;
        hdc = BeginPaint( &ps );
        DrawText( hdc, _T("Hello, ATL!"), -1, &ps.rcPaint, DT_CENTER | DT_VCENTER | DT_SINGLELINE );
        EndPaint( &ps );
        return 0;
    }
};

猜想與疑問:留待下章解答

本章不解釋細節,繼續猜想:

  • 視窗類註冊由建構函數與 DECLARE_WND_CLASS 宏完成。
  • BEGIN_MSG_MAP/END_MSG_MAP 為 ATL 訊息映射,MESSAGE_HANDLER 分發訊息。
  • 視窗樣式與模板參數 CWinTraits 有關。

疑問:CWindowImpl、CWindow、CWinTraits 是什麼?視窗類何時註冊?訊息分發如何實現?請期待下章,或直接閱讀 atlwin.h 原始碼。

補敘:CComModule 在 ATL 7.0 的變化

本書針對 ATL 3.0/VC6.0,疏忽了 CComModule 在 ATL 7.0 的演進。感謝網友指點,ATL 7.0 中 CComModule 功能拆分為多個類:

  • CAtlBaseModule:模組與資源實例 HINSTANCE
  • CAtlComModule:COM 類資訊
  • CAtlWinModule:視窗類資訊
  • CAtlDebugInterfacesModule:介面除錯支援
  • CAtlModule 派生類:CAtlDllModuleT (DLL)、CAtlExeModuleT (EXE)、CAtlServiceModuleT (服務)

拆分原因:功能顆粒化、不同功能分層、自動宣告全域實例、消除手動呼叫 Init/Term(移至建構/解構函數)。但為相容性與 WTL 考量,本系列仍沿用 ATL 3.0 CComModule。

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