青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆-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()方法?,F在我們返回主窗口,添加一個帶有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 閱讀(420) 評論(0)  編輯 收藏 引用 所屬分類: WTL

只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产精品视频精品视频| 国产日韩精品视频一区二区三区| 亚洲国产精品一区在线观看不卡 | aaa亚洲精品一二三区| 一区二区亚洲| 伊人婷婷欧美激情| 在线观看国产精品网站| 在线日韩成人| 一区二区不卡在线视频 午夜欧美不卡在 | 久久久欧美一区二区| 久久全国免费视频| 亚洲电影免费观看高清完整版在线| 美女脱光内衣内裤视频久久网站| 欧美 日韩 国产精品免费观看| 91久久国产综合久久| 宅男在线国产精品| 在线播放视频一区| 亚洲视频在线二区| 亚洲尤物在线视频观看| 午夜天堂精品久久久久| 久久影院午夜论| 亚洲国产欧美一区| 亚洲男人影院| 欧美xxx在线观看| 国产精品成人一区二区艾草| 国产亚洲成人一区| 99国产精品私拍| 理论片一区二区在线| 日韩一级网站| 老司机精品视频网站| 国产精品美女一区二区在线观看| 激情欧美一区二区| 亚洲免费视频观看| 亚洲人成网站精品片在线观看| 午夜视频在线观看一区二区三区| 欧美另类专区| 亚洲国产婷婷香蕉久久久久久99| 久久激情综合| 亚洲男女自偷自拍| 欧美日韩综合久久| 亚洲精品少妇| 欧美二区乱c少妇| 欧美一区亚洲一区| 国产欧美视频一区二区三区| 中文国产成人精品久久一| 欧美激情片在线观看| 久久理论片午夜琪琪电影网| 国产美女在线精品免费观看| 亚洲欧美日韩国产一区二区三区| 亚洲精选久久| 欧美精品一区二区三区蜜桃| 亚洲黄色免费网站| 久久精品亚洲| 亚欧成人精品| 国产精品一区二区女厕厕| 午夜精品久久久久久久蜜桃app| 久久夜色精品国产欧美乱极品| 先锋资源久久| 亚洲午夜激情网页| 欧美一级网站| 亚洲视频播放| 模特精品在线| 久久久久久久高潮| 国产精品va| 亚洲高清久久网| 欧美—级高清免费播放| 国产欧亚日韩视频| 久久香蕉国产线看观看av| 欧美aaaaaaaa牛牛影院| 99在线精品视频| 免费观看一级特黄欧美大片| 久久精品国产亚洲5555| 亚洲欧美日韩成人| 亚洲国产精品成人精品| 久久不射网站| 久久精品国产免费观看| 亚洲淫片在线视频| 一本色道久久88亚洲综合88| 欧美在线一区二区| 久久国产婷婷国产香蕉| 国产精品www.| 一区二区毛片| 亚洲伊人观看| 欧美午夜精彩| 亚洲精品在线三区| 一区二区三区色| 欧美精品色综合| 亚洲激情成人在线| 亚洲精品美女在线观看播放| 美国三级日本三级久久99| 男人的天堂亚洲| 在线免费高清一区二区三区| 久久久精品免费视频| 久久野战av| 亚洲国产另类精品专区| 欧美成年人网站| 最近中文字幕日韩精品| 亚洲精品激情| 欧美日韩中文字幕在线视频| 一本色道久久综合亚洲精品不| 亚洲天堂成人在线视频| 欧美亚韩一区| 亚洲欧美视频在线观看视频| 久久精品国产亚洲aⅴ| 激情综合久久| 欧美18av| 99在线热播精品免费99热| 亚洲欧美精品一区| 国产在线拍偷自揄拍精品| 久久久久久夜精品精品免费| 欧美黄色精品| 亚洲午夜久久久久久尤物 | 一区二区三区四区蜜桃| 欧美日韩综合久久| 午夜性色一区二区三区免费视频| 久久精品国产清高在天天线| 激情六月婷婷久久| 欧美激情导航| 亚洲欧美国产三级| 欧美成人午夜激情在线| 亚洲色图在线视频| 国产午夜精品全部视频播放| 久久国产66| 亚洲精品视频一区二区三区| 欧美在线视频不卡| 亚洲精品一区二区三区99| 国产精品国产三级国产 | 亚洲电影免费在线观看| 欧美日韩国产一级片| 午夜精品久久久久久| 欧美韩日一区| 久久不射2019中文字幕| 亚洲精品护士| 国产一区二区日韩精品欧美精品| 欧美成人亚洲| 欧美一级播放| 日韩视频在线观看免费| 久久亚洲精品一区| 一区二区三区精品视频在线观看 | 久久三级福利| 91久久国产综合久久91精品网站| 欧美日韩亚洲一区二区三区在线观看| 亚洲欧美日韩在线一区| 亚洲激精日韩激精欧美精品| 久久久激情视频| 亚洲午夜av| 亚洲精品色婷婷福利天堂| 国产最新精品精品你懂的| 欧美日本一道本在线视频| 久久久久一区二区| 午夜欧美精品久久久久久久| 亚洲精品九九| 欧美成在线视频| 久久久999精品| 亚洲欧美日韩综合国产aⅴ| 亚洲精品一区二区三区福利| 在线成人亚洲| 国内一区二区在线视频观看 | 亚洲视频在线观看三级| 亚洲国产第一页| 国产综合亚洲精品一区二| 国产精品无码专区在线观看| 欧美日韩免费观看一区二区三区 | 欧美人妖另类| 美女爽到呻吟久久久久| 欧美一级网站| 先锋资源久久| 欧美中文字幕在线播放| 亚洲欧美激情在线视频| 亚洲制服丝袜在线| 亚洲欧美精品一区| 性欧美大战久久久久久久久| 亚洲香蕉成视频在线观看| 一区二区三区成人| 在线视频亚洲欧美| 亚洲色诱最新| 亚洲午夜女主播在线直播| 中日韩高清电影网| 亚洲无线视频| 午夜精品久久| 久久人人精品| 欧美高清日韩| 国产精品高潮在线| 国产欧美91| 伊人夜夜躁av伊人久久| 尤物精品在线| 亚洲精品久久7777| 亚洲桃色在线一区| 欧美亚洲免费电影| 欧美在线综合| 欧美jizzhd精品欧美喷水 | 久久久精品网| 蜜臀av性久久久久蜜臀aⅴ四虎| 老司机免费视频一区二区| 免费观看日韩| 欧美午夜精品久久久久久浪潮| 国产精品久久久久久久久| 国产精品国产自产拍高清av王其| 国产日韩av高清| 亚洲国产美女久久久久|