• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆-250  評論-20  文章-55  trackbacks-0
            README.TXT

            在你開始使用WTL或著在本文章的討論區張貼消息之前,我想請你先閱讀下面的材料。

            你需要開發平臺SDK(Platform SDK)。你要使用WTL不能沒有它,你可以使用在線升級安裝開發平臺SDK,也可以下載全部文件后在本地安裝。在使用之前要將SDK的包含文件(.h頭文件)和庫文件(.Lib文件)路徑添加到VC的搜索目錄,SDK有現成的工具完成這個工作,這個工具位于開發平臺SDK程序組的“Visual Studio Registration”文件夾里。

            你需要安裝 WTL。你可以從微軟的網站上下載WTL的7.0版,(*編輯注:WTL也可以到VC知識庫http://www.vckbase.com/tools下載) 在安裝之前可以先查看“Introduction to WTL - Part 1”和“Easy installation of WTL”這兩篇文章,了解一下所要安裝的文件的信息,雖然現在這些文章有些過時,但還是可以提供很多有用的信息。有一件我認為不該在本篇文章中提到的事是告訴VC如何搜索WTL的包含文件路徑,如果你用的VC6,用鼠標點擊 Tools\Options,轉到Directories標簽頁,在顯示路徑的列表框中選擇Include Files,然后將WTL的包含文件的存放路徑添加到包含文件搜索路徑列表中。

            你需要了解MFC。很好地了解MFC將有助于你理解后面提到的有關消息映射的宏并能夠編輯那些標有“不要編輯(DO NOT EDIT)”的代碼而不會出現問題。

            你需要清楚地知道如何使用Win32 API編程。如果你是直接從MFC開始學習Windows編程,沒有學過API級別的消息處理方式,那很不幸你會在使用WTL時遇到麻煩。如果不了解Windows消息中WPARAM參數和LPARAM參數的意義,應該明白需要讀一些這方面的文章(在CodeProject有大量的此類文章)。

            你需要知道 C++ 模板的語法,你可以到VC Forum FAQ 相關的連接尋求答案。

            我只是討論了一些涵蓋VC 6的特點,不過據我了解所有的程序都可以在VC 7上使用。由于我不使用VC 7,我無法對那些在VC 7中出現的問題提供幫助,不過你還是可以放心的在此張貼你的問題,因為其他的人可能會幫助你。

            對本系列文章的總體介紹

            WTL 具有兩面性,確實是這樣的。它沒有MFC的界面(GUI)類庫那樣功能強大,但是能夠生成很小的可執行文件。如果你象我一樣使用MFC進行界面編程,你會覺得MFC提供的界面控件封裝使用起來非常舒服,更不用說MFC內置的消息處理機制。當然,如果你也象我一樣不希望自己的程序僅僅因為使用了MFC的框架就增加幾百K的大小的話,WTL就是你的選擇。當然,我們還要克服一些障礙:

            • ATL樣式的模板類初看起來有點怪異
            • 沒有類向導的支持,所以要手工處理所有的消息映射。
            • MSDN沒有正式的文檔支持,你需要到處去收集有關的文檔,甚至是查看WTL的源代碼。
            • 買不到參考書籍
            • 沒有微軟的官方支持
            • ATL/WTL的窗口與MFC的窗口有很大的不同,你所了解的有關MFC的知識并不全部適用與WTL。

            從另一方面講,WTL也有它自身的優勢:

            • 不需要學習或掌握復雜的文檔/視圖框架。
            • 具有MFC的基本的界面特色,比如DDX/DDV和命令狀態的自動更新功能(譯者加:比如菜單的Check標記和Enable標記)。
            • 增強了一些MFC的特性(比如更加易用的分隔窗口)。
            • 可生成比靜態鏈接的MFC程序更小的可執行文件(譯者加:WTL的所有源代碼都是靜態鏈接到你的程序中的)。
            • 你可以修正自己使用的WTL中的錯誤(BUG)而不會影響其他的應用程序(相比之下,如果你修正了有BUG的MFC/CRT動態庫就可能會引起其它應用程序的崩潰。
            • 如果你仍然需要使用MFC,MFC的窗口和ATL/WTL的窗口可以“和平共處”。(例如我工作中的一個原型就使用了了MFC的CFrameWnd,并在其內包含了WTL的CSplitterWindow,在CSplitterWindow中又使用了MFC的CDialogs -- 我并不是為了炫耀什么,只是修改了MFC的代碼使之能夠使用WTL的分割窗口,它比MFC的分割窗口好的多)。

            在這一系列文章中,我將首先介紹ATL的窗口類,畢竟WTL是構建與ATL之上的一系列附加類,所以需要很好的了解ATL的窗口類。介紹完ATL之后我將介紹WTL的特性以并展示它是如何使界面編程變得輕而易舉。

            對第一章的簡單介紹

            WTL是個很酷的工具,在理解這一點之前需要首先介紹ATL。WTL是構建與ATL之上的一系列附加類,如果你是個嚴格使用MFC的程序員那么你可能沒有機會接觸到ATL的界面類,所以請容忍我在開始WTL之前先羅索一些別的東西,繞道來介紹一下ATL是很有必要地。

            在本文的第一部分,我將給出一點ATL的背景知識,包括一些編寫ATL代碼必須知道的基本知識,快速的解釋一些令人不知所措的ATL模板類和基本的ATL窗口類。

            ATL 背景知識 ATL 和 WTL 的發展歷史

            “活動模板庫”(Active Template Library)是一個很古怪的名字,不是嗎?那些年紀大的人可能還記得它最初被稱為“網絡組件模板庫”,這可能是它更準確的稱呼,因為ATL的目的就是使編寫組件對象和ActiveX控件更容易一些(ATL是在微軟開發新產品ActiveX-某某的過程中開發的,那些ActiveX-某某現在被稱為某某.NET)。由于ATL是為了便于編寫組件對象而存在的,所以只提供了簡單的界面類,相當于MFC的窗口類(CWnd)和對話框類(CDialog)。幸運的是這些類非常的靈活,能夠在其基礎上構建象WTL這樣的附加類。

            WTL現在已經是第二次修正了,最初的版本是3.1,現在的版本是7(WTL的版本號之所以這樣選擇是為了與ATL的版本匹配,所以不存在1和2這樣的版本號)。WTL 3.1可以與VC 6和VC 7一起使用,但是在VC 7下需要定義幾個預處理標號。WTL 7向下兼容WTL 3.1,并且不作任何修改就可以與VC 7一起使用,現在看來沒有任何理由還使用3.1來進行新的開發工作。

            ATL-style 模板

            即使你能夠毫不費力地閱讀C++的模板類代碼,仍然有兩件事可能會使你有些頭暈,以下面這個類的定義為例:

            class  CMyWnd : public CWindowImpl<CMyWnd>
            {
                ...
            };  

            這樣作是合法的,因為C++的語法解釋說即使CMyWnd類只是被部分定義,類名CMyWnd已經被列入遞歸繼承列表,是可以使用的。將類名作為模板類的參數是因為ATL要做另一件詭秘的事情,那就是編譯期間的虛函數調用機制。

            如果你想要了解它是如何工作地,請看下面的例子:

            template <class T>
            class B1
            {
            public: 
                void SayHi() 
                {
                    T* pT = static_cast<T*>(this);   // HUH?? 我將在下面解釋
             
                    pT->PrintClassName();
                }
            protected:
                void PrintClassName() { cout << "This is B1"; }
            };
             
            class D1 : public B1<D1>
            {
                // No overridden functions at all
            };
             
            class D2 : public B1<D2>
            {
            protected:
                void PrintClassName() { cout << "This is D2"; }
            };
             
            main()
            {
                D1 d1;
                D2 d2;
             
                d1.SayHi();    // prints "This is B1"
                d2.SayHi();    // prints "This is D2"
            }

            這句代碼static_cast<T*>(this) 就是竅門所在。它根據函數調用時的特殊處理將指向B1類型的指針this指派為D1或D2類型的指針,因為模板代碼是在編譯其間生成的,所以只要編譯器生成正確的繼承列表,這樣指派就是安全的。(如果你寫成:

            class D3 : public B1<D2>

            就會有麻煩) 之所以安全是因為this對象只可能是指向D1或D2(在某些情況下)類型的對象,不會是其他的東西。注意這很像C++的多態性(polymorphism),只是SayHi()方法不是虛函數。

            要解釋這是如何工作的,首先看對每個SayHi()函數的調用,在第一個函數調用,對象B1被指派為D1,所以代碼被解釋成:

            void B1<D1>::SayHi()
            {
                D1* pT = static_cast<D1*>(this);
             
                pT->PrintClassName();
            }

            由于D1沒有重載PrintClassName(),所以查看基類B1,B1有PrintClassName(),所以B1的PrintClassName()被調用。

            現在看第二個函數調用SayHi(),這一次對象被指派為D2類型,SayHi()被解釋成:

            void B1<D2>::SayHi()
            {
                D2* pT = static_cast<D2*>(this);
             
                pT->PrintClassName();
            }

            這一次,D2含有PrintClassName()方法,所以D2的PrintClassName()方法被調用。

            這種技術的有利之處在于:

            • 不需要使用指向對象的指針。
            • 節省內存,因為不需要虛函數表。
            • 因為沒有虛函數表所以不會發生在運行時調用空指針指向的虛函數。
            • 所有的函數調用在編譯時確定(譯者加:區別于C++的虛函數機制使用的動態編連),有利于編譯程序對代碼的優化。

            節省虛函數表在這個例子中看起來無足輕重(每個虛函數只有4個字節),但是設想一下如果有15個基類,每個類含有20個方法,加起來就相當可觀了。

            ATL 窗口類

            好了,關于ATL的背景知識已經講的構多了,到了該正式講ATL的時候了。ATL在設計時接口定義和實現是嚴格區分開的,這在窗口類的設計中是最明顯的,這一點類似于COM,COM的接口定義和實現是完全分開的(或者可能有多個實現)。

            ATL有一個專門為窗口設計的接口,可以做全部的窗口操作,這就是CWindow。它實際上就是對HWND操作的包裝類,對幾乎所有以HWND句柄為第一個參數的窗口API的進行了封裝,例如:SetWindowText() 和 DestroyWindow()。CWindow類有一個公有成員m_hWnd,使你可以直接對窗口的句柄操作,CWindow還有一個操作符HWND,你可以講CWindow對象傳遞給以HWND為參數的函數,但這與CWnd::GetSafeHwnd()(譯者加:MFC的方法)沒有任何等同之處。

            CWindow 與 MFC 的CWnd類有很大的不同,創建一個CWindow對象占用很少的資源,因為只有一個數據成員,沒有MFC窗口中的對象鏈,MFC內部維持這一個對象鏈,此對象鏈將HWND映射到CWnd對象。還有一點與MFC的CWnd類不同的是當一個CWindow對象超出了作用域,它關聯的窗口并不被銷毀掉,這意味著你并不需要隨時記得分離你所創建的臨時CWindow對象。

            在ATL類中對窗口過程的實現是CWindowImpl。CWindowImpl 含有所有窗口實現代碼,例如:窗口類的注冊,窗口的子類化,消息映射以及基本的WindowProc()函數,可以看出這與MFC的設計有很大的不同,MFC將所有的代碼都放在一個CWnd類中。

            還有兩個獨立的類包含對話框的實現,它們分別是CDialogImpl 和 CAxDialogImpl,CDialogImpl 用于實現普通的對話框而CAxDialogImpl實現含有ActiveX控件的對話框。

            定義一個窗口的實現

            任何非對話框窗口都是從CWindowImpl 派生的,你的新類需要包含三件事情:

            1. 一個窗口類的定義
            2. 一個消息映射鏈
            3. 窗口使用的默認窗口類型,稱為window traits

            窗口類的定義通過DECLARE_WND_CLASS宏或DECLARE_WND_CLASS_EX宏來實現。這輛個宏定義了一個CWndClassInfo結構,這個結構封裝了WNDCLASSEX結構。DECLARE_WND_CLASS宏讓你指定窗口類的類名,其他參數使用默認設置,而DECLARE_WND_CLASS_EX宏還允許你指定窗口類的類型和窗口的背景顏色,你也可以用NULL作為類名,ATL會自動為你生成一個類名。

            讓我們開始定義一個新類,在后面的章節我會逐步的完成這個類的定義。

            class CMyWindow : public CWindowImpl<CMyWindow>
            {
            public:
                DECLARE_WND_CLASS(_T("My Window Class"))
            };

            接下來是消息映射鏈,ATL的消息映射鏈比MFC的簡單的多,ATL的消息映射鏈被展開為switch語句,switch語句正確的消息處理者并調用相應的函數。使用消息映射鏈的宏是BEGIN_MSG_MAP 和 END_MSG_MAP,讓我們為我們的窗口添加一個空的消息映射鏈。

            class CMyWindow : public CWindowImpl<CMyWindow>
            {
            public:
                DECLARE_WND_CLASS(_T("My Window Class"))
             
                BEGIN_MSG_MAP(CMyWindow)
                END_MSG_MAP()
            };

            我將在下一節展開講如何如何添加消息處理到消息映射鏈。最后,我們需要為我們的窗口類定義窗口的特征,窗口的特征就是窗口類型和擴展窗口類型的聯合體,用于創建窗口時指定窗口的類型。窗口類型被指定為參數模板,所以窗口的調用者不需要為指定窗口的正確類型而煩心,下面是是同ATL類CWinTraits定義窗口類型的例子:

            typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,WS_EX_APPWINDOW> CMyWindowTraits;
             
            class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CMyWindowTraits>
            {
            public:
                DECLARE_WND_CLASS(_T("My Window Class"))
             
                BEGIN_MSG_MAP(CMyWindow)
                END_MSG_MAP()
            };

            調用者可以重載CMyWindowTraits的類型定義,但是一般情況下這是沒有必要的,ATL提供了幾個預先定義的特殊的類型,其中之一就是CFrameWinTraits,一個非常棒的框架窗口:

            typedef CWinTraits<WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
            WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> CFrameWinTraits;
            填寫消息映射鏈

            ATL的消息映射鏈是對開發者不太友好的部分,也是WTL對其改進最大的部分。類向導至少可以讓你添加消息響應,然而ATL沒有消息相關的宏和象MFC那樣的參數自動展開功能,在ATL中只有三種類型的消息處理,一個是WM_NOTIFY,一個是WM_COMMAND,第三類是其他窗口消息,讓我們開始為我們的窗口添加WM_CLOSE 和 WM_DESTROY的消息相應函數。

            class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
            {
            public:
                DECLARE_WND_CLASS(_T("My Window Class"))
             
                BEGIN_MSG_MAP(CMyWindow)
                    MESSAGE_HANDLER(WM_CLOSE, OnClose)
                    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
                END_MSG_MAP()
             
                LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    DestroyWindow();
                    return 0;
                }
             
                LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    PostQuitMessage(0);
                    return 0;
                }
            };

            你可能注意到消息響應函數的到的是原始的WPARAM 和 LPARAM值,你需要自己將其展開為相應的消息所需要的參數。還有第四個參數bHandled,這個參數在消息相應函數調用被ATL設置為TRUE,如果在你的消息響應處理完之后需要ATL調用默認的WindowProc()處理該消息,你可以講bHandled設置為FALSE。這與MFC不同,MFC是顯示的調用基類的響應函數來實現的默認的消息處理的。

            讓我們也添加一個對WM_COMMAND消息的處理,假設我們的窗口有一個ID為IDC_ABOUT的About菜單:

            class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>
            {
            public:
                DECLARE_WND_CLASS(_T("My Window Class"))
             
                BEGIN_MSG_MAP(CMyWindow)
                    MESSAGE_HANDLER(WM_CLOSE, OnClose)
                    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
                    COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
                END_MSG_MAP()
             
                LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    DestroyWindow();
                    return 0;
                }
             
                LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    PostQuitMessage(0);
                    return 0;
                }
             
                LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
                {
                    MessageBox ( _T("Sample ATL window"), _T("About MyWindow") );
                    return 0;
                }
            };

            需要注意得是COMMAND_HANDLER宏已經將消息的參數展開了,同樣,NOTIFY_HANDLER宏也將WM_NOTIFY消息的參數展開了。

            高級消息映射鏈和嵌入類

            ATL的另一個顯著不同之處就是任何一個C++類都可以響應消息,而MFC只是將消息響應任務分給了CWnd類和CCmdTarget類,外加幾個有PreTranslateMessage()方法的類。ATL的這種特性允許我們編寫所謂的“嵌入類”,為我們的窗口添加特性只需將該類添加到繼承列表中就行了,就這么簡單!

            一個基本的帶有消息映射鏈的類通常是模板類,將派生類的類名作為模板的參數,這樣它就可以訪問派生類中的成員,比如m_hWnd(CWindow類中的HWND成員)。讓我們來看一個嵌入類的例子,這個嵌入類通過響應WM_ERASEBKGND消息來畫窗口的背景。

            template <class T, COLORREF t_crBrushColor>
            class CPaintBkgnd : public CMessageMap
            {
            public:
                CPaintBkgnd() { m_hbrBkgnd = CreateSolidBrush(t_crBrushColor); }
                ~CPaintBkgnd() { DeleteObject ( m_hbrBkgnd ); }
             
                BEGIN_MSG_MAP(CPaintBkgnd)
                    MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
                END_MSG_MAP()
             
                LRESULT OnEraseBkgnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    T*   pT = static_cast<T*>(this);
                    HDC  dc = (HDC) wParam;
                    RECT rcClient;
             
                    pT->GetClientRect ( &rcClient );
                    FillRect ( dc, &rcClient, m_hbrBkgnd );
                    return 1;    // we painted the background
                }
             
            protected:
                HBRUSH m_hbrBkgnd;
            };

            讓我們來研究一下這個新類。首先,CPaintBkgnd有兩個模板參數:使用CPaintBkgnd的派生類的名字和用來畫窗口背景的顏色。(t_ 前綴通常用來作為模板類的模板參數的前綴)CPaintBkgnd也是從CMessageMap派生的,這并不是必須的,因為所有需要響應消息的類只需使用BEGIN_MSG_MAP宏就足夠了,所以你可能看到其他的一些嵌入類的例子代碼,它們并不是從該基類派生的。

            構造函數和析構函數都相當簡單,只是創建和銷毀Windows畫刷,這個畫刷由參數t_crBrushColor決定顏色。接著是消息映射鏈,它響應WM_ERASEBKGND消息,最后由響應函數OnEraseBkgnd()用構造函數創建的畫刷填充窗口的背景。在OnEraseBkgnd()中有兩件事需要注意,一個是它使用了一個派生的窗口類的方法(即GetClientRect()),我們如何知道派生類中有GetClientRect()方法呢?如果派生類中沒有這個方法我們的代碼也不會有任何抱怨,由編譯器確認派生類T是從CWindow派生的。另一個是OnEraseBkgnd()沒有將消息參數wParam展開為設備上下文(DC)。(WTL最終會解決這個問題,我們很快就可以看到,我保證)

            要在我們的窗口中使用這個嵌入類需要做兩件事:首先,將它加入到繼承列表:

            class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
                              public CPaintBkgnd<CMyWindow, RGB(0,0,255)>

            其次,需要CMyWindow將消息傳遞給CPaintBkgnd,就是將其鏈入到消息映射鏈,在CMyWindow的消息映射鏈中加入CHAIN_MSG_MAP宏:

            class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
                              public CPaintBkgnd<CMyWindow, RGB(0,0,255)> 
            {
            ...
            typedef CPaintBkgnd<CMyWindow, RGB(0,0,255)> CPaintBkgndBase;
             
                BEGIN_MSG_MAP(CMyWindow)
                    MESSAGE_HANDLER(WM_CLOSE, OnClose)
                    MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
                    COMMAND_HANDLER(IDC_ABOUT, OnAbout)
                    CHAIN_MSG_MAP(CPaintBkgndBase)
                END_MSG_MAP()
            ...
            };

            任何CMyWindow沒有處理的消息都被傳遞給CPaintBkgnd。應該注意的是WM_CLOSE,WM_DESTROY和IDC_ABOUT消息將不會傳遞,因為這些消息一旦被處理消息映射鏈的查找就會中止。使用typedef是必要地,因為宏是預處理宏,只能有一個參數,如果我們將CPaintBkgnd<CMyWindow, RGB(0,0,255)>作為參數傳遞,那個“,”會使預處理器認為我們使用了多個參數。

            你可以在繼承列表中使用多個嵌入類,每一個嵌入類使用一個CHAIN_MSG_MAP宏,這樣消息映射鏈就會將消息傳遞給它。這與MFC不同,MFC地CWnd派生類只能有一個基類,MFC自動將消息傳遞給基類。

            ATL程序的結構

            到目前為止我們已經有了一個完整地主窗口類(即使不完全有用),讓我們看看如何在程序中使用它。一個ATL程序包含一個CComModule類型的全局變量_Module,這和MFC的程序都有一個CWinApp類型的全局變量theApp有些類似,唯一不同的是在ATL中這個變量必須命名為_Module。

            下面是stdafx.h文件的開始部分:

            // stdafx.h:
            #define STRICT
            #define VC_EXTRALEAN
             
            #include <atlbase.h>        // 基本的ATL類
            extern CComModule _Module;  // 全局_Module
            #include <atlwin.h>         // ATL窗口類

            atlbase.h已經包含最基本的Window編程的頭文件,所以我們不需要在包含windows.h,tchar.h之類的頭文件。在CPP文件中聲明了_Module變量:

            // main.cpp:
            CComModule _Module;

            CComModule含有程序的初始化和關閉函數,需要在WinMain()中顯示的調用,讓我們從這里開始:

            // main.cpp:
            CComModule _Module;
             
            int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
                               LPSTR szCmdLine, int nCmdShow)
            {
                _Module.Init(NULL, hInst);
                _Module.Term();
            }

            Init()的第一個參數只有COM的服務程序才有用,由于我們的EXE不含有COM對象,我們只需將NULL傳遞給Init()就行了。ATL不提供自己的WinMain()和類似MFC的消息泵,所以我們需要創建CMyWindow對象并添加消息泵才能使我們的程序運行。

            // main.cpp:
            #include "MyWindow.h"
            CComModule _Module;
             
            int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hInstPrev,
                               LPSTR szCmdLine, int nCmdShow)
            {
                _Module.Init(NULL, hInst);
             
                CMyWindow wndMain;
                MSG msg;
             
                // Create & show our main window
                if ( NULL == wndMain.Create ( NULL, CWindow::rcDefault, 
                                             _T("My First ATL Window") ))
                {
                    // Bad news, window creation failed
                    return 1;
                 }
             
                wndMain.ShowWindow(nCmdShow);
                wndMain.UpdateWindow();
             
                // Run the message loop
                while ( GetMessage(&msg, NULL, 0, 0) > 0 )
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);
                }
             
                _Module.Term();
                return msg.wParam;
            }

            上面的代碼唯一需要說明的是CWindow::rcDefault,這是CWindow中的成員(靜態數據成員),數據類型是RECT。和調用CreateWindow() API時使用CW_USEDEFAULT指定窗口的寬度和高度一樣,ATL使用rcDefault作為窗口的最初大小。

            在ATL代碼內部,ATL使用了一些類似匯編語言的魔法將主窗口的句柄與相應的CMyWindow對象聯系起來,在外部看來就是可以毫無問題的在線程之間傳遞CWindow對象,而MFC的CWnd卻不能這樣作。

            這就是我們的窗口:

             [First ATL window - 4K]

            我得承認這確實沒有什么激動人心的地方。我們將添加一個About菜單并顯示一個對話框,主要是為它增加一些情趣。

            ATL中的對話框

            我們前面提到過,ATL有兩個對話框類,我們的About對話框使用CDialogImpl。生成一個新對話框和生成一個主窗口幾乎一樣,只有兩點不同:

            1. 窗口的基類是CDialogImpl而不是CWindowImpl。
            2. 你需要定義名稱為IDD的公有成員用來保存對話框資源的ID。

            現在開始為About對話框定義一個新類:

            class CAboutDlg : public CDialogImpl<CAboutDlg>
            {
            public:
                enum { IDD = IDD_ABOUT };
             
                BEGIN_MSG_MAP(CAboutDlg)
                END_MSG_MAP()
            };

            ATL沒有在內部實現對“OK”和“Cancel”兩個按鈕的響應處理,所以我們需要自己添加這些代碼,如果用戶用鼠標點擊標題欄的關閉按鈕,WM_CLOSE的響應函數就會被調用。我們還需要處理WM_INITDIALOG消息,這樣我們就能夠在對話框出現時正確的設置鍵盤焦點,下面是完整的類定義和消息響應函數。

            class CAboutDlg : public CDialogImpl<CAboutDlg>
            {
            public:
                enum { IDD = IDD_ABOUT };
             
                BEGIN_MSG_MAP(CAboutDlg)
                    MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)
                    MESSAGE_HANDLER(WM_CLOSE, OnClose)
                    COMMAND_ID_HANDLER(IDOK, OnOKCancel)
                    COMMAND_ID_HANDLER(IDCANCEL, OnOKCancel)
                END_MSG_MAP()
             
                LRESULT OnInitDialog(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    CenterWindow();
                    return TRUE;    // let the system set the focus
                }
             
                LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                    EndDialog(IDCANCEL);
                    return 0;
                }
             
                LRESULT OnOKCancel(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
                {
                    EndDialog(wID);
                    return 0;
                }
            };

            我使用一個消息響應函數同時處理“OK”和“Cancel”兩個按鈕的WM_COMMAND消息,因為命令響應函數的wID參數就已經指明了消息是來自“OK”按鈕還是來自“Cancel”按鈕。

            顯示對話框的方法與MFC相似,創建一個新對話框類的實例,然后調用DoModal()方法。現在我們返回主窗口,添加一個帶有About菜單項的菜單用來顯示我們的對話框,這需要再添加兩個消息響應函數,一個是響應WM_CREATE,另一個是響應菜單的IDC_ABOUT命令。

            class CMyWindow : public CWindowImpl<CMyWindow, CWindow, CFrameWinTraits>,
                              public CPaintBkgnd<CMyWindow,RGB(0,0,255)>
            {
            public:
                BEGIN_MSG_MAP(CMyWindow)
                    MESSAGE_HANDLER(WM_CREATE, OnCreate)
                    COMMAND_ID_HANDLER(IDC_ABOUT, OnAbout)
                    // ...
                    CHAIN_MSG_MAP(CPaintBkgndBase)
                END_MSG_MAP()
             
                LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
                {
                HMENU hmenu = LoadMenu ( _Module.GetResourceInstance(),
                                         MAKEINTRESOURCE(IDR_MENU1) );
             
                    SetMenu ( hmenu );
                    return 0;
                }
             
                LRESULT OnAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled)
                {
                CAboutDlg dlg;
             
                    dlg.DoModal();
                    return 0;
                }
                // ...
            };

            在指定對話框的父窗口的方式上有些不同,MFC是通過構造函數將父窗口的指針傳遞給對話框而在ATL中是將父窗口的指針作為DoModal()方法的第一個參數傳遞給對話框的,如果象上面的代碼一樣沒有指定父窗口,ATL會使用GetActiveWindow()得到的窗口(也就是我們的主框架窗口)作為對話框的父窗口。

            對LoadMenu()方法的調用展示了CComModule的另一個方法-GetResourceInstance(),它返回你的EXE的HINSTANCE實例,和MFC的AfxGetResourceHandle()方法相似。(當然還有CComModule::GetModuleInstance(),它相當于MFC的AfxGetInstanceHandle()。)

            這就是主窗口和對話框的顯示效果:

             [About box - 5K]

            我會繼續講WTL,我保證!

            我會繼續講WTL的,只是會在第二部分。我覺得既然這些文章是寫給使用MFC的開發者的,所以有必要在投入WTL之前先介紹一些ATL。如果你是第一次接觸到ATL,那現在你就可以嘗試寫一些小程序,處理消息和使用嵌入類,你也可以嘗試用類向導支持消息映射鏈,使它能夠自動添加消息響應。現在就開始,右鍵單擊CMyWindow項,在彈出的上下文菜單中單擊“Add Windows Message Handler”菜單項。

            在第二部分,我將全面介紹基本的WTL窗口類和一個更好的消息映射宏。

            posted on 2007-03-09 11:46 jay 閱讀(395) 評論(0)  編輯 收藏 引用 所屬分類: WTL
            久久亚洲国产午夜精品理论片| 婷婷久久精品国产| 国产精品99精品久久免费| 伊人久久免费视频| 婷婷综合久久狠狠色99h| 久久精品国产亚洲AV无码麻豆 | 国产免费久久精品99久久| 久久国产成人| 亚洲乱码日产精品a级毛片久久| 婷婷久久五月天| 色婷婷综合久久久久中文| 丰满少妇高潮惨叫久久久| 99久久人妻无码精品系列蜜桃| 国产精品久久久久久影院 | 成人午夜精品久久久久久久小说 | 午夜精品久久久内射近拍高清 | 久久se精品一区二区影院 | 91精品国产综合久久香蕉 | 亚洲色欲久久久久综合网| 91精品国产91久久综合| 亚洲国产视频久久| 国产成人精品久久综合| 久久精品国产亚洲AV不卡| 青草久久久国产线免观| 97久久精品人人澡人人爽| 99久久免费国产精品特黄| 久久99久国产麻精品66| 久久精品国产亚洲Aⅴ香蕉| 久久国产精品久久久| 久久精品人人做人人妻人人玩| 久久精品国产清自在天天线| 亚洲美日韩Av中文字幕无码久久久妻妇| 香蕉久久夜色精品国产小说| 久久91精品久久91综合| 99久久婷婷国产综合亚洲| 亚洲AV无码久久寂寞少妇| 怡红院日本一道日本久久 | 色综合久久无码五十路人妻| 亚洲国产一成久久精品国产成人综合| 99久久久久| 久久久久无码中|