??xml version="1.0" encoding="utf-8" standalone="yes"?>久久婷婷色香五月综合激情,久久综合综合久久综合,三级三级久久三级久久 http://m.shnenglu.com/yishanhante/category/3741.htmlzh-cnWed, 21 May 2008 21:24:54 GMTWed, 21 May 2008 21:24:54 GMT60COM Objects and Interfaceshttp://m.shnenglu.com/yishanhante/articles/19716.htmljayjayTue, 13 Mar 2007 05:57:00 GMThttp://m.shnenglu.com/yishanhante/articles/19716.htmlhttp://m.shnenglu.com/yishanhante/comments/19716.htmlhttp://m.shnenglu.com/yishanhante/articles/19716.html#Feedback0http://m.shnenglu.com/yishanhante/comments/commentRss/19716.htmlhttp://m.shnenglu.com/yishanhante/services/trackbacks/19716.html 一个对象实C个接口,他的意思就是该对象使用代码实现了接口的每个Ҏ

q且些函数通向 com 库提供了 com 的二q制指针。然?/span> com 使这些函数运行在

h了一个指向该接口的Q何客L?/font>

 

Interfaces and Interface Implementations

 

COM 在接口的定义和实C有根本的差别。一个接口实际上是由一l定义了用法的相

联系的函数原型组成,只是他不能够被实现。这些函数原型就相当?/span> C++ 中含有纯虚拟函数的基cR一个接口定义制定了接口的成员函数、调用方法、返回类型、他们的参数的数量和cdQ这些函数要q什么。但是这里ƈ没有与接口实现相关的东西。一个接口的实现是E序员在一个借口定义上提供的执行相关动作的代码?/span>

     一个接口的实现是E序员在一个借口定义上提供的执行相关动作的代码。客戯用完全是军_于接口的定义?/span>

 

Interface Pointers and Interfaces

 

接口实现的一个实例,实际上就是一个指向一l方法的指针Q即是指指向一个接口的?/font> 数表Q该函数表引用了该接口所有方法的实现?/font>

     每个接口Q是一个固定的一l方法的集合Q在q行旉过 globally unique interface identifier (IID) 来定位。这里, IID ?/span> com 支持?/span> globally unique identifier (GUID) 的特D的实例。这样做׃会生单一pȝ上相同名字、接口的多个版本?/span> COM 之间的冲H了?/span>

  • 一?span lang="EN-US">COM接口与C++cL不一LQ?/span>
  • 一?span lang="EN-US">COM接口不是一个对象——他只是单的兌一l函敎ͼ是客户和E序之间通信的二q制标准。只要他提供了指向借口Ҏ的指针,q个对象可以用M语言来实C。;
  • COM接口十强cd的——每个接口有他自q借口标识W;

·         COM接口的不变性——你不能够用老版本的接口标识W定义新的版本,接口的IID定义的接口合同是明确的、唯一?/span> ?/span>

 

IUnknown and Interface Inheritance

 

     l承?/span> COM 里ƈ不意味着代码的重用。因为接口没有实现关联,借口l承q意味着代码l承。他的意思仅仅是Q一个接口同一个合同关联,像 C++ 的纯虚拟基类的创建和修改P可以dҎ或者更q一步的加强Ҏ的用。在 COM 里没有选择性ѝ如果一个接口由另一个接口承的话,他就包含了另一个接口定义的所有的Ҏ?/span>

 

Using and Implementing IUnknown

 

    COM 为实现和使用对象和对象的内部通信提供了一个丰富的标准集合。对IUnknown接口的实现和使用的细节,请参见下面主题:QueryInterface: Navigating in an Object 。?o:p>

   

Rules for Implementing QueryInterface

 

理实现一?span lang="EN-US">COM对象?/span> IUnknown::QueryInterface Ҏ的三个主要规则:

    1Q对象必要有一个标识符Q?/font>  

    2Q一个对象实例的接口集合必须是静态的Q?b style="mso-bidi-font-weight: normal">staticQ;  

    3Q在对象中从M一个其他的接口查询此接口都应该成功?/span>  

 

通过引用计数来管理对象的生命周期

     使用 AddRef Q)     //增加引用 
                RealaseQ)   //减少引用



jay 2007-03-13 13:57 发表评论
]]>
ATL3.0中的H口c(3Q?/title><link>http://m.shnenglu.com/yishanhante/articles/19690.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Tue, 13 Mar 2007 02:08:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19690.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19690.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19690.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19690.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19690.html</trackback:ping><description><![CDATA[ <strong>ATL中的对话框类Q?/strong> <br />现在我们对ATL中的H口cL了一定的了解Q接着我们来学习对话框cR在你的目中,可能有很多对话框资源Q从最单的“关于”模式对话框到复杂的满是控g的非模式对话框。ATL提供了CSimpleDialogcdCDialogImplcL化我们用对话框资源的过E?<br /><br />CSimpleDialog <br />CSimpleDialog是一个从模版创徏模式对话框的cR它提供了一些标准按U(如OK和CANCELQ的处理q程。你可以CSimpleDialog惌成是一U消息对话框QMessage BoxQ,不同的是你可以在对话框编辑器中编辑它的外观?<br /><br />要显C样一个对话框Q比如当你点几Z帮助”菜单中的“关于”菜单项时显C关于对话框Q你需要在ȝ口类中添加如下的消息映射Q?<pre>BEGIN_MSG_MAP( CMyMainWindow ) COMMAND_ID_HANDLER( ID_HELP_ABOUT, onHelpAbout ) ... LRESULT onHelpAbout( WORD, WORD, HWND, BOOL& ) { CSimpleDialog<IDD_DIALOG1> dlg; int ret = dlg.DoModal(); return 0; }</pre>我们可以看到对话框资源的IDQIDD_DIALOG1Q被作ؓ一个模版参C递给CSimpleDialogc,DoModalҎ昄对话框。当用户点击OK按钮ӞCSimpleDialogcd闭对话框q返回按钮的ID。(CSimpleDialogcdCҎ钮IDOKQIDCANCELQIDABORTQIDRETRYQIDIGNOREQIDYES和IDNO的响应。) <br /><br />CDialogImpl <br />CSimpleDialogcd能够处理单的模式对话框,对于更加复杂的对话框或者模式对话框Q就要用到CDialogImplcR(其实CSimpleDialog是CDialogImpl中的一U特例。) <br /><br />如果我们需要实C个非模式对话框,我们必须从CDialogImplzZ个新c,q将新类的类名作为模板参C递给CDialogImplc,p前面的CWindowImpl一P <pre>class CMyModelessDialog: public CDialogImpl<cmymodelessdialog> { </cmymodelessdialog></pre>和CSimpleDialog不同Q我们不需要将对话框资源的ID作ؓ模板参数传递给它,但是我们必须这个类和对话框资源联系hQ我们通过在类中定义一个枚丑֏量实玎ͼ <pre>public: enum { IDD = IDD_DIALOG1 }; </pre>然后定义消息映射表: <pre>BEGIN_MSG_MAP( CMyDialog ) MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) MESSAGE_HANDLER( WM_CLOSE, OnClose ) ... END_MSG_MAP() </pre>响应函数的定义和前面的一P但是有一炚w要注意,如果你实现的是一个非模式对话框,那么在WM_CLOSE消息的响应函C必须调用DestroyWindowQ?pre>LRESULT OnClose( UINT, WPARAM, LPARAM, BOOL& ) { DestroyWindow(); return 0; } ... }; // CMyModelessDialog </pre>要在屏幕上创样一个对话框Q需要创个类的一个实例ƈ调用CreateҎQ?<pre>CMyModelessDialog dlg; dlg.Create( wndParent ); </pre>如果对话框资源没有选中WS_VISIBLE属性,我们需要这栯对话框显C出来: <pre>dlg.ShowWindow( SW_SHOW );</pre>下面的例子有一个非模式的对话框可以接受用户输入的字W串Q然后在ȝ口中昄q个字符丌Ӏ对话框中有一个编辑框控g和一个按钮;当按钮被点击Ӟ对话框调用它所属窗口的DoSomethingҎ对编辑框中的字符串进行处理,它所属的H口是一个超cd的列表框控gQDoSomethingҎ的功能是字W串d到列表框中?<pre>#include "atlbase.h" CComModule _Module; #include "atlwin.h" #include "resource.h" class CMyWindow: public CWindowImpl<CMyWindow> { public: DECLARE_WND_SUPERCLASS( "MyWindow", "listbox" ) BEGIN_MSG_MAP( CMyWindow ) MESSAGE_HANDLER( WM_DESTROY, OnDestroy ) END_MSG_MAP() LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& ) { PostQuitMessage( 0 ); return 0; } void DoSomething( LPCTSTR s ) { SendMessage( LB_ADDSTRING, 0, reinterpret_cast<LPARAM>(s) ); } }; class CMyDialog: public CDialogImpl<CMyDialog> { public: enum { IDD = IDD_DIALOG1 }; BEGIN_MSG_MAP( CMyDialog ) COMMAND_ID_HANDLER( IDC_BUTTON1, OnButton ) MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) MESSAGE_HANDLER( WM_CLOSE, OnClose ) END_MSG_MAP() LRESULT OnButton(WORD, WORD, HWND, BOOL&) { char buf[100]; m_ed.GetWindowText( buf, 100 ); m_owner.DoSomething( buf ); return 0; } LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& ) { m_owner.Attach( GetParent() ); CenterWindow( m_owner ); m_ed = GetDlgItem( IDC_EDIT1 ); return TRUE; } LRESULT OnClose( UINT, WPARAM, LPARAM, BOOL& ) { DestroyWindow(); m_owner.Detach(); return 0; } CMyWindow m_owner; CWindow m_ed; }; CMyDialog dlg; int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { _Module.Init( NULL, hInstance ); CMyWindow win; win.Create( NULL, CWindow::rcDefault, _T("modeless dialog test"), WS_OVERLAPPEDWINDOW|WS_VISIBLE ); dlg.Create( win ); dlg.ShowWindow( SW_SHOW ); MSG msg; while( GetMessage( &msg, NULL, 0, 0 ) ){ if( !IsWindow(dlg) || !dlg.IsDialogMessage( &msg ) ){ DispatchMessage( &msg ); } } _Module.Term(); return 0; } </pre><strong><a id="A13" name="A13"></a>指定H口cȝ信息Q?/strong><br />q篇文章的大部分内容都是在讲q怎样处理H口cȝ行ؓ——窗口怎样响应消息。在行ؓ之外Q一个窗口类q具有一些其它的重要的属性,比如样式、类名、背景颜色和指针{等。这一节介l怎样使用ATL中提供的宏来指定q些属性?<br /><br />使用Window Traits指定H口的样?<br />到目前ؓ止,所有例子中的窗口样式都是在调用CreateҎ时指定的Q?<pre>CMyWindow wnd; wnd.Create( NULL, CWindow::rcDefault, _T("Hello"), WS_OVERLAPPEDWINDOW|WS_VISIBLE ); </pre>如果你不指定M样式和扩展样式,ATL用默认的样式Q这些默认的样式是作为窗口的特征定义的,默认特征是CControlWinTraitsQ定义如下:<pre>typedef CWinTraits<WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |WS_CLIPSIBLINGS, 0> CControlWinTraits; </pre>CWinTraits是一个模板类Q它需?个参敎ͼH口样式、扩展窗口样式?<br /><br />在CWindowImpl的定义中QCControlWinTraits作ؓ默认的模板参C递给CWindowImplQ?<pre>template <class T, class TBase = CWindow, class TWinTraits = CControlWinTraits> class CWindowImpl : public ... </pre>所以在默认情况下,从CWindowImplz的窗口都h可视、子H口、裁剪兄弟窗口、裁减子H口的属性?<br /><br />我们也能定义自己的窗口特征: <pre>typedef CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE,0> MyTraits; </pre>然后Q从CWindowImplz一个窗口类Q指定自qH口特征Q?<pre>class CMyWindow: public CWindowImpl<CMyWindow,CWindow,MyTraits> {...}; </pre>或者象下面q样更加直接Q?<pre>class CMyWindow: public CWindowImpl< CMyWindow, CWindow, CWinTraits<WS_OVERLAPPEDWINDOW|WS_VISIBLE,0> > {...};</pre>注意Q我们必L供全部的三个模板参数Q派生类Q基c(CWindowQ和特征cR?<br /><br />CMyWindowH口现在h的默认的样式为“可见的弹出H口”,所以我们可以在CreateҎ中省略样式参敎ͼ <pre>CMyWindow wnd; wnd.Create( NULL, CWindow::rcDefault, _T("Hello") ); // style: WS_OVERLAPPEDWINDOW|WS_VISIBLE </pre>我们也可以重写窗口特征:<pre>ovwnd.Create( NULL, CWindow::rcDefault, _T("Hello"), WS_OVERLAPPEDWINDOW ); // not visible </pre>H口特征也可以包含扩展样式:<pre>class CClientWindow: public CWindowImpl<CClientWindow, CWindow, CWinTraits< WS_OVERLAPPEDWINDOW|WS_VISIBLE, WS_EX_CLIENTEDGE > > {...}; </pre>DECLARE_WND_CLASS <br />使用DECLARE_WND_CLASS宏可以指定窗口的cdQ?<pre>DECLARE_WND_CLASS("my window class");</pre>q等价于Q?pre>DECLARE_WND_CLASS_EX( "my window class", CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS, // default style COLOR_WINDOW // default color ); </pre>DECLARE_WND_CLASS_EX <br />使用DECLARE_WND_CLASS_EX宏可以指定窗口类名、样式和背景颜色Q?<pre>class CMyWindow: public CWindowImpl<CMyWindow> { public: DECLARE_WND_CLASS_EX( "my window class", // class name CS_HREDRAW|CS_VREDRAW, // class style COLOR_WINDOW // background color ); BEGIN_MSG_MAP(CMyWindow) ... </pre>所谓的H口cd是指注册的窗口类的名字,如果我们不指定窗口类名,ATL自动生成一个,但是当我们用Spy++之类的工L时候,你将会发现我们自己取的类名比"ATL:00424bd0"之类的名字要有用得多?<br /><br />cL式是按照按位或组合的?<br /><br />背景颜色必须是标准系l颜色之一?<br /><br />CWndClassInfo <br />我们也可以定义超出DECLARE_WND_宏能力之外的H口cR如果你看看DECLARE_WND_CLASS的定义你׃发现它定义了一个CWndClassInfol构Qƈ且一个函数返回这U结构类型的| <pre>#define DECLARE_WND_CLASS(WndClassName) \ static CWndClassInfo& GetWndClassInfo() \ { \ static CWndClassInfo wc = \ { \ { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, \ StartWindowProc, \ 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, \ WndClassName, NULL }, \ NULL, NULL, IDC_ARROW, TRUE, 0, _T("") \ }; \ return wc; \ } </pre>CWndClassInfol构提供了更灉|的自定义的可能,它是q样定义的: <pre>struct CWndClassInfo { struct WNDCLASSEX { UINT cbSize; UINT style; WNDPROC lpfnWndProc; int cbClsExtra; int cbWndExtra; HINSTANCE hInstance; HICON hIcon; HCURSOR hCursor; HBRUSH hbrBackground; LPCSTR lpszMenuName; LPCSTR lpszClassName; HICON hIconSm; } m_wc; LPCSTR m_lpszOrigName; WNDPROC pWndProc; LPCSTR m_lpszCursorID; BOOL m_bSystemCursor; ATOM m_atom; CHAR m_szAutoName[13]; ATOM Register(WNDPROC* p); }; </pre>例如Q要指定一个窗口的指针Q我们可以将m_lpszCursorID讄为指针的名字Q如果它是一个系l指针,m_bSystemCursor讄为TRUEQ否则设|ؓFALSE。注意DECLARE_WND_CLASS宏是怎样这两个成员变量分别讄为IDC_ARROW ?TRUE的。既然DECLARE_WND_宏不能让我们改写q些默认的|我们可以q样做: <pre>class CMyWindow: public CWindowImpl<CMyWindow> { public: static CWndClassInfo& GetWndClassInfo() { // a manual DECLARE_WND_CLASS macro expansion // modified to specify an application-defined cursor: static CWndClassInfo wc = { { sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS, StartWindowProc, 0, 0, NULL, NULL, NULL, (HBRUSH)(COLOR_WINDOW + 1), NULL, "MyWindow", NULL }, NULL, NULL, MAKEINTRESOURCE(IDC_CURSOR1), FALSE, 0, _T("") }; return wc; } ... </pre><strong><a name="A14"></a>l论Q?/strong><br />ATL提供了一U简单的、雅致的q且功能强大的窗口编E模式。在那些方便的封装好了的函数、消息映和宏之外,q有一些技术诸如链接、窗口的子类化和类化、被包含的窗口和消息反射{也使得设计和实现窗口和对话框非常灵zR或许ATLlh最q印象是Q功能强大、灵zL好Q但是不会占用太多的内存和系l开销?img src ="http://m.shnenglu.com/yishanhante/aggbug/19690.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-13 10:08 <a href="http://m.shnenglu.com/yishanhante/articles/19690.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ATL3.0中的H口c(2Q?/title><link>http://m.shnenglu.com/yishanhante/articles/19689.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Tue, 13 Mar 2007 02:07:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19689.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19689.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19689.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19689.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19689.html</trackback:ping><description><![CDATA[ <div> <a id="A10" name="A10"> </a> <strong>被包含的H口Q?/strong> <br />一个被包含的窗口是一个不响应M消息的窗口,它将收到的所有消息重新发送到另外一个窗口的消息映射Q这个另外的H口是它的容器H口。通常情况下,被包含的H口是它的容器窗口的子窗口,但情况ƈ不是Lq样。容器窗口ƈ不是必须{同于父H口Q包含与被包含的关系取决于C++c,被包含的H口是容器窗口类的一个数据成员,而父H口和子H口的关pM现在屏幕上,它们的关pL创徏H口时确定的?<br /><br />一个被包含的窗口徏立在已注册的H口cȝ基础之上Q比如编辑框控g。如果一个编辑框被包含,那么发送到它的消息实际上被它的容器H口的消息映处理。用这U方法,可以改变~辑框控件的标准行ؓ。这有点cM于子cd但是不需要定义新cL子类化控件。和前面那个定义CnoNumEditcd应WM_CHAR消息的例子相比,处理WM_CHAR消息的容器窗口类看v来如下: <pre>class CMyWindow: public CWindowImpl<cmywindow> { CContainedWindow m_contained; public: CMyWindow(): m_contained( _T("edit"), this, 99 ) { } ... </cmywindow></pre>CmyWindow是一个容器窗口类Q它的构造函数对CcontainedWindowcd的成员做q样的初始化Q被包含的窗口是~辑框,发送它的消息到“this”(它的父窗口)Q用可选消息映表99?pre>BEGIN_MSG_MAP( CMyWindow ) MESSAGE_HANDLER( WM_CREATE, OnCreate ) MESSAGE_HANDLER( WM_DESTROY, OnDestroy ) ALT_MSG_MAP( 99 ) // contained window''s messages come here... MESSAGE_HANDLER( WM_CHAR, OnChar ) END_MSG_MAP() </pre>当父H口被创建的时候,被包含的H口也被创徏Q在WM_CREATE消息的响应函CQ。因包含的控件是以编辑框为基的,所以它在屏q上看v来象一个编辑框Q?<pre>LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& ) { RECT rc = { 10, 10, 200, 35 }; m_contained.Create( *this, rc, _T("non-numeric edit"), WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 666 ); return 0; } </pre>在这个例子中Q容器窗口同时也是被包含H口的父H口?<br /><br />当被包含的窗口收到WM_CHAR消息Ӟ容器H口的OnChar成员函数被调用。这个函数和前面的CnoNumEdit例子中的相同Q但是在q个例子中,它时容器cȝ成员函数?<pre>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; } LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& ) { PostQuitMessage( 0 ); return 0; } }; </pre>我们同样也可以用被包含的H口来子cd对话框中已经存在的控Ӟ和正规的子类化不同,被子cd的窗口的消息时被容器H口捕获的。在下面的例子中Q一个对话框子类化了一个编辑框控gQ把它{化成了被包含的窗口;那个对话框(容器Q捕获WM_CHAR消息q忽略掉数字字符Q然后在发送到~辑框控件。(CdialogImpl在ATL中的对话框类一节讲q。) <pre>class CMyDialog: public CDialogImpl<CMyDialog> { public: enum { IDD = IDD_DIALOG1 }; // contained window is an edit control: CMyDialog(): m_contained( "edit", this, 123 ) { } BEGIN_MSG_MAP( CMyDialog ) MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) ALT_MSG_MAP( 123 ) // contained window''s messages come here... MESSAGE_HANDLER( WM_CHAR, OnChar ) END_MSG_MAP() LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& bHandled ) { // when the dialog box is created, subclass its edit control: m_contained.SubclassWindow( GetDlgItem(IDC_EDIT1) ); bHandled = FALSE; return 0; } 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; } CContainedWindow m_contained; }; </pre><strong><a id="A11" name="A11"></a>消息反射Q?/strong><br />前面讲述了一些扩展窗口功能的ҎQ这些方法是通过使窗口响应发往H口的消息实现的。和前面的方法相反,消息反射是ɽH口能够响应从它们自己发出的消息?<br /><br />当用户和控g交互的时候,控g通常使发送一个WM_COMMAND或者WM_NOTIFY消息l它的父H口Q然后父H口做出响应Q比如: <pre>class CParentWindow: CWindowImpl<CParentWindow> { // 假设q个H口有一个按钮型的子H口Q? // q且?ID ?ID_BUTTON BEGIN_MSG_MAP( CParentWindow ) COMMAND_ID_HANDLER( ID_BUTTON, OnButton ) MESSAGE_HANDLER( WM_CTLCOLORBUTTON, OnColorButton ) ... </pre>当按钮被按下的时候,它发送一个命令消息给父窗口,然后CParentWindow::OnButton被调用。同理,当按钮需要被l制的时候,它发送WM_CTLCOLORBUTTON消息l父H口QCParentWindow::OnColorButton响应q个消息Q它使用特定的画L制控件?<br /><br />某些情况下,让控件自己响应它发送出ȝ消息比让父窗口响应要好得多。ATL提供了消息反的机制Q当控g向父H口发送消息的时候,父窗口能够将消息反射l控件?<pre>class CParentWindow: CWindowImpl<cparentwindow> { BEGIN_MSG_MAP( CParentWindow ) MESSAGE_HANDLER( WM_CREATE, OnCreate ) MESSAGE_HANDLER( WM_DESTROY, OnDestroy ) ...other messages that CParentWindow will handle... REFLECT_NOTIFICATIONS() END_MSG_MAP() ... </cparentwindow></pre>当父H口收到一个消息,先查扑֮的消息映表Q如果没有和q个消息相匹配的入口Q则REFLECT_NOTIFICATIONS宏得该消息被反给发送这个消息的控g。控件可以提供响应反消息的处理函数Q如下: <pre>class CHandlesItsOwnMessages: CWindowImpl<CHandlesItsOwnMessage> { public: DECLARE_WND_SUPERCLASS( _T("Superbutton"), _T("button") ) BEGIN_MSG_MAP( CHandlesItsOwnMessage ) MESSAGE_HANDLER( OCM_COMMAND, OnCommand ) MESSAGE_HANDLER( OCM_CTLCOLORBUTTON, OnColorButton ) DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() ... </pre>注意Q反消息的消息标志以OCM_开_而不是WM_。这可以让你区分q个消息I竟是否是被反射回来的?<br /><br />q个控g要么是这个类的实例,要么是一个被子类化的按钮控g。例如: <pre>// in CParentWindow: CHandlesItsOwnMessages m_button; LRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL& ) { RECT rc; // initialize appropriately m_button.Create( *this, rc, _T("click me"), WS_CHILD|WS_VISIBLE ); ... </pre>或者,如果q个按钮控g是已存在的(例如Q父H口是一个对话框Q:<pre>m_button.SubclassWindow( GetDlgItem(ID_BUTTON) ); </pre>下面的例子定义了一个CstaticLinkc,它是一个Static控gQ当点击它的时候,打开一个指定的|页。所有从CstaticLink发送出ȝ消息都被它的父窗口反回来(在这个例子中Q用到对话框Q请看ATL中的对话框类q一节)。除了响应反回的命令消息,CstaticLinkq处理反回的WM_CTLCOLORSTATIC消息以便它能够让自己在点d和点d昄不同的颜艌Ӏ?<pre>#include "stdafx.h" #include "resource.h" CComModule _Module; class CStaticLink : public CWindowImpl<CStaticLink> { /* Based on CStaticLink by Paul DiLascia, C++ Q&A, Microsoft Systems Journal 12/1997. Turns static controls into clickable "links" -- when the control is clicked, the file/program/webpage named in the control''s text (or set by SetLinkText()) is opened via ShellExecute(). Static control can be either text or graphic (bitmap, icon, etc.). */ public: DECLARE_WND_SUPERCLASS( _T("StaticLink"), _T("Static") ) CStaticLink() : m_colorUnvisited( RGB(0,0,255) ), m_colorVisited( RGB(128,0,128) ), m_bVisited( FALSE ), m_hFont( NULL ) { } void SetLinkText( LPCTSTR szLink ) { USES_CONVERSION; m_bstrLink = T2OLE( szLink ); } BEGIN_MSG_MAP(CStaticLink) // uses message reflection: WM_* comes back as OCM_* MESSAGE_HANDLER( OCM_COMMAND, OnCommand ) MESSAGE_HANDLER( OCM_CTLCOLORSTATIC, OnCtlColor ) MESSAGE_HANDLER( WM_DESTROY, OnDestroy ) // not a reflected message DEFAULT_REFLECTION_HANDLER() END_MSG_MAP() LRESULT OnDestroy( UINT, WPARAM, LPARAM, BOOL& ) { if( m_hFont ) DeleteObject( m_hFont ); return 0; } LRESULT OnCommand( UINT, WPARAM wParam, LPARAM, BOOL& ) { USES_CONVERSION; int code = HIWORD( wParam ); if( code == STN_CLICKED || code == STN_DBLCLK ){ if( m_bstrLink.Length() == 0 ){ GetWindowText( &m_bstrLink ); } if( (int)ShellExecute( *this, _T("open"), OLE2T(m_bstrLink), NULL, NULL, SW_SHOWNORMAL ) > 32 ){ m_bVisited = TRUE; // return codes > 32 => success Invalidate(); }else{ MessageBeep( 0 ); ATLTRACE( _T("Error: CStaticLink couldn''t open file") ); } } return 0; } LRESULT OnCtlColor( UINT, WPARAM wParam, LPARAM, BOOL& ) { // notify bit must be set to get STN_* notifications ModifyStyle( 0, SS_NOTIFY ); HBRUSH hBr = NULL; if( (GetStyle() & 0xff) <= SS_RIGHT ){ // it''s a text control: set up font and colors if( !m_hFont ){ LOGFONT lf; GetObject( GetFont(), sizeof(lf), &lf ); lf.lfUnderline = TRUE; m_hFont = CreateFontIndirect( &lf ); } HDC hDC = (HDC)wParam; SelectObject( hDC, m_hFont ); SetTextColor( hDC, m_bVisited ? m_colorVisited : m_colorUnvisited ); SetBkMode( hDC, TRANSPARENT ); hBr = (HBRUSH)GetStockObject( HOLLOW_BRUSH ); } return (LRESULT)hBr; } private: COLORREF m_colorUnvisited; COLORREF m_colorVisited; BOOL m_bVisited; HFONT m_hFont; CComBSTR m_bstrLink; }; // CStaticLink class CReflectDlg : public CDialogImpl<CReflectDlg> { public: enum { IDD = IDD_DIALOG1 }; BEGIN_MSG_MAP(CReflectDlg) COMMAND_RANGE_HANDLER( IDOK, IDCANCEL, OnClose ) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) REFLECT_NOTIFICATIONS() // reflect messages back to static links END_MSG_MAP() LRESULT OnInitDialog(UINT, WPARAM, LPARAM, BOOL&) { CenterWindow( GetParent() ); // a textual static control: s1.SubclassWindow( GetDlgItem(IDS_TEST1) ); // a static control displaying an icon s2.SubclassWindow( GetDlgItem(IDS_TEST2) ); // set the icon''s link s2.SetLinkText( _T("http://www.microsoft.com") ); return 1; } LRESULT OnClose(UINT, WPARAM wID, HWND, BOOL& ) { ::EndDialog( m_hWnd, wID ); return 0; } private: CStaticLink s1, s2; }; // CReflectDlg int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow) { _Module.Init( NULL, hInstance ); CReflectDlg dlg; dlg.DoModal(); _Module.Term(); return 0; } </pre><strong><a id="A12" name="A12"></a></strong></div> <img src ="http://m.shnenglu.com/yishanhante/aggbug/19689.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-13 10:07 <a href="http://m.shnenglu.com/yishanhante/articles/19689.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>ATL3.0中的H口c?/title><link>http://m.shnenglu.com/yishanhante/articles/19687.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Tue, 13 Mar 2007 01:52:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19687.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19687.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19687.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19687.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19687.html</trackback:ping><description><![CDATA[ <div>摘要Q讨论Active Template Library (ATL) 3.0中的一些类Q这些类围绕着Windows API建立了一个面向对象的~程框架Q用这个框Ӟ可以化Microsoft&reg; Windows&reg;~程q且只需要很的pȝ开销。内容包括:考察对窗口做了简单封装的CWindowc;使用CWindowImplq行消息处理和消息映;使用ATL中的对话框类以及扩展现有H口cȝ功能的方法?<br /><br /><strong><a id="A1" name="A1"></a>介:</strong><br />虽然Active Template Library (ATL)主要是ؓ了支持COM开发而设计的Q但它确实包含了很多可用于窗口设计的cR这些窗口类和ATL中的其它cMP都是Z模版的,q且只需要花费很系l开销。这文章就向我们演CZ使用ATL创徏H口和对话框q进行消息处理的基本Ҏ?<br />q篇文章假设读者熟悉C++语言和WindowsE序设计Q但是ƈ不一定要求读者具有COM斚w的知识?<br /><br /><strong><a id="A2" name="A2"></a>CWindow:</strong><br />在ATLH口cMQCWindow是最基本的。这个类对Windows APIq行了面向对象的包装Q它装了一个窗口句柄,q提供一些成员函数来操作它,q些函数包装了相应的Windows API?<br /><br />标准的WindowsE序设计看v来象q样Q?<br /><pre>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 ); </pre>使用ATL中的CWindowcdQ等效代码如下:<pre>CWindow win; win.Create( "button", NULL, CWindow::rcDefault, "Click me", WS_CHILD ); win.ShowWindow( nCmdShow ); win.UpdateWindow(); </pre>我们应该在我们的大脑中我们应该保持这样一个概念:ATL的窗口对象与Windowspȝ中的H口是不同的。Windowspȝ中的H口指的是操作系l中l持的一块数据,操作pȝ靠这块数据来操作屏幕上的一块区域。而一个ATLH口对象Q是CWindowcȝ一个实例,它是一个C++对象Q它的内部没有保存Q何有兛_q区域或者窗口数据结构的内容Q只保存了一个窗口的句柄Q这个句柄保存在它的数据成员m_hWnd中,CWindow对象和它在屏q上昄出来的窗口就是靠q个句柄联系h的?br />理解了ATL中的H口对象和Windowspȝ中窗口的区别Q就更加Ҏ理解CWindow对象的构造与H口的创建是两个分开的过E。我们再看看前面的代码,׃发现Q首先是一个CWindow对象被构造: <pre>CWindow win;</pre>然后创徏它的H口Q?pre>win.Create( "button", NULL, CWindow::rcDefault, "Click me", WS_CHILD ); </pre>我们也可以构造一个CWindow对象Q然后把它和一个已l存在的H口兌hQ这h们就可以通过CWindowcȝ成员函数来操作这个已l存在的H口。这U方法非常有用,因ؓCWindowcL供的函数都是装好了的,用v来很方便Q比如CWindowcM的CenterWindow, GetDescendantWindow{函数用h比直接使用Windows API方便得多?<pre>HWND hWnd = CreateWindow( szWndClass, "Main window", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); // 下面的方法中可以任选一U: // CWindow win( hWnd ); // 通过构造函数关? // ? // CWindow win; // win = hWnd; // 通过赋值操作符兌 // ? // CWindow win; // win.Attach( hWnd ); // 使用Attach()Ҏ兌 win.CenterWindow(); // 现在可以使用win对象来代替hWndq行操作 win.ShowWindow( nCmdShow ); win.UpdateWindow(); </pre>CWindowcM提供了一个HWND操作W,可以把CWindowcȝ对象转化为窗口句柄,q样QQ何要求用HWND的地斚w可以使用CWindowcȝ对象代替Q?<pre>::ShowWindow( win, nCmdShow ); // 此API函数本来要求HWNDcd的参?/pre>CWindowcM得对H口的操作更单,而且不会增加pȝ开销——它l过~译和优化后的代码与使用UAPI~程的代码是{h的?br /><br />不幸的是QCWindowcM能让我们自己军_H口如何响应消息。当Ӟ我们可以使用CWindowcL供的Ҏ来一个窗口居中或隐藏Q甚臛_以向一个窗口发送消息,但是当窗口收到消息后怎么处理则取决于创徏q个H口时用的H口c,如果我们是创建的是”button”类的窗口,那么它的表现p个按钮,如果用”listbox”类创徏Q那它就h跟列表框相同的行为,使用CWindowcL们没有办法改变这炏V幸好,ATL为我们提供了另外一个类CWindowImplQ它允许我们指定H口的新行ؓ?<br /><br /><strong><a id="A3" name="A3"></a>CWindowImpl:</strong><br />CWindowImplcL从CWindowcL生的Q所以我们依然可以用CWindowcM的成员函敎ͼ但是CWindowImplcȝ功能更强大,它允许我们指定窗口怎样处理消息。在传统的窗口编E中Q如果我们要处理H口消息Q我们必M用窗口函敎ͼ但是使用ATLQ我们只需要在我们的ATLH口cM定义一个消息映?<br /><br />首先Q从CWindowImplcL生自qH口c,如下Q?<pre>class CMyWindow : public CWindowImpl<cmywindow> { </cmywindow></pre>注意Q我们自qcd必须作ؓ一个模版参C递给CWindowImplcR?<br /><br />然后在类的定义里面定义如下的消息映射Q?<pre>BEGIN_MSG_MAP(CMyWindow) MESSAGE_HANDLER(WM_PAINT,OnPaint) MESSAGE_HANDLER(WM_CREATE,OnCreate) MESSAGE_HANDLER(WM_DESTROY,OnDestroy) END_MSG_MAP() </pre>下面q句<pre>MESSAGE_HANDLER(WM_PAINT,OnPaint)</pre>的意思是Q当WM_PAINT消息到达Ӟ调用CMyWindow::OnPaint成员函数?<br /><br />最后就是定义处理消息的函数了,如下Q?<pre>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 </pre>q些函数中的参数意义为:W一个是消息IDQ中间的两个参数的意义取决于消息cdQ第四个参数是一个标志,用它来决定这个消息是已经处理完了q是需要进一步的处理。关于这些参敎ͼ我们在Message Map结有更详细的讨论?<br /><br />当窗口收C个消息,它将从消息映表的顶部开始查扑֌配的消息处理函数Q因此把最常用的消息放在消息映表的前面是个不错的注意。如果没有找到匹配的消息处理函数Q则q个消息被发送到默认的窗口过E进行处理?<br /><br />ATL的消息映表装了Windows的消息处理过E,它比传统的窗口函C的大量switch分支或者if语句看v来更加直观?<br /><br />要创Z个基于CWindowImplzcȝH口Q请调用CWindowImplcȝCreateҎQ?<pre>CMyWindow wnd; // 构造一?CMyWindow cȝ对象 wnd.Create( NULL, CWindow::rcDefault, _T("Hello"), WS_OVERLAPPEDWINDOW|WS_VISIBLE ); </pre>注意QCWindowImplcȝCreateҎ与CWindowcȝCreateҎ略有不同Q在CWindowcȝCreate中,我们必须指定一个注册了的窗口类Q但是CWindowImpl则不同,它创Z个新的窗口类Q因此,不需要ؓ它指定窗口类?<br /><br /><strong><a id="A4" name="A4"></a>一个简单而完整的CZQ?br /></strong><br />q篇文章中的大部分示例都只是代码片段Q但是下面列出的是一个完整的Hello world的示例程序。虽然我们用的是ATLQ但是没有涉及到COMQ因此在使用Visual C++&reg;建立目的时候,我们选择Win32&reg; application而不是ATL COMQ?<br />在stdafx.h文g中,加入下面几行Q?<pre>#include <atlbase.h> extern CComModule _Module; #include <atlwin.h> </pre>在hello.cpp文g中,写如下代码:<pre>#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; } </pre><p>在这个示例程序中QCmyWindow是从CWindowImplz的,它的消息映射捕获了两个消息WM_PAINT和WM_DESTROYQ当收到WM_PAINT消息Ӟ它的成员函数OnPaint处理q个消息q在H口上输出“Hello world”,当收到WM_DESTROY消息Ӟ也就是当用户关闭q个H口的时候,调用OnDestroy函数处理q个消息Q在OnDestroy函数中调用PostQuitMessage来结束消息@环?<br /><br />WinMain函数中创Z一个CmyWindowcȝ实例q实C一个标准的消息循环。(有一些地方,我们必须遵@ATL的规范,比如在这里我们必M用_Module。) <br /><br /><strong><a id="A5" name="A5"></a>消息映射Q?</strong><br />有三l用于消息映的宏,他们分别是: </p><ul><li>H口消息映射宏,用于所有的H口消息Q如WM_CREATE、WM_PAINT{)Q? </li><li>命o消息映射宏,专用于WM_COMMAND消息Q比如由控g或菜单发出的消息Q; </li><li>通知消息映射宏,专用于WM_NOTUFY消息Q通常由通用控g发出此消息,比如工具栏控件或列表视图控gQ?</li></ul><p>H口消息映射宏: <br />有两个窗口消息映宏Q他们分别是Q?/p><ul><li>MESSAGE_HANDLER </li><li>MESSAGE_RANGE_HANDLER </li></ul><p>W一个宏一个特定的消息映射到相应的处理函数Q第二个宏将一l消息映到一个处理函数。消息处理函数都要求h如下的原形: <br /></p><pre>LRESULT MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled); </pre><p>其中Q参数uMsg是消息标识,wParam和lParam是两个附加与消息的参敎ͼQ他们的具体意义取决与消息类别。) <br /><br />消息处理函数使用bHandled来标志消息是否已l被完全捕获Q如果bHandled被设|成FALSEQ程序将l箋在消息映表的后l部分查找这个消息的其它处理函数。这个特性得我们对一个消息用多个处理函数成为可能。什么时候需要对一个消息用多个处理函数呢Q可能是在对多个c链接时Q也可能是我们只惛_一个消息做出响应但是ƈ不真正捕获它。在处理函数被调用之前,bHandled被置为TRUEQ所以如果我们不在函数的l尾昑ּ地将它置为FALSEQ则消息映射表的后箋部分不会被l查找,也不会有其它的处理函数被调用?<br /><br />命o消息映射宏: <br />命o消息映射宏只处理命o消息QWM_COMMAND消息Q,但是它能让我们根据消息类型或者发送命令消息的控gID来指定消息处理函数?</p><ul><li>COMMAND_HANDLER映射一个特定控件的一条特定消息到一个处理函敎ͼ </li><li>COMMAND_ID_HANDLER映射一个特定控件的所有消息到一个处理函敎ͼ </li><li>COMMAND_CODE_HANDLER映射L控g的一个特定消息到一个处理函敎ͼ </li><li>COMMAND_RANGE_HANDLER映射一定范围内的控件的所有消息到一个处理函敎ͼ </li><li>COMMAND_RANGE_CODE_HANDLER映射一定范围内的控件的一条特定消息到一个处理函数?</li></ul><p>命o消息处理函数应该h如下的原形: </p><pre>LRESULT CommandHandler(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled); </pre><p>其中Q参数wNotifyCode代表消息代码QwID代表发送消息的控g的IDQhWndCtl代表发送消息的控g的窗口句柄,bHandled的意义如前所q?<br /><br />通知消息映射宏: <br />通知消息映射宏用来处理通知消息QWM_NOTUFY消息Q,它根据通知消息的类型和发送通知消息的控件的不同消息映到不同的处理函敎ͼq些宏与前面讲的命o消息映射宏是{h的,唯一的不同就是它处理的是通知消息而不是命令消息?</p><ul><li>NOTIFY_HANDLER </li><li>NOTIFY_ID_HANDLER </li><li>NOTIFY_CODE_HANDLER </li><li>NOTIFY_RANGE_HANDLER </li><li>NOTIFY_RANGE_CODE_HANDLER </li></ul><p>通知消息处理函数都需要如下的原ŞQ?/p><pre>LRESULT NotifyHandler(int idCtrl, LPNMHDR pnmh, BOOL& bHandled); </pre>其中Q参数idCtrl代表发送通知消息的控件的IDQ参数pnmh是指向一个NMHDRl构的指针,bHandled的意义如前所q?<br /><br />通知消息包含了一个指向消息细节的l构的指针,例如Q当一个列表视图控件发送一个通知消息Q这个消息就包含了一个指向NMLVDISPINFOl构的指针,所有类gNMLVDISPINFO的结构都包含一个NMHDRl构的头Qpnmh指向这个头Q如果需要访问这U结构中头部以外的其它数据成员,可以pnmh转化成相应类型的指针?<br /><br />例如Q我们如果要处理列表视图控g发出的LVN_ENDLABELEDIT通知消息Q我们可以把下面q行代码攑ֈ消息映射表中Q?<pre>NOTIFY_HANDLER( ID_LISTVIEW, LVN_ENDLABELEDIT, OnEndLabelEdit)</pre>q个通知消息附带的额外信息包含在一个NMLVDISPINFOl构中,因此Q消息处理函数看h应该象下面这个样子: <pre>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; ... </pre>可以看出Qpnmh指针被{化成NMLVDISPINFO*cdQ以便访问头部结构以外的数据?<br /><br /><strong><a id="A6" name="A6"></a>为现有的H口cL加功能:</strong><br />有许多向现有的窗口添加功能的Ҏ。如果这个类是ATLH口c,我们可以从这个窗口类z自己的类Q就象Base Class Chaining中描q的一栗这U方法主要是一个C++cȝl承加上一Ҏ息映的链接?<br /><br />如果我们x展一个预定义的窗口类Q如按纽cL列表框类Q的功能Q我们可以超cd它。就是创Z个基于这个预定义cȝ新类Qƈ在消息映表中添加消息映以增强它的功能?<br /><br />有些时候,我们需要改变一个已l存在的H口实例的行为,而不是一个窗口类——或许我们要让一个对话框上的~辑框做点什么特别的事情。在q种情况下,我们可以写一个新的ATLH口c,q子cdq个已经存在的编辑框。Q何本该发送到q个~辑框的消息都会先被发送到q个子类的对象?<br /><br />另外一U可选的ҎQ我们也可以让这个编辑框成ؓ一个被包含的窗口,所有发送到q个~辑框的消息都会l过它的容器H口Q我们可以在q个容器H口中ؓq个被包含的H口实现Ҏ的消息处理?<br /><br />最后的一U方法就是消息反,当一个窗口收C个消息后不处理它Q而是反射l发送这个消息的H口自己处理Q这U技术可以用来创包含的控件?<br /><br /><strong><a id="A7" name="A7"></a>基类消息链(Base Class ChainingQ:</strong><br />如果我们已经有一些实C特定功能的ATLH口c,我们可以从它们派生新cM充分利用l承的优炏V比如: <pre>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" ); </pre>可是Q上面的代码有一个问题。当我们在调试模式下q行q个E序Q一个窗口出CQ如果我们在q个H口中单击,“button down”将出现在输出窗口中Q这是CDrivedcȝ功能Q可是,当我们关闭这个窗口的时候,E序q不退出,管CBasecd理了WM_DESTROY消息q且CDrivedcL从CBasecL生的?<br /><br />WhyQ因为我们必L地一个消息映表链接到另外一个。如下: <pre>BEGIN_MSG_MAP( CDerived ) MESSAGE_HANDLER( WM_LBUTTONDOWN, OnButtonDown ) CHAIN_MSG_MAP( CBase ) // 链接到基c? END_MSG_MAP() </pre>现在QQ何在CDrivedcM没有被处理的消息都会被传到CBasecM?<br /><br />Z么不自动派生类的消息映和它的基类的消息映链接v来呢Q这是因为在ATL的体pȝ构中有很多多重承的情况Q这U情况下没有办法知道I竟应该链接到哪个基c,所以只好让E序员自己来做决定?<br /><br />可选的消息映射Q?<br />消息映射铑օ许多个类同时q行消息处理Q同时也带来了问题:如果我们在多个类中都要响应WM_CREATE消息Q但是不同的c需要基cL供不同的处理Q怎么办呢Qؓ了解册个问题,ATL使用了可选的消息映射Q将消息映射表分成很多节Q每一节用不同的数字标识,每一节都是一个可选的消息映射表?<pre>// 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() </pre>如上Q基cȝ消息映射表由3节组成:一个默认的消息映射表(隐含的标识ؓ0Q和两个可选的消息映射表(标识?00?01Q?<br /><br />当你链接消息映射表时Q指定你所希望的方案的标识Q如下: <pre>class CDerived: public CBase { BEGIN_MSG_MAP( CDerived ) CHAIN_MSG_MAP_ALT( CBase, 100 ) END_MSG_MAP() ... </pre>CDrivedcȝ消息映射表链接到CBasecM标识号ؓ100的可选节Q因此当WM_PAINT到达ӞCBase::OnPaint2被调用?<br />Q译者注Q我觉得q种Ҏ不太合乎C++的思想Q基cȝ~写者不一定总能知道z自它的类会有哪些需求,而且把所有不同的版本都在基类中实玎ͼ基类中无用的代码量会大大增加。更好的办法应该是把基类中的消息处理函数声明函数。MQ我觉得q一节q不能体现出可选消息映的真正用途。) <br /><br />其它cd的链Q?<br />除了基类消息映射链,ATL也提供了成员链(member chainingQ和动态链(dynamic chaining)Q这些很用到的链技术超Z我们q篇文章的讨_但是可以单提一下。成员链允许把消息映链接到一个类的成员变量,动态链允许在运行时q行动态链接。如果你想了解更多,请参考ATL文档中的CHAIN_MSG_MAP_DYNAMIC 和CHAIN_MSG_MAP_MEMBER的相兛_宏V?<br /><br /><strong><a id="A8" name="A8"></a>H口的超cdQ?/strong><br />类化定义一个类Qƈ为预定义的窗口类Q如按钮cL列表框类Q添加新的功能,下面的例子超cd一个按钮,让这个按钮在被单ȝ时候发鸣?<pre>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 </pre>DECLARE_WND_SUPERCLASS宏声明了q个H口的类名(“BeepButton”)和被类化的cdQ“Button”)。它的消息映表只有一个入口项Q将WM_LBUTTONDOWN消息映射到OnLButtonDown函数。其余的消息都让默认的窗口过E处理,除了可以发出蜂鸣外,CbeepButton需要和其它的按钮表现相同,因此在OnLButtonDown函数的最后,需要将bHandled讄为FALSEQ让默认的窗口过E在OnLButtonDown函数完成后对WM_LBUTTONDOWN消息q行其它的处理。(另外的一U方法是直接调用DefWindowProc函数。) <br /><br />到目前ؓ止,我们所做的只是定义了一个新c;我们依然需要创Z些真正的CbeepButtonH口Q下面的cd义了两个CbeepButtoncd的成员变量,因此Q当q个cȝH口被创建时Q将会创Z个CbeepButtoncd的子H口?<pre>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 </pre><strong><a id="A9" name="A9"></a>H口的子cdQ?/strong><br /><br />子类化允许我们改变一个已l存在的H口的行为,我们l常用它来改变控件的行ؓ。它的实现机制是插入一个消息映表来截取发向控件的消息。D例说明:假设有一个对话框Q对话框上有一个编辑框控gQ我们想让这个控件只接受不是数字的字W。我们可以截获发往q个控g的WM_CHAR消息q抛弃接收到的数字字W。下面的cd现这个功能: <pre>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; } }; </pre>q个cd处理一个消息WM_CHARQ如果这个字W是数字的话Q则调用MessageBeep( 0 )q返回,q样可以有效地忽略这个字W。如果不是数字,则将bHandled讄为FALSEQ指明默认的H口q程q个消息需要进一步处理?<br /><br />现在我们子cd一个编辑框控gQ以便CnoNumEdit能够抢先处理发到q个~辑框得消息。(下面得例子用CCdialogImplc,q个cL们将在ATL中的对话框类一节中介绍。)在这个例子中QCmyDialogcM用到了一个对话框资源QID号ؓIDD_DIALOG1Q,对话框中有一个编辑框控gQID号ؓIDC_EDIT1Q,当对话框初始化的时候,~辑框经qSubclassWindow而变成一个不接受数字的编辑框Q?<pre>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; }; </pre><strong><a id="A10" name="A10"></a></strong></div> <div> </div> <br /> <br /> <div id="djptx79" class="dashed"> </div> <br />文章引用? <a target="_blank">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnvc60/html/atlwindow.asp</a><img src ="http://m.shnenglu.com/yishanhante/aggbug/19687.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-13 09:52 <a href="http://m.shnenglu.com/yishanhante/articles/19687.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用ATL建立轻量U的COM对象【二?/title><link>http://m.shnenglu.com/yishanhante/articles/19659.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Mon, 12 Mar 2007 13:29:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19659.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19659.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19659.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19659.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19659.html</trackback:ping><description><![CDATA[ <p> <b>h?/b> <br /> <br />    在本文的<a >W一部分</a>Q我们简要介l了ATL的一些背景知识以及ATL所面向的开发技术和环境。在q一部分 开始走qATLQ讲qATL~程的基本方法、原则和必须要注意的问题?br />    理解ATL最Ҏ的方法是考察它对客户端编E的支持。对于COM~程新手而言Q一个棘手的主要问题之一是正管理接口指针的引用计数。COM的引用计数法则是没有q行时强?性的Q也是说每一个客L必须保证对对象的承诺?br />    有经验的COM~程者常怹惯于使用文档中(如《Inside OLE》)提出的标准模式。调用某个函数或ҎQ返回接口指针,在某个时间范围内使用q个接口指针Q然后释攑֮。下面是使用q种模式的代码例子: </p> <pre>void f(void) { IUnknown *pUnk = 0; // 调用 HRESULT hr = GetSomeObject(&pUnk); if (SUCCEEDED(hr)) { // 使用 UseSomeObject(pUnk); // 释放 pUnk->Release(); } } </pre>    q个模式在COME序员心中是如此Ҏ蒂固Q以至于他们常常不写实际使用指针的语句,而是先在代码块末敲入Release语句。这很像CE序员用switch语句时的条g反射一P先敲入break再说?br />    其实调用Release实在不是什么可怕的负担Q但是,客户端程序员面两个相当严重的问题。第一个问题与获得多接口指针有兟뀂如果某个函数需要在做Q何实际工作之前获得三个接口指针,也就是说在第一个用指针的语句之前必须要由三个调用语句。在书写代码Ӟq常常意味着E序员需要写许多嵌套条g语句Q如Q?br /><pre>void f(void) { IUnknown *rgpUnk[3]; HRESULT hr = GetObject(rgpUnk); if (SUCCEEDED(hr)) { hr = GetObject(rgpUnk + 1); if (SUCCEEDED(hr)) { hr = GetObject(rgpUnk + 2); if (SUCCEEDED(hr)) { UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]); rgpUnk[2]->Release(); } rgpUnk[1]->Release(); } rgpUnk[0]->Release(); } } </pre>    像这L语句常常促ɽE序员将TAB键设|成一个或两个I格Q甚x愿用大一点的昄器。但事情q不L按你惌的那P׃U种原因目团队中的COMlg~程人员往往得不?所想的g支持Q而且在公司确定关于TAB键的使用标准之前Q程序员常常求助于用有很大争议但仍然有效的“GOTO”语句:<br /><pre>void f(void) { IUnknown *rgpUnk[3]; ZeroMemory(rgpUnk, sizeof(rgpUnk)); if (FAILED(GetObject(rgpUnk))) goto cleanup; if (FAILED(GetObject(rgpUnk+1))) goto cleanup; if (FAILED(GetObject(rgpUnk+2))) goto cleanup; UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]); cleanup: if (rgpUnk[0]) rgpUnk[0]->Release(); if (rgpUnk[1]) rgpUnk[1]->Release(); if (rgpUnk[2]) rgpUnk[2]->Release(); } </pre>q样的代码虽然不那么专业Q但臛_减少了屏q的水^滚动?br />使用以上q些代码D|在着更加手的问题,那就是在到C++异常时。如果函数UseObjects丢出异常Q则释放指针的代码被完全屏蔽掉了?解决q个问题的一个方法是使用Win32的结构化异常处理QSEHQ进行终l操作:<br /><pre>void f(void) { IUnknown *rgpUnk[3]; ZeroMemory(rgpUnk, sizeof(rgpUnk)); __try { if (FAILED(GetObject(rgpUnk))) leave; if (FAILED(GetObject(rgpUnk+1))) leave; if (FAILED(GetObject(rgpUnk+2))) leave; UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]); } __finally { if (rgpUnk[0]) rgpUnk[0]->Release(); if (rgpUnk[1]) rgpUnk[1]->Release(); if (rgpUnk[2]) rgpUnk[2]->Release(); } </pre>    可惜Win32 SHE在C++中的表现q不如想象得那么好。较好的Ҏ是用内建的C++异常处理模型Q同时停止用没有加工过的指针。标准C++库有一个类Qauto_ptrQ在其析构函C?M一个操作指针的delete调用Q即使在出现异常时也能保证调用)。与之类|ATL有一个COM指针QCComPtrQ它的析构函C正确调用Release?br />    <a >CComPtr</a>cd现客L基本的COM引用计数模型。CComPtr有一个数据成员,它是一个未l过M加工的COM接口指针。其cd被作为模板参C递:<br /><pre> CComPtr<IUnknown> unk; CComPtr<IClassFactory> cf; </pre>    ~省的构造函数将q个原始指针数据成员初始化ؓNULL。智能指针也有构造函敎ͼ它的参数要么是原始指针,要么是相同类型的参数。不论哪U情况,指针都调用AddRef控制引用。CComPtr的赋值操作符 既可以处理原始指针,也可以处理智能指针,q且在调用新分配指针的AddRef之前自动释放保存的指针。最重要的是QCComPtr的析构函数释放保存的接口Q如果非I)。请看下列代码:<br /><pre>void f(IUnknown *pUnk1, IUnknown *pUnk2) { // 如果非空Q构造函数调用pUnk1的AddRef CComPtr<iunknown> unk1(pUnk1); // 如果非空Q构造函数调用unk1.p的AddRef CComPtr<iunknown> unk2 = unk1; // 如果非空Qoperator= 调用unk1.p的Releaseq且 //如果非空Q调用unk2.p的AddRef unk1 = unk2; //如果非空Q析构函数释放unk1 ?unk2 } </iunknown></iunknown></pre>    除了正确实现COM的AddRef ?Release规则之外QCComPtrq允许实现原始和指针的透明操作Q参?a >附表?/a>所C。也是说下面的代码按照你所惌的方式运行:<br /><pre>void f(IUnknown *pUnkCO) { CComPtr<iclassfactory> cf; HRESULT hr; // 使用操作W?& 获得?&cf.p 的存? hr = pUnkCO->QueryInterface(IID_IClassFactory,(void**)&cf); if (FAILED(hr)) throw hr; CComPtr<iunknown> unk; // 操作W?-> 获得对cf.p的存? // 操作W?& 获得?&unk.p的存? hr = cf->CreateInstance(0, IID_IUnknown, (void**)&unk); if (FAILED(hr)) throw hr; // 操作W?IUnknown * q回 unk.p UseObject(unk); // 析构函数释放unk.p ?cf.p } </iunknown></iclassfactory></pre>    除了~Z对Release的显式调用外Q这D代码像是纯_的COM代码。有了CComPtrcȝ武装Q前面所遇到的麻烦问题顿时变得简单了Q?br /><pre>void f(void) {<br /> CComPtr<IUnknown> rgpUnk[3];<br /> if (FAILED(GetObject(&rgpUnk[0]))) return;<br /> if (FAILED(GetObject(&rgpUnk[1]))) return;<br /> if (FAILED(GetObject(&rgpUnk[2]))) return;<br /> UseObjects(rgpUnk[0], rgpUnk[1], rgpUnk[2]);<br /> }<br /></pre>׃CComPtrҎ作符重蝲用法的扩展,使得代码的编译和q行无懈可击?br />    假定模板cȝ道它所操纵的指针类型,你可能会问:那ؓ什么智能指针不能在它的功能操作W或构造函C自动调用QueryInterfaceQ从而更有效地包装IUnknown呢?在Visual C++ 5.0出来以前Q没有办法将某个接口的GUID与它的本w的C++cd兌h——Visual C++ 5.0用私有的declspec某个IID与一个接口定义绑定在一赗因为ATL的设?考虑C它要与大量不同的C++~译器一起工作,它需要用与编译器无关的手D|供GUID。下面我们来探讨另一个类——CComQIPtrcR?br />    CComQIPtr与CComPtr关系很密切(实际上,它只增加了两个成员函敎ͼ。CComQIPtr必须要两个模板参敎ͼ一个是被操U늚指针cd Q另一个是对应于这个指针类型的GUID。例如,下列代码声明了操UIDataObject 和IPersist接口的智能指针:<br /><pre> CComQIPtr<IDataObject, &IID_IDataObject> do; CComQIPtr<IPersist, &IID_IPersist> p; </pre>    CComQIPtr的优Ҏ它有重蝲的构造函数和赋值操作符。同cȝ本(例如Q接受相同类型的接口Q仅仅AddRef双的赋?初始化操作。这实际上就是CComPtr的功能。异cȝ本(接受cd不一致的接口Q正调用QueryInterface来决定是否这个对象确实支持所h的接口:<br /><pre> void f(IPersist *pPersist) { CComQIPtr<IPersist, &IID_IPersist> p; // 同类赋?- AddRef''s p = pPersist; CComQIPtr<IDataObject, &IID_IDataObject> do; // 异类赋?- QueryInterface''s do = pPersist; } </pre>    在第二种赋D句中Q因为pPersist是非IDataObject *cdQ但它是z于IUnknown的接口指针,CComQIPtr通过pPersist调用QueryInterface来试图获得这个对象的IDataObject接口指针。如果QueryInterface调用成功Q则此智能指针将含有作ؓl果的原始IDataObject指针。如果QueryInterface调用p|Q则do.p被|ؓnull。如果QueryInterfaceq回的HRESULT值很重要Q但又没有办法从赋值操作获得其值时Q则必须昑ּ调用QueryInterface?br />    既然有了CComQIPtrQ那Z么还要CComPtr呢?由几个理由:首先QATL最初的发布版本只支持CComPtrQ所以它׃直合法地保留下来了。其二(也是最重要的理由)Q由于重载的构造函数和赋值操作,对IUnknown使用CComQIPtr是非法的。因为所有COM接口的类型定义都必须与IUnknown兼容?br /><pre> CComPtr<IUnknown> unk; </pre>从功能上它{同?<pre> CComQIPtr<IUnknown, &IID_IUnknown> unk; </pre>前者正。后者是错误的用法。如果你q样写了QC++~译器将提醒你改正?br />    CComPtr作ؓ首选的另外一个理由可能是一些开发h员相信静悄悄地调用QueryInterfaceQ没有警告,削弱了C++pȝ的类型。毕竟,C++在没有进行强制类型{换的情况下不允许对类型不一致的原始指针 q行赋值操作,所以ؓ什么要用智能指针的道理也在q,q运的是开发h员可以选择最能满需要的指针cd?br />    许多开发h员将指针看成是对q于的复杂编EQ务的化。我最初也是这么认为的。但只要留意它们使用COM指针的方法。就会逐渐认识到它们引入的潜在危险与它们解决的问题一样多?br />关于q一点,我用一个现成的使用原始指针的函Cؓ例:<br /><pre> void f(void) { IFoo *pFoo = 0; HRESULT hr = GetSomeObject(&pFoo); if (SUCCEEDED(hr)) { UseSomeObject(pFoo); pFoo->Release(); } } </pre>它自然而然转换C用CComPtr?br /><pre> void f(void) { CComPtr<IFoo> pFoo = 0; HRESULT hr = GetSomeObject(&pFoo); if (SUCCEEDED(hr)) { UseSomeObject(pFoo); pFoo->Release(); } } </pre>    注意CComPtr ?CComQIPtr输出所有受控接口成员,包括AddRef和Release。可惜当客户端通过操作W?>的结果调用ReleaseӞ指针很健?Q会二次调用构造函C的Release。显然这是错误的Q编译器和链接器也欣然接受了q个代码。如果你q气好的话,调试器会很快捕获到这个错误?br />    使用ATL指针的另一个要引v注意的风险是cd强制转换操作W对原始指针提供的访问。如果隐式强制{换操作符的用存在争议。当 ANSI/ISO C++ 委员会在军_采用某个C++串类Ӟ他们明确止隐式cd转换。而是要求必须昑ּ使用c_str函数在需要常量char *Qconst char *Q的地方传递标准C++丌ӀATL提供了一U隐含式的类型{换操作符利地解决了q个问题。通常Q这个{换操作符可以Ҏ你的喜好来用,允许你将指针传递到需要用原始指针的函数?br /><pre> void f(IUnknown *pUnk) { CComPtr<iunknown> unk = pUnk; // 隐式调用操作WIUnknown *() CoLockObjectExternal(unk, TRUE, TRUE); } </iunknown></pre>q段代码能正运行,但是下面的代码也不会产生警告信息Q编译正帔R过Q?<pre>HRESULT CFoo::Clone(IUnknown **ppUnk) { CComPtr<iunknown> unk; CoCreateInstance(CLSID_Foo, 0, CLSCTX_ALL, IID_IUnknown, (void **) &unk); // 隐式调用操作WIUnknown *() *ppUnk = unk; return S_OK; } </iunknown></pre>    在这U情况下Q智能指针(unkQ对原始值针**ppUnk的赋D发了与前面代码段相同的强制类型{换。在W一个例子中Q不需要用AddRef。在W二个例子中Q必要用AddRef?br />    有关使用指针的更详细一般信息,请参见Scott Meyer的《More Effective C++》(Addison-Wesley, 1995q出版)。国内目前还没有q本书的中译本或影印本。有关COM指针的更多特定信息,请参见Don Box的一关于智能指针的专题文章http://www.develop.com/dbox/cxx/SmartPointer.htm?Q待l)<br /><img src ="http://m.shnenglu.com/yishanhante/aggbug/19659.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-12 21:29 <a href="http://m.shnenglu.com/yishanhante/articles/19659.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>CComPtr ?CComQIPtr http://m.shnenglu.com/yishanhante/articles/19619.htmljayjayMon, 12 Mar 2007 03:39:00 GMThttp://m.shnenglu.com/yishanhante/articles/19619.htmlhttp://m.shnenglu.com/yishanhante/comments/19619.htmlhttp://m.shnenglu.com/yishanhante/articles/19619.html#Feedback0http://m.shnenglu.com/yishanhante/comments/commentRss/19619.htmlhttp://m.shnenglu.com/yishanhante/services/trackbacks/19619.html对于操作原始的接口指针是比较ȝ的,需要我们自己控制引用记数、API 调用、异常处理。于?ATL 提供?/p>

2个智能指针的模板包装c,CComPtr<> ?CComQIPtr<>Q这两个c都?<atlbase.h> 中声明。CComQIPtr<>

包含?CComPtr<>的所有功能,因此我们可以完全?CComQIPtr<> 来用智能接口指针,唯一要说明的一点就

是:CComQIPtr<> ׃使用了运符的重载功能,它会自动帮我们调用QueryInterface()函数Q因?

CComQIPtr<> 唯一的缺点就是不能定?IUnknown * 指针?/p>

    // 指针 smart pointerQ按照匈牙利命名法,一般以 sp 开头来表示变量cd
    CComPtr < IUnknown > spUnk; // 正确
    // 假设 IFun 是一个接口类?br />    CComPtr < IFun > spFun; // 正确
    CComQIPtr < IFun > spFun; // 正确
    CComQIPtr < IFun, &IID_IFun > spFun; // 正确
    CComQIPtr < IUnknown > spUnk; // 错误QCComQIPtr不能定义IUnknown指针
l智能指针赋值的ҎQ    CComQIPtr < IFun > spFun; // 调用构造函敎ͼq没有赋|被包装的内部

接口指针?NULL
   
    CComQIPtr < IFun > spFun( pOtherInterface ); // 调用构造函敎ͼ内部接口指针赋gؓ
    // 通过 pOtherInterface q个普通接口指针调用QueryInterface()得到的IFun接口指针
   
     
    CComQIPtr < IFun > spFun ( pUnknown ); // 调用构造函敎ͼ由IUnknown的QueryInterface()得到

IFun接口指针
   
    CComQIPtr < IFun > spFun = pOtherInterface; // = q算W重载,含义和上面一?br />    spFun = spOtherInterface; // 同上
    spFun = pUnknown; // 同上
   
    pUnknown->QueryInterface( IID_IFun, &sp ); // 也可以通过QueryInterface赋?br />   
    // 指针赋值后Q可以用条g语句判断是否合法有效
    if ( spFun ){}  // 如果指针有效
    if ( NULL != spFun ){} // 如果指针有效
   
    if ( !spFun ){}  // 如果指针无效
    if ( NULL == spFun ){} // 如果指针无效
指针调用函数的方法:    spFun.CoCreateInstance(...); // {h?API

函数::CoCreateInstance(...)
    spFun.QueryInterface(...); // {h?API 函数::QueryInterface()
   
    spFun->Add(...); // 调用内部接口指针的接口函?/p>

    // 调用内部接口指针的QueryInterface()函数Q其实效果和 spFun.QueryInterface(...) 一?br />    spFun->QueryInterface(...); 
   
    spFun.Release(); // 释放内部的接口指针,同时内部指针赋gؓ NULL
    spFun->Release(); // 错!Q!一定不要这么用?br />    // 因ؓq个调用q不把内部指针清I,那么析构的时候会被再ơ释放(释放了两ơ)



jay 2007-03-12 11:39 发表评论
]]>
COM lg设计与应用(七)~译、注册、调?/title><link>http://m.shnenglu.com/yishanhante/articles/19617.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Mon, 12 Mar 2007 03:01:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19617.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19617.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19617.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19617.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19617.html</trackback:ping><description><![CDATA[ <p> <b>一、前a</b> <br /> <br />  <a target="_blank">上两回中</a>Q咱们用 ATL 写了W一?COM lgE序Q这回中Q主要介l编译、注册和调用Ҏ。示例程序你已经下蝲了吗Q如果还没有下蝲Qvc6.0 的用L<a >q里</a>Qvc.net 的用L<a >q里</a>?b><br /></b><br /><br /><b>二、关于编?br /><i><br />  2-1 最依?/i></b><br />  “最依赖”,表示~译器会?ATL 中必M用的一些函数静态连接到目标E序中。这L标文件尺怼E大Q但独立性更强,安装方便Q反之系l执行的时候需要有 ATL.DLL 文g的支持。如何选择讄为“最依赖”呢Q答案是Q删除预定义宏“_ATL_DLL”,操作Ҏ见图一、图二?br /><br /><img height="446" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut7pic1.jpg" width="677" border="0" /><br />图一、在vc6.0中,讄Ҏ<br /><br /><img height="455" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut7pic2.jpg" width="645" border="0" /><br />图二、在 vc.net 2003中,讄Ҏ<br /><br />  <i><b>2-2 CRT?/b></i><br />  如果?ATL lgE序中调用了 CRT 的运行时d函数Q比如开qx sqrt() Q那么编译的时候可能会报错“error LNK2001: unresolved external symbol _main”。怎么办?删除预定义宏“_ATL_MIN_CRT”!操作Ҏ也见图一、图二。(vc.net 2003 中的q个目属性叫“在 ATL 中最?CRT”)<br /><br />  <i><b>2-3 MBCS/UNICODE</b></i><br />  q个不多说了Q在预定义宏中,分别使用 _MBCS ?_UNICODE?br /><br />  <i><b>2-4 IDL 的编?/b></i><br />  COM 在设计初期,定了一个目标:要能实现跨语a的调用。既然是跨语a的,那么lg的接口描q就必须在Q何语a环境中都要能够认识。怎么办??.h 文g描述Q?----- C语言E序员笑了,真方便!BASIC E序员哭?-( 因此Q微软用了一个新的文件格?--IDL文gQ接口定义描q语aQ。IDL 是一个文本文Ӟ它的语言语法比较单,很象C。具?IDL 文g的讲解,见下一回《COM lg设计与应用(八)之添加新接口》。IDL l过~译Q生成二q制的等L型库文g TLB 提供l其它语a来用。图三示意了 ATL COM E序~译的过E:<br /><br /><img height="330" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut7pic3.jpg" width="561" border="0" /><br />图三、ATL lgE序~译q程<br /><br />  说明1Q编译后Q类型库?TLB 文g形式单独存在Q同时也保存在目标文件的资源中。因此,我们来?#import 引入cd库的时候,既可以指?TLB 文gQ也可以指定目标文gQ?br />  说明2Q我们作?C/C++ 的程序员Q还是比较q福的。因?IDL ~译后,Ҏ为我们提供了 C 语言形式的接口文件?br />  说明3QIDL ~译后生成代?存根源程序,有:dlldata.c、xxx_p.c、xxxps.def、xxxps.makQ我们可以用 NMAKE.EXE 再次~译来生真正的代理/存根DLL目标文g(?)?br /><br /><br /><b>三、关于注?br /><br /></b>  <b><i>情况1Q?/i></b>当我们?ATL ~写lgE序Q注册不用我们来负责。编译成功后QIDE 会帮我们自动注册Q?br />  <i><b>情况2Q?/b></i>当我们?MFC ~写lgE序Q由于编译器不知道你写的是否?COM lgQ所以它不会帮我们自动注册。这个时候,我们可以执行菜单“Tools\Register Control”来注册?br />  <i><b>情况3Q?/b></i>当我们写一个具?COM 功能?EXE E序Ӟ注册的方法就是运行一ơ这个程序;<br />  <i><b>情况4Q?/b></i>当我们需要用第三方提供的组件程序时Q可以命令行q行“regsvr32.exe 文g名”来注册。顺便说一句,反注册的Ҏ是“regsvr32.exe /u 文g名”;<br />  <i><b>情况5Q?/b></i>当我们需要在E序中(比如安装E序Q需要执行注册,那么Q?/p> <pre>typedef HRESULT (WINAPI * FREG)(); TCHAR szWorkPath[ MAX_PATH ]; ::GetCurrentDirectory( sizeof(szWorkPath), szWorkPath ); // 保存当前q程的工作目? ::SetCurrentDirectory( lg目录 ); // 切换到组件的目录 HMODULE hDLL = ::LoadLibrary( lg文g?); // 动态装载组? if(hDLL) { FREG lpfunc = (FREG)::GetProcAddress( hDLL, _T("DllRegisterServer") ); // 取得注册函数指针 // 如果是反注册Q可以取?DllUnregisterServer"函数指针 if ( lpfunc ) lpfunc(); // 执行注册。这里ؓ了简单,没有判断q回? ::FreeLibrary(hDLL); } ::SetCurrentDirectory(szWorkPath); // 切换回原先的q程工作目录 </pre>  上面的示例,在多数情况下可以化掉切换工作目录的代码部分。但是,如果q个lg在装载的时候,它需要同时加载一些必M赖的DLLӞ有可能由于它自nE序?BUG D无法正确定位。咳......q是让我们自己写的程序,来I补它的错误吧......谁让׃是好人呢 Q谁让咱们的水^比他高呢Q谁让咱们在 <a target="_blank">vckbase</a> 上是个“榜眼”呢......<br /><br /><br /><b>四?/b><b>关于lg调用<br /><br /></b>  ȝ来说Q调用组件程序大概有如下ҎQ?br />  <table cellspacing="1" width="100%" border="1"><tbody><tr><td width="22%">#include Ҏ</td><td width="78%">IDL~译后,为方便C/C++E序员的使用Q会产生xxx.h和xxx_i.c文g。我们真q福Q直?include后就可以使用?/td></tr><tr><td width="22%">#import Ҏ</td><td width="78%">比较通用的方法,vc 会帮我们产生包装c,让我们的调用更方?/td></tr><tr><td width="22%">加蝲cd库包装类 Ҏ</td><td width="78%">如果lg提供?IDispatch 接口Q用q个Ҏ调用lg是最单的啦。不q还没讲IDispatchQ只能看以后的文章啦</td></tr><tr><td width="22%">加蝲ActiveX包装c?Ҏ</td><td width="78%">ActiveX q没介绍呢,以后再说?/td></tr></tbody></table><p>  下蝲CZE序后,请逐项览使用ҎQ?/p><table cellspacing="1" width="100%" border="1"><tbody><tr><td align="middle" width="5%"><b>CZ</b></td><td width="11%"><p align="center"><b>Ҏ</b></p></td><td width="177%"><p align="center"><b>要说?/b></p></td></tr><tr><td align="middle" width="5%">1</td><td width="11%">#include</td><td width="177%">完全用最基本?API 方式调用lgQ大家熟悉调用原理</td></tr><tr><td align="middle" width="5%">2</td><td width="11%">#include</td><td width="177%">大部分?API 方式Q?CComBSTR 化对字符串的使用</td></tr><tr><td align="middle" width="5%">3</td><td width="11%">#include</td><td width="177%">展示指针 CComPtr<> 的用方?/td></tr><tr><td align="middle" width="5%">4</td><td width="11%">#include</td><td width="177%">展示指针 CComPtr<> ?CComQIPtr<> 混合的用方?/td></tr><tr><td align="middle" width="5%">5</td><td width="11%">#include</td><td width="177%">展示指针 CComQIPtr<> 的用方?/td></tr><tr><td align="middle" width="5%">6</td><td width="11%">#include</td><td width="177%">展示指针的释放方?/td></tr><tr><td align="middle" width="5%">7</td><td width="11%">#import</td><td width="177%">vc 包装的智能指?IxxxPtr、_bstr_t、_variant_t 的用方法和异常处理</td></tr><tr><td align="middle" width="5%">8</td><td width="11%">#import</td><td width="177%">import 后的命名I间的用方?/td></tr></tbody></table><p>  CZE序中都写有注释Q请读者仔l阅dƈ同时参?MSDN 的函数说明。这里,我给大家介绍一下“智能指针”:<br />  对于操作原始的接口指针是比较ȝ的,需要我们自己控制引用记数、API 调用、异常处理。于?ATL 提供?个智能指针的模板包装c,CComPtr<> ?CComQIPtr<>Q这两个c都?<atlbase.h> 中声明。CComQIPtr<> 包含?CComPtr<>的所有功能,因此我们可以完全?CComQIPtr<> 来用智能接口指针,唯一要说明的一点就是:CComQIPtr<> ׃使用了运符的重载功能,它会自动帮我们调用QueryInterface()函数Q因?CComQIPtr<> 唯一的缺点就是不能定?IUnknown * 指针?/p><pre>   // 指针 smart pointerQ按照匈牙利命名法,一般以 sp 开头来表示变量cd    CComPtr < IUnknown > spUnk; // 正确    // 假设 IFun 是一个接口类?    CComPtr < IFun > spFun; // 正确    CComQIPtr < IFun > spFun; // 正确    CComQIPtr < IFun, &IID_IFun > spFun; // 正确    CComQIPtr < IUnknown > spUnk; // 错误QCComQIPtr不能定义IUnknown指针</pre>l智能指针赋值的ҎQ?pre>   CComQIPtr < IFun > spFun; // 调用构造函敎ͼq没有赋|被包装的内部接口指针?NULL       CComQIPtr < IFun > spFun( pOtherInterface ); // 调用构造函敎ͼ内部接口指针赋gؓ    // 通过 pOtherInterface q个普通接口指针调用QueryInterface()得到的IFun接口指针       CComQIPtr < IFun > spFun( spOtherInterface ); // 调用构造函敎ͼ内部接口指针赋gؓ    // 通过 spOtherInterface q个只能接口指针调用QueryInterface()得到的IFun接口指针       CComQIPtr < IFun > spFun ( pUnknown ); // 调用构造函敎ͼ由IUnknown的QueryInterface()得到IFun接口指针       CComQIPtr < IFun > spFun = pOtherInterface; // = q算W重载,含义和上面一?    spFun = spOtherInterface; // 同上    spFun = pUnknown; // 同上       pUnknown->QueryInterface( IID_IFun, &sp ); // 也可以通过QueryInterface赋?       // 指针赋值后Q可以用条g语句判断是否合法有效    if ( spFun ){} // 如果指针有效    if ( NULL != spFun ){} // 如果指针有效       if ( !spFun ){} // 如果指针无效    if ( NULL == spFun ){} // 如果指针无效</pre>指针调用函数的方法:<pre>   spFun.CoCreateInstance(...); // {h?API 函数::CoCreateInstance(...)    spFun.QueryInterface(...); // {h?API 函数::QueryInterface()       spFun->Add(...); // 调用内部接口指针的接口函?    // 调用内部接口指针的QueryInterface()函数Q其实效果和 spFun.QueryInterface(...) 一?    spFun->QueryInterface(...);       spFun.Release(); // 释放内部的接口指针,同时内部指针赋gؓ NULL    spFun->Release(); // 错!Q!一定不要这么用?    // 因ؓq个调用q不把内部指针清I,那么析构的时候会被再ơ释放(释放了两ơ)</pre>?.....不说了,不说了,大家多看书,多看MSNDQ多看示例程序吧?写篏?-(<br /><br /><b>五、小l?/b><br /><br />  敬请x《COM lg设计与应???-----如何增加 ATL lg中的W二个接? <p></p><hr /> ?Q编译代?存根Qvc6.0 中稍微麻烦,我们在后面介l“进E外lg”和“远E组件”的时候再介绍。在 vc.net 2003 下则比较单,因ؓ代理/存根作ؓ单独的一个工E项目会自动加到我们的解x案中了?img src ="http://m.shnenglu.com/yishanhante/aggbug/19617.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-12 11:01 <a href="http://m.shnenglu.com/yishanhante/articles/19617.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>COM lg设计与应用(六)?ATL 写第一个组?/title><link>http://m.shnenglu.com/yishanhante/articles/19606.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Mon, 12 Mar 2007 02:40:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19606.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19606.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19606.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19606.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19606.html</trackback:ping><description><![CDATA[ <strong>一、前a<br /><br /></strong>  1、与 <a target="_blank"><font color="#002c99">《COM lg设计与应???/font></a>的内容基本一致。但本回讲解的是?vc.net 2003 下的使用ҎQ即使你不再使用vc6.0Q也请和上一回的内容Q参照比寏V?br />  2、这W一个组Ӟ除了所?COM lg必须?IUnknown 接口外,我们再实C个自己定义的接口 IFunQ它有两个函敎ͼ Add()完成两个数值的加法QCat()完成两个字符串的q接?br />  3、下?.....好好听讲! 开始了:-)<br />  <p><b>二?/b><b>建立 ATL 工程</b><br /><br />  步骤2.1Q徏立一个解x案?br />  步骤2.2Q在 该解x案中Q新Z?vc++ ?ATL 目。示例程序叫 Simple2Qƈ选择DLL方式Q见图一、图二?br /><br /><img height="361" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic01.jpg" width="531" border="0" /><br />图一、新?ATL 目<br /><br /><img height="449" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic02.jpg" width="615" border="0" /><br />图二、选择非属性化的DLLlgcd<br /><br />  <i><b>属性化</b></i> 属性化~程Q是未来的方向,但我们现在先不要选它?br />  <i><b>动态链接库(DLL)</b></i> 选择它?br />  <i><b>可执行文?EXE)</b></i> 以后再讲?br />  <i><b>服务(EXE)</b></i> 表示建立一个系l服务组件程序,pȝ启动后就会加载ƈ执行的程序?br />  <b><i>允许合ƈ代理/存根(stub)代码</i></b> 选择该项表示把“代?存根”代码合q到lgE序中,否则需要单独编译,单独注册代理存根E序。代?存根Q这个是什么概念?q记得我们在<a ><font color="#002c99">上回?/font></a>中介l的吗?当调用者调用进E外或远E组件功能的时候,其实是代?存根负责数据交换的。关于代?存根的具体变成和操作Q以后再说啦......<br />  <b><i>支持</i></b><i><b> MFC</b></i> 除非有特D的原因Q我们写 ATL E序Q最好不要选择该项。你可能会说Q如果没有MFC的支持,那CString怎么办呀Q告诉你个秘密吧Q一般h我都不告诉他Q我后半辈子靠着q个U密zȝ了:<br />  1、你会STL吗?可以?STL 中的 string 代替Q?br />  2、自己写?MyString c,嘿嘿Q?br />  3、悄悄地、秘密地、不要告诉别人(特别是别告诉微YQ,?MFC 中的 CString 源码拿过来用Q?br />  4、?CComBSTR c,臛_也能化我们字W串操作Q?br />  5、直接用 API 操作字符Ԍ反正我们大家学习 C 语言的时候,都是从这里干L。({于没说Q呵呵)<br />  <b><i>支持 COM+ 1.0</i></b> 支持事务处理?COM+ 功能。COM+ 也许在第 99 回介l吧?br /><br /><br /><b>三、添?ATL 对象c?/b><br /><br />  步骤3.1Q菜?目\dc?.."Q或者用鼠标右键?目中弹?d\dc?.."Qƈ选择 ATL 单对象。见图三?br /><br /><img height="418" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic03.jpg" width="531" border="0" /><br />图三、选择建立ATL单对?br /><br />  除了单对?只实C IUnknown 接口)Q还可以选择“ATL控g?ActiveXQ实C10多个接口)......可以选择的组件对象类型很多,但本质上Q就是让向导帮我们默认加上一些接口。在以后的文章中Q陆l介l吧?br /><br />  步骤3.2Q增加自定义c?CFun(接口 IFun) ,见图四?br /><br /> <img height="449" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic04.jpg" width="615" border="0" /><br />囑֛、填写名U?br /><br />  其实Q我们只需要输入简Uͼ其它的项目会自动填写。没什么多说的Q只请大家注意一?ProgID ,默认?ProgID 构造方式ؓ“项目名.U名”?br /><br />  步骤3.3Q填写接口属性选项Q见?五?br /><br /><img height="449" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic05.jpg" width="615" border="0" /><br />图五、接口选项<br /><br />  <b><i>U程模型</i></b> COM 中的U程Q我认ؓ是最讨厌Q最复杂的部分。COM U程和公寓的概念Q留待后l介l。现在吗......大家都?单元"(Apartment)Q它代表什么那Q简单地_当在U程中调用组件函数的时候,q些调用会排队进行。因此,q种模式下,我们可以暂时不用考虑同步的问题??)<br />  <b><i>接口</i></b>。双?Dual)Q这个非?非常重要Q非帔R常常用,但我们今天不??)?b>切记Q切讎ͼ我们的这W一?COM E序中,一定要选择“自定义”!Q!Q?/b>Q如果你选错了,请删除全部内容,重新来过。)<br />  <i><b>聚合</b></i> 我们写的lgQ将来是否允许被别h聚合(?)使用。“只能创Zؓ聚合”,有点cM C++ 中的U虚c,你要是dE师Q只负责设计但不亲自写代码的话,才选择它?br />  <i><b>ISupportErrorInfo</b></i> 是否支持丰富信息的错误处理接口。以后就讌Ӏ?br />  <b><i>q接</i></b><i><b>?/b></i> 是否支持q接Ҏ口(事g、回调)。以后就讌Ӏ?br />  <i><b>IObjectWithSite</b></i> 是否支持IE的调?br /><br /><br /><b>四、添加接口函?br /></b><br /><img height="471" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic06.jpg" width="422" border="0" /><br />囑օ、调出增加接口方法的菜单<br /><br /><img height="449" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic07.jpg" width="615" border="0" /><br />图七、增加接口函?Add<br /><br />  h照图C的ҎQ增加Add()函数Q增加Cat()函数 。[in]表示参数方向是输入;[out]表示参数方向是输出;[out,retval]表示参数方向是输出,同时可以作ؓ函数q算l果的返回倹{一个函CQ可以有多个[in]、[out]Q但[retval]只能有一个,q且要和[out]l合后在最后一个位|??)<br /><br /><img height="599" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut6pic08.jpg" width="490" border="0" /><br />囑օ、接口函数定义完成后的图C?br /><br />  我们都知道,要想改变 C++ 中的cd敎ͼ需要修改两个地方:一是头文g(.h)中类的函数声明,二是函数?.cpp)文g的实现处。而我们现在用 ATL 写组件程序,则还要修改一个地方,是接口定义(IDL)文g。别着?IDL 下次p讨论啦?br /><br /><br /><b>五、实现接口函?/b><br /><br />  鼠标双点囑օ中CFun\基项和接口\Add(...)可以开始输入函数实CQ?/p><pre>STDMETHODIMP CFun::Add(long n1, long n2, long *pVal) { *pVal = n1 + n2; return S_OK; }</pre>q个太简单了Q不再浪费“口条”。下面我们实现字W串q接的Cat()函数Q?pre>STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal) { int nLen1 = ::SysStringLen( s1 ); // s1 的字W长? int nLen2 = ::SysStringLen( s2 ); // s2 的字W长? *pVal = ::SysAllocStringLen( s1, nLen1 + nLen2 );// 构造新?BSTR 同时?s1 先保存进? if( nLen2 ) { ::memcpy( *pVal + nLen1, s2, nLen2 * sizeof(WCHAR) ); // 然后?s2 再连接进? // wcscat( *pVal, s2 ); } return S_OK; }</pre>学生Q上面的函数实现Q完全是调用基本?API 方式完成的?br />老师Q是的,说实话,的确比较烦琐?br />学生Q我们是用memcpy()完成q接W二个字W串功能的,那么Z么不用函?wcscat()那?<br />老师Q多数情况下可以Q但你需要知道:׃BSTR包含有字W串长度Q因此实际的BSTR字符串内容中是可以存储L''\0''的,而函?wcscat() 是以L''\0''作ؓ复制l束标志Q因此可能会丢失数据。明白了吗?<br />学生Q明白,明白。我看过<a target="_blank"><font color="#002c99">《COM lg设计与应??之数据类型?/font></a>后就明白了。那么老师Q有没有单一些的Ҏ那?<br />老师Q有呀Q你?.....<pre>STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal) { CComBSTR sResult( s1 ); sResult.AppendBSTR( s2 ); *pVal = sResult.Copy(); // *pVal = sResult.Detach(); return S_OK; }</pre>学生Q哈哈,好!使用?CComBSTRQ这个就单多了。CComBSTR::Copy()和CComBSTR::Detach()有什么区别?<br />老师QCComBSTR::Copy() 会制造一?BSTR 的副本,另外CComBSTR::CopyTo()也有cM功能。而CComBSTR::Detach()是对象与内部的 BSTR 指针剥离Q这个函数由于没有复制过E,因此速度E微快一点点。但要注意,一但剥dQ就不能再用该对象啦?br />学生Q老师Q您讲的太牛啦,我对您的敬Ԓ如巍巍泰山,直入云霄......<br />老师QSTOPQSTOPQ留作业?.....<br />  1、自己先按照今天讲的内容写出q个lgQ?br />  2、不你懂不懂,一定要去观?IDL 文gQCPP 文gQ?br />  3、编译后Q看都生了些什么文Ӟ如果是文本的文gQ就打开看看Q?br />  4、下载本文的CZE序(vc.net 2003版本)~译q行Q看看效果。然后预习一下示例程序中的调用方法;<br />学生Q知道啦Q快下课吧,我要上厕所Q我都憋的不行了......<br />老师Q下课!别忘了顶我的帖子呀......<br /><br /><br /><b>六、小l?/b><br /><br />  本回介绍W一个ATLlgE序的徏立步骤,而如何用该lgQ敬请关注《COM lg设计与应??》? <hr /> ?QApartmentQ系l通过隐藏的窗口消息来排队lg调用Q因此我们可以暂时不考虑同步问题。注意,是暂时哈?br />?Q双接口表示在一个接口中Q同时支持自定义接口?IDispatch 接口。以后,以后Q以后就讌Ӏ因为双接口非常重要Q我们以后会天天讌Ӏ夜夜讲、常常讲------U“三讜y?)<br />?Q组件的重用Ҏ?个,聚合和包宏V?br />?Q这些都?IDL 文g中的概念Q以后用C么,׃l什么?img src ="http://m.shnenglu.com/yishanhante/aggbug/19606.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-12 10:40 <a href="http://m.shnenglu.com/yishanhante/articles/19606.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>COM lg设计与应用(五)?ATL 写第一个组?/title><link>http://m.shnenglu.com/yishanhante/articles/19605.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Mon, 12 Mar 2007 02:39:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19605.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19605.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19605.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19605.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19605.html</trackback:ping><description><![CDATA[ <strong>一、前a<br /><br /></strong>  1、如果你在?vc5.0 及以前的版本Q请你升Uؓ vc6.0 ?vc.net 2003Q?br />  2、如果你在?vc6.0 (ATL 3.0)请阅L回内容;<br />  3、如果你在?vc.net(ATL 7.0)请阅M回内容;(当然读读本文内容也不?<br />  4、这W一个组Ӟ除了所?COM lg必须?IUnknown 接口外,我们再实C个自己定义的接口 IFunQ它有两个函敎ͼ Add()完成两个数值的加法QCat()完成两个字符串的q接?br />  5、下?.....好好听讲! 开始了:-) <p><b><br />二?/b><b>建立 ATL 工程</b><br />  步骤2.1Q徏立一个工作区(WorkSpace)?br />  步骤2.2Q在工作ZQ徏立一?ATL 工程(Project)。示例程序叫 Simple1Qƈ选择DLL方式Q见图一?br /><br /><img height="467" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic01.jpg" width="612" border="0" /><br />图一、徏?ATL DLL 工程<br /><br />  <i><b>Dynamic Link Library(DLL)</b></i> 表示建立一?DLL 的组件程序?br />  <i><b>Executable(EXE)</b></i> 表示建立一?EXE 的组件程序?br />  <i><b>Service(EXE)</b></i> 表示建立一个服务程序,pȝ启动后就会加载ƈ执行的程序?br />  <i><b>Allow merging of proxy/stub code</b></i> 选择该项表示把“代?存根”代码合q到lgE序中,否则需要单独编译,单独注册代理存根E序。代?存根Q这个是什么概念?q记得我们在<a >上回?/a>中介l的吗?当调用者调用进E外或远E组件功能的时候,其实是代?存根负责数据交换的。关于代?存根的具体变成和操作Q以后再说啦......<br />  <i><b>Support MFC</b></i> 除非有特D的原因Q我们写 ATL E序Q最好不要选择该项。你可能会说Q如果没有MFC的支持,那CString怎么办呀Q告诉你个秘密吧Q一般h我都不告诉他Q我后半辈子靠着q个U密zȝ了:<br />  1、你会STL吗?可以?STL 中的 string 代替Q?br />  2、自己写?MyString c,嘿嘿Q?br />  3、悄悄地、秘密地、不要告诉别人(特别是别告诉微YQ,?MFC 中的 CString 源码拿过来用Q?br />  4、?CComBSTR c,臛_也能化我们字W串操作Q?br />  5、直接用 API 操作字符Ԍ反正我们大家学习 C 语言的时候,都是从这里干L。({于没说Q呵呵)<br />  <i><b>Support MTS</b></i> 支持事务处理Q也是是否支持 COM+ 功能。COM+ 也许在第 99 回介l吧?br /><br /><b><br />三、增?ATL 对象c?br /></b><br />  步骤3.1Q菜?Insert\New ATL Object...Q或者用鼠标右键?ClassView 卡片中弹单)q择Object 分类Q选中 Simple Object 目。见图二?br /><br /><img height="257" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic02.jpg" width="413" border="0" /><br />图二、选择建立单COM对象<br /><br />  <i><b>Category Object</b></i> 普通组件。其中可以选择的组件对象类型很多,但本质上Q就是让向导帮我们默认加上一些接口。比如我们?"Simple Object"Q则向导l我们的lg加上 IUnknown 接口Q我们?"Internet Explorer Object"Q则向导除了加上 IUnknown 接口外,再增加一个给 IE 所使用?IObjectWithSite 接口。当然了Q我们完全可以手工增加Q何接口?br />  <i><b>Category Controls</b></i> ActiveX 控g。其中可以选择?ActiveX cd也很多。我们在后箋的专门介l?ActiveX ~程中再讨论?br />  <i><b>Category Miscellaneous</b></i> 辅助杂类lg?br />  <i><b>Categroy Data Access</b></i> 数据库类lg(我最讨厌数据库编E了Q所以我也不??br /><br />  步骤3.2Q增加自定义c?CFun(接口 IFun) ,见图三?br /><br /><img height="260" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic03.jpg" width="421" border="0" /><br />图三、输入类中的各项名称<br />   其实Q我们只需要输入短?Short Name)Q其它的目会自动填写。没什么多说的Q只请大家注意一?ProgID ,默认?ProgID 构造方式ؓ“工E名.短名”?br /><br />   步骤3.3Q填写接口属性,见图四?br /><img height="260" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic04.jpg" width="421" border="0" /><br />囑֛、接口属?br /><br />  <i><b>Threading Model</b></i> 选择lg支持的线E模型。COM 中的U程Q我认ؓ是最讨厌Q最复杂的部分。COM U程和公寓的概念Q留待后l介l。现在吗......大家都?ApartmentQ它代表什么那Q简单地_当在U程中调用组件函数的时候,q些调用会排队进行。因此,q种模式下,我们可以暂时不用考虑同步的问题??)<br />   <i><b>Interface</b></i> 接口基本cd。Dual 表示支持双接??)Q这个非?非常重要Q非帔R常常用,但我们今天不讌ӀCustom 表示自定义借口?b>切记Q切讎ͼ我们的这W一?COM E序中,一定要选择它!Q!Q?/b>Q如果你选错了,请删除全部内容,重新来过。)<br />   Aggregation 我们写的lgQ将来是否允许被别h聚合(?)使用。Only 表示必须被聚合才能用,有点cM C++ 中的U虚c,你要是dE师Q只负责设计但不亲自写代码的话,才选择它?br />  <i><b>Support ISupportErrorInfo</b></i> 是否支持丰富信息的错误处理接口。以后就讌Ӏ?br />  <i><b>Support Connection Points</b></i> 是否支持q接Ҏ口(事g、回调)。以后就讌Ӏ?br />  <i><b>Free Threaded Marshaler</b></i> 以后也不Ԍq打死你,我也不说Q??)<br /><br /><b><br />四、添加接口函?br /></b><br /><img height="365" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic05.jpg" width="282" border="0" /><br />图五、调出增加接口方法的菜单<br /><br /><img height="311" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic06.jpg" width="486" border="0" /><br />囑օ、增加接口函?Add<br /><br /><img height="311" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic07.jpg" width="486" border="0" /><br />图七、增加接口函?Cat<br /><br />  请严格按照图六的方式Q增加Add()函数Q由于图七中增加Cat()函数的参数比较长Q我没有适当的输入空|请大家自p入的时候注意一下。[in]表示参数方向是输入;[out]表示参数方向是输出;[out,retval]表示参数方向是输出,同时可以作ؓ函数q算l果的返回倹{一个函CQ可以有多个[in]、[out]Q但[retval]只能有一个,q且要和[out]l合后在最后一个位|??)<br /><img height="454" src="http://www.vckbase.net/document/journal/vckbase43/images/comtut5pic08.jpg" width="535" border="0" /><br />囑օ、接口函数定义完成后的图C?br />  我们都知道,要想改变 C++ 中的cd敎ͼ需要修改两个地方:一是头文g(.h)中类的函数声明,二是函数?.cpp)文g的实现处。而我们现在用 ATL 写组件程序,则还要修改一个地方,是接口定义(IDL)文g。别着?IDL 下次p讨论啦?br />  ׃ vc6.0 的BUGQ导致大家在增加完接口和接口函数后,可能不会向上图(囑օQ所表现的样式。解x法:<br />  </p><table cellspacing="1" width="100%" border="1"><tbody><tr><td align="middle" width="3%">1</td><td width="60%">关闭工程Q然后重新打开</td><td width="37%">该方法常常有?/td></tr><tr><td align="middle" width="3%">2</td><td width="60%">关闭 IDEQ然后重新运?/td><td width="37%"> </td></tr><tr><td align="middle" width="3%">3</td><td width="60%">打开 IDL 文gQ检查接口函数是否正,如不正确请修?/td><td width="37%"> </td></tr><tr><td align="middle" width="3%">4</td><td width="60%">打开 IDL 文gQ随便修改一?加一个空|再删除这个空?Q然后保?/td><td width="37%">该方法常常有?/td></tr><tr><td align="middle" width="3%">5</td><td width="60%">打开 h/cpp 文gQ检查函数是否存在或是否正确Q有则改?/td><td width="37%">无则嘉勉Q不说完q个成语心理别扭</td></tr><tr><td align="middle" width="3%">6</td><td width="60%">删除 IDL/H/CPP 中的接口函数Q然?/td><td width="37%">再来一?/td></tr><tr><td align="middle" width="3%">7</td><td width="60%">重新建立工程、重新安装vc、重新安装windows、砸计算?/td><td width="37%">砸!</td></tr></tbody></table><p><b><br />五、实现接口函?/b><br /><br />  鼠标双点囑օ中CFun\IFun\Add(...)可以开始输入函数实CQ?/p><pre>STDMETHODIMP CFun::Add(long n1, long n2, long *pVal) { *pVal = n1 + n2; return S_OK; }</pre>q个太简单了Q不再浪费“口条”。下面我们实现字W串q接的Cat()函数Q?pre>STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal) { int nLen1 = ::SysStringLen( s1 ); // s1 的字W长? int nLen2 = ::SysStringLen( s2 ); // s2 的字W长? *pVal = ::SysAllocStringLen( s1, nLen1 + nLen2 ); // 构造新?BSTR 同时?s1 先保存进? if( nLen2 ) { ::memcpy( *pVal + nLen1, s2, nLen2 * sizeof(WCHAR) ); // 然后?s2 再连接进? // wcscat( *pVal, s2 ); } return S_OK; }</pre>学生Q上面的函数实现Q完全是调用基本?API 方式完成的?br />老师Q是的,说实话,的确比较烦琐?br />学生Q我们是用memcpy()完成q接W二个字W串功能的,那么Z么不用函?wcscat()那?<br />老师Q多数情况下可以Q但你需要知道:׃BSTR包含有字W串长度Q因此实际的BSTR字符串内容中是可以存储L''\0''的,而函?wcscat() 是以L''\0''作ؓ复制l束标志Q因此可能会丢失数据。明白了吗?<br />学生Q明白,明白。我看过?a >COM lg设计与应??</a>之数据类型》后明白了。那么老师Q有没有单一些的Ҏ那?<br />老师Q有呀Q你?.....<pre>STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal) { CComBSTR sResult( s1 ); sResult.AppendBSTR( s2 ); *pVal = sResult.Copy(); // *pVal = sResult.Detach(); return S_OK; }</pre>学生Q哈哈,好!使用?CComBSTRQ这个就单多了。CComBSTR::Copy()和CComBSTR::Detach()有什么区别?<br />老师QCComBSTR::Copy() 会制造一?BSTR 的副本,另外CComBSTR::CopyTo()也有cM功能。而CComBSTR::Detach()是对象与内部的 BSTR 指针剥离Q这个函数由于没有复制过E,因此速度E微快一点点。但要注意,一但剥dQ就不能再用该对象啦?br />学生Q老师Q您讲的太牛啦,我对您的敬Ԓ如巍巍泰山,直入云霄......<br />老师QSTOPQSTOPQ留作业?.....<br />   1、自己先按照今天讲的内容写出q个lgQ?br />   2、不你懂不懂,一定要去观?IDL 文gQCPP 文gQ?br />   3、编译后Q看都生了些什么文Ӟ如果是文本的文gQ就打开看看Q?br />   4、下载本文的CZE序(vc6.0版本)~译q行Q看看效果。然后预习一下示例程序中的调用方法;<br />学生Q知道啦Q快下课吧,我要上厕所Q我都憋的不行了......<br />老师Q下课!别忘了顶我的帖子呀......<br /><br /><br /><b>六、小l?/b><br /><br />  本回介绍W一个ATLlgE序的徏立步骤,而如何用该lgQ敬请关注《COM lg设计与应??》?br />  <hr /> ?QApartmentQ系l通过隐藏的窗口消息来排队lg调用Q因此我们可以暂时不考虑同步问题。注意,是暂时哈?br />?Q双接口表示在一个接口中Q同时支持自定义接口?IDispatch 接口。以后,以后Q以后就讌Ӏ因为双接口非常重要Q我们以后会天天讌Ӏ夜夜讲、常常讲------U“三讜y?)<br />?Q组件的重用Ҏ?个,聚合和包宏V?br />?Q名U的功能很好听,但微软根本就没有实现?br />?Q这些都?IDL 文g中的概念Q以后用C么,׃l什么?br /><img src ="http://m.shnenglu.com/yishanhante/aggbug/19605.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-12 10:39 <a href="http://m.shnenglu.com/yishanhante/articles/19605.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用ATL建立轻量U的COM对象【一?/title><link>http://m.shnenglu.com/yishanhante/articles/19602.html</link><dc:creator>jay</dc:creator><author>jay</author><pubDate>Mon, 12 Mar 2007 02:01:00 GMT</pubDate><guid>http://m.shnenglu.com/yishanhante/articles/19602.html</guid><wfw:comment>http://m.shnenglu.com/yishanhante/comments/19602.html</wfw:comment><comments>http://m.shnenglu.com/yishanhante/articles/19602.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://m.shnenglu.com/yishanhante/comments/commentRss/19602.html</wfw:commentRss><trackback:ping>http://m.shnenglu.com/yishanhante/services/trackbacks/19602.html</trackback:ping><description><![CDATA[ <p>本文假设你熟悉C++和COM?br /><br /><b>摘要Q?/b><br />    ATL——活动模板库QThe Active Template LibraryQ,其设计旨在让Z用C++方便灉|地开发COM对象。ATL本n相当y灉|Q这是它最大的优点。用它可以创量的,自包含的Q可复用的二q制代码Q不用Q何附加的q行时DLLs支持?br />    ׃COM技术良好的口碑Q越来越多的E序员已l走q或正在走进COM的编E世界。它像盛夏里的冰镇啤酒Q从来不会让你失望。可惜作Z个C++E序员来_C++从不与我分nCOM的极致以及我对COM的情有独钟?<br />    C++与COM之间若即若离Q和q_处,一ơ又一ơ在每个对象中用同样z的几行代码实现IUnknown。我敢肯定将来C++~译器和链接器会实现C++对象和COM对象之间自然 的无意识的对应和映射Q目前这个环境只存在于实验室中,因此它肯定不是一个你我今天可以购买的产品。眼下可得到的最接近q个环境的东西就是活动模板库——ATL?br /><br /><b>Z么用ATL?<br />    </b>ATL是在单层Qsingle-tierQ应用逐渐q时Q分布式应用逐渐成ؓLq样一个环境中诞生的, 它最初的版本是在四个C++头文件中Q其中有一个还是空的。它所形成的出色的构架专门用于开发现代分布式应用所需的轻量COMlg。作Z个模块化的标准组ӞATL不像MFC有厚重的基础l构Q省时好用的库得成百上千的E序员一ơ又一ơ轻村֮现IUnknown 和IClassFactory?br />    ATL的构架ƈ不打包|万象,无所不能。其W一个版本对实现IUnknownQIClassFactoryQIDispatchQIconnectionPointContainer及COM枚D提供非常 C的支持。第二个版本除了可以~写ActiveX控g外,q对最初的W一个版本中ATLc进行了增强。ATL不提供集合(collectionsQ和ԌstringsQ的处理 Q它假设你用标准的C++库进行这些处理;不支持ODBC——这个世界正在{UdZCOM的不需要包装的数据存取方式Q不支持WinSock打包c?-sockets本n也是新的东西QATL也不支持完整的Win32 API打包cZ—ATL2.0的实现机制提供了对话框和WndProcs支持。此外ATL中没有MFC中的文档/视图模型。取而代之的是ATL那更具׾~性和灉| 性的通过COM接口Q如ActiveX控gQ与ZUI的对象之间的沟通模式?br />    使用正确的工具非常关键。如果你正在~写一个不可见的COMlgQ那么ATL与MFC比v来,从开发效率,可׾~性,q行时性能以及可执行文件大各斚w来看QATL可能 都是最好的选择。对于现代基于ActiveX控g的用L面,ATL所产生的代码也比MFC更小更快。另一斚wQ与MFC的类向导相比QATL需要更多的COM知识。ATL与STL一P对于单层应用没什么帮助,而MFC在这斚w保持着它的优势?br />    ATL的设计在很大E度上来自STL的灵感,STL与所有ANSI/ISO兼容的C++~译器一起已l被U_成ؓ标准C++库的一部分。像STL一PATL大胆使用C++模板。模板是C++中众多具有争议的Ҏ之一。每每用不当都会导致执行؜乱,降低性能 和难以理解的代码。明智地使用模板所产生的通用性效果和cd安全Ҏ则是其它方法所望尘莫及的。ATL与STL一样陷入了两个极端。幸q的?在L大胆使用C++模板的同Ӟ~译器和链接器技术也在以同样的步伐向前发展。ؓ当前和将来的开发进行STL和ATL的合理选择?br />    管模板在内部得到广泛的使用Q但是在用ATL技术时Q你不用L入或兛_那些模板中的括弧。因为ATL本n带有ATL对象向导Q参见图一Q:<br /><br /><img height="233" src="http://www.vckbase.net/document/journal/vckbase13/images/atlcomjoy1.gif" width="400" border="0" /><br /><br /><img height="265" src="http://www.vckbase.net/document/journal/vckbase13/images/atlcomjoy2.gif" width="400" border="0" /><br /><br /><img height="265" src="http://www.vckbase.net/document/journal/vckbase13/images/atlcomjoy3.gif" width="400" border="0" /><br />图一 ATL 对象向导<br /><br /><br />    对象向导产生大量ZATL模板cȝ省的对象实现代码Q即框架代码Q。这些缺省的对象cd?a >附表一</a>所列。ATL对象向导允许M?快速徏立COM对象q且在分分钟之内让它q行hQ不用去考虑COM或ATL的细节问题。当ӞZ能充分驾驭ATLQ你必须掌握C++Q模板和COM~程技术。对于大型的对象c,只要在ATL对象向导所产生的缺省实玎ͼ框架代码Q中加入Ҏ实现来输出定制接口,q也是大多数开发h员开始实现COM对象时的重点所在?br />    初次接触ATLӞ其体pȝ构给人的感觉是神U和不可思议?xxxxime xime="7"><a target="_blank">HelloATL</a></xxxxime>是一个最单的ZATL的进E内服务器源代码 以及用SDKQ纯_用C++~写Q实现的同样一个进E内服务器源代码。在真正构徏Z个COMlg之前Q代码需要经q反反复复多ơ斟酌和修改。对于想加速开发COMlg速度的主组件开发h员来_ATL体系l构q不是什么大问题Q因为对象向g生了所需要的全部框架代码Q只 要你加入Ҏ定义卛_。对于认真的COM开发h员和pȝ~程人员来说QATL提供了一个用C++建立COMlg的高U的Q可扩展的体pȝ构。一旦你理解和掌握了q个体系l构q能N对象向导Q你׃看到ATL的表现能力和强大的功?Q它完全可以和原始的COM~程技术媲?br />    另外一个用ATL开发COMlg的理由是Visual C++ 5.0+集成开发环境(IDEQ对ATL的高度支持?微Y在Visual C++ 5.0+中将ATL所要用到的接口定义语言QIDLQ集成到了C++~辑器中。(待箋Q?br /><br /></p> <img src ="http://m.shnenglu.com/yishanhante/aggbug/19602.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://m.shnenglu.com/yishanhante/" target="_blank">jay</a> 2007-03-12 10:01 <a href="http://m.shnenglu.com/yishanhante/articles/19602.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <p>лǵվܻԴȤ</p> <a href="http://m.shnenglu.com/" title="精品视频久久久久">精品视频久久久久</a> <div class="friend-links"> </div> </div> </footer> <a href="http://www.0x5e.cn" target="_blank">ݺݾþۺ˲</a>| <a href="http://www.fzbn.net.cn" target="_blank">žžþȻ㽶ͼƬ</a>| <a href="http://www.mrzqjn.cn" target="_blank">VۺVŷþ</a>| <a href="http://www.pydjango.cn" target="_blank">ɫþùƷ12p</a>| <a href="http://www.ltak.cn" target="_blank">˾Ʒþ</a>| <a href="http://www.pb-wines.cn" target="_blank">þۺɫһ</a>| <a href="http://www.huangjisoo.cn" target="_blank">99þѹƷ</a>| <a href="http://www.haole001.cn" target="_blank">ٸ޾þþþþ</a>| <a href="http://www.kyj123.cn" target="_blank">þþþƷ鶹</a>| <a href="http://www.52wysq.cn" target="_blank">Ʒþþþþþþ</a>| <a href="http://www.yweishang.cn" target="_blank">ƷþþþþĻһ </a>| <a href="http://www.ichz.cn" target="_blank">㽶þһ޶ӰԺ</a>| <a href="http://www.enliangjiancai.cn" target="_blank">Ʒþþ</a>| <a href="http://www.kh875.cn" target="_blank">99þþƷҹһ</a>| <a href="http://www.ulpj.cn" target="_blank">ۺϾƷ㽶þ97</a>| <a href="http://www.chechuai.cn" target="_blank">91Ʒ91Ⱦþþþø</a>| <a href="http://www.530taiji.cn" target="_blank">ھƷþþþþþɬ</a>| <a href="http://www.kkfo.cn" target="_blank">þ޾Ʒҳ</a>| <a href="http://www.inqh.cn" target="_blank">һɫþ88ۺպƷ </a>| <a href="http://www.gongnian.cn" target="_blank">˾þ777777</a>| <a href="http://www.adlai.cn" target="_blank">þþ뾫Ʒҹ</a>| <a href="http://www.laobianjing.cn" target="_blank">ھƷþۺ88</a>| <a href="http://www.wanhejingshui.cn" target="_blank">AVҰ¾þ</a>| <a href="http://www.zynsbank.cn" target="_blank">99ŷƷþþѿ </a>| <a href="http://www.galidun.cn" target="_blank">þ99þóѲ</a>| <a href="http://www.d2338.cn" target="_blank">97Ʒ˾þô߽</a>| <a href="http://www.cqmh.com.cn" target="_blank">þպƷһ</a>| <a href="http://www.ptrjmfv.cn" target="_blank">þ޾ƷƵ</a>| <a href="http://www.soba30.cn" target="_blank">þþþþþþòҰ߳</a>| <a href="http://www.wxwyx.cn" target="_blank">ۺϾþϵ</a>| <a href="http://www.88354.com.cn" target="_blank">ŷ龫Ʒþþþþþ</a>| <a href="http://www.gh688.cn" target="_blank">þþƷˮavۺ</a>| <a href="http://www.nic-xie.cn" target="_blank">þþþŮۺ</a>| <a href="http://www.ltak.cn" target="_blank">ŷִִþþ</a>| <a href="http://www.dripnews.cn" target="_blank">Ʒgzþþ</a>| <a href="http://www.yywhqy.cn" target="_blank">þþþþҹƷƷ</a>| <a href="http://www.ezchem.cn" target="_blank">ԭۺϾþô˵</a>| <a href="http://www.jqqingzhou.cn" target="_blank">þþƷaĻ þþƷaĻؿ </a>| <a href="http://www.10711.com.cn" target="_blank">Ʒþþþþ</a>| <a href="http://www.jfhtgj.cn" target="_blank">99þѹƷػ</a>| <a href="http://www.pkjx.net.cn" target="_blank">97Ʒ˾þþô߽ </a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>