ATLWTL第六部分.docx

上传人:b****1 文档编号:12449996 上传时间:2023-04-19 格式:DOCX 页数:19 大小:273.09KB
下载 相关 举报
ATLWTL第六部分.docx_第1页
第1页 / 共19页
ATLWTL第六部分.docx_第2页
第2页 / 共19页
ATLWTL第六部分.docx_第3页
第3页 / 共19页
ATLWTL第六部分.docx_第4页
第4页 / 共19页
ATLWTL第六部分.docx_第5页
第5页 / 共19页
点击查看更多>>
下载资源
资源描述

ATLWTL第六部分.docx

《ATLWTL第六部分.docx》由会员分享,可在线阅读,更多相关《ATLWTL第六部分.docx(19页珍藏版)》请在冰豆网上搜索。

ATLWTL第六部分.docx

ATLWTL第六部分

第六部分-掌控ActiveX控件

∙下载示例工程-63.2KB

内容

∙简介

∙以AppWizard开始

∙创建工程

∙生成的代码

∙使用资源编辑器添加控件

∙用于掌控控件的ATL类

∙CAxDialogImpl

∙AtlAxWin和CAxWindow

∙调用控件的方法

∙接收控件激发的事件

∙在VC6里添加处理器

∙在VC7里添加处理器

∙事件的知会

∙VC6里的知会

∙VC7里的知会

∙示例工程概述

∙运行时创建ActiveX控件

∙键盘处理

∙下一步

∙修订历史

简介

在这第六部分里,我将介绍ATL对在对话框中掌控(hosting)ActiveX控件的支持。

由于ActiveX控件是ATL的专项,所以这儿并没有相关的WTL类。

不过,因为ATL掌控控件的方式与MFC迥异,所以这是我们要介绍的一个重要主题。

我会介绍如何掌控控件以及接收(sink)事件,并开发一个相比用MFC的ClassWizard写就的应用毫无功能损失的应用程序。

当然,你可以在你写的WTL应用中使用ATL对控件掌控的支持。

本文的示例工程演示了如何掌控IE的Web浏览器控件。

我选择浏览器控件是基于以下两个不错的理由:

1.每个人的机器上都有它,而且

2.它有很多方法并会激发(fire)很多事件,因此用于演示目的,它是确是个很好的控件。

我肯定比不上那些花了很多时间使用IE的Web浏览器控件编写定制浏览器的人们。

但是,通读本文之后,你就会有足够的知识开始编写你自己的定制浏览器了!

以AppWizard开始

创建工程

WTL的AppWizard可以为我们创建马上就能掌控ActiveX控件的应用。

下面我们要创建一个称为 IEHoster 的新工程。

像上一章一样,我们要使用一个非模态对话框,只不过这次要把 EnableActiveXControlHosting 复选框选中,就象这样:

选中这个复选框会使得我们的主对话框从 CAxDialogImpl 中派生,因此能够掌控ActiveX控件。

在VC6的向导里,在第二页上还有另外一个复选框,其文字为 HostActiveXControls,但是选中它对结果代码没有任何影响,所以在第一页里就可以点击Finish按钮完成了。

生成的代码

在这一节里,我会先介绍一些原来没有见过的由AppWizard生成的代码片断;下一节里,我再详细介绍ActiveX掌控类。

第一个需要检视的文件是stdafx.h,其中的包含文件有:

#include

#include

externCAppModule_Module;

#include

#include

#include

#include

//..otherWTLheaders...

atlcom.h和atlhost.h相对重要。

它们包括了一些COM相关的类(比如智能指针 CComPtr),以及用来掌控控件的窗口类。

接下来,再看maindlg.h中 CMainDlg 的声明:

classCMainDlg:

publicCAxDialogImpl,

publicCUpdateUI,

publicCMessageFilter,publicCIdleHandler

CMainDlg 现在是派生于 CAxDialogImpl,后者是使对话框能够掌控ActiveX控件的第一步。

最后,是 WinMain() 中的一行新代码:

intWINAPI_tWinMain(...)

{

//...

_Module.Init(NULL,hInstance);

AtlAxWinInit();

intnRet=Run(lpstrCmdLine,nCmdShow);

_Module.Term();

returnnRet;

}

AtlAxWinInit() 注册了一个名为 AtlAxWin 的窗口类。

该类在ATL为ActiveX控件创建宿主窗口时使用。

由于ATL7的一个改动,你必须给 _Module.Init() 传递一个LIBID。

论坛中的一些人建议在VC7中使用如下代码:

_Module.Init(NULL,hInstance,&LIBID_ATLLib);

这个改动在我这儿工作的很好。

使用资源编辑器添加控件

ATL允许你象在MFC应用中一样使用资源编辑器向对话框上添加ActiveX。

首先,在对话框编辑器中右击,选择 InsertActiveXcontrol:

VC会显示一个你的系统上所安装的控件的列表。

向下滚动到 MicrosoftWebBrowser 并点击 OK,可以将该控件插入到对话框中。

查看一下新控件的属性并将其ID设置为IDC_IE。

对话框看起来应该象下面这样,在编辑器中控件也是可见的:

如果你现在就编译并运行这个应用,你就可以在对话框中看到Web浏览器控件。

由于我们还没有告诉它应该导航到何处,所以它显示的是一个空白页。

在下一节里,我将介绍有关创建和掌控ActiveX控件的ATL类,然后我们再看如果使用这些类来和浏览器进行通讯。

用于掌控控件的ATL类

在对话框中掌控一个ActiveX控件的时候,会有两个类协同工作:

CAxDialogImpl 和 CAxWindow。

它们处理控件容器必须实现的所有接口,并为常见的操作(比如对COM控件查询一个特定的接口)提供一些辅助函数。

CAxDialogImpl

第一个要介绍的就是 CAxDialogImpl。

在你写对话框类的时候,你应该从 CAxDialogImpl 而不是 CDialogImpl 派生,这样才能掌控控件。

CAxDialogImpl 覆盖了 Create() 和 DoModal(),这两个函数由全局函数 AtlAxCreateDialog() 和 AtlAxDialogBox() 分别调用。

因为IEHost对话框是由 Create() 创建的,所以我们应该仔细打量一下 AtlAxCreateDialog()。

AtlAxCreateDialog() 先加载对话框资源,并使用辅助类 _DialogSplitHelper 遍历所有的控件,以寻找那些由资源编辑器生成的并标明是一个需要创建的ActiveX控件的项。

例如,下面是IEHost.rc文件中为Web浏览器生成的项:

CONTROL"",IDC_IE,"{8856F961-340A-11D0-A96B-00C04FD705A2}",

WS_TABSTOP,7,7,116,85

第一个参数是窗口标题(一个空串),第二个是控件ID,第三个是窗口类名。

_DialogSplitHelper:

:

SplitDialogTemplate() 一看到窗口类是由'{' 开头就知道这是一个ActiveX控件项,它会在内存中创建一个新的对话框模板,在新模板里,那些特殊的 CONTROL 项由创建 AtlAxWin窗口的项所替代。

内存中的新项相当于如下定义:

CONTROL"{8856F961-340A-11D0-A96B-00C04FD705A2}",

IDC_IE,"AtlAxWin",WS_TABSTOP,7,7,116,85

其结果是一个 AtlAxWin 窗口会使用相同的ID被创建出来,而且其窗口标题就是ActiveX控件的GUID。

因此,如果你调用GetDlgItem(IDC_IE),则返回的 HWND 值是 AtlAxWin 窗口的,而不是ActiveX控件自己的。

一旦 SplitDialogTemplate() 返回,AtlAxCreateDialog() 再调用 CreateDialogIndirectParam() 以使用修改过的模板来创建对话框。

AtlAxWin和CAxWindow

正如上述指出的,AtlAxWin 是用来作为一个ActiveX控件的容器窗口的。

随 AtlAxWin 使用的还有一个特殊的窗口接口类,名叫 CAxWindow。

当从一个对话框模板中创建 AtlAxWin 时,AtlAxWin 的窗口过程,即 AtlAxWindowProc(),会处理 WM_CREATE 并在消息的响应中创建ActiveX控件。

也可以在运行时创建ActiveX控件而不必在对话框模板中,不过我们在后面才会介绍。

WM_CREATE 处理器会调用全局的 AtlAxCreateControl(),将 AtlAxWin 的窗口标题传递给它,我们知道,窗口标题已经被设置成了Web浏览器的GUID。

AtlAxCreateControl() 再调用更多的函数,但最后代码会到达 CreateNormalizedObject() 处,它会把窗口标题转换为GUID并最终调用 CoCreateInstance() 来创建ActiveX控件。

因为ActiveX控件是 AtlAxWin 的一个子窗口,所以对话框就不能直接访问控件了。

但是,CAxWindow 具有与控件通讯的方法。

最常用的方法之一是 QueryControl(),它又会调用到控件的 QueryInterface()。

比方说,你可以使用 QueryControl() 来从Web浏览器控件中得到一个IWebBrowser2 接口,并使用该接口把浏览器导航到某个URL。

调用控件的方法

现在,我们的对话框里就有一个Web浏览器了,我们可以使用它的COM接口来和它交互。

我们要做的第一件事情是使用它的IWebBrowser2 接口导航到一个新的URL。

在 OnInitDialog() 处理器里,我们可以把掌控着浏览器的 AtlAxWin 附着到一个 CAxWindow 变量上。

CAxWindowwndIE=GetDlgItem(IDC_IE);

接下来,我们声明一个 IWebBrowser2 接口指针并使用 CAxWindow:

:

QueryControl() 向浏览器控件查询该接口:

CComPtrpWB2;

HRESULThr;

hr=wndIE.QueryControl(&pWB2);

QueryControl() 调用Web浏览器的 QueryInterface(),如果成功的话,IWebBrowser2 就会返回给我们。

然后我们可以调用 Navigate():

if(pWB2)

{

CComVariantv;//emptyvariant

pWB2->Navigate(CComBSTR("

&v,&v,&v,&v);

}

接收控件激发的事件

从Web浏览器获取一个接口是相当简单的,而且这还可以允许我们从一个方向-即向控件进行通讯,还有很多的通讯,是以事件的形式从控件而来。

ATL中具有封装了连接点和事件接收的类,使得我们可以接收到由浏览器激发的事件。

要使用这一支持,我们要做四件事:

1.将 IDispEventSimpleImpl 添加到 CMainDlg 的继承列表

2.写一个事件接收映射以表明我们要处理哪些事件

3.为这些事件编写处理器

4.把控件连接到接收映射上(这一过程称为知会(advising))(译注:

对于advise/advising在COM方面的使用,业界尚没有一个被广泛接受的统一译法,此处译者姑且译为知会,之所以没有译为通知,是因为在文中很难与notify/notification区别开来)

VC的IDE在此过程中可以提供极大的帮助-它会为你对 CMainDlg 进行改动,还可以查询ActiveX控件的类型库,显示控件可以激发的事件的列表。

由于VC6和VC7中添加处理器的用户界面不同,下面我分开来介绍。

在VC6里添加处理器

有两种方法可以调出添加处理器的界面:

1.在ClassView窗格里,右击 CMainDlg 并选择菜单中的 AddWindowsMessageHandler。

2.在查看 CMainDlg 的代码时,或者在资源编辑器中查看相关的对话框时,点击WizardBar上Action按钮的下拉箭头,并选择菜单中的 AddWindowsMessageHandler。

选择该命令之后,VC会显示一个对话框,其中有一个题为 classorobjecttohandle 的控件列表。

在列表中选中 IDC_IE,则VC会把WebBrowser控件可以激发的事件填充到 NewWindowsmessage/events 列表中。

因为我们要为DownloadBegin事件添加处理器,所以要选中该事件并点击 AddandEdit 按钮。

VC就会提示你要求给出方法名:

在你第一次添加事件处理器时,VC会对 CMainDlg 做一点改动,使其可以成为一个事件接收器。

头文件中的改动有点零散,汇总起来就是下面的这些代码:

#import"C:

\WINNT\System32\shdocvw.dll"

classCMainDlg:

publicCAxDialogImpl,

publicCUpdateUI,

publicCMessageFilter,publicCIdleHandler,

publicIDispEventImpl

{

//...

public:

BEGIN_SINK_MAP(CMainDlg)

SINK_ENTRY(IDC_IE,0x6a,OnDownloadBegin)

END_SINK_MAP()

void__stdcallOnDownloadBegin()

{

//TODO:

AddCodeforeventhandler.

}

};

#import 语句是一个编译器指令,用以读取 shdocvw.dll (WebBrowserActiveX控件的实现就在此文件中)中的类型库,并为能使用控件中的组件类和接口创建封装类。

通常你会把此指令放到 stdafx.h 中,不过在本例中,我们其实根本不需要它,因为PlatformSDK中已经有了含有WebBrowser的接口和方法的头文件了。

继承列表中现在已经有了 IDispEventImpl。

它有两个模板参数,第一个是我们指派给ActiveX控件的ID,即IDC_IE,第二个是派生于IDispEventImpl 的类的名字。

接收映射由 BEGIN_SINK_MAP 和 END_SINK_MAP 宏隔起来。

每一个 SINK_ENTRY 宏列出了一个 CMainDlg 要处理的事件,宏的参数分别为控件ID(又是 IDC_IE),事件的分派ID,以及事件到达时要调用的函数的名字。

VC会从ActiveX控件的类型库中读取分派ID,所以不必担心应该指定什么数值(exdispid.h 头文件中列出了IE和资源浏览器发送的许多事件的ID,如果你到其中查看,你可以看到0x6A对应着常量DISP_DOWNLOADBEGIN)。

最后面是一个新方法,OnDownloadBegin()。

对那些有参数的事件,VC会为方法设置正确的原型。

所有的事件处理器都是 __stdcall 调用规范,因为它们是COM方法。

在VC7里添加处理器

也有两种方法可以添加事件处理器。

你可以在对话框编辑器中的ActiveX控件上右击,并在菜单上选择 AddEventHandler。

你可以在显示出的对话框里选择事件名并设置处理器的名字。

点击 AddandEdit 按钮将添加该处理器,对 CMainDlg 做必要的更改,并打开 maindlg.cpp 文件,高亮显示着新添加的处理器。

另一个方法是查看 CMainDlg 的属性页,展开 Controls 结点,然后是 IDC_IE 结点。

在 IDC_IE 结点下,你可以找到控件激发的事件。

你可以点击事件名边上的箭头,选择菜单上的 [MethodName] 来添加处理器。

你还可以稍后修改处理器的名字,当然还是在属性页里改。

VC7对 CMainDlg 的修改和VC6几乎一样,一个例外是并不添加 #import 指令。

事件的知会

最后一步是知会到控件,CMainDlg 想要接收由WebBrowser控件激发的事件。

此过程在VC6和VC7里仍然不一样,所以还需要分别介绍。

相同的是,知会都发生在 OnInitDialog() 里,反知会(unadvising)发生在 OnDestroy() 里。

VC6中的知会

VC6的ATL里有一个全局函数 AtlAdviseSinkMap()。

该函数接收一个具有接收映射的C++对象的指针(通常为this指针),以及一个布尔值。

如果布尔值为true,则对象是希望开始接收事件,如果为false,则对象希望停止接收事件。

AtlAdviseSinkMap() 知会对话框中所有的控件开始或者停止向C++对象发送事件。

要使用此函数,就要为 WM_INITDIALOG 和 WM_DESTROY 添加处理器,然后再像这样调用 AtlAdviseSinkMap():

BOOLCMainDlg:

:

OnInitDialog(...)

{

//Beginsinkingevents

AtlAdviseSinkMap(this,true);

}

voidCMainDlg:

:

OnDestroy()

{

//Stopsinkingevents

AtlAdviseSinkMap(this,false);

}

AtlAdviseSinkMap() 返回一个 HRESULT 表示知会成功与否。

如果 AtlAdviseSinkMap() 在 中失败了,那么你就不能从有的(或者是全部的)ActiveX控件处得到事件。

VC7中的知会

在VC7里,CAxDialogImpl 有一个名为 AdviseSinkMap() 的方法封装了 AtlAdviseSinkMap()。

AdviseSinkMap() 接收一个布尔参数,其意义与AtlAdviseSinkMap() 的第二个参数相同。

AdviseSinkMap() 检查到类里有一个接收映射,就会调用 AtlAdviseSinkMap()。

相对于VC6,最大的不同在于 CAxDialogImpl() 已经有了为你调用 AdviseSinkMap() 的 WM_INITDIALOG 和 WM_DESTROY 的处理器。

要想受益于此特性,就要在 CMainDlg 消息映射的开头添加一个 CHAIN_MSG_MAP 宏,就像这样:

BEGIN_MSG_MAP(CMainDlg)

CHAIN_MSG_MAP(CAxDialogImpl

//restofthemessagemap...

END_MSG_MAP()

示例工程概述

我们已经知道了事件接收是怎么工作的,现在我们来看看整个IEHost工程。

正像我们所讨论的,它掌控了Web浏览器控件,并处理了六个事件。

它还显示了一个事件的列表,这样你就可以知道定制浏览器是怎样使用这些事件来在UI上提供进度的。

应用处理的事件有:

∙BeforeNavigate2 和 NavigateComplete2:

这两个事件可以使应用监测到URL导航。

如果你愿意,你可以在 BeforeNavigate2 的响应里取消导航。

∙DownloadBegin 和 DownloadComplete:

应用程序使用这两个事件来控制表示浏览器正在工作的“等待”信息。

更精致的程序还会像IE一样使用个动画。

∙CommandStateChange:

此事件告诉应用什么时候可以使用“后退”和“前进”导航命令。

应用会据此相应地启用或者禁止后退和前进按钮。

∙StatusTextChange:

好几种情况下都会激发此事件,例如当鼠标光标移动到超链接上时。

此事件会发送一个字符串,应用会响应此事件,将字符串显示到浏览器窗口下面的一个静态控件里。

应用里还有四个控制浏览器的按钮:

后退、前进、停止以及重新加载。

这些按钮会调用到相应的 IWebBrowser2 方法。

事件以及伴随事件的数据都被记录到了列表控件里,所以事件一激发你就能看到。

你可以关闭任一事件的日志,这样你就可以只观测其中的一两个。

为了演示一些实质性的事件处理,在 BeforeNavigate2 处理器中会检查URL,如果其中包含了“”,则本次导航会被取消。

作为IE的插件而不是HTTP代理运行的广告和弹出窗口拦截器使用的正是这个方法。

下面是作此检查的代码。

void__stdcallCMainDlg:

:

OnBeforeNavigate2(

IDispatch*pDisp,VARIANT*URL,VARIANT*Flags,

VARIANT*TargetFrameName,VARIANT*PostData,

VARIANT*Headers,VARIANT_BOOL*Cancel)

{

CStringsURL=URL->bstrVal;

//Youcanset*CanceltoVARIANT_TRUEtostopthe

//navigationfromhappening.Forexample,tostop

//navigatestoeviltrackingcompanieslike:

if(sURL.Find(_T(""))>0)

*Cancel=VARIANT_TRUE;

}

下面是我们的应用在浏览论坛时的样子:

IEHost还演示了另外好几个在前文中介绍过的WTL特性:

CBitmapButton(用于浏览器控制按钮),CListViewCtrl(用于事件记录),DDX(用于跟踪复选框的状态),以及 CDialogResize。

运行时创建ActiveX控件

在运行时而不是在资源编辑器中创建ActiveX控件也是可以的。

About对话框演示了这一技术。

对话框资源包含了一个占位用的分组框,表明了浏览器控件该在什么位置:

在 OnInitDialog() 中,我们使用 CAxWindow 来创建一个新的 AtlAxWin,它会与占位控件使用相同的 RECT,而占位控件随即被销毁:

LRESUL

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 考试认证 > 其它考试

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1