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

隨筆-250  評論-20  文章-55  trackbacks-0
摘要:討論Active Template Library (ATL) 3.0中的一些類,這些類圍繞著Windows API建立了一個面向對象的編程框架,使用這個框架,可以簡化Microsoft® Windows®編程并且只需要很少的系統開銷。內容包括:考察對窗口做了簡單封裝的CWindow類;使用CWindowImpl進行消息處理和消息映射;使用ATL中的對話框類以及擴展現有窗口類的功能的方法。

簡介:
雖然Active Template Library (ATL)主要是為了支持COM開發而設計的,但它確實包含了很多可用于窗口設計的類。這些窗口類和ATL中的其它類一樣,都是基于模版的,并且只需要花費很少系統開銷。這篇文章就向我們演示了使用ATL創建窗口和對話框并進行消息處理的基本方法。
這篇文章假設讀者熟悉C++語言和Windows程序設計;但是并不一定要求讀者具有COM方面的知識。

CWindow:
在ATL窗口類中,CWindow是最基本的。這個類對Windows API進行了面向對象的包裝,它封裝了一個窗口句柄,并提供一些成員函數來操作它,這些函數包裝了相應的Windows API。

標準的Windows程序設計看起來象這樣:
HWND hWnd = ::CreateWindow( "button", "Click me", 
WS_CHILD, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
::ShowWindow( hWnd, nCmdShow );
::UpdateWindow( hWnd );
使用ATL中的CWindow類后,等效代碼如下:
CWindow win;
win.Create( "button", NULL, CWindow::rcDefault, "Click me",
WS_CHILD );
win.ShowWindow( nCmdShow );
win.UpdateWindow();
我們應該在我們的大腦中我們應該保持這樣一個概念:ATL的窗口對象與Windows系統中的窗口是不同的。Windows系統中的窗口指的是操作系統中維持的一塊數據,操作系統靠這塊數據來操作屏幕上的一塊區域。而一個ATL窗口對象,是CWindow類的一個實例,它是一個C++對象,它的內部沒有保存任何有關屏幕區域或者窗口數據結構的內容,只保存了一個窗口的句柄,這個句柄保存在它的數據成員m_hWnd中,CWindow對象和它在屏幕上顯示出來的窗口就是靠這個句柄聯系起來的。
理解了ATL中的窗口對象和Windows系統中窗口的區別,就更加容易理解CWindow對象的構造與窗口的創建是兩個分開的過程。我們再看看前面的代碼,就會發現,首先是一個CWindow對象被構造:
CWindow win;
然后創建它的窗口:
win.Create( "button", NULL, CWindow::rcDefault, "Click me",
WS_CHILD );
我們也可以構造一個CWindow對象,然后把它和一個已經存在的窗口關聯起來,這樣我們就可以通過CWindow類的成員函數來操作這個已經存在的窗口。這種方法非常有用,因為CWindow類提供的函數都是封裝好了的,用起來很方便,比如CWindow類中的CenterWindow, GetDescendantWindow等函數用起來就比直接使用Windows API方便得多。
HWND hWnd = CreateWindow( szWndClass, "Main window",
WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, NULL, NULL, hInstance, NULL );
// 下面的方法中可以任選一種:
//      CWindow win( hWnd );      // 通過構造函數關聯
// 或
//      CWindow win;
//      win = hWnd;               // 通過賦值操作符關聯
// 或
//      CWindow win;
//      win.Attach( hWnd );      // 使用Attach()方法關聯
win.CenterWindow();      // 現在可以使用win對象來代替hWnd進行操作
win.ShowWindow( nCmdShow );
win.UpdateWindow();
CWindow類也提供了一個HWND操作符,可以把CWindow類的對象轉化為窗口句柄,這樣,任何要求使用HWND的地方都可以使用CWindow類的對象代替:
::ShowWindow( win, nCmdShow );      // 此API函數本來要求HWND類型的參數
CWindow類使得對窗口的操作更簡單,而且不會增加系統開銷——它經過編譯和優化后的代碼與使用純API編程的代碼是等價的。

不幸的是,CWindow類不能讓我們自己決定窗口如何響應消息。當然,我們可以使用CWindow類提供的方法來使一個窗口居中或隱藏,甚至可以向一個窗口發送消息,但是當窗口收到消息后怎么處理則取決于創建這個窗口時使用的窗口類,如果我們是創建的是”button”類的窗口,那么它的表現就象個按鈕,如果用”listbox”類創建,那它就具有跟列表框相同的行為,使用CWindow類我們沒有辦法改變這點。幸好,ATL為我們提供了另外一個類CWindowImpl,它允許我們指定窗口的新行為。

CWindowImpl:
CWindowImpl類是從CWindow類派生的,所以我們依然可以使用CWindow類中的成員函數,但是CWindowImpl類的功能更強大,它允許我們指定窗口怎樣處理消息。在傳統的窗口編程中,如果我們要處理窗口消息,我們必須使用窗口函數;但是使用ATL,我們只需要在我們的ATL窗口類中定義一個消息映射。

首先,從CWindowImpl類派生自己的窗口類,如下:
class CMyWindow : public CWindowImpl
{
注意,我們自己的類名必須作為一個模版參數傳遞給CWindowImpl類。

然后在類的定義里面定義如下的消息映射:
BEGIN_MSG_MAP(CMyWindow)
   MESSAGE_HANDLER(WM_PAINT,OnPaint)
   MESSAGE_HANDLER(WM_CREATE,OnCreate)
   MESSAGE_HANDLER(WM_DESTROY,OnDestroy)
END_MSG_MAP()
下面這句
MESSAGE_HANDLER(WM_PAINT,OnPaint)
的意思是,當WM_PAINT消息到達時,將調用CMyWindow::OnPaint成員函數。

最后就是定義處理消息的函數了,如下:
LRESULT OnPaint(
   UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{ ... 
}
LRESULT OnCreate(
   UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{ ...
}
LRESULT OnDestroy(
   UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{ ... 
}
}; // CmyWindow
這些函數中的參數意義為:第一個是消息ID,中間的兩個參數的意義取決于消息類型,第四個參數是一個標志,用它來決定這個消息是已經處理完了還是需要進一步的處理。關于這些參數,我們在Message Map小結有更詳細的討論。

當窗口收到一個消息,它將從消息映射表的頂部開始查找匹配的消息處理函數,因此把最常用的消息放在消息映射表的前面是個不錯的注意。如果沒有找到匹配的消息處理函數,則這個消息被發送到默認的窗口過程進行處理。

ATL的消息映射表封裝了Windows的消息處理過程,它比傳統的窗口函數中的大量switch分支或者if語句看起來更加直觀。

要創建一個基于CWindowImpl派生類的窗口,請調用CWindowImpl類的Create方法:
CMyWindow wnd;      // 構造一個 CMyWindow 類的對象
wnd.Create( NULL, CWindow::rcDefault, _T("Hello"),
   WS_OVERLAPPEDWINDOW|WS_VISIBLE );
注意,CWindowImpl類的Create方法與CWindow類的Create方法略有不同,在CWindow類的Create中,我們必須指定一個注冊了的窗口類,但是CWindowImpl則不同,它創建一個新的窗口類,因此,不需要為它指定窗口類。

一個簡單而完整的示例:

這篇文章中的大部分示例都只是代碼片段,但是下面列出的是一個完整的Hello world的示例程序。雖然我們使用的是ATL,但是沒有涉及到COM,因此在使用Visual C++®建立項目的時候,我們選擇Win32® application而不是ATL COM:
在stdafx.h文件中,加入下面幾行:
#include <atlbase.h>
extern CComModule _Module;
#include <atlwin.h>
在hello.cpp文件中,寫如下代碼:
#include "stdafx.h"
CComModule _Module;
class CMyWindow : public CWindowImpl<CMyWindow> {
   BEGIN_MSG_MAP( CMyWindow )
      MESSAGE_HANDLER( WM_PAINT, OnPaint )
      MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
   END_MSG_MAP()

   LRESULT OnPaint( UINT, WPARAM, LPARAM, BOOL& ){
      PAINTSTRUCT ps;
      HDC hDC = GetDC();
      BeginPaint( &ps );
      TextOut( hDC, 0, 0, _T("Hello world"), 11 );
      EndPaint( &ps );
      return 0;
   }

   LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& ){
      PostQuitMessage( 0 );
      return 0;
   }
};

int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR, int )
{
   _Module.Init( NULL, hInstance );

   CMyWindow wnd;
   wnd.Create( NULL, CWindow::rcDefault, _T("Hello"),
      WS_OVERLAPPEDWINDOW|WS_VISIBLE );

   MSG msg;
   while( GetMessage( &msg, NULL, 0, 0 ) ){
      TranslateMessage( &msg );
      DispatchMessage( &msg );
   }

   _Module.Term();
   return msg.wParam;
}

在這個示例程序中,CmyWindow是從CWindowImpl派生的,它的消息映射捕獲了兩個消息WM_PAINT和WM_DESTROY,當收到WM_PAINT消息時,它的成員函數OnPaint處理這個消息并在窗口上輸出“Hello world”,當收到WM_DESTROY消息時,也就是當用戶關閉這個窗口的時候,調用OnDestroy函數處理這個消息,在OnDestroy函數中調用PostQuitMessage來結束消息循環。

WinMain函數中創建了一個CmyWindow類的實例并實現了一個標準的消息循環。(有一些地方,我們必須遵循ATL的規范,比如在這里我們必須使用_Module。)

消息映射:
有三組用于消息映射的宏,他們分別是:

  • 窗口消息映射宏,用于所有的窗口消息(如WM_CREATE、WM_PAINT等);
  • 命令消息映射宏,專用于WM_COMMAND消息(比如由控件或菜單發出的消息);
  • 通知消息映射宏,專用于WM_NOTUFY消息(通常由通用控件發出此消息,比如工具欄控件或列表視圖控件)

窗口消息映射宏:
有兩個窗口消息映射宏,他們分別是:

  • MESSAGE_HANDLER
  • MESSAGE_RANGE_HANDLER

第一個宏將一個特定的消息映射到相應的處理函數;第二個宏將一組消息映射到一個處理函數。消息處理函數都要求具有如下的原形:

LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);

其中,參數uMsg是消息標識,wParam和lParam是兩個附加與消息的參數,(他們的具體意義取決與消息類別。)

消息處理函數使用bHandled來標志消息是否已經被完全捕獲,如果bHandled被設置成FALSE,程序將繼續在消息映射表的后續部分查找這個消息的其它處理函數。這個特性使得我們對一個消息使用多個處理函數成為可能。什么時候需要對一個消息使用多個處理函數呢?可能是在對多個類鏈接時,也可能是我們只想對一個消息做出響應但是并不真正捕獲它。在處理函數被調用之前,bHandled被置為TRUE,所以如果我們不在函數的結尾顯式地將它置為FALSE,則消息映射表的后續部分不會被繼續查找,也不會有其它的處理函數被調用。

命令消息映射宏:
命令消息映射宏只處理命令消息(WM_COMMAND消息),但是它能讓我們根據消息類型或者發送命令消息的控件ID來指定消息處理函數。

  • COMMAND_HANDLER映射一個特定控件的一條特定消息到一個處理函數;
  • COMMAND_ID_HANDLER映射一個特定控件的所有消息到一個處理函數;
  • COMMAND_CODE_HANDLER映射任意控件的一個特定消息到一個處理函數;
  • COMMAND_RANGE_HANDLER映射一定范圍內的控件的所有消息到一個處理函數;
  • COMMAND_RANGE_CODE_HANDLER映射一定范圍內的控件的一條特定消息到一個處理函數。

命令消息處理函數應該具有如下的原形:

LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled);

其中,參數wNotifyCode代表消息代碼,wID代表發送消息的控件的ID,hWndCtl代表發送消息的控件的窗口句柄,bHandled的意義如前所述。

通知消息映射宏:
通知消息映射宏用來處理通知消息(WM_NOTUFY消息),它根據通知消息的類型和發送通知消息的控件的不同將消息映射到不同的處理函數,這些宏與前面講的命令消息映射宏是等價的,唯一的不同就是它處理的是通知消息而不是命令消息。

  • NOTIFY_HANDLER
  • NOTIFY_ID_HANDLER
  • NOTIFY_CODE_HANDLER
  • NOTIFY_RANGE_HANDLER
  • NOTIFY_RANGE_CODE_HANDLER

通知消息處理函數都需要如下的原形:

LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled);
其中,參數idCtrl代表發送通知消息的控件的ID,參數pnmh是指向一個NMHDR結構的指針,bHandled的意義如前所述。

通知消息包含了一個指向消息細節的結構的指針,例如,當一個列表視圖控件發送一個通知消息,這個消息就包含了一個指向NMLVDISPINFO結構的指針,所有類似于NMLVDISPINFO的結構都包含一個NMHDR結構的頭,pnmh就指向這個頭,如果需要訪問這種結構中頭部以外的其它數據成員,可以將pnmh轉化成相應類型的指針。

例如,我們如果要處理列表視圖控件發出的LVN_ENDLABELEDIT通知消息,我們可以把下面這行代碼放到消息映射表中:
NOTIFY_HANDLER( ID_LISTVIEW, LVN_ENDLABELEDIT, OnEndLabelEdit)
這個通知消息附帶的額外信息包含在一個NMLVDISPINFO結構中,因此,消息處理函數看起來應該象下面這個樣子:
LRESULT OnEndLabelEdit(int idCtrl, LPNMHDR pnmh, BOOL& bHandled)
{
   // The item is -1 if editing is being canceled. 
   if ( ((NMLVDISPINFO*)pnmh)->item.iItem == -1) return FALSE; 
   ...
可以看出,pnmh指針被轉化成NMLVDISPINFO*類型,以便訪問頭部結構以外的數據。

為現有的窗口類添加功能:
有許多向現有的窗口添加功能的方法。如果這個類是ATL窗口類,我們可以從這個窗口類派生自己的類,就象Base Class Chaining中描述的一樣。這種方法主要是一個C++類的繼承加上一點消息映射的鏈接。

如果我們想擴展一個預定義的窗口類(如按紐類或列表框類)的功能,我們可以超類化它。就是創建一個基于這個預定義類的新類,并在消息映射表中添加消息映射以增強它的功能。

有些時候,我們需要改變一個已經存在的窗口實例的行為,而不是一個窗口類——或許我們要讓一個對話框上的編輯框做點什么特別的事情。在這種情況下,我們可以寫一個新的ATL窗口類,并子類化這個已經存在的編輯框。任何本該發送到這個編輯框的消息都會先被發送到這個子類的對象。

另外一種可選的方法:我們也可以讓這個編輯框成為一個被包含的窗口,所有發送到這個編輯框的消息都會經過它的容器窗口;我們可以在這個容器窗口中為這個被包含的窗口實現特殊的消息處理。

最后的一種方法就是消息反射,當一個窗口收到一個消息后不處理它,而是反射給發送這個消息的窗口自己處理,這種技術可以用來創建自包含的控件。

基類消息鏈(Base Class Chaining):
如果我們已經有一些實現了特定功能的ATL窗口類,我們可以從它們派生新類以充分利用繼承的優點。比如:
class CBase: public CWindowImpl< CBase >
// simple base window class: shuts down app when closed
{
   BEGIN_MSG_MAP( CBase )
      MESSAGE_HANDLER( WM_DESTROY, OnDestroy )
   END_MSG_MAP()

   LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& )
   {
      PostQuitMessage( 0 );
      return 0;
   }
};

class CDerived: public CBase
// derived from CBase; handles mouse button events
{
   BEGIN_MSG_MAP( CDerived )
      MESSAGE_HANDLER( WM_LBUTTONDOWN, OnButtonDown )
   END_MSG_MAP()

   LRESULT OnButtonDown( UINT, WPARAM, LPARAM, BOOL& )
   {
      ATLTRACE( "button down\n" );
      return 0;
   }
};

// in WinMain():
   ...
   CDerived win;
   win.Create( NULL, CWindow::rcDefault, "derived window" );
可是,上面的代碼有一個問題。當我們在調試模式下運行這個程序,一個窗口出現了,如果我們在這個窗口中單擊,“button down”將出現在輸出窗口中,這是CDrived類的功能,可是,當我們關閉這個窗口的時候,程序并不退出,盡管CBase類處理了WM_DESTROY消息并且CDrived類是從CBase類派生的。

Why?因為我們必須明確地將一個消息映射表鏈接到另外一個。如下:
BEGIN_MSG_MAP( CDerived )
   MESSAGE_HANDLER( WM_LBUTTONDOWN, OnButtonDown )
   CHAIN_MSG_MAP( CBase ) // 鏈接到基類
END_MSG_MAP()
現在,任何在CDrived類中沒有被處理的消息都會被傳到CBase類中。

為什么不自動將派生類的消息映射和它的基類的消息映射鏈接起來呢?這是因為在ATL的體系結構中有很多多重繼承的情況,這種情況下沒有辦法知道究竟應該鏈接到哪個基類,所以只好讓程序員自己來做決定。

可選的消息映射:
消息映射鏈允許多個類同時進行消息處理,同時也帶來了問題:如果我們在多個類中都要響應WM_CREATE消息,但是不同的類需要基類提供不同的處理,怎么辦呢?為了解決這個問題,ATL使用了可選的消息映射:將消息映射表分成很多節,每一節用不同的數字標識,每一節都是一個可選的消息映射表。
// in class CBase:
   BEGIN_MSG_MAP( CBase )
      MESSAGE_HANDLER( WM_CREATE, OnCreate1 )
      MESSAGE_HANDLER( WM_PAINT, OnPaint1 )
      ALT_MSG_MAP( 100 )
      MESSAGE_HANDLER( WM_CREATE, OnCreate2 )
      MESSAGE_HANDLER( WM_PAINT, OnPaint2 )
      ALT_MSG_MAP( 101)
      MESSAGE_HANDLER( WM_CREATE, OnCreate3 )
      MESSAGE_HANDLER( WM_PAINT, OnPaint3 )
   END_MSG_MAP()
如上,基類的消息映射表由3節組成:一個默認的消息映射表(隱含的標識為0)和兩個可選的消息映射表(標識為100和101)。

當你鏈接消息映射表時,指定你所希望的方案的標識,如下:
class CDerived: public CBase {
   BEGIN_MSG_MAP( CDerived )
      CHAIN_MSG_MAP_ALT( CBase, 100 )
   END_MSG_MAP()
   ...
CDrived類的消息映射表鏈接到CBase類中標識號為100的可選節,因此當WM_PAINT到達時,CBase::OnPaint2被調用。
(譯者注:我覺得這種方法不太合乎C++的思想,基類的編寫者不一定總能知道派生自它的類會有哪些需求,而且把所有不同的版本都在基類中實現,基類中無用的代碼量會大大增加。更好的辦法應該是把基類中的消息處理函數聲明為虛函數。總之,我覺得這一小節并不能體現出可選消息映射的真正用途。)

其它類型的鏈:
除了基類消息映射鏈,ATL也提供了成員鏈(member chaining)和動態鏈(dynamic chaining),這些很少使用到的鏈技術超出了我們這篇文章的討論范圍,但是可以簡單提一下。成員鏈允許把消息映射鏈接到一個類的成員變量,動態鏈允許在運行時進行動態鏈接。如果你想了解更多,請參考ATL文檔中的CHAIN_MSG_MAP_DYNAMIC 和CHAIN_MSG_MAP_MEMBER的相關內容。

窗口的超類化:
超類化定義一個類,并為預定義的窗口類(如按鈕類或列表框類)添加新的功能,下面的例子超類化一個按鈕,讓這個按鈕在被單擊的時候發出蜂鳴。
class CBeepButton: public CWindowImpl< CBeepButton >
{
public:
   DECLARE_WND_SUPERCLASS( _T("BeepButton"), _T("Button") )
   BEGIN_MSG_MAP( CBeepButton )
      MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
   END_MSG_MAP()

   LRESULT OnLButtonDown( UINT, WPARAM, LPARAM, BOOL& bHandled )
   {
      MessageBeep( MB_ICONASTERISK );
      bHandled = FALSE; // alternatively: DefWindowProc()
      return 0;
   }
}; // CBeepButton
DECLARE_WND_SUPERCLASS宏聲明了這個窗口的類名(“BeepButton”)和被超類化的類名(“Button”)。它的消息映射表只有一個入口項,將WM_LBUTTONDOWN消息映射到OnLButtonDown函數。其余的消息都讓默認的窗口過程處理,除了可以發出蜂鳴外,CbeepButton需要和其它的按鈕表現相同,因此在OnLButtonDown函數的最后,需要將bHandled設置為FALSE,讓默認的窗口過程在OnLButtonDown函數完成后對WM_LBUTTONDOWN消息進行其它的處理。(另外的一種方法是直接調用DefWindowProc函數。)

到目前為止,我們所做的只是定義了一個新類;我們依然需要創建一些真正的CbeepButton窗口,下面的類定義了兩個CbeepButton類型的成員變量,因此,當這個類的窗口被創建時,將會創建兩個CbeepButton類型的子窗口。
const int ID_BUTTON1 = 101;
const int ID_BUTTON2 = 102;

class CMyWindow: public CWindowImpl< CMyWindow, CWindow,
CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE> >
{
   CBeepButton b1, b2;

   BEGIN_MSG_MAP( CMyWindow )
      MESSAGE_HANDLER( WM_CREATE, OnCreate )
      COMMAND_CODE_HANDLER( BN_CLICKED, onClick )
   END_MSG_MAP()

   LRESULT onClick(WORD wNotifyCode, WORD wID, HWND hWndCtl,
      BOOL& bHandled)
   {
      ATLTRACE( "Control %d clicked\n", wID );
      return 0;
   }

   LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& )
   {
      RECT r1 = { 10, 10, 250, 80 };
      b1.Create(*this, r1, "beep1", WS_CHILD|WS_VISIBLE, 0, ID_BUTTON1);
      RECT r2 = { 10, 110, 250, 180 };
      b2.Create(*this, r2, "beep2", WS_CHILD|WS_VISIBLE, 0, ID_BUTTON2);
      return 0;
   }
}; // CMyWindow
窗口的子類化:

子類化允許我們改變一個已經存在的窗口的行為,我們經常用它來改變控件的行為。它的實現機制是插入一個消息映射表來截取發向控件的消息。舉例說明:假設有一個對話框,對話框上有一個編輯框控件,我們想讓這個控件只接受不是數字的字符。我們可以截獲發往這個控件的WM_CHAR消息并拋棄接收到的數字字符。下面的類實現這個功能:
class CNoNumEdit: public CWindowImpl< CNoNumEdit >
{
   BEGIN_MSG_MAP( CNoNumEdit )
      MESSAGE_HANDLER( WM_CHAR, OnChar )
   END_MSG_MAP()

   LRESULT OnChar( UINT, WPARAM wParam, LPARAM, BOOL& bHandled )
   {
      TCHAR ch = wParam;
      if( _T(''0'') <= ch && ch <= _T(''9'') )
         MessageBeep( 0 );
      else
         bHandled = FALSE;
      return 0;
   }
};
這個類只處理一個消息WM_CHAR,如果這個字符是數字的話,則調用MessageBeep( 0 )并返回,這樣可以有效地忽略這個字符。如果不是數字,則將bHandled設置為FALSE,指明默認的窗口過程這個消息需要進一步處理。

現在我們將子類化一個編輯框控件,以便CnoNumEdit能夠搶先處理發到這個編輯框得消息。(下面得例子用到了CdialogImpl類,這個類我們將在ATL中的對話框類一節中介紹。)在這個例子中,CmyDialog類中用到了一個對話框資源(ID號為IDD_DIALOG1),對話框中有一個編輯框控件(ID號為IDC_EDIT1),當對話框初始化的時候,編輯框經過SubclassWindow而變成一個不接受數字的編輯框:
class CMyDialog: public CDialogImpl<CMyDialog>
{
public:
   enum { IDD = IDD_DIALOG1 };
   BEGIN_MSG_MAP( CMyDialog )
      MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog )
   END_MSG_MAP()

   LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& )
   {
      ed.SubclassWindow( GetDlgItem( IDC_EDIT1 ) );
      return 0;
   }

   CNoNumEdit ed;
};


 

文章引用自: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvc60/html/atlwindow.asp
posted on 2007-03-13 09:52 jay 閱讀(798) 評論(0)  編輯 收藏 引用 所屬分類: ATL
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            日韩午夜在线观看视频| 久久国产免费| 久久久午夜精品| 午夜在线成人av| 性亚洲最疯狂xxxx高清| 亚洲欧美日韩国产一区二区三区| 亚洲专区在线| 久久精品午夜| 欧美激情在线有限公司| 亚洲国产成人久久综合| 欧美成人精品| 亚洲精品老司机| 亚洲性夜色噜噜噜7777| 亚洲欧美一级二级三级| 久久精品亚洲乱码伦伦中文 | 欧美剧在线观看| 欧美日韩一区高清| 国产亚洲成av人片在线观看桃| 精久久久久久久久久久| 99精品免费网| 久久婷婷综合激情| 亚洲国产精品久久人人爱蜜臀 | 亚洲国产高清自拍| 亚洲一区二区三区激情| 久久精品成人| 欧美日韩综合在线| 亚洲国产黄色| 欧美伊人久久| 亚洲精品一区二区三区99| 香蕉av777xxx色综合一区| 欧美精品18videos性欧美| 国产欧美一区二区三区另类精品| 亚洲黄色在线观看| 久久电影一区| 亚洲午夜久久久| 欧美好骚综合网| 激情综合色丁香一区二区| 亚洲尤物在线视频观看| 欧美成在线视频| 香蕉国产精品偷在线观看不卡| 欧美全黄视频| 亚洲精品久久久久久久久久久久久 | 欧美激情欧美激情在线五月| 国产日本欧美在线观看| 一区二区三区久久网| 欧美福利精品| 亚洲男女自偷自拍图片另类| 亚洲欧美日韩国产一区| 欧美喷水视频| 在线欧美三区| 欧美a级在线| 久久久久这里只有精品| 国产一区二区三区视频在线观看| 亚洲欧美日韩系列| 亚洲伦理自拍| 欧美亚日韩国产aⅴ精品中极品| 欧美高清在线观看| 一区二区三区三区在线| 欧美韩日精品| 亚洲激情电影在线| 欧美大片一区| 蜜桃av一区二区在线观看| 激情婷婷久久| 久久久久久9| 久久高清福利视频| 在线成人黄色| 亚洲国产精品欧美一二99| 猫咪成人在线观看| 亚洲激情欧美| 久久精品一区二区三区不卡牛牛 | 久久国产精品久久精品国产| 国产精品推荐精品| 性亚洲最疯狂xxxx高清| 午夜精品视频网站| 国产一区二区三区在线观看精品| 亚洲欧洲99久久| 亚洲欧美日韩精品久久久久| 国产色综合天天综合网| 老司机67194精品线观看| 久久精品最新地址| 亚洲成人影音| 亚洲美女在线视频| 国产深夜精品| 国产日韩视频| 欧美韩日一区二区三区| 国产精品99久久久久久久久久久久| 欧美成人精品不卡视频在线观看| 亚洲国产成人在线| 亚洲国产专区校园欧美| 欧美激情在线免费观看| 中文欧美字幕免费| 先锋影音国产一区| 亚洲国产精品高清久久久| 91久久夜色精品国产网站| 欧美午夜精彩| 久久久亚洲人| 欧美精品v日韩精品v韩国精品v| 亚洲先锋成人| 久久久免费精品视频| 99国产精品私拍| 久久激情久久| 亚洲男人的天堂在线| 国产在线观看精品一区二区三区| 午夜欧美电影在线观看| 欧美 亚欧 日韩视频在线| 欧美亚洲免费电影| 欧美3dxxxxhd| 久久综合色一综合色88| 欧美日韩一区二区三区在线看| 久久国产一区| 欧美三级第一页| 欧美国产精品日韩| 国产日韩欧美一区二区三区四区| 亚洲高清精品中出| 国产亚洲一级高清| 亚洲免费观看在线视频| 亚洲国产电影| 久久国产精品99国产| 亚洲视频福利| 欧美精品日韩一区| 欧美~级网站不卡| 国产日韩亚洲欧美精品| 一本一本久久a久久精品综合妖精| 亚洲第一中文字幕| 欧美在线观看www| 欧美在线观看视频在线 | 久久久99免费视频| 亚洲在线一区二区三区| 欧美日韩精品免费看| 欧美大学生性色视频| 国产一区二区三区在线观看精品 | 国产精自产拍久久久久久| 亚洲免费观看在线视频| 亚洲精品久久久久久久久久久| 久久精品一区二区| 久久久亚洲欧洲日产国码αv| 国产手机视频一区二区| 香蕉久久夜色| 欧美激情1区2区3区| 久久高清免费观看| 国产精品美女久久久| 亚洲视频免费在线| 一本色道久久综合亚洲精品不 | 国产香蕉久久精品综合网| 亚洲欧美日韩一区在线观看| 亚洲午夜精品福利| 国产精品久久久久一区二区三区共| 99re国产精品| 亚洲一区二区在线免费观看视频 | 久久综合福利| 亚洲高清一区二| 欧美本精品男人aⅴ天堂| 亚洲国产精品va在线观看黑人| 亚洲美女黄色片| 欧美日韩123| 亚洲欧美日韩在线| 久久中文字幕一区| 亚洲人线精品午夜| 欧美日韩在线播放三区四区| 一区二区三区久久| 久久精品一区四区| 亚洲国产精品女人久久久| 欧美激情在线免费观看| 99国产精品99久久久久久| 欧美影院成年免费版| 在线观看国产成人av片| 欧美福利电影在线观看| 一本色道久久综合亚洲精品不| 午夜精品久久久久久久99樱桃| 国产一区二区三区久久久久久久久| 久久激五月天综合精品| 最新中文字幕亚洲| 欧美一级大片在线免费观看| 国内外成人免费激情在线视频| 美国三级日本三级久久99| 一本色道久久精品| 蜜桃av综合| 亚洲一区二区免费看| 黄色精品一区| 国产精品久久久久永久免费观看 | 欧美在线视频二区| 亚洲国产欧美国产综合一区| 亚洲免费一级电影| 亚洲国产日日夜夜| 国产精品嫩草99a| 免费不卡视频| 亚洲欧美日韩成人高清在线一区| 欧美大片在线看免费观看| 亚洲欧美日韩另类精品一区二区三区| 在线观看三级视频欧美| 国产精品久久久久9999高清| 美玉足脚交一区二区三区图片| 亚洲午夜激情免费视频| 亚洲激情精品| 久久在线免费视频| 午夜在线a亚洲v天堂网2018| 99热这里只有成人精品国产| 国产一区二区三区直播精品电影 | 国产综合一区二区| 国产精品久久久久毛片软件 |