📄 wtl for mfc programmers, part vi - hosting activex controls - wtl.htm
字号:
<html>
<head>
<title>WTL for MFC Programmers, Part VI - Hosting ActiveX Controls</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
</head>
<body bgcolor="#33CCCC" text="#000000">
<p align="center"><b><font style="FONT-SIZE: 16pt" size="4" color="#0033CC">WTL
for MFC Programmers, Part VI - Hosting ActiveX Controls</font></b><br>
<br>
</p>
<p align="left">原作 :<b><font color="#CC3366">Michael Dunn</font></b> [<a href="http://www.codeproject.com/wtl/WTL4MFC6.asp">英文原文</a>]<br>
翻译 :<a href="mailto:inte2000@163.com">Orbit(桔皮干了)</a> [<a href="http://www.winmsg.com/cn/orbit.htm">http://www.winmsg.com/cn/orbit.htm</a>]</p>
<p align="left"><a href="demo/WTL4MFC6_demo.zip">下载演示程序代码</a></p>
<H2><font color="#FFFF66">本章内容</font></H2>
<UL>
<LI><A href="#intro">介绍</A>
<LI><A href="#appwizard">从使用向导开始</A>
<UL>
<LI><A href="#createproject">建立工程</A>
<LI><A href="#appwizcode">自动生成的代码</A> </LI></UL>
<LI><A href="#addreseditor">使用资源编辑器添加控件</A>
<LI><A href="#atlhostclasses">ATL中使用控件的类</A>
<UL>
<LI><A href="#CAxDialogImpl">CAxDialogImpl</A>
<LI><A href="#AtlAxWin">AtlAxWin和CAxWindow</A> </LI></UL>
<LI><A href="#callmethods">调用控件的方法</A>
<LI><A href="#sinkevents">响应控件触发的事件</A>
<UL>
<LI><A href="#changemaindlg">CMainDlg的修改</A>
<LI><A href="#writesinkmap">填写事件映射链</A>
<LI><A href="#writehandler">编写事件处理函数</A> </LI></UL>
<LI><A href="#sampleoverview">回顾例子工程</A>
<LI><A href="#runtimecreating">运行时创建ActiveX控件</A>
<LI><A href="#keyboard">键盘事件处理</A>
<LI><A href="#upnext">继续</A>
<LI><A href="#revisionhistory">修改记录</A> </LI></UL>
<H2><A name=intro></A><font color="#FFFF66">介绍</font></H2>
<P>在第六章,我将介绍ATL对在对话框中使用ActiveX控件的支持,由于ActiveX控件就是ATL的专业,所以WTL没有添加其他的辅助类。不过,在ATL中使用ActiveX控件与在MFC中有很大的不同,所以需要重点介绍。我将介绍如何包容一个控件并处理控件的事件,开发ATL应用程序相对于MFC的类向导来说有点不方便。在WTL程序中自然可以使用ATL对包容ActiveX控件的支持。</P>
<p>例子工程演示如何使用IE的浏览器控件,我选择浏览器控件有两个好处:</p>
<OL>
<LI>每台计算机都有这个控件,并且
<LI>它有很多方法和事件,是个用来做演示的好例子。</LI>
</OL>
<P>我当然无法与那些花了大量时间编写基于IE浏览器控件的定制浏览器的人相比,不过,当你读完本篇文章之后,你就知道如何开始编写自己定制的浏览器!</P>
<H2><A name=appwizard></A><font color="#FFFF66">从使用向导开始</font></H2>
<H3><A name=createproject></A><font color="#FFFF66">创建工程</font></H3>
<P>WTL的向导可以创建一个支持包容ActiveX控件的程序,我将开始一个名为IEHoster的新工程。我们像上一章一样使用无模式对话框,只是这次要选上支持ActiveX控件包容(Enable
ActiveX Control Hosting),如下图:</P>
<P><IMG height=387 alt=" [AppWizard - 22K] "
src="images/appwiz6.png"
width=477 align=bottom border=0></P>
<P>选上这个check box将使我们的对话框从CAxDialogImpl派生,这样就可以包容ActiveX控件。在向导的第二页还有一个名为包容ActiveX控件的check
box,但是选择这个好像对最后的结果没有影响,所以在第一页就可以点击“Finish”结束向导。</P>
<H3><A name=appwizcode></A><font color="#FFFF66">向导生成的代码</font></H3>
<P>在这一节我将介绍一些以前没有见过的新代码(由向导生成的),下一节介绍ActiveX包容类的细节。</P>
<p>首先要看的文件是stdafx.h,它包含了这些文件:</p>
<PRE><SPAN class=cpp-preprocessor><font color="#0033FF">#include <atlbase.h></font></SPAN>
<font color="#0033FF"><SPAN class=cpp-preprocessor>#include <atlapp.h></SPAN>
<SPAN class=cpp-keyword>extern</SPAN> CAppModule _Module;
<SPAN class=cpp-preprocessor>#include <atlcom.h></SPAN>
<SPAN class=cpp-preprocessor>#include <atlhost.h></SPAN>
<SPAN class=cpp-preprocessor>#include <atlwin.h></SPAN>
<SPAN class=cpp-preprocessor>#include <atlctl.h></SPAN>
<SPAN class=cpp-comment>// .. other WTL headers ...</SPAN></font></PRE>
<P>atlcom.h和atlhost.h是很重要的两个,它们含有一些COM相关类的定义(比如智能指针CComPtr),还有可以包容控件的窗口类。</P>
<p>接下来看看maindlg.h中声明的CMainDlg类:</p>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">class</font></SPAN><font color="#0033FF"> CMainDlg : <SPAN class=cpp-keyword>public</SPAN> CAxDialogImpl<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> CUpdateUI<CMainDlg>,
<SPAN class=cpp-keyword>public</SPAN> CMessageFilter, <SPAN class=cpp-keyword>public</SPAN> CIdleHandler</font></PRE>
<P>CMainDlg现在是从CAxDialogImpl类派生的,这是使对话框支持包容ActiveX控件的第一步。</P>
<p>最后,看看WinMain()中新加的一行代码:</p>
<PRE><SPAN class=cpp-keyword><font color="#0033FF">int</font></SPAN><font color="#0033FF"> WINAPI _tWinMain(...)
{
<SPAN class=cpp-comment>//...</SPAN>
_Module.Init(NULL, hInstance);
<B>AtlAxWinInit();</B>
<SPAN class=cpp-keyword>int</SPAN> nRet = Run(lpstrCmdLine, nCmdShow);
_Module.Term();
<SPAN class=cpp-keyword>return</SPAN> nRet;
}</font></PRE>
<P>AtlAxWinInit()注册了一个类名未AtlAxWin的窗口类,ATL用它创建ActiveX控件的包容窗口。</P>
<H2><A name=addreseditor></A><font color="#FFFF66">使用资源编辑器添加控件</font></H2>
<P>和MFC的程序一样,ATL也可以使用资源编辑器向对话框添加控件。首先,在对话框编辑器上点击鼠标右键,在弹出的菜单中选择“Insert ActiveX
control”:</P>
<P><IMG height=345 alt=" [Insert menu - 8K] "
src="images/insertmenu6.png"
width=355 align=bottom border=0></P>
<P>VC将系统安装的控件显示在一个列表中,滚动列表选择“Microsoft Web Browser”,单击Insert按钮将控件加入到对话框中。查看控件的属性,将ID设为IDC_IE。对话框中的控件显示应该是这个样子的:</P>
<P><IMG height=244 alt=" [ IE control in editor - 6K] "
src="images/iectrl6.png"
width=350 align=bottom border=0></P>
<P>如果现在编译运行程序,你会看到对话框中的浏览器控件,它将显示一个空白页,因为我们还没有告诉它到哪里去。</P>
<p>在下一节,我将介绍与创建和包容ActiveX控件有关的ATL类,同时我们也会明白这些类是如何与浏览器交换信息的。</p>
<H2><A name=atlhostclasses></A><font color="#FFFF66">ATL中使用控件的类</font></H2>
<P>在对话框中使用ActiveX控件需要两个类协同工作:CAxDialogImpl和CAxWindow。它们处理所有控件容器必须实现的接口方法,提供通用的功能函数,例如查询控件的某个特殊的COM接口。</P>
<H3><A name=CAxDialogImpl></A><font color="#FFFF66">CAxDialogImpl</font></H3>
<P>第一个类是CAxDialogImpl,你的对话框要能够包容控件就必须从CAxDialogImpl类派生而不是从CDialogImpl类派生。CAxDialogImpl类重载了Create()和DoModal()函数,这两个函数分别被全局函数AtlAxCreateDialog()和AtlAxDialogBox()调用。既然IEHoster对话框是由Create()创建的,我们看看AtlAxCreateDialog()到底做了什么工作。</P>
<p>AtlAxCreateDialog()使用辅助类_DialogSplitHelper装载对话框资源,这个辅助类遍历所以对话框的控件,查找由资源编辑器创建的特殊的入口,这些特殊的入口表示这是一个ActiveX控件。例如,下面是IEHoster.rc文件中浏览器控件的入口:
<PRE><font color="#0033FF">CONTROL <SPAN class=cpp-string>""</SPAN>,IDC_IE,<SPAN class=cpp-string>"{8856F961-340A-11D0-A96B-00C04FD705A2}"</SPAN>,
WS_TABSTOP,<SPAN class=cpp-literal>7</SPAN>,<SPAN class=cpp-literal>7</SPAN>,<SPAN class=cpp-literal>116</SPAN>,<SPAN class=cpp-literal>85</SPAN></font></PRE>
<P>第一个参数是窗口文字(空字符串),第二个是控件的ID,第三个是窗口的类名。_DialogSplitHelper::SplitDialogTemplate()函数找到以'{'开始的窗口类名时就知道这是一个ActiveX控件的入口。它在内存中创建了一个临时对话框模板,在这个新模板中这些特殊的控件入口被创建的AtlAxWin窗口代替,新的入口是在内存中的等价体:</P>
<PRE><font color="#0033FF">CONTROL <SPAN class=cpp-string>"{8856F961-340A-11D0-A96B-00C04FD705A2}"</SPAN>,IDC_IE,<SPAN class=cpp-string>"AtlAxWin"</SPAN>,
WS_TABSTOP,<SPAN class=cpp-literal>7</SPAN>,<SPAN class=cpp-literal>7</SPAN>,<SPAN class=cpp-literal>116</SPAN>,<SPAN class=cpp-literal>85</SPAN></font></PRE>
<P>结果就是创建了一个相同ID的AtlAxWin窗口,窗口的标题是ActiveX控件的GUID。所以你调用GetDlgItem(IDC_IE)返回的值是AtlAxWin窗口的句柄而不是ActiveX控件本身。</P>
<P>SplitDialogTemplate()函数完成工作后,AtlAxCreateDialog()接着调用CreateDialogIndirectParam()函数使用修改后的模板创建对话框。</P>
<H3><A name=AtlAxWin></A><font color="#FFFF66">AtlAxWin and CAxWindow</font></H3>
<P>正如上面讲到的,AtlAxWin实际上是ActiveX控件的宿主窗口,AtlAxWin还会用到一个特殊的窗口接口类:CAxWindow,当AtlAxWin从模板创建一个对话框后,AtlAxWin的窗口处理过程,AtlAxWindowProc(),就会处理WM_CREATE消息并创建相应的ActiveX控件。ActiveX控件还可以在运行其间动态创建,不需要对话框模板,我会在后面介绍这种方法。</P>
<P>WM_CREATE的消息处理函数调用全局函数AtlAxCreateControl(),将AtlAxWin窗口的窗口标题作为参数传递给该函数,大家应该记得那实际就是浏览器控件的GUID。AtlAxCreateControl()有会调用一堆其他函数,不过最终会用到CreateNormalizedObject()函数,这个函数将窗口标题转换成GUID,并最终调用CoCreateInstance()创建ActiveX控件。</P>
<P>由于ActiveX控件是AtlAxWin的子窗口,所以对话框不能直接访问控件,当然CAxWindow提供了这些方法通控件通信,最常用的一个是QueryControl(),这个方法调用控件的QueryInterface()方法。例如,你可以使用QueryControl()从浏览器控件得到IWebBrowser2接口,然后使用这个接口将浏览器引导到指定的URL。</P>
<H2><A name=callmethods></A><font color="#FFFF66">调用控件的方法</font></H2>
<P>既然我们的对话框有一个浏览器控件,我们可以使用COM接口与之交互。我们做得第一件事情就是使用IWebBrowser2接口将其引导到一个新URL处。在OnInitDialog()函数中,我们将一个CAxWindow变量与包容控件的AtlAxWin联系起来。</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -