CSDN博客

img skyremember

如何对webbrowser和IE编程

发表于2008/9/28 9:37:00  15817人阅读

 

如何对webbrowserIE编程

一、

因为工作缘故,需要研究对IE编程,所以翻译了MS的有关资料,供参考。

IE的体系

WebBrowser Host

首先,必须有COM的基础知识,因为IE本身就是COM技术的典型应用。我们看到最上层是WebBrowser的宿主(Host),也就是任何你想重用(ReUse)webbrowser control的应用程序,可以是vb程序,也可以是vc或者任何其他语言的应用程序。应用程序必须是可容纳activex控件的容器。

Webbrowser control既是activex control也是activex document 的宿主。作为控件,它可以置于任何activex容器,作为文档容器,它针对特殊的类型,调用特殊类型注册的文档server以显示文档。如果你想显示一个.doc文档,webbrowser control装载windows word,,对于html文档,webbrowser装载名为mshtml的组件(如图所示)。针对不同的文档,你不需要开发不同的应用,仅仅是调用webbrowser control即可。

Shdocvw

Shdocvw.dll包含了webbrowser control ,控制webbrowser control (就像控制其他任何activex com 控件一样),提供浏览能力给上层宿主。请注意webbrowser control位于第二级中。甚至IE也不直接而是通过shdocw.dll来使用webbrowser control的。尽管shdocvw提供了大部分的浏览功能,但是并不提供IE窗口的呈现功能。尽管如此,shdocvw还是提供了简单创建web 应用程序的能力。之后章节会讲到如何将ie的高级特性加入到你的应用程序。

MSHTML

早期的mshtml具有读取显示html的功能。MSHTML实际上是一个active 文档服务器,但是却可以作为其他控件的容器(如图中所示的)。记得吗,你可以将activex控件置于html中,此时mshtml就是一个activex宿主,还可以控制其他的控件如vbscript 脚本引擎和javascript脚本引擎,java applet ,geszhong,各种插件别意外,plug-in都是按照ie activex规范写的。

二、

WebBrowser Control Internet Explorer

二者又太多的共同点,你都是通过COM接口来访问其功能。当调用webbrowser控件时,使用的是webbrowser对象,在vc中是使用class ID CLSID_WebBrowser的接口类。

当自动化ie时,必须建立名为 InternetExplorer的对象,vc等语言中使用名为class ID CLSID_InternetExplorer的接口类。

 

接口

webbrowser4个接口(如图),其中3个提供全部的功能,第4DWebBrowserEvents2接口暴露事件

ie4 之前,仅有IwebBrowserIwebBrowserApp两个接口,且二者共享相同的功能,当开发ie4时候,决定加入第三个接口IWebBrowser2扩展webbrowser的功能。该接口派生于第二个接口。

IWebBrowser

IWebBrowser 仅仅是WebBrowser control的最初接口, 提供基本的例如导航web页面的功能。 作为其他派生接口的基础接口,有8个方法和30个属性。

方法

 描述

GoBack

导航到历史列表中的上一个页面

GoForward

导航到历史历表中的下一个项目

GoHome

导航到缺省页面.

GoSearch

导航到缺省搜索页面

Navigate

导航到一个页面或者页面

Refresh

刷新当前页面

Refresh2

可以选择刷新级别,如下:

REFRESH_NORMAL 普通刷新,并且不发送HTTP pragma:nocache 到服务器.

REFRESH_IFEXPIRED 仅仅当页面失效时才发送刷新请求

REFRESH_CONTINUE 内部使用,不要在编程时使用

REFRESH_COMPLETELY 发送HTTP pragma:nocache header 到服务器

Stop

停止当前导航

尽管WebBrowser control 提供了你需要的导航回退以及前向的方法,但是没有提供存取历史列表的途径. 你可与直接通过属性LocationUR来获取当前页的URL.

VB或者VC++中调用GoBack GoForward 方法式很容易的.你可以使用WebBrowser control 或者 Internet Explorer对象. 举例来讲, 在VB中你可以如下调用:

WebBrowser1.GoBack           ' Hosting WebBrowser control
InternetExplorer1.GoForward  ' Automating Internet Explorer object

VC++中如下:

m_webBrowser.GoBack();            // Hosting WebBrowser control
m_pInternetExplorer->GoForward(); // Automating Internet Explorer object

尽管 GoBack GoForward  方法十分重要,当控制WebBrowser control或者自动化操作 Internet Explorer, Navigate is 却是最重要的方法. Navigate 允许你导航到特定的你所想的web页或者文件. Navigate 带有5个参数, 允许你指定特定的URL以实现导航以及其他定义导航行为的信息.

第一个参数是 URL, 指示web页的位置和名称或者何处的文件你想装载。 (次擦书类型是 BSTR.) URL 可以是你所使用的标准URL 协议类型的一种,例如HTTP 和 FILE, 或者是全路经文件名的本地文件系统, 例如 C:/MyFile.htm.

第二个参数, Flags, 你可指定如何或者甚至何处装载特定的URL. (参数的类型是指向 VARIANT的指针) Flags 的值来自名为 BrowserNavConstants 的枚举类型 ,定义于 ExDisp.h 头文件中且有6个值, 详细解释见下列表. 你可以指定一个或者多个Flags 参数值 (请留意有些当前并未实现).

·         navOpenInNewWindow. 将导致一个新的窗口打开以显示URL. 缺省,如果你在自己的应用程序中控制 WebBrowser control,这些值将导致新的 Internet Explorer 浏览器窗口打开.

  • navNoHistory. 指定此URL将不加入到URL历史列表中
  • navNoReadFromCache. 当前未实现.
  • navNoWriteToCache. 当前未实现.
  • navAllowAutoSearch. 如果指定的URL未找到,自动搜索功能将试图导航到通常的顶级域名如.com .net .org以找到正确的URL。如果失败, URL将传递给搜索引擎.
  • navBrowserBar. 如果可能,将 URL 装入到 Explorer 的地址栏Bar

第三个参数 TargetFrameName. 指定web页的哪一个frame将会发生导航. (参数的类型是指向 VARIANT的指针)此字符串将为以存在于web页中的名字或者一个指定的值如 _top_search.  _top 值指定 URL 将被当前最顶层web页装入并不在任何一个当前页中存在的frame.  _search 指示搜索面板将被打开. (该值仅在你自动化操控Internet Explorer时发生.) 如果你指定的frame名称并未发现,, 新的Internet Explorer 窗口将被打开。

你可以使用 PostData 参数 (第四个)通过HTTPpost事务将指定特定的数据发送到服务器. (参数的类型是指向 VARIANT的指针) Post 处理用于将在HTML表单收集的数据发送到服务器。如果此参数不指定任何数据 ,  Navigate 方法将使用Get方法. 另外,如果你不指定 HTTP 协议的URL (换句话讲,如果你指定例如 FILE 协议的 URL), PostData 参数将被忽略。

你也可以使用第五个参数, Headers, 以发送HTTP 头信息到服务器. (T参数的类型是指向 VARIANT的指针) 这些加入的头信息将被 WebBrowser 控件如常发送出去. 作为 PostData 参数, 如果你并不指定第一个参数, Headers 将被忽略.
 
 
如何对webbrowser和IE编程(二)
出处  

调用 Navigate 方法比GoBack 以及 GoForward  要困难,但如果你正仅仅导航到URL 且并不传递任何附加参数, 它的调用很容易。 举例来说, VB中导航到 Microsoft 主页, 可如下:

WebBrowser1.Navigate "http://www.microsoft.com"

VB代码调用Navigate 如此容易因为所有参数出第一个参数外都可以是可选. 如此相对照,VC++不可以省略任何参数. 如果你想使用VC++不是勇任何特别的参数导航到 Microsoft的 home page, 你必须传递空的VARIANT 结构。 MFC 应用程序,你必须如下调用 Navigate

COleVariant vtEmpty;
m_webBrowser.Navigate(_T("http://www.microsoft.com"), &vtEmpty, &vtEmpty,
                 &vtEmpty, &vtEmpty);

该例子展示了在MFC应用程序中控制WebBrowser控件. 我传递一个普通的字符串而替代 BSTR 因为 MFC 提供了一个 WebBrowser 控件的包装类,第一个载包装类中是LPCTSTR, 一个普通的字符串. 其他参数为指向 VARIANT 结构的指针。 如果你不想指定任何特别的参数, 别仅仅传递 NULL —那样做, 你的应用程序会招来崩溃。你必须传递一个空的VARIANT 结构的指针. 前面的代码使用了 COleVariant 类, 那是一个与 CComVariant 类相似的类COleVariant 简单包装了 VARIANT 使得VARIANT 结构易于使用.

属性 现在你看到了所有的 IwebBrowser 的方法,你大概向了解其属性。(我是不是太苛求了?)  IWebBrowser 接口有30个属性,描述于表6-2. 我一已按照IWebBrowser  的Vtable中的字母序列出。

Table 6-2. IWebBrowser 属性按照vtable排序

属性

描述

Application

返回宿主WebBrowser 控件的自动化对象 (IDispatch) 实现,如果对象不可用,则返回的WebbOrwser控件的自动化对象

Parent

返回WebBrowser控件的父控件的自动化实现 (IDispatch) ,通常是容器举例来讲,,您的宿主或者Internet Explorer 窗口。

Container

返回WebBrowser控件容器的自动化对象 (IDispatch) 。通常,该值返回同 Parent 属性一样的值。.

Document

返回活动文档的自动化实现 (IDispatch) 。如果 HTML 当前显示在WebBrowser Document 属性给出你存取 DHTML 对象模型的途径.

TopLevelContainer

返回一个布尔值指示IE是否是WebBRowser控件的顶级容器。在 words中,如果IE是宿主应用承需则返回true

Type

返回已经在WebBrowser中装载的对象的类型。举例,如果HTML document被装载, Type 将返回 Microsoft HTML Document 5.0. 如果文档是Word 文档, Type 返回 Microsoft Word Document.

Left

.返回或设置控件在容器窗口左边距

Top

.返回或设置控件在容器窗口顶部边距

Width

返回或设置webbrowser控件在窗口中的水平方向的向苏为单位宽度

Height

返回或设置webbrowser控件在窗口中的处置方向的向像素为单位高度

LocationName

返回一个WebBrowser当前显示的资源名称的字符串 (换句话讲, HTML , Word 文档,folder, 诸如此类) 如果资源是 HTML 页,字符串是标题。如果资源是文件或者文件夹,字符串为文件名或者文件夹名。举例, foo.doc Word文档)或者  Temp temp directory.

LocationURL

返回WebBrowser正在显示的资源的URL

Busy

返回一个布尔值指示WebBrowser 当前是否正在装入 URL.。如果当前属性返回 true, 你可以使用 Stop 方法取消代掉导航。

看完表 6-2, 清晰展示了你可使用的属性. 当中的一些可能需要更多的解释. IWebBrowser 使用的相当多的一个属性是 LocationURL, 给出了当前你装入WebBrowser窗口的 URL。 获取 LocationURL 值在VB中相当容易:

Dim strLocation
strLocation = WebBrowser1.LocationURL

Visual C++的MFC应用,  MFC 包装类使的访问LocationURL 很容易. 你简单调用 GetLocationURL 方法,该方法返回一个 CString 对象. 当你想在自动化IE中或者webBrowser的MFC应用中访问 LocationURL when automating Internet Explorer or when hosting the WebBrowser control in a C++ application that's not built by using MFC, 你必须调用携带一指向BSTR的指针 BSTR 将包含返回值. 此处展示如何在 C++中使用:

BSTR bstrURL;
m_pInternetExplorer->get_LocationURL(&bstrURL);

 

存取 Document 属性在VB中相当容易,当定义一个变量后(举例, HtmlDoc), 值需要 set 为 Document 属性:

Set HtmlDoc = WebBrowser1.Document

MFC 中也很容易,使用 MFC 包装类的GetDocument方法:

IDispatch* pDisp;
pDisp = m_webBrowser.GetDocument();

GetDocument 返回指向代表文档 IDispatch 接口的指针. 如果 GetDocument 失败, 该对象将为NULL.

MFC C++ 应用或者任何自动化IE的C++ 应用访问Document 属性就只能够采用调用COM的方法—通过属性的get_方法. (注艺属性实只读,所以这里无put_ 方法.) 当调用 Document  get_ 方法,你传递一个接受 IDispatch 的指针.此处展示了如何调用:

IDispatch* pDisp;
HRESULT hr = m_pInternetExplorer->get_Document(&pDisp);

当使用这些代码,你可以使用 SUCCEEDED 宏( Win32 API的一部分), 以监测调用是否成功。在尝试使用Idispatch接口前你应当确信调用成功。

 

 

如何对webbrowser和IE编程(三)
出处  

IWebBrowserApp

IWebBrowserApp 接口仅仅在IE中实现。 典型的,你使用InternetExplorer 对象创建一个IE的实例. 然后你使用 IWebBrowserApp 接口操纵此实例. (今天采用IWebBrowser2 替代) IWebBrowserApp 接口继承自 IWebBrowser, 所以它提供 IWebBrowser 的全部功能。

因为IWebBrowserApp呈现一个IE窗口的实例, 所以它的方法和属性典型地允许你控制浏览器窗口的用户接口。 这些方法和属性并非包含在WebBrowser的 IWebBrowser 中的:寄宿control的应用程序提供诸如状态条, 工具条, 以及菜单条等用户接口. WebBrowser 控件仅仅用于装载web页以及其它类型文件。

IWebBrowserApp 有4个方法和10 各属性。

方法 IWebBrowserApp 接口的属性很直观立如下表, 但是有2个有理由值的讨论:GetPropertyPutProperty. 此两个方法允许你在IE属性包(property bag)中存储一个属性一边你能够在其后重新找回他们。 你将典型地从一个web页上存储一些状态信息到另一个web页 (事实上,使用IWebBrowser2 接口你可以在宿主一个WebBrowser 控件时候使用此方法)

Table 6-3. IWebBrowserApp  vtable 次序的方法

方法

描述

Quit

促使 Internet Explorer 窗口关闭。换句话讲,将自动化操作关闭IE实例

ClientToWindow

转换一个点从窗口坐标到客户坐标。

PutProperty

存储一个属性值到ie的属性包,将可以随手采用GetProperty.获取

GetProperty

获取先前由 PutProperty.方法存储的属性值

VB代码:

InternetExplorer1.PutProperty "CurrentPicture", 10
InternetExplorer1.GetProperty("CurrentPicture")

VC++代码:

CSomeClass::PutGetProperty(VARIANT vtCurrentValue, VARIANT* vtNewValue)
{
   HRESULT hr;
   hr = m_pInternetExplorer->PutProperty(L"CurrentPicture",
                               vtCurrentValue);
 

   if (SUCCEEDED(hr))
   {
     // Notice that vtNewValue is already a pointer, so you
     // don't have to pass the address to GetProperty.
     //
     hr = m_pInternetExplorer->GetProperty(L"CurrentPicture", 
                                 vtNewValue);
   }
 

   return hr;
}

PutGetProperty 方法接受包含当前属性值的 VARIANT 变量指针且将接收属性值。

属性 IWebBrowserApp 接口有10 个属性. Table 6-4. IWebBrowserApp vtable 次序的属性

属性

描述

Name

返回对象的名称t. (举例,当自动化IE Name 属性将返回 Microsoft Internet Explorer.)

HWND

返回IE窗口的句柄

FullName

返回ie可执行文件的全路径 (iexplore.exe).

Path

返回IE应用程序的全路径.

Visible

监测和设置IE窗口是否可见。 (换句话讲,你可与使用此属性显示/隐藏)

StatusBar

显示或者隐藏IE状态条,也可监测当前状态条

StatusText

设置或隐藏状态条文字

ToolBar

显示或者隐藏IE工具条,也可监测当前工具条状态 

MenuBar

显示或者隐藏IE菜单条,也可监测当前工具菜单态 

FullScreen

设置或者检测一个值指示IE当前是否最大化显示。最大化显示,iE占据整个屏幕

Table 6-4 中的属性列表直截了当且易于使用。举例来说, 如果你想从你的Vb程序中获取IE状态条中的文字,你应当使用如下代码:

Dim strStatusText
strStatusText = InternetExplorer1.StatusText

使用MFC包装类的VC++程序代码如下:

BSTR bstrStatusText;
HRESULT hr = m_pInternetExplorer->get_StatusText(&bstrStatusText);

 

 

IWebBrowser2

因为COM规则要求接口恒久不变, 要加入新的工呢高COM,你必须增加新的接口。 新的接口可扩展自其他接口已扩展功能。 举例,当心的需求引入WebBrowser 和Internet Explorer 接口, 开发者被要求建立一个新的接口: IWebBrowser2.

早期, IWebBrowser2 继承自 IWebBrowser IWebBrowserApp, 还提供不包含在着两个接口中的功能. 所以你应当使用 IWebBrowser2 接口替代 来操纵WebBrowser 控件或者 Internet Explorer.

IWebBrowser2 接口有4个方法和8个属性.

方法   IWebBrowser2 接口方法如下描述,按照 vtable 次序, in Table 6-5.

也许IWebBrowser2 、接口踵使用最多的方法是ExecWB. 它通过WebBrowser实现了一个  IOleCommandTarget 接口的Exec的包装实现。 在 ExecWB 方法 被创建前, 你不可以直接从VB中调用IOleCommandTarget::Exec, 因为VB不可访问 IOleCommandTarget 接口 ,因而VB不可直接使用该接口的ExecWB方法。 IOleCommandTarget::Exec 方法过去大量使用,所以 WebBrowser 开发者决定创建ExecWB 以使事情变得容易。

 

Table 6-5. IWebBrowser2  Vtable 次序的方法

方法

描述

Navigate2

功能基于Navigate 方法,不同在于 Navigate2 允许你导航到飞URL表达的地方,例如Windows shell folder. (Windows shell folder 是指向标示符指针, 或者windows shell命名空间中的 PIDL,)

QueryStatusWB

IoleCommandTarget接口的QueryStatus方法在 WebBrowser.中的包装实现

ExecWB

IoleCommandTarget接口的Exec方法在 WebBrowser.中的包装实现

ShowBrowserBar

显示或者隐藏特定的浏览器条. 该方法仅仅用于InternetExplorer 对象

 

为什么你无论如何都要调用ExecWB方法?因为它提供了你需要的功能(通过IOleCommandTarget::Exec)不是暴露于webbrowser接口。你大概疑惑于为社么开发者不实现扩展属性和方法。记住:COM规则是一旦接口发表就不可改变。所以你不能够不能增加功能而不创建新的接口。

ExecWB 方法允许WebBrowser 开发者增加新的功能而不用创建新的接口。再之, 那是由WebBrowser 通过 ExecWB 代表性的暴露不常用的工的途径, 譬如调用 Save As 对话框或者缩放web页的字体。  ExecWB 工作的方法是传递一个你想调用的command ID 和必需的参数. 太多的 command IDs ,他们包含于OLECMDID 实现文档头文件he DocObj.h .

 

 

作为举例, 支持缩放web页的内容字体。Internet Explorer 允许你通过View菜单的Text Size子菜单改变字体的大小从最小到最大。 ExecWB 方法暴露了允许你改变显示在浏览器中的文字大小。对于Zoom 命令, 你可指定特殊值0, 1, 2, 3, or 4,  0 是最小字体 ,4是最大字体。以下举例为改变字体为最大(VB):

WebBrowser1.ExecWB OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, _
                   CLng(4), Null

再次代码中, 我调用 ExecWB 方法, 传递OLECMDID_ZOOM 常量给第一个参数. 此常量指定 zoom 操作将被执行。 第二个参数传递给ExecWB 不要提示用户。另外的选择, 你可以提醒用户而是用OLECMDEXECOPT_PROMPTUSER. (见 DocObj.h.)

第三个输入参数指示要设定的文字尺寸。我想是文字尽可能的大,所以我指定了4 。注意我在调用中使用了VB的Clng函数包装此值,转换我指定的此值为一个variant—输入需要的类型.最后为一个输出参数包含任何返回值。因为返回值不需要关心,我传递Null.

 

 

属性 IWebBrowser2 接口有8个属性。 (Table 6-6 按照vtable次序展示其)。他们都很直观,我仅仅解释恰宏很特别的一个—AddressBar.

Table 6-6. IWebBrowser2 Properties in Vtable Order

属性

描述

ReadyState

返回WebBrowser 的状态(换句话讲 ,该属性指示文档是否完成装载). 尽管你可以使用 ReadyState 属性, 使用 DocumentComplete 时间检测文档是否装载还是比较好些。 (那意味着所有HTML和文档被装载

Offline

.返回或者设置检测webbrowser是否处于脱机模式的变量

Silent

返回或设置 WebBrowser 是否处于沉默模式。如果处于沉默模式,意味着没有对话框可以被显示.

RegisterAsBrowser

.设置或者检测Webbrowser是否是作为顶层浏览器被登记

RegisterAsDropTarget

.设置或返回WebBrowser是否作为导航的拖放对象 。如果登记委托放对象,用户可直接拖放连接到浏览器  

TheaterMode

返回或者设置Internet Explorer是否处于theater 或者 normal window 模式.。在theater 模式, Internet Explorer占据整个屏幕就像处于FullScreen 模式,但也有最小化的用户接口元素 (此属性仅仅展示于InternetExplorer 对象)

AddressBar

显示或者隐藏地址栏(此属性仅仅展示于InternetExplorer 对象)

Resizable

返回或者设置Internet Explorer是否可被调整大小, 你可利用此属性防止用户改变webbrowser的大小(此属性仅仅展示于InternetExplorer 对象)

 

 

AddressBar 属性允许你显示/隐藏Internet Explorer 地址栏—一个包含可输入URL的文本框 允许你导航到某个Url。 能够显示或者隐藏地址条允许你完全控制你的用户的导航体验

在你的企业网络或者中小学控制用户导航体验. 在此环境下,你将控制用户的导航以至于可确定某些不恰当地页面不可访问。你可创建你自己的浏览器。但是如果你没有时间和资源这么做,你可以自动化IE来代替.自动化IE允许你管掉所有的用户界面允许用户导航到某个web页,像菜单条, 工具条,以地址栏. 之后你可建立一定数量的可访问web页连接列表给用户。

 

关掉这些用户接口很容易. VB代码:

InternetExplorer1.AddressBar = False
InternetExplorer1.ToolBar = False
InternetExplorer1.MenuBar = False

 

如何对webbrowser和IE编程(四) 收藏

新一篇: 如何对webbrowser和IE编程(五)  | 旧一篇: 如何对webbrowser和IE编程(三)

寄宿WebBrowser 控件

我们现在开始在VB和VC中创建一些程序来寄宿(Host)WebBrowser控件。在你完成本部分的样本,你将对如何创建寄宿一个WebBrowser控件的应用程序又一个基本的了解。当你看到如此容易的加入Web浏览功能加入到你的应用程序,我相信你会立即开始实践的。

使用VB

VB中,你可以在5分钟内开发一个全功能的自己的web浏览 。以下步骤为建立一个web浏览器程序:

1.    启动VB.

2.       选择“Standard EXE ,进入设计模式。

3.       WebBrowser 控件未自动化包含到控件工具箱(Control Toolbox)。 要增加 WebBrowser 控件到 控件工具箱,选择 Project 菜单下的Components. ,如下对话将显示:

 

Figure 6-3. 组件对话框.

4.       如果Controls TAB页未显示,点击 Controls TAB 。然后勾选中列表中的“ Microsoft Internet Controls”,点击OK关闭对话框。Vb将会增加WebBrowser 控件到控件工具箱,如图所示:

 

Figure 6-4. Visual Basic 控件工具条在增加了WebBrowser 控件后的图标

5.       为增加 WebBrowser 控件至窗体,点击WebBrowser 控件,然后确定在窗体中的大小。一旦你增加了一个控件到表单,Visual Basic 将指派其名称为 WebBrowser1.

6.       调整表单的尺寸一边导航时候可看到更多的web内容。预留一些空间给地址栏。表单看起来如 Figure 6-5.

7.       双击空区域以增加Load 事件。为了能够使WebBrowser 导航到一个web页,你仅需要调用如 GoHome, GoSearch, Navigate, 或者 Navigate2.等导航方法。

8.       调用GoHome 方法到用户主页。代码如下:

Private Sub Form_Load()
   WebBrowser1.GoHome
End Sub

Figure 6-5. Visual Basic form after adding the WebBrowser control.

如此就完成了! 你已经创建了一个全功能的web浏览器. 照我的时间,仅仅不好过分钟。为确信它可工作,你可启动进行测试。你的应用程序将装入webbrowser控件兵导航到主页。保存工程为VbWebHost.

尽管你已经拥有一个可工作的Internet应用程序,你仍需要做些工作视你的应用程序更像一个真实的web浏览器。为了可导航增加一些控件到表单。为输入URL增加一个 label, 一个文字输入框, Go 按钮,  Back 按钮,  Forward 按钮, 以及Stop 按钮. 表单应该看起来如Figure 6-6.

Table 6-7 分派给你的控件属性。

Figure 6-6. Visual Basic form after adding controls.

Table 6-7. 控件属性

控件

属性

Label

Caption = "Address:"

TextBox

Name = txtAddress; Text = "" (In other words, remove the default text.)

Go Button

Name = btnGo; Caption = "Go"

Back Button

Name = btnBack; Caption = "< Back"

Forward Button

Name = btnFwd; Caption = "Forward >"

Stop Button

Name = btnStop; Caption = "Stop"

增加一些代码,调用WebBrowser 控件的方法使得工作正常。 举例来说,当用户输入文字到文字输入框且点击Go 按钮,使用 Navigate 方法处理导航。 当然,你必须缺新用户真实输入了一些文字到文字输入框。

同样, 你也可以使用GoBack, GoForward, 以及 Stop 方法以实现Back, Forward, 和 Stop 按钮。记住 GoBackGoForward 方法当前状态下无效。Visual Basic 代码看起来类似如下:

Option Explicit
 

Private Sub btnBack_Click()
   On Error Resume Next
   WebBrowser1.GoBack
End Sub
 

Private Sub btnFwd_Click()
   On Error Resume Next
   WebBrowser1.GoForward
End Sub
 

Private Sub btnGo_Click()
   WebBrowser1.Navigate txtAddress.Text
End Sub
 

Private Sub btnStop_Click()
   WebBrowser1.Stop
End Sub
 

Private Sub Form_Load()
   WebBrowser1.GoHome
End Sub

注意 On Error Resume Next 特别用于Back 和Forward 按钮的click事件处理. 当当前URL前后无历史列表,这些方法将返回错误。Visual Basic错误捕获用于处理他们。

现在测试程序,在浏览器完成导航到主页,输入URL 到文字框点击Go 按钮。你将有两个 URLs 在历史列表中。点击 Back按钮退回到主页, 接着点击 Forward 按钮前移。在web页转载时候点击 Stop 按钮确信Stop 按钮工作正常。

 

 

打印web页

用为用户经常想打印在web浏览器中的页面,你可能细想加入打印功能到你的应用程序。过去打印在VB中笨重难以实现(使用sendkey),现在可以使用ExecWB方法来轻松实现,且非常可靠。

哟增加打印功能,首先加入Print 按钮到表单。 (如前面) 命名按钮为 btnPrint, 改变标题为 Print. 看起来如Figure 6-7.

Figure 6-7. Visual Basic form after adding a Print button.

下一步, 双击 Print 按钮以增加Click事件处理代码.事件处理过程中, 调用 ExecWB 方法, 传递打印需要的命令ID: OLECMDID_PRINT.如果你想打印前提醒用户, 指定OLECMDEXECOPT_PROMPTUSER; 其他情形指定OLECMDEXECOPT_DONTPROMPTUSER。 本例中,我们打印前提示 。打印命令没有输入输出,所以你指定第三个和第四个参数 Null 。代码应当如下:

Private Sub btnPrint_Click()
   On Error Resume Next
   WebBrowser1.ExecWB OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER, _
                      Null, Null
End Sub

 

 

使用 Visual C++

Vc中创建浏览器应用程序毕vb中稍微困难。如果白手起家创建米的宿应用程序 (无MFC 或 ATL), 需要实现大量的COM接口来寄宿WebBrowser 控件。你必须还要利用COM的API CoCreateInstance  创建WebBrowser 控件的实例,指定 CLSID_WebBrowser a作为你想创建的对象的CLSID 。并且还要 将控件"site"于你的应用程序.

因为建立ActiveX 控件容器费本文讨论主题,所以着重讨论简单途径寄宿webbrowser控件。

使用 MFC

你可以创建3 种类型的MFC应用程序: 单文档接口 (SDI), 多文档接口 (MDI), 以及基于对话框的应用程序。因为对话框MFC类似VB,所以本处将讨论采用SDI来寄宿webbrowser控件。一旦你知道如何采用SDI 应用来寄宿webbrowser控件,转为MDI 将会容易些。

在演示SDI的例子中,我将使用WebBrowser (CWebBrowser2) 包装类MFC的内置CHtmlView 类将帮助你理解如何在MFC中寄宿WebBrowser 。

CHtmlView 类无需太多解释。要使用它,必须在MFC AppWizard第6步中选择你的应用程序的基类为ChtmlView, 如 Figure 6-8. 在完成 wizard后, 你的应用程序的视图类奖派生自 CHtmlView. 然后你可以直接调用IWebBrowser2 接口的不同方法.

Figure 6-8. MFC AppWizard - Step 6 of 6 dialog box.

为创建MFC 单文档接口应用程序,启动Visual C++ 新建菜单。新的对话框展示如 Figure 6-9.

Figure 6-9. Visual C++ New dialog box.

Projects 页, 选择 MFC AppWizard (exe) 项, 输入一个项目名称 (譬如 MfcWebHost之类), 点击OK. MFC AppWizard is 步骤一显示. (看 Figure 6-10.)

Figure 6-10. Step 1 of the MFC AppWizard.

选择 Single Document 。然后认可缺省选项,点击完成. 要增加WebBrowser 到你的工程,选择菜单Project/Add To Project/Components,如下图Figure 6-11所示:

 

Figure 6-11. Selecting Components And Controls.

Visual C++ 收集所有的你的系统中的组件和控件信息展示在Components 和Controls 陈列对话框,如图Figure 6-12所示.

Figure 6-12. Components And Controls Gallery dialog box.

双击 Registered ActiveX Controls , 定位到并选择Microsoft Web Browser, 点击插入按钮,提示你是否想加组件, 点击 OK。wizard 显示确认对话框如 Figure 6-13.

Figure 6-13. Confirm Classes dialog box.

缺省情况下, CWebBrowser2 将被选择。CWebBrowser2 类是VC为你创建的WebBrowser 控件的包装类。 因为该类特定实现于MFC, 所以你仅可在MFC项目中使用。点击OK 按钮增加CWebBrowser2 到项目中. 然后关闭陈列对话框

包含 WebBrowser2.h 在你的view 类的头文件—MfcWebHostView.h中:

#include "WebBrowser2.h"

创建private 或者 protected 数据成员,命名为 m_webBrowser. 声明如下:

protected:
   CWebBrowser2 m_webBrowser;

WM_CREATE 消息建立消息处理句柄. 在此事件处理中, 使用m_webBrowserCreate 方法加入创建一个webbrowser控件的新实例。 (Create 方法是包装类为你创建的.) OnCreate 消息处理代码看起来如下:

int CMfcWebHostView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
   if (CView::OnCreate(lpCreateStruct) == -1)
      return -1;
   
   // Create WebBrowser control
   //
   if (!m_webBrowser.Create(NULL, WS_CHILD|WS_VISIBLE,
                     CRect(), this, NULL))
   {
     return -1;
   }
   
   return 0;
}

现在为WM_SIZE 消息创建消息处理. 当应用程序改变大小时候修改WebBrowser 控件的大小。如果你不包含以下代码,你的View窗口永远也不会显示webbrowser控件。 此处展示该消息处理代码:

void CMfcWebHostView::OnSize(UINT nType, int cx, int cy) 
{
   CView::OnSize(nType, cx, cy);
   
   
   // Resize WebBrowser control
   //
   m_webBrowser.MoveWindow( 0, 0, cx, cy );
   m_webBrowser.UpdateWindow();
}

覆盖OnInitialUpdate m基类的代码在应用程序首次创建时导航到用户主页。此处展示 OnInitialUpdate 消息处理代码:

void CMfcWebHostView::OnInitialUpdate() 
{
   CView::OnInitialUpdate();
   
   // Navigate to the user's home page.
   //
   m_webBrowser.GoHome();
}   

编译且运行程序.将导航到用户的主页,如Figure 6-14.

Figure 6-14. MfcWebHost application.

现在加入一些访问internet的功能。 增加 Navigate 包含一些Go Back, Go Forward, Go Home, Go Search, Go To A Web Page、Stop等子菜单, 你的菜单看起来类似Figure 6-15.

Figure 6-15. Navigate menu.

现在你可以加入快捷健到你的菜单,例如Alt-Left 组合键给Go Back. 你可以使用你习惯的组合健

 

为每一个菜单项建立实现句柄。增加菜单消息处理句柄代码是较为容易的。举例, GoBack 方法实现Go Back 菜单项代码看起来如下:

void CMfcWebHostView::OnNavigateGoBack() 
{
   m_webBrowser.GoBack();
}
 

void CMfcWebHostView::OnNavigateGoForward() 
{
   m_webBrowser.GoForward();
}
 

void CMfcWebHostView::OnNavigateGoHome() 
{
   m_webBrowser.GoHome();
}
 

void CMfcWebHostView::OnNavigateGoSearch() 
{
   m_webBrowser.GoSearch();
}
 

void CMfcWebHostView::OnNavigateStop() 
{
   m_webBrowser.Stop();
}

如上面提到,如果history列表不存在前项或者后项而用户点击Go Back 或者 Go Forward ,将会发生错误.

 

Go To A Web Page 菜单项是特别情形. 对此菜单项, 一个对话框将显示询问用户想去的URL. (见 Figure 6-16.)

Figure 6-16. Enter A URL For Navigation dialog box.

另外的选择,你可在工具条建立编辑框 用于导航. 在本例子中, 我选择了对话框。当建立对话框,你可以使用ClassWizard 建立新的对话框类.命名为CAddressDlg. 对话框类应当包含名为m_strAddress CString ,它将控制用户输入的地址。如果你的ClassWizard 创建此类成员,该成员将会是public的. 改变数据成员为protected, 且建立如下的访问存取方法:

public:
   const CString& GetAddress() const { return m_strAddress; }
 

protected:
   CString m_strAddress;

现在建立Go To A Web Page 菜单项的消息处理句柄.该菜单句柄将建立显示CAddressDlg 对话框. (确信 CAddressDlg 的头文件包含在 MfcWebHostView.cpp中.) 在用户输入URL 且点击OK后, 应用程序将利用webbrowser的Navigate 方法导航到URL 。 代码如下:

void CMfcWebHostView::OnNavigateGoToAWebPage() 
{
   CAddressDlg dlgAddr;
   
   // Show the dialog box. If the user clicks OK,
   // make sure a URL was entered. If one was entered,
   // navigate to that URL by using the Navigate method.
   //
   if (dlgAddr.DoModal() == IDOK)
   {
     CString strAddress = dlgAddr.GetAddress();
 

     if (!strAddress.IsEmpty())
     {
       COleVariant vtEmpty;
 

       m_webBrowser.Navigate(strAddress, &vtEmpty,
                        &vtEmpty, &vtEmpty, &vtEmpty);
     }
   }
}

在以上代码中, 一个CAddressDlg 类的实例被创建。DoModal 用于显示对话框。如果用户点击OK 按钮,应用程序应当检查 URL是否合法。

 

 

 

使用 ATL

在过去,寄宿WebBrowser控件的ATL应用程序项比较标准的C++没有任何优势。但是现在,新的ATL3的ActiveX控件容器类允许你较容易创建宿主WebBrowser 控件的。

因为实现ATL用户界面特征如菜单和工具条还是使用Win32实现,本例我将展示其本质上灵活的一面。我只想展示如何使用新的容器类寄宿webbrowser控件。我不准备实现任何用户界面。本例子仅仅实现一个容纳WebBrowser的框架。

ATL建立一个WebBrowser 宿主应用程序, 启动0 Visual C++, 执行以下及个步骤:

1.    新建.

2.       选择ATL COM AppWizard, 输入 AtlWebHost 作为工程名

3.       点击 OK, 选择 Executable (EXE),完成。

4.       在新建工程信息对话框点击OK,  wizard 将建立ATL可执行文件工程的基本代码。

5.       向工程中加入宿主WebBrowser 的控件A。具体操作,在ClassView面板中右击  AtlWebHost

6.       从上下文菜单中选择New ATL Object. ATL Object Wizard 对话框将显示

7.       从面板中选择HTML Control, Figure 6-17所示:

Figure 6-17. ATL Object Wizard with HTML Control selected.

8.       点击Next ,为控件输入一个短小名,例如 AtlWbHost.wizard 自动填写其它部分

9.       保持推荐值直到OK.

wizard 将建立CAtlWbHost 类, 包含样本代码寄宿WebBrowser 控件.实例化WebBrowser控件的关键代码在OnCreate 方法的实现代码中, 当 WM_CREATE 消息被发送到关联此类的windows窗体时被调用。wizard 为 OnCreate 插入的代码如下:

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, 
                 BOOL& /*bHandled*/)
{
   CAxWindow wnd(m_hWnd);
   HRESULT hr = wnd.CreateControl(IDH_ATLWBHOST);
   if (SUCCEEDED(hr))
      hr = wnd.SetExternalDispatch(static_cast<IAtlWbHostUI*>(this));
   if (SUCCEEDED(hr))
      hr = wnd.QueryControl(IID_IWebBrowser2, (void**)&m_spBrowser);
   return SUCCEEDED(hr) ? 0 : -1;
}

在这段代码中, 类型微CAxWindow 的windows对象被首先创建,该对象提供ActiveX 控件容器支持. 下一步, 用CAxWindow 类的CreateControl方法创建WebBrowser控件。注意传递给CreateControl  的是HTML页的资源ID,所以当WebBrowser控件被创建时候HTML页被显示。然后SetExternalDispatch 方法被调用。该方法实现于IDocHostUIHandler 接口。

如果任何一个步骤都没有错误发生, 代码将用CAxWindowQueryControl 方法查询 IWebBrowser2 接口.如果一切顺利, QueryControl 返回IWebBrowser2 接口的指针,存储于m_spBrowser 成员变量. 缺省情况下, wizard 将其标记为public.

提醒


作为我所认为的良好的面向对象编程习惯, 我习惯改变m_spBrowser 成员为  protected . 当然你可以决定是否也如此做

如果现在就编译执行代码, 什么事情也不会发生。你必须加入创建和显示窗体的代码。要实现,你必须首先在CAtlWbHost 类中实现Run方法. (该名字无关痛痒。 你可选择任何你感兴趣的名字.) 示例代码如下:

STDMETHODIMP Run()
{
   //
   // Create and show the window.
   //
   RECT rcClient = { CW_USEDEFAULT, 0, 0, 0 };
 

   if (Create(GetDesktopWindow(), rcClient, _T("ATL Browser"),
              WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, (UINT)NULL) == NULL)
   {
     return E_FAIL;
   }
 

   ShowWindow(SW_SHOWNORMAL);
   return S_OK;
}

Create 方法被调用,  WM_CREATE 消息发送到窗口; 因此, OnCreate 方法被调用,OnCreate中包含之前讨论的寄宿WebBrowser 控件的代码。

提醒


Create 方法属于CWindowImpl 类,而该类是CcomControl 的基础类。. CAtlWbHost 派生于CComControl, 这意味着你不如直接调用CWindowImpl 的方法。

下一步必须调用Run 方法以创建和显示窗体。 此调用将被生成于AtlWebHost.cpp 文件直接存在于你的应用程序消息循环之上。但在Run 被调用之前, CAtlWbHost 类的实例必须被创建。你不可以简单的象其他C++对象一样简单采用New操作符创建一个实例 。你必须采用CComObject 的CreateInstance 方法建立该类的实例。 在你的应用程序的消息泵处(CAtlWebHost.cpp中), 插入如下代码实例化CAtlWbHost 类, 然后调用 Run 方法:

CComObject<CAtlWbHost>* pWbHost;
HRESULT hr = CComObject<CAtlWbHost>::CreateInstance(&pWbHost);
 

if (SUCCEEDED(hr))
   pWbHost->Run();
 

// Message pump
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
   DispatchMessage(&msg);

如果现在编译执行, 它将可以工作了。它装入WebBrowser 控件, 导航到wizard automatically 插入到你的应用程序的HTML资源页. 你可以导航到某些有实际意义的地方. 你可在 OnCreate 函数中调用GoHome 。 在你加入调用 GoHome 之后, OnCreate 函数看起来如下:

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
          LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
   CAxWindow wnd(m_hWnd);
   HRESULT hr = wnd.CreateControl(IDH_ATLWBHOST);
   if (SUCCEEDED(hr))
      hr = wnd.SetExternalDispatch(static_cast<IAtlWbHostUI*>(this));
   if (SUCCEEDED(hr))
      hr = wnd.QueryControl(IID_IWebBrowser2, (void**)&m_spBrowser);
 

   if (SUCCEEDED(hr))  
      m_spBrowser->GoHome();
 

   return SUCCEEDED(hr) ? 0 : -1;
}

当你编译执行,你的应用程序看起来类似图Figure 6-18.

Figure 6-18. AtlWebHost application.

打印 Web 页

所有打印只需要调用传递OLECMDID_PRINTExecWB 方法。

MfcWebHost 要支持打印, 应当建立一个ID_FILE_PRINT菜单的菜单句柄,在菜单句柄中调webbrowser的ExecWB ,以及传递 OLECMDID_PRINT. 你也可打印前提醒用户, 仅需要我们多做少量代码. 尽管如此,如果你想知道用户是否按下OK按钮或者取消按钮, 检查 ExecWB的返回值。针对打印命令, 如果用户点几OK以初始化打印, ExecWB 将返回S_OK。 如果用户点击取消, ExecWB 将返回 S_OK以外的值. (说 " S_OK以外的值" 是因为取消按钮的返回值不具有典型代表性) 以下代码为当用户选择文件菜单中打印命令时的调用情况。

void CMfcWebHostView::OnFilePrint() 
{
   m_webBrowser.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER,
                       NULL, NULL);
}

作为打印的附加功能, WebBrowser control 试图提供打印页设置功能.采用打印页设置, 用户可以改变例如页头页脚等设置。要实现业设置功能,代码调用 ExecWB 并且传递 OLECMDID_PAGESETUP. 代码如下:

void CMfcWebHostView::OnFilePageSetup() 
{
   m_webBrowser.ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_PROMPTUSER,
                       NULL, NULL);
}

使用 Visual C++

Vc中创建浏览器应用程序毕vb中稍微困难。如果白手起家创建米的宿应用程序 (无MFC 或 ATL), 需要实现大量的COM接口来寄宿WebBrowser 控件。你必须还要利用COM的API CoCreateInstance  创建WebBrowser 控件的实例,指定 CLSID_WebBrowser a作为你想创建的对象的CLSID 。并且还要 将控件"site"于你的应用程序.

因为建立ActiveX 控件容器费本文讨论主题,所以着重讨论简单途径寄宿webbrowser控件。

使用 MFC

你可以创建3 种类型的MFC应用程序: 单文档接口 (SDI), 多文档接口 (MDI), 以及基于对话框的应用程序。因为对话框MFC类似VB,所以本处将讨论采用SDI来寄宿webbrowser控件。一旦你知道如何采用SDI 应用来寄宿webbrowser控件,转为MDI 将会容易些。

在演示SDI的例子中,我将使用WebBrowser (CWebBrowser2) 包装类MFC的内置CHtmlView 类将帮助你理解如何在MFC中寄宿WebBrowser 。

CHtmlView 类无需太多解释。要使用它,必须在MFC AppWizard第6步中选择你的应用程序的基类为ChtmlView, 如 Figure 6-8. 在完成 wizard后, 你的应用程序的视图类奖派生自 CHtmlView. 然后你可以直接调用IWebBrowser2 接口的不同方法.

Figure 6-8. MFC AppWizard - Step 6 of 6 dialog box.

为创建MFC 单文档接口应用程序,启动Visual C++ 新建菜单。新的对话框展示如 Figure 6-9.

Figure 6-9. Visual C++ New dialog box.

Projects 页, 选择 MFC AppWizard (exe) 项, 输入一个项目名称 (譬如 MfcWebHost之类), 点击OK. MFC AppWizard is 步骤一显示. (看 Figure 6-10.)

Figure 6-10. Step 1 of the MFC AppWizard.

选择 Single Document 。然后认可缺省选项,点击完成. 要增加WebBrowser 到你的工程,选择菜单Project/Add To Project/Components,如下图Figure 6-11所示:

 

Figure 6-11. Selecting Components And Controls.

Visual C++ 收集所有的你的系统中的组件和控件信息展示在Components 和Controls 陈列对话框,如图Figure 6-12所示.

Figure 6-12. Components And Controls Gallery dialog box.

双击 Registered ActiveX Controls , 定位到并选择Microsoft Web Browser, 点击插入按钮,提示你是否想加组件, 点击 OK。wizard 显示确认对话框如 Figure 6-13.

Figure 6-13. Confirm Classes dialog box.

缺省情况下, CWebBrowser2 将被选择。CWebBrowser2 类是VC为你创建的WebBrowser 控件的包装类。 因为该类特定实现于MFC, 所以你仅可在MFC项目中使用。点击OK 按钮增加CWebBrowser2 到项目中. 然后关闭陈列对话框

包含 WebBrowser2.h 在你的view 类的头文件—MfcWebHostView.h中:

#include "WebBrowser2.h"

创建private 或者 protected 数据成员,命名为 m_webBrowser. 声明如下:

protected:
   CWebBrowser2 m_webBrowser;

WM_CREATE 消息建立消息处理句柄. 在此事件处理中, 使用m_webBrowserCreate 方法加入创建一个webbrowser控件的新实例。 (Create 方法是包装类为你创建的.) OnCreate 消息处理代码看起来如下:

int CMfcWebHostView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
   if (CView::OnCreate(lpCreateStruct) == -1)
      return -1;
   
   // Create WebBrowser control
   //
   if (!m_webBrowser.Create(NULL, WS_CHILD|WS_VISIBLE,
                     CRect(), this, NULL))
   {
     return -1;
   }
   
   return 0;
}

现在为WM_SIZE 消息创建消息处理. 当应用程序改变大小时候修改WebBrowser 控件的大小。如果你不包含以下代码,你的View窗口永远也不会显示webbrowser控件。 此处展示该消息处理代码:

void CMfcWebHostView::OnSize(UINT nType, int cx, int cy) 
{
   CView::OnSize(nType, cx, cy);
   
   
   // Resize WebBrowser control
   //
   m_webBrowser.MoveWindow( 0, 0, cx, cy );
   m_webBrowser.UpdateWindow();
}

覆盖OnInitialUpdate m基类的代码在应用程序首次创建时导航到用户主页。此处展示 OnInitialUpdate 消息处理代码:

void CMfcWebHostView::OnInitialUpdate() 
{
   CView::OnInitialUpdate();
   
   // Navigate to the user's home page.
   //
   m_webBrowser.GoHome();
}   

编译且运行程序.将导航到用户的主页,如Figure 6-14.

Figure 6-14. MfcWebHost application.

现在加入一些访问internet的功能。 增加 Navigate 包含一些Go Back, Go Forward, Go Home, Go Search, Go To A Web Page、Stop等子菜单, 你的菜单看起来类似Figure 6-15.

Figure 6-15. Navigate menu.

现在你可以加入快捷健到你的菜单,例如Alt-Left 组合键给Go Back. 你可以使用你习惯的组合健

 

为每一个菜单项建立实现句柄。增加菜单消息处理句柄代码是较为容易的。举例, GoBack 方法实现Go Back 菜单项代码看起来如下:

void CMfcWebHostView::OnNavigateGoBack() 
{
   m_webBrowser.GoBack();
}
 

void CMfcWebHostView::OnNavigateGoForward() 
{
   m_webBrowser.GoForward();
}
 

void CMfcWebHostView::OnNavigateGoHome() 
{
   m_webBrowser.GoHome();
}
 

void CMfcWebHostView::OnNavigateGoSearch() 
{
   m_webBrowser.GoSearch();
}
 

void CMfcWebHostView::OnNavigateStop() 
{
   m_webBrowser.Stop();
}

如上面提到,如果history列表不存在前项或者后项而用户点击Go Back 或者 Go Forward ,将会发生错误.

 

Go To A Web Page 菜单项是特别情形. 对此菜单项, 一个对话框将显示询问用户想去的URL. (见 Figure 6-16.)

Figure 6-16. Enter A URL For Navigation dialog box.

另外的选择,你可在工具条建立编辑框 用于导航. 在本例子中, 我选择了对话框。当建立对话框,你可以使用ClassWizard 建立新的对话框类.命名为CAddressDlg. 对话框类应当包含名为m_strAddress CString ,它将控制用户输入的地址。如果你的ClassWizard 创建此类成员,该成员将会是public的. 改变数据成员为protected, 且建立如下的访问存取方法:

public:
   const CString& GetAddress() const { return m_strAddress; }
 

protected:
   CString m_strAddress;

现在建立Go To A Web Page 菜单项的消息处理句柄.该菜单句柄将建立显示CAddressDlg 对话框. (确信 CAddressDlg 的头文件包含在 MfcWebHostView.cpp中.) 在用户输入URL 且点击OK后, 应用程序将利用webbrowser的Navigate 方法导航到URL 。 代码如下:

void CMfcWebHostView::OnNavigateGoToAWebPage() 
{
   CAddressDlg dlgAddr;
   
   // Show the dialog box. If the user clicks OK,
   // make sure a URL was entered. If one was entered,
   // navigate to that URL by using the Navigate method.
   //
   if (dlgAddr.DoModal() == IDOK)
   {
     CString strAddress = dlgAddr.GetAddress();
 

     if (!strAddress.IsEmpty())
     {
       COleVariant vtEmpty;
 

       m_webBrowser.Navigate(strAddress, &vtEmpty,
                        &vtEmpty, &vtEmpty, &vtEmpty);
     }
   }
}

在以上代码中, 一个CAddressDlg 类的实例被创建。DoModal 用于显示对话框。如果用户点击OK 按钮,应用程序应当检查 URL是否合法。

 

 

 

使用 ATL

在过去,寄宿WebBrowser控件的ATL应用程序项比较标准的C++没有任何优势。但是现在,新的ATL3的ActiveX控件容器类允许你较容易创建宿主WebBrowser 控件的。

因为实现ATL用户界面特征如菜单和工具条还是使用Win32实现,本例我将展示其本质上灵活的一面。我只想展示如何使用新的容器类寄宿webbrowser控件。我不准备实现任何用户界面。本例子仅仅实现一个容纳WebBrowser的框架。

ATL建立一个WebBrowser 宿主应用程序, 启动0 Visual C++, 执行以下及个步骤:

1.    新建.

2.       选择ATL COM AppWizard, 输入 AtlWebHost 作为工程名

3.       点击 OK, 选择 Executable (EXE),完成。

4.       在新建工程信息对话框点击OK,  wizard 将建立ATL可执行文件工程的基本代码。

5.       向工程中加入宿主WebBrowser 的控件A。具体操作,在ClassView面板中右击  AtlWebHost

6.       从上下文菜单中选择New ATL Object. ATL Object Wizard 对话框将显示

7.       从面板中选择HTML Control, Figure 6-17所示:

Figure 6-17. ATL Object Wizard with HTML Control selected.

8.       点击Next ,为控件输入一个短小名,例如 AtlWbHost.wizard 自动填写其它部分

9.       保持推荐值直到OK.

wizard 将建立CAtlWbHost 类, 包含样本代码寄宿WebBrowser 控件.实例化WebBrowser控件的关键代码在OnCreate 方法的实现代码中, 当 WM_CREATE 消息被发送到关联此类的windows窗体时被调用。wizard 为 OnCreate 插入的代码如下:

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, 
                 BOOL& /*bHandled*/)
{
   CAxWindow wnd(m_hWnd);
   HRESULT hr = wnd.CreateControl(IDH_ATLWBHOST);
   if (SUCCEEDED(hr))
      hr = wnd.SetExternalDispatch(static_cast<IAtlWbHostUI*>(this));
   if (SUCCEEDED(hr))
      hr = wnd.QueryControl(IID_IWebBrowser2, (void**)&m_spBrowser);
   return SUCCEEDED(hr) ? 0 : -1;
}

在这段代码中, 类型微CAxWindow 的windows对象被首先创建,该对象提供ActiveX 控件容器支持. 下一步, 用CAxWindow 类的CreateControl方法创建WebBrowser控件。注意传递给CreateControl  的是HTML页的资源ID,所以当WebBrowser控件被创建时候HTML页被显示。然后SetExternalDispatch 方法被调用。该方法实现于IDocHostUIHandler 接口。

如果任何一个步骤都没有错误发生, 代码将用CAxWindowQueryControl 方法查询 IWebBrowser2 接口.如果一切顺利, QueryControl 返回IWebBrowser2 接口的指针,存储于m_spBrowser 成员变量. 缺省情况下, wizard 将其标记为public.

提醒


作为我所认为的良好的面向对象编程习惯, 我习惯改变m_spBrowser 成员为  protected . 当然你可以决定是否也如此做

如果现在就编译执行代码, 什么事情也不会发生。你必须加入创建和显示窗体的代码。要实现,你必须首先在CAtlWbHost 类中实现Run方法. (该名字无关痛痒。 你可选择任何你感兴趣的名字.) 示例代码如下:

STDMETHODIMP Run()
{
   //
   // Create and show the window.
   //
   RECT rcClient = { CW_USEDEFAULT, 0, 0, 0 };
 

   if (Create(GetDesktopWindow(), rcClient, _T("ATL Browser"),
              WS_VISIBLE | WS_OVERLAPPEDWINDOW, 0, (UINT)NULL) == NULL)
   {
     return E_FAIL;
   }
 

   ShowWindow(SW_SHOWNORMAL);
   return S_OK;
}

Create 方法被调用,  WM_CREATE 消息发送到窗口; 因此, OnCreate 方法被调用,OnCreate中包含之前讨论的寄宿WebBrowser 控件的代码。

提醒


Create 方法属于CWindowImpl 类,而该类是CcomControl 的基础类。. CAtlWbHost 派生于CComControl, 这意味着你不如直接调用CWindowImpl 的方法。

下一步必须调用Run 方法以创建和显示窗体。 此调用将被生成于AtlWebHost.cpp 文件直接存在于你的应用程序消息循环之上。但在Run 被调用之前, CAtlWbHost 类的实例必须被创建。你不可以简单的象其他C++对象一样简单采用New操作符创建一个实例 。你必须采用CComObject 的CreateInstance 方法建立该类的实例。 在你的应用程序的消息泵处(CAtlWebHost.cpp中), 插入如下代码实例化CAtlWbHost 类, 然后调用 Run 方法:

CComObject<CAtlWbHost>* pWbHost;
HRESULT hr = CComObject<CAtlWbHost>::CreateInstance(&pWbHost);
 

if (SUCCEEDED(hr))
   pWbHost->Run();
 

// Message pump
MSG msg;
while (GetMessage(&msg, 0, 0, 0))
   DispatchMessage(&msg);

如果现在编译执行, 它将可以工作了。它装入WebBrowser 控件, 导航到wizard automatically 插入到你的应用程序的HTML资源页. 你可以导航到某些有实际意义的地方. 你可在 OnCreate 函数中调用GoHome 。 在你加入调用 GoHome 之后, OnCreate 函数看起来如下:

LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, 
          LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
   CAxWindow wnd(m_hWnd);
   HRESULT hr = wnd.CreateControl(IDH_ATLWBHOST);
   if (SUCCEEDED(hr))
      hr = wnd.SetExternalDispatch(static_cast<IAtlWbHostUI*>(this));
   if (SUCCEEDED(hr))
      hr = wnd.QueryControl(IID_IWebBrowser2, (void**)&m_spBrowser);
 

   if (SUCCEEDED(hr))  
      m_spBrowser->GoHome();
 

   return SUCCEEDED(hr) ? 0 : -1;
}

当你编译执行,你的应用程序看起来类似图Figure 6-18.

Figure 6-18. AtlWebHost application.

打印 Web 页

所有打印只需要调用传递OLECMDID_PRINTExecWB 方法。

MfcWebHost 要支持打印, 应当建立一个ID_FILE_PRINT菜单的菜单句柄,在菜单句柄中调webbrowser的ExecWB ,以及传递 OLECMDID_PRINT. 你也可打印前提醒用户, 仅需要我们多做少量代码. 尽管如此,如果你想知道用户是否按下OK按钮或者取消按钮, 检查 ExecWB的返回值。针对打印命令, 如果用户点几OK以初始化打印, ExecWB 将返回S_OK。 如果用户点击取消, ExecWB 将返回 S_OK以外的值. (说 " S_OK以外的值" 是因为取消按钮的返回值不具有典型代表性) 以下代码为当用户选择文件菜单中打印命令时的调用情况。

void CMfcWebHostView::OnFilePrint() 
{
   m_webBrowser.ExecWB(OLECMDID_PRINT, OLECMDEXECOPT_PROMPTUSER,
                       NULL, NULL);
}

作为打印的附加功能, WebBrowser control 试图提供打印页设置功能.采用打印页设置, 用户可以改变例如页头页脚等设置。要实现业设置功能,代码调用 ExecWB 并且传递 OLECMDID_PAGESETUP. 代码如下:

void CMfcWebHostView::OnFilePageSetup() 
{
   m_webBrowser.ExecWB(OLECMDID_PAGESETUP, OLECMDEXECOPT_PROMPTUSER,
                       NULL, NULL);
}

 

 

 如何对webbrowser和IE编程(五) 收藏

新一篇: 如何对webbrowser和IE编程(六) | 旧一篇: 如何对webbrowser和IE编程(四)

自动化 Internet Explorer

自动化打开了开发基于web应用的世界。 它允许你使用VB或者VC定制成熟的应用。自动化的好处:通过属性和方法可以改变IE的外观;你可以提供诸如导航条等用户接口以便控制用户的导航。

自动化IE很容易。你建立一个简单的应用启动一个IE实例,然后使用控制webbrowser的途径- IWebBrowser2 接口来控制IE实例。

提醒


术语自动化(automation真实的含义是通过自动化接口-- IDispatch.控制一个COM对象。但是在此是指控制IE的技术,你不需要直接通过IDispatch

使用VB

前面已经介绍了如何五分钟在VB中使用webbrowser来创建全功能的浏览器应用. 你也可以大致使用此时间用VB自动化IE。让我们开始。

启动一个Standard EXE 工程,选择References 菜单项. 引用对话框展开如Figure 6-19:

Figure 6-19. References dialog box.

滚动下拉,选中 Microsoft Internet Controls 检查框,点击OK 。加入一个命令按钮到窗体,命名为btnStart, 修改标题为 Start IE5. 然后双击加入click事件代码。

当用户点击Start IE5 按钮, 你想应用程序启动一个Internet Explorer 5实例. 先建立一个类型为 InternetExplorer 的全局变量. 命名为InternetExplorer1.

现在, 在btnStart的Click 事件中, 加入如果上一个实例没有创建就创建新IE实例的代码。你可以使用CreateObject 或者Vb的New 关键字.如下:

Set InternetExplorer1 = New InternetExplorer

该代码创建一个新实例,但是实例是隐藏的,要显示该实例,设定Visible 属性为 True, 如下:

InternetExplorer1.Visible = True

现在你需要导航到某个web页,你可以如下调用InternetExplorer 对象的Navigate方法, 如下:

InternetExplorer1.Navigate "http://www.microsoft.com/"

至此,整个Vb的自动化IE的源代码看起来如下:

Option Explicit
Dim InternetExplorer1 As InternetExplorer
 

Private Sub btnStart_Click()
   ' Only create a new instance of Internet Explorer
   ' if one hasn't already been created.
   '
   If Not InternetExplorer1 Is Nothing Then
      Exit Sub
   End If
 

   Set InternetExplorer1 = New InternetExplorer
   
   ' Make Internet Explorer visible and navigate
   ' to Microsoft's home page.
   '
   InternetExplorer1.Visible = True
   InternetExplorer1.Navigate "http://www.microsoft.com/"
End Sub
 

Private Sub Form_Load()
   Set InternetExplorer1 = Nothing
End Sub

运行应用程序看到IE启动了! 新的IE实例将被启动导航到MS的主页。者不太困难,是不是?现在让我们加入一些实在的较酷的特征允许你控制你自己创建的IE实例。

首先保存工程为 VbAutoIE.vbp, 且保存你的表单 VbAutoIE.frm. 然后加入一些控制到你的表单,如图Figure 6-20. 这些允许你显示或者隐藏IE中不同的用户接口特征如地址栏、菜单条、状态条和工具条等。你也可以加入文字到状态条。

Figure 6-20. Visual Basic form with controls to customize the Internet Explorer user interface.

现在如下表设定每一个控件的属性如表6-8.创建4个选项组,每一个包含 一个显示和一个隐藏选项按钮如Figure 6-20.

Table 6-8. Control Properties for a Visual Basic Program Automating Internet Explorer

Control

Properties

Frame1-4

Captions = "AddressBar", "MenuBar", "StatusBar ", and "ToolBar", respectively

Hide Option Buttons

Caption = "Hide"; Index = 0; Value = False; Names = optAddrBar, optMenuBar, optStatusBar, and optToolBar, respectively

Show Option Buttons

Caption = "Show"; Index = 1; Value = True; Names = optAddrBar, optMenuBar, optStatusBar, and optToolBar, respectively

Label

Caption = "Status Text"

TextBox

Name = txtStatusText. Remove the default text for the Text property

CommandButton

Caption = "Change"; Name = btnChange

加入控制InternetExplorer 对象的代码控制浏览器的用户接口。看看清单6-1

Listing 6-1.

VbAutoIE.bas

Option Explicit
Dim InternetExplorer1 As InternetExplorer
Const HideBar = 0
Const ShowBar = 1
Private Sub btnChange_Click()
   On Error Resume Next
   InternetExplorer1.StatusText = txtStatusText.Text
End Sub
 

Private Sub btnStart_Click()
   ' Only create a new instance of Internet Explorer
   ' if one hasn't already been created.
   '
   If Not InternetExplorer1 Is Nothing Then
      Exit Sub
   End If
   
   Set InternetExplorer1 = New InternetExplorer
   
   ' Set the user interface features to match the
   ' entries specified by the user.
   '
   If optAddrBar(ShowBar).Value = True Then
      InternetExplorer1.AddressBar = True
   Else
      InternetExplorer1.AddressBar = False
   End If
   
   If optMenuBar(ShowBar).Value = True Then
      InternetExplorer1.MenuBar = True
   Else
      InternetExplorer1.MenuBar = False
   End If
   
   If optToolBar(ShowBar).Value = True Then
      InternetExplorer1.ToolBar = True
   Else
      InternetExplorer1.ToolBar = False
   End If
 

   If optStatusBar(ShowBar).Value = True Then
      InternetExplorer1.StatusBar = True
   Else
      InternetExplorer1.StatusBar = False
   End If
   
   ' Make Internet Explorer visible and navigate
   ' to Microsoft's home page.
   '
   InternetExplorer1.Visible = True
   InternetExplorer1.Navigate "http://www.microsoft.com/"
End Sub
 

Private Sub Form_Load()
   Set InternetExplorer1 = Nothing
End Sub
 

Private Sub Form_Unload(Cancel As Integer)
   On Error Resume Next
   InternetExplorer1.Quit
End Sub
 

Private Sub optAddrBar_Click(Index As Integer)
   On Error Resume Next
   InternetExplorer1.AddressBar = CBool(Index)
End Sub
 

Private Sub optMenuBar_Click(Index As Integer)
   On Error Resume Next
   InternetExplorer1.MenuBar = CBool(Index)
End Sub
 

Private Sub optStatusBar_Click(Index As Integer)
   On Error Resume Next
   InternetExplorer1.StatusBar = CBool(Index)
End Sub
 

Private Sub optToolBar_Click(Index As Integer)
   On Error Resume Next
   InternetExplorer1.ToolBar = Index
End Sub

在清单6-1, 当表单被装载,  InternetExplorer1 对象设定为Nothing.当Start IE5 按钮被点击, 我们检查确信没有上一个实例启动,如果启动了我们直接返回。

如果上一实例没有启动,我们采用关键字New 创建一个新实例。然后我们检查选项组的状态.我们依据选项当前值进行IS属性的设置。然后设置Visible 属性为 True. 最后我们使用Navigate 方法导航到MS的主页.

 

 

 

如何对webbrowser和IE编程(六)收藏

新一篇: 如何对webbrowser和IE编程(七) | 旧一篇: 如何对webbrowser和IE编程(五)

使用VC++和COM API

尽管使用VC不像Vb中那么容易自动化IE,但是也不太难,尤其是你理解了CON和COM API。无论你使用MFC, ATL, 或者标准 C++自动化IE,方法都是一样—你使用COM API来实现.

VC++中创建一个Internet Explorer实例包括要调用COM API CoCreateInstance ,指定第一个参数为 CLSID_InternetExplorer 。创建IE自动化实例不像创建包含webbrowser的Activex控件困难。你不需要实现容器或者site对象的必要接口。

来看看如何容易的使用CoCreateInstance 创建IE实例吧。启动Visual C++, 新建MFC AppWizard (exe) 应用程序命名为 MfcAutoIE.选择dialog-based option, 认可其他缺省选项.

现在加入如VbAutoIE中的控件到表单.对话框看起来如 6-21. 分派ID到对话框的各个控件。 确信控件TAB次序如 Figure 6-22. (tab 次序影响到radio按钮的工作)

Figure 6-21. MfcAutoIE dialog.

Figure 6-22. MfcAutoIE dialog tab order.

现在用右键菜单设置每一个隐藏radio按钮的Group ,属性设置如表 6-9.

Table 6-9. Member Variables for MfcAutoIE Dialog Controls

Control

Type

Member Variable

Hide radio button for AddressBar

int

m_nAddressBar

Edit box

CString

m_strStatusText

Hide radio button for MenuBar

int

m_nMenuBar

Hide radio button for StatusBar

int

m_nStatusBar

Hide radio button for ToolBar

int

m_nToolBar

我们使用ClassWizard 为表 6-9 中的控件建立变量时,他们自动加入到 CMfcAutoIEDlg. 成员变量设置为-1.

编译MfcAutoIE Example之前我们设置编译的Directory次序 属性

编译之前,你需要处理一些重要任务:

1.    确信你已经从MSDN中下载IE5的头文件和库文件。

2. Tools/Options 菜单的Directories, 确信lib路径中包含Internet Explorer 5 Windows 2000库文件.载列表中。

3. 配置Include

 

现在我们加入代码使之工作。 首先你应当包含ExDisp.h到你的对话框头文件—MfcAutoIEDlg.h. ExDisp.h 是包含了WebBrowser接口和类ID的头文件.确信你已经从MSDN下载了最新的版本。

建立一个private 或者protected 的数据类型,指向IWebBrowser2  的指针,  命名为 m_pInetExplorer. 如下

protected:
   IWebBrowser2* m_pInetExplorer;

现在在构造函数中初始化m_pInetExplorerNULL 。你必须也初始化COM. 放置一个CoInitialize 的COM API调用在构造函数。构造函数看起来应当如下:

CMfcAutoIEDlg::CMfcAutoIEDlg(CWnd* pParent /*=NULL*/)
   : CDialog(CMfcAutoIEDlg::IDD, pParent),
     m_pInetExplorer(NULL)
{
   //{{AFX_DATA_INIT(CMfcAutoIEDlg)
   m_strStatusText = _T("");
   m_nAddressBar = -1;
   m_nMenuBar = -1;
   m_nStatusBar = -1;
   m_nToolBar = -1;
   //}}AFX_DATA_INIT
   //Note that LoadIcon does not require a subsequent DestroyIcon
   //in Win32.
   m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 

   // Initialize COM
   CoInitialize(NULL);
}

建立一个析构函数,以便调用CoUninitialize API 函数用以反初始化 COM. 此处的析构函数:

CMfcAutoIEDlg::~CMfcAutoIEDlg()
{
   // Uninitialize COM.
   //
   CoUninitialize();
}

在预备工作之后, 让我们开始实现具体的控制。首先为Start IE5 按钮建立一个消息循环. 通常,你可以使用ClassWizard建立消息循环处理句柄. 在此消息句柄, 采用 CoCreateInstance API 建立一个IE实例。 此处初建的Internet Explorer实例初始化为隐藏, 所以你必须使用Visible属性使他可见。为了导航到用户的主页,使用GoHome 方法。此处为消息处理句柄代码:

void CMfcAutoIEDlg::OnStartIE5() 
{
   // If an instance of Internet Explorer has
   // not already been created, create one.
   // This instance will initially be hidden,
   // so make it visible by using the Visible
   // property. Also, navigate to the user's
   // home page by using the GoHome method.
   //
   if (m_pInetExplorer)
      MessageBox
         (_T("Only one instance of Internet Explorer is allowed."));
   else
   {
      HRESULT hr;
      hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_SERVER,
                           IID_IWebBrowser2, (LPVOID*)&m_pInetExplorer);
 

      if (SUCCEEDED(hr))
      {
         // Set the radio buttons to their correct values.
         SetRadioButtons();
 

         m_pInetExplorer->put_Visible(VARIANT_TRUE);
         m_pInetExplorer->GoHome();
      }
   }
}

在此代码中,我们首先检查是否IE的上一个实例已经建立。如果已经建立,将会显示一个错误消息框。(你仅仅能够启动一个IE的实例,随后你将可以看到如何检测IE窗口被关闭以便重置IE的数据成员。)如果一个IE的实例还未创建, CoCreateInstanceCLSID_InternetExplorer 作为第一个参数被调用. (此类 ID 定义于ExDisp.h ,为Internet Explorer的唯一标示符.)

第二个参数我们设定为NULL ,因为我们不希望此COM对象被聚合(be aggregated). 我们希望IE运行在一个单独的进程空间,所以我们指定第三个参数为特殊值 CLSCTX_SERVER .我们利用第四个参数指示哪一个接口我们将通过CoCreateInstance 调用创建一个COM对象后被创建。在此例中,我们将一直希望获得IWebBrowser2, 所以我们指定IID_IWebBrowser2 为第四个参数值。最后我们传送存储接口指针的变量. 对于此参数,你必须采用void类型传递一个指针的地址 (确信你传送的指针指向一个接口类型。)

如果一个IE实例成功创建,名为SetRadioButtons 的成员变量被创建。这是一个protected 成员函数,检查当前地址栏、菜单条、工具条和状态条等的状态 ,用来设置radio 按钮组的状态。.为检查每一个的状态我们简单的获取属性关联的当前值 。

C++ 应用中使用COM 没有包装类, 属性被引用为使用 get_ and put_ 方法.为检取每一个用户问题的状态,我们简单的调用每一个关联的(UI)项目的get_ 方法 .除了 get_ToolBar外其他get_方法返回指向VARIANT_BOOL 数据类型,返回值指示用户接口是否可见或者隐藏。如果用户接口元素可见,将返回VARIANT_TRUE 。如果该项目是隐藏的,将返回VARIANT_FALSE 。这些值区别于TRUE FALSE 的用法.

提醒


当在Visual C++涉及 VARIANT_BOOL , 你必须使用VARIANT_TRUE 或者 VARIANT_FALSE 代替 TRUE or FALSE. VARIANT_TRUE定义值 0xffff, TRUE 定义为 1. 如果你比较 VARIANT_TRUE TRUE, 你将发现在Vb中不匹配,你可以在涉及到VARIANT_BOOL. 使用truefalse是由于VB替你实现了转换。

get_ToolBar 方法不同于其他属性,因为它指向一个整型指针,所以我们如果发现返回非0,工具条可见。如果整型值是0,工具条将隐藏:

 

提醒


如果一个属性只读, put_ 将不会出现。同样,如果一个属性只写,将不会出现get_ 方法。

此处为 SetRadioButtons 方法的代码:

void CMfcAutoIEDlg::SetRadioButtons()
{
   VARIANT_BOOL vtBool = VARIANT_TRUE;
 

   // Get the current state of the AddressBar.
   //
   m_pInetExplorer->get_AddressBar(&vtBool);
   m_nAddressBar = (vtBool == VARIANT_TRUE) ? 1 : 0;
 

   // Get the current state of the MenuBar.
   //
   m_pInetExplorer->get_MenuBar(&vtBool);
   m_nMenuBar = (vtBool == VARIANT_TRUE) ? 1 : 0;
 

   // Get the current state of the StatusBar.
   //
   m_pInetExplorer->get_StatusBar(&vtBool);
   m_nStatusBar = (vtBool == VARIANT_TRUE) ? 1 : 0;
 

   // Get the current state of the ToolBar.
   // Unlike the other get methods, get_ToolBar
   // takes a pointer to an integer.
   //
   m_pInetExplorer->get_ToolBar(&m_nToolBar);
 

   UpdateData(FALSE);  // Initializes dialog box with changed values.
}

:

void CMfcAutoIEDlg::OnAddrBarShowHide() 
{
   UpdateData(TRUE);
 

   if (m_pInetExplorer)
   {
      VARIANT_BOOL vtShow = 
         m_nAddressBar ? VARIANT_TRUE : VARIANT_FALSE;
 

      m_pInetExplorer->put_AddressBar(vtShow);
   }
}
 

void CMfcAutoIEDlg::OnMenuBarShowHide() 
{
   UpdateData(TRUE);
 

   if (m_pInetExplorer)
   {
      VARIANT_BOOL vtShow = 
         m_nMenuBar ? VARIANT_TRUE : VARIANT_FALSE;
 

      m_pInetExplorer->put_MenuBar(vtShow);
   }
}
 

void CMfcAutoIEDlg::OnStatusBarShowHide() 
{
   UpdateData(TRUE);
 

   if (m_pInetExplorer)
   {
      VARIANT_BOOL vtShow = 
         m_nStatusBar ? VARIANT_TRUE : VARIANT_FALSE;
 

      m_pInetExplorer->put_StatusBar(vtShow);
   }
}
 

void CMfcAutoIEDlg::OnToolBarShowHide() 
{
   UpdateData(TRUE);
 

   if (m_pInetExplorer)
      m_pInetExplorer->put_ToolBar(m_nToolBar);
}

注意到OnToolBarShowHide 方法中我们将m_nToolBar 数据成员传递给put_ToolBar 方法替代了VARIANT_BOOL. 我们暂时不解释为什么,就像get_ToolBar, put_ToolBar 传递一个整型而不是 VARIANT_BOOL.

现在利用ClassWizard 建立一个Change 按钮的消息循环处理句柄 。此消息的句柄将在任何时候设定set Internet Explorer 的状态条为edit 输入框内容。 代码看起来如下:

void CMfcAutoIEDlg::OnChangeStatusText() 
{
   UpdateData(TRUE);
 

   if (m_pInetExplorer)
   {
      _bstr_t bstrStatusText = m_strStatusText.AllocSysString();
      m_pInetExplorer->put_StatusText(bstrStatusText);
   }
}

确信你在第一个任务就是调用传递TRUE的 UpdateData and .这样的目的是更新对阿框中所有相关的控件, 包括 m_strStatusText. 如果m_pInetExplorer 不是NULL—那意味着 Internet Explorer一个实例已经被创建— StatusText 被设定为用户输入的文字。 StatusText 同样事采用put_ 设定Visible 属性。 该函数需要一个BSTR的入口参数,所以调用 AllocSysString 方法使得m_strStatusText 数据成员变量分派为BSTR 可以传递给put_StatusText. AllocSysStringCString 的方法,返回一个Cstring对象中值的 BSTR 类型。BSTR 返回自 AllocSysString 存储于_bstr_t类型的变量, 一个 COM 编译器支持的Visual C++类。为了使用此类,你必须包含comdef.h 头文件,置于对话框实现文件CMfcAutoIEDlg—MfcAutoIEDlg.cpp. 该类将小心处理BSTR 离开当前范围的状况,所以你不需要调用SysFreeString Win32 API 以释放调用 put_StatusText.之后的BSTR.

至此,我们基本上完成了。为了使用户输入Enter时不关闭对话框,重写基类对话框的OnOK方法。在此方法中,简单的不做任何事。确信你没有调用基类的CDialog::OnOK 方法,否则,对话框会关闭。

现在来重写OnCancel 的基类方法, 该方法调用于用户按下ESC或者点击对话框的右键菜单的关闭或者对话框的X按钮。在 OnCancel中,如果一个Internet Explorer 实例已经被创建, 在调用Quit 方法时关闭InternetExplorer 对象。 Quit 甚至可以在用户默认关掉Internet Explorer. 如果出现此情形, Quit 方法将返回 RPC_S_SERVER_UNAVAILABLE, 标示Internet Explorer 不再可用.你可以简单忽略此错误消息。以下为OnCancel的代码:

void CMfcAutoIEDlg::OnCancel()
{
   if (m_pInetExplorer)
      m_pInetExplorer->Quit();
 

   CDialog::OnCancel();
}

最后, 你必须释放掉指向IE的IWebBrowser2 接口指针。你可以在对话框的析构函数中处理。此处为更型后的析构函数:

 

CMfcAutoIEDlg::~CMfcAutoIEDlg()
{
   // Release the WebBrowser interface pointer
   //
   if (m_pInetExplorer)
   {
      m_pInetExplorer->Release();
      m_pInetExplorer = NULL;
   }
 

   // Uninitialize COM
   //
   CoUninitialize();
}
现在,通常我们可以编译运行对话框应用程序。启动IE实例,测试。

发表于 @ 2005年07月07日 00:22:00|评论(3 )|编辑

新一篇: 如何对webbrowser和IE编程(七) | 旧一篇: 如何对webbrowser和IE编程(五)

评论

#alan 发表于2005-07-14 09:43:00  IP: 61.186.252.*
不知道在VC下如何对Netscape编程,而不是仅仅的开个process,调用commandline来执行,这样不能对NS进行control,不知你是否有何高见,
thanks
#windcbf 发表于2005-08-02 21:10:00  IP: 61.186.252.*
netscape 的核心是 mozilla,所以看看怎么对mozilla进行编程。 区别在于,IE是用COM,Mozilla是用XPCOM
#xiao_q654321 发表于2007-12-25 17:20:19  IP: 218.108.199.*
您好,我是这样做的:
CWnd m_wndBrowser;////IWebBrowser2 的容器
IWebBrowser2 *m_pBrowser;

m_wndBrowser.CreateControl(CLSID_WebBrowser, NULL, WS_VISIBLE | WS_CHILD,
rectClient, this, AFX_IDW_PANE_FIRST);
LPUNKNOWN lpUnk = m_wndBrowser.GetControlUnknown();
HRESULT hr = lpUnk->QueryInterface(IID_IWebBrowser2, (void**) &m_pBrowser);
可以显示网页,但奇怪的是网页的按键事件无法激活,请教存在什么问题?
网页的js:
document.onkeydown=keyDown;
function keyDown(){ var keycode = event.keyCode; alert keyCode;}

 

如何对webbrowser和IE编程(七)收藏

新一篇: 关于Cookie | 旧一篇: 如何对webbrowser和IE编程(六)

加入高级功能

本节将展示如何加入高级功能到你的应用程序或者ActiveX控件.将展示如何从一个ActiveX控件中访问Internet Explorer的 IWebBrowser2,以及如何从一个Web页面的frame中获得WebBrowser 对象。将会展示实现一些并非容易实现的功能.本节有一定难度.所有代码采用C++ 和COM实现,你应当有一定坚实的基础才可以完成本节的理解

ActiveX 控件中访问Internet Explorer 的IWebBrowser2

Activx控件中访问IWebBrowser2 接口提供了用户定制浏览器的能力,虽然以此作为自己的控件功能不大光明,且你只能够在VC编写的activeX控件中访问。(尽管可以在VB写的控件中访问document和window控件,但是你不可以直接访问WebBrowser 自身) .

存取  IWebBrowser2 接口经过四个步骤:

1.    在类文件中包含 ExDisp.h .

2.       调用控件的站点的IOleClientSite::GetContainer 方法,该方法返回由Internet Explorer.实现的 IOleContainer 接口的指针。

3.       如果步骤2成功,用 IOleContainer 指针查询IServiceProvider 接口。.

4.       如果步骤3 成功, 调用IServiceProvider 的方法QueryService 的到 IWebBrowser2 接口。

QueryService 方法携带3个参数. 第一个参数指定你想访问的服务。为得到IWebBrowser2 指针, 需要指定SID_SInternetExplorer 或者SID_SWebBrowserApp 来指定要访问的服务. (现阶段,他们全部定义为IID_IWebBrowserApp.) 第二个参数指定你想接收的接口的ID。此参数你应当指定为IID_IWebBrowser2. 最后,第三个参数你需要指定哪一个变量接收返回的接口指针。

Call any method or property of IWebBrowser2. 当完成后,确信你已经释放掉你获取的接口指针.以下为代码参数.

////////////////////////////////////////////////////////
// Begin Step 1
 

#include <ExDisp.h>
#include <shlguid.h>
 

// End Step 1
////////////////////////////////////////////////////////
 

CSomeClass::SomeMethod(){
 

////////////////////////////////////////////////////////
// Begin Step 2
 

IOleContainer* pContainer;
 

// m_pClientSite is a pointer to IOleClientSite.
// This is the client site for your control.
//
HRESULT hr = m_pClientSite->GetContainer(&pContainer);
if (FAILED(hr))
   return hr;
 

// End Step 2
////////////////////////////////////////////////////////
 

////////////////////////////////////////////////////////
// Begin Step 3
 

IServiceProvider* pServiceProvider;
 

hr = pContainer->QueryInterface(IID_IServiceProvider,
                                (void**)&pServiceProvider);
pContainer->Release();
 

if (FAILED(hr))
   return hr;
 

// End Step 3
////////////////////////////////////////////////////////
 

 

////////////////////////////////////////////////////////
// Begin Step 4
 

IWebBrowser2* pWebBrowser;
 

hr = pServiceProvider->QueryService(SID_SWebBrowserApp,
                           IID_IWebBrowser2,
                           (void**)&pWebBrowser);
pServiceProvider->Release();
 

if (FAILED(hr))
   return hr;
 

// End Step 4
////////////////////////////////////////////////////////
 

 

////////////////////////////////////////////////////////
// Begin Step 5
 

// Call some IWebBrowser2 methods and/or properties.
 

// End Step 5
////////////////////////////////////////////////////////
}

利用一个控件打印web页

早于5的Internet Explorer,通常用于ActiveX中 访问IWebBrowser2 接口以提供打印web页面的功能。尽管ie5允许你直接在web页的script中使用 window.print, 但是建立一个提供打印web页的功能的示范还是比较好教您使用IWebBrowser2接口的入门教程.在此之前,必要知道如何使用ATL建立一个activeX控件以实现打印功能. 关于此点,我将认为您已经知道如何使用ATL创建一个控件.

启动Visual C++, 新建 ATL DLL工程. 你可将工程命名为 AtlPrint. 下一步, 使用Wizard增加一个Lite Control 到你的工程。 你可以命名你的控件为PrintCtl. 为IPrintCtl 接口添加一个方法Print. 之后你将在脚本中使用此方法打印当前Web page.

在实现Print 方法前,首先包含ExDisp.h 和 shlguid.h 头文件到PrintCtl.cpp 实现文件.

下一步使用之前给出的获取IWebBrowser2 接口的方法获得接口并调用ExecWB 以实现打印当前Web页. 你可以使用ATL 智能接口指针类—CComPtr 以及 CComQIPtr—以实现查询接口处理引用次数等艰苦的工作.此处为智能指针示勇代码:

STDMETHODIMP CPrintCtl::Print()
{
   HRESULT hr = E_FAIL;
 

   if (m_spClientSite)
   {
      CComPtr<IOleContainer> spContainer;
   
      hr = m_spClientSite->GetContainer(&spContainer);
      ATLASSERT(SUCCEEDED(hr));
 

      if (SUCCEEDED(hr))
      {
         CComQIPtr<IServiceProvider, &IID_IServiceProvider>
            spServiceProvider(spContainer);
 

         ATLASSERT(spServiceProvider);
 

         if (!spServiceProvider)
            hr = E_FAIL;
         else
         {
            CComPtr<IWebBrowser2> spWebBrowser;
 

            hr = spServiceProvider->QueryService(SID_SInternetExplorer,
                                                 IID_IWebBrowser2,
                                                 (void**)&spWebBrowser);
            ATLASSERT(SUCCEEDED(hr));
 

            if (SUCCEEDED(hr))
            {         
               spWebBrowser->ExecWB(OLECMDID_PRINT, 
                                 OLECMDEXECOPT_PROMPTUSER,
                                 NULL, NULL);
            }
         }
      }
   }
 

   return hr;
}

现在编译ATL ActiveX control. 为测试打印功能,你必须加入一些script. Listing 6-2 展示了这部分代码:

Listing 6-2.

PrintCtl.htm

<HTML>
<HEAD>
   <TITLE>ATL 3.0 test page for object PrintCtl</TITLE>
 

   <SCRIPT LANGUAGE="VBS">
      Sub btnPrint_onclick
         PrintCtl.Print
      End Sub
   </SCRIPT>
</HEAD>
<BODY>
   <OBJECT ID="PrintCtl"
      CLASSID="CLSID:320B04E4-B55B-11D2-A9BA-444553540001">
   </OBJECT>
   <P>
   <BUTTON ID="btnPrint">Print Page</BUTTON>
</BODY>
</HTML>

当宿主WebBrowser控件的时候存取帧的IWebBrowser2 接口

如果一个Web 页包含了多帧,每一帧都包含了一个WebBrowser 对象。 当宿主WebBrowser 控件, 你将可被允许从应用程序访问WebBrowser 以控制帧.此控允许你控制以在帧中导航, 刷新, 以及诸如此类。一旦你拥有了一个帧中的 WebBrowser 对象的IWebrowser2 接口指针, 你可以掉用IWebrowser2 接口的任何方法和属性。

VC++中你可以访问一个帧中的WebBrowser. VB中,你可在帧中存取document , 你可以访问WebBrowser 但是不可以访问装载于WebBrowser窗口中的HTML文档对象的 IoleContainer 接口. 存取 IOleContainer 要求访问帧的 WebBrowser 对象. 本节讲述VC++d的标准技术细节,即WebBrowser 控件的宿主能访问在包含的WebBrowser控件装载的web页面的帧窗口的WebBrowser对象模型.

下面的代码展示如何访问web页面的每一个帧的WebBrowser对象以刷新每一帧的内容.其中重要的片断用于用HTML document 对象的IOleContainer::EnumObjects  方法枚举页面中的embeddings(嵌入)对象. 每一个嵌入对象表现为一个控件. 利用IWebBrowser2 接口查询每一个控件对象,此代码可检测到控件是否是一个子帧.如果为获得 IWebBrowser2 而成功调用QueryInterface, 其结果为帧中的WebBrowser对象的引用. (数据成员m_webBrowserCWebBrowser2 类型的—MFC 包装类)

// Get the IDispatch of the document.
//
LPDISPATCH lpDisp = NULL;
lpDisp = m_webBrowser.GetDocument();
 

if (lpDisp)
{
   IOleContainer* pContainer;
 

   // Get the container.
   //
   HRESULT hr = lpDisp->QueryInterface(IID_IOleContainer,
                                       (void**)&pContainer);
   lpDisp->Release();
 

   if (FAILED(hr))
      return hr;
 

   // Get an enumerator for the frames.
   //
   IEnumUnknown* pEnumerator;
 

   hr = pContainer->EnumObjects(OLECONTF_EMBEDDINGS, &pEnumerator);
   pContainer->Release();
 

   if (FAILED(hr))
      return hr;
 

   IUnknown* pUnk;
   ULONG uFetched;
 

   // Enumerate and refresh all the frames.
   //
   for (UINT i = 0; S_OK == pEnumerator->Next(1, &pUnk, &uFetched); i++)
   {
      // QI for IWebBrowser here to see whether we have 
      // an embedded browser.
      IWebBrowser2* pWebBrowser;
 

      hr = pUnk->QueryInterface(IID_IWebBrowser2, (void**)&pWebBrowser);
      pUnk->Release();
 

      if (SUCCEEDED(hr))
      {
         // Refresh the frame.
         pWebBrowser->Refresh();
         pWebBrowser->Release();
      }
   }
 

   pEnumerator->Release();
}

请留意在代码中我们首先通过GetDocument 方法获得了文档的IDispatch 对象指针, 该方法是WebBrowser 包装类的成员指针.然后我们访问文档的IOleContainer 接口. IOleContainer 接口提供了能够枚举页面中全部嵌入对象的功能. 然后我们通过调用IOleContainerEnumObjects 方法得到枚举器(EnumObjects. EnumObjects 返回的 IEnumUnknown 接口的指针可以用于枚举全部嵌入(embeddings)对象. 代码的下一步, 我们遍历全部嵌入对象,查询每一个对象的IWebBrowser2 接口. 如果查询成功, 我们已经获得了帧的IWebBrowser2 接口指针.我们可以调用它的任何方法和属性,在此例中,我们仅仅调用了每一个帧的Refresh.

忠告


ActiveX 控件宿主于一个html页也有类似情。也许,如果你建立一个访问ie WebBrowser 控件或者页面帧中的WebBrowser 的控件,不要将你的控件标记为脚本安全和初始化安全。.

调用 查找, 察看源码, 以及 Internet 选项

浏览WebBrowser 控件的方法和属性, 你可轻易见到控件提供的功能.但是3个可编程由WebBrowser控件提供的项目不容易发觉到:查找对话框, 察看源代码菜单项,以及Internet选项对话框. 如果你曾经用过ie,你无疑很熟悉他们. 查找对话框, 允许你在WEB页中查找文本,通过处理 Ctrl-F 或者选择edit菜单的Find项来调用.

察看源代码菜单项,允许你显示WEB页的html代码, 可通过选择View菜单的Source项来选择或者右击web页(在弹处菜单中)选择察看源代码. Internet 选项对话框如图6-23所示, 可通过选择Tools菜单的Internet Option项来调用.

Figure 6-23. Internet Options dialog box.

能够在你的web应用中提供以上功能的确可以带来对用户的友善性,但是调用途径缺不是通过WebBrowser的方法和属性可以实现. 实际上你应当从调用IOleCommandTargetExec 方法来调用实现于WebBrowser中的以上功能.当调用Exec, 你传递名为 CGID_IWebBrowser 的GUID 作为你想调用的命令的ID.尽管ExecWB 方法是IOleCommandTarget::Exec 方法的包装, 但是你不能够通过ExecWB 来调用 Find, View Source, 或者 Internet Options 对话框,以为ExecWB 不允许你指定命令组GUID. 那意味着该项技术仅能够用于VC++—你不能够直接从VB中调用。.

忠告


本代简要展示你未收入文档的命令组GUID ,那意味着可以将来改编。尽管代码已经在Internet Explorer 3.x, 4.x, 5中测试,,但是不保证在将来的版本中成功运行

以下步骤实现 Find, View Source, 以及Internet Options 命令:

1.    定义WebBrowser 控件的命令组GUID:

DEFINE_GUID(CGID_IWebBrowser,0xED016940L,0xBD5B,0x11cf,0xBA,
         0x4E,0x00,0xC0,0x4F,0xD7,0x08,0x16);

2.       定义用于Find, View Source, Internet OptionsID:

     
     #define HTMLID_FIND 1
     #define HTMLID_VIEWSOURCE 2
     #define HTMLID_OPTIONS 3

3.       在需要的时候执行Find, View Source, Internet Options 命令. 举例来说,你可创建工具方法接收命令ID并调用 IOleCommandTarget::Exec, 如下片断所示. (注意在MFC代码中,m_webBrowser WebBrowser 控件的实例. 同样, nCmdID 是定义的ID)

HRESULT CYourView::ExecCmdTarget(DWORD nCmdID)
{
   LPDISPATCH lpDispatch = NULL;
   LPOLECOMMANDTARGET lpOleCommandTarget = NULL;
   HRESULT hr = E_FAIL;
 

   // Get the IDispatch of the document.
   //
   lpDispatch = m_webBrowser.GetDocument();
   ASSERT(lpDispatch);
 

   if (lpDispatch)
   {
      // Get a pointer for the IOleCommandTarget interface.
      //
      hr = lpDispatch->QueryInterface(IID_IOleCommandTarget,
                                      (void**)&lpOleCommandTarget);
      lpDispatch->Release();
 

      ASSERT(lpOleCommandTarget);
 

      if (SUCCEEDED(hr))
      {
         // Invoke the given command id 
         // for the WebBrowser control.
         hr = lpOleCommandTarget->Exec
              (pguidCmdGroup, nCmdID, 0, NULL, NULL);
         lpOleCommandTarget->Release();
      }
   }
 

   return hr;
}

 

 

 

 

分发WebBrowser 控件

现在你知道了如何利用WebBrowser 控件和IE创建专用应用程序, 你大概感兴趣知道那些IE组件你需要打包在你的应用程序以便你的应用程序可以在系统商正常工作即使没有IE5被安装.无论你宿主 WebBrowser 控件还是自动化Internet Explorer, 你必须至少打包IE最小化安装的组件. 为理解为什么,请再一次察看图6-1展示的体系.你可以看到每一个组件是如何的依赖其他组件—而且这幅图仅仅展示了其表面.它未能展示 webbrowser控件和IE多样特性的组件间的全部特征.因为由如此众多的组件调用,为了你的应用程序能够正常工作,你应当确信至少IE最小化安装于用户的操作系统.

别担心,你不需要打包每一个IE的组件.  Internet Explorer 5 安装程序允许你定制你的安装以使你打包你仅需的组件.另外, 如果你使用Internet Explorer Administration Kit (可在 http://ieak.microsoft.com/找到) 建立你的安装程序, 你可以将IE默认安装,免掉多个确认安装步骤的对话框.

 

 

如何对webbrowser和IE编程(八)收藏

新一篇: 如何对webbrowser和IE编程(九)  | 旧一篇: 关于Cookie

Internet Explorer 事件

 

IWebBrowser2 的属性和方法给了你确切的控制导航和用户接口的途径,但是如果你不能够检测到浏览器正在处理什么以及何时处理什么,你还是没有全面的控制它. 因此,WebBrowser控件和Internet Explorer暴露出事件,通过此你可以必要时监视活动以及处理某些活动. 举例来说,假设你建立一个intranet应用程序, 你想限制用户用户访问某些web页.利用Internet Explorer的时间处理句柄,你可以指令应用程序在用户试图访问受限的URL时候取消导航完成。

 

 

事件 和 引出的接口

无论何时一个COM 对象需要客户应用程序一个事件发生了, COM 对象发送一个叫做事件的消息. 发送消息的处理过程会激发一个事件. 但如果事件没有任何监听者会如何? 事件每次都发生吗? 显然, 客户应用程序监听这些事件并控制COM对象.如果一个客户应用想接收来自COM 对象的事件,它"advises" 实际的COM 对象的.

一个COM 对象为了通客户通信, 对象自身必须支持一个或者多个外引接口.一个 COM 对象支持的外引接口是作为可连接对象引用. 要成为一个可连接对象,COM对象必须实现IConnectionPointContainer 接口。通过此接口,客户可认识到那些外引接口被服务器支持. 外引接口实际通过连接点由客户挂接入COM实现。实现外引接口的客户部分众所周知是通过事件接收槽(event sink)实现的.

单一的连接点由服务器支持每一个外引接口.每一个连接点能够操纵一种类型的外引接口且至少支持IConnectionPoint 接口. 图7-1 描述了可连接的对象和它的客户之间的关系.

Figure 7-1. Connectable object and its client.

每一个服务器必须实现2个接口以便实现客户可以接收事件—IConnectionPointContainerIConnectionPoint. 我们将先看看这些接口之后将检视实现事件的不同途径.

IConnectionPointContainer

每一个可连接对象实现了IConnectionPointContainer. 通过此接口, 试图接收事件的客户可找出关于可连接对象支持的不同的连接点. 通过调用QueryInterface using 客户可以获得服务器支持的任何接口的指针. (你可以使用任何接口指针调用QueryInterface, 因为全部 COM 接口继承自IUnknown.) 之后客户可以使用IConnectionPointContainer 接口的2个方法中的之一获取可连接点。 如表7-1, 获得可连接点

Table 7-1 Methods of the IConnectionPointContainer Interface

方法

描述

EnumConnectionPoints

列举可连接对象支持的全部可连接对象

FindConnectionPoint

让客户查询可连接对象关于它是否支持一个特殊接口。客户指定特殊的接口(可连接点)的接口标示 (IID) 。如果可连接对象支持此接口,返回IConnectionPoint 接口的指针。

IConnectionPoint

一旦客户知道哪个连接点可被连接对象服务器支持, 客户就可建立同可连接对象的连接。客户通知可连接对象将要在全部事件中接收那些事件.当客户不再需要接收来自可连接对象的事件,客户解除对对象的通知. 表7-2 展示了 IConnectionPoint 接口可被客户连接的. (大多数时候,你将连接到仅仅表中头两个)

Table 7-2 Methods of the IConnectionPoint Interface

方法

描述

Advise

在客户和可连接对象的某一个可连接点间建立连接。 客户必须传递它的事件接收槽的IUnknown 接口。 事件接收槽必须实现IDispatch 接口以接收事件。 典型地,党课连接对象激发事件,可连接对象将调用 IDispatch 接口的Invoke 方法。 Advise 方法返回一个 cookie ,当客户中断连接时候,调用Unadvise方法时候需要携带此cookie

Unadvise

中断连接.

GetConnectionInterface

返回由连接点管理的外发接口的IID . GetConnectionInterface 方法让客户将IConnectionPoint翻译为一个IID.

GetConnectionPointContainer

得到刻连接对象的IConnectionPointContainer 接口

EnumConnections

枚举刻连接对象的当前可连接点.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

接收事件的途径

依靠开发工具你创建客户应用程序,你可以接收事件通过不同的途径. 显然, 在Vb中接收事件同在VC中接收事件相比是如此不同和容易.在 C++ 应用中,你可以用不同的技术,通过使用 ATL, MFC, 或者标准C++.

Visual Basic 中接收事件

Visual Basic是创建大多数类型应用的最轻松的工具, 所以我告诉你VB是处理事件最溶的工具时也不要惊奇. ATL 和 Visual Basic 示例我们同样的工作,但是ATL花费了我4个小时, 而 Visual Basic 例子仅仅只花20 分钟.别说我错了—我是ATL, 和 MFC, C++的忠实信徒,  尤其是你建立一个接口的时候.但是 Visual Basic当建立客户应用程序从类似IE这样的服务器接收事件时是伟大的工具.

OK, 如何从Visual Basic 应用程序中接收事件?当宿主WebBrowser 控件,你不必做任何特别的事. Visual Basic 在form上为WebBrowser 控件接收事件.你所需要做的全部事情就是未你要接收的任何事件创建一个事件处理句柄.

你象创建其他事件句柄一样创建句柄 (例如Form_Load event). 从Procedure下拉列表框中选择你象控制的句柄, 在事件句柄中,加入任何你型在事件激发时执行的任何代码.

当自动化服务器时候接收事件, 例如在VB应用中的Internet Explorer,过程直截了当.首先设置对服务器的类型库的引用, 你可以访问Project/References 菜单.之后,采用WithEvents 关键字声明服务器对象的变量.举例, 如果你自动化Internet Explorer, 你将声明变量如下:

Dim WithEvents InternetExplorer1 As InternetExplorer

下一步,采用new或者其他 关键字创建实例变量 ,如下::

Set InternetExplorer1 = CreateObject("InternetExplorer.Application.1")

或者:

Set InternetExplorer1 = New InternetExplorer

当你采用以上途径生成实例接收事件, Visual Basic 自动为你初始化和管理事件接收.你不必担心连接点问题,VB为你处理它们.

在你输入建立服务器的代码之后,你插入符合服务器事件的方法调用.   举例来说, 如果你想控制由IE激活的DownloadBegin event, 你应当声明类似如下的方法声明:

Private Sub InternetExplorer1_DownloadBegin()
   ' Insert your best Visual Basic code here.
End Sub

当你不再想接收来自服务器的事件,简单设置变量为Nothing:

Set InternetExplorer1 = Nothing

发表于 @ 2005年07月24日 16:28:00|评论(1 )|编辑

新一篇: 如何对webbrowser和IE编程(九)  | 旧一篇: 关于Cookie

评论

#Philp 发表于2006-07-31 18:01:00  IP: 59.107.17.*
您好,
我通过search窗口找到某个浏览元件的Internet Exploer_Server的Handle, 我通过

(*pfObjectFromLresult)( lRes, IID_IHTMLDocument, 0, (void**)&spDoc );

成功取得IHTMLDocument2的接口。但这个Internet Exploer_Servere的上层不是Shell Embedding,所以无法通过IHTMLDocument2.QueryInterface来取得IWebBrowser2接口。请问,在这种情况下怎样查询browser是否空闲呢?怎样使browser在完成文档读入时告知程序?

谢谢

如何对webbrowser和IE编程(九) 收藏

新一篇: 关于WEB2.0的思考 | 旧一篇: 如何对webbrowser和IE编程(八)

接收事件的途径

依靠开发工具你创建客户应用程序,你可以接收事件通过不同的途径. 显然, 在Vb中接收事件同在VC中接收事件相比是如此不同和容易.在 C++ 应用中,你可以用不同的技术,通过使用 ATL, MFC, 或者标准C++.

Visual Basic 中接收事件

Visual Basic是创建大多数类型应用的最轻松的工具, 所以我告诉你VB是处理事件最溶的工具时也不要惊奇. ATL 和 Visual Basic 示例我们同样的工作,但是ATL花费了我4个小时, 而 Visual Basic 例子仅仅只花20 分钟.别说我错了—我是ATL, 和 MFC, C++的忠实信徒,  尤其是你建立一个接口的时候.但是 Visual Basic当建立客户应用程序从类似IE这样的服务器接收事件时是伟大的工具.

OK, 如何从Visual Basic 应用程序中接收事件?当宿主WebBrowser 控件,你不必做任何特别的事. Visual Basic 在form上为WebBrowser 控件接收事件.你所需要做的全部事情就是未你要接收的任何事件创建一个事件处理句柄.

你象创建其他事件句柄一样创建句柄 (例如Form_Load event). 从Procedure下拉列表框中选择你象控制的句柄, 在事件句柄中,加入任何你型在事件激发时执行的任何代码.

当自动化服务器时候接收事件, 例如在VB应用中的Internet Explorer,过程直截了当.首先设置对服务器的类型库的引用, 你可以访问Project/References 菜单.之后,采用WithEvents 关键字声明服务器对象的变量.举例, 如果你自动化Internet Explorer, 你将声明变量如下:

Dim WithEvents InternetExplorer1 As InternetExplorer

下一步,采用new或者其他 关键字创建实例变量 ,如下::

Set InternetExplorer1 = CreateObject("InternetExplorer.Application.1")

或者:

Set InternetExplorer1 = New InternetExplorer

当你采用以上途径生成实例接收事件, Visual Basic 自动为你初始化和管理事件接收.你不必担心连接点问题,VB为你处理它们.

在你输入建立服务器的代码之后,你插入符合服务器事件的方法调用.   举例来说, 如果你想控制由IE激活的DownloadBegin event, 你应当声明类似如下的方法声明:

Private Sub InternetExplorer1_DownloadBegin()
   ' Insert your best Visual Basic code here.
End Sub

当你不再想接收来自服务器的事件,简单设置变量为Nothing:

Set InternetExplorer1 = Nothing

C++中接收事件

C++ 应用程序中接收事件比Vb中多一些工作.但如果你在MFC对话框程序中宿主过WebBrowser控件, 你可以在classwizard中选择你想控制的事件.使用C++的其他应用程序宿主WebBrowser 或者自动化Internet Explorer 需要多一点的工作,但是仍然不需要更多的工作.在C++客户接收事件,仅仅需要以下5个步骤:

1.    获取连接点容器的指针 (IConnectionPointContainer).

2.       调用IconnectionPointContainer 的方法 FindConnectionPoint 找出你想接收的事件。对 Internet Explorer来讲, 你应当为DWebBrowserEvents2 连接点接口实现事件. (作为可选, 你可以调用 EnumConnectionPoints 以枚举服务器支持的全部连接点)

3.       实现接入你想接收事件的连接点的通报(Advise)。 当实现通告时,传递一个事件接收槽的Iunknown接口的指针。 记住,事件接收槽必须实现 IDispatch 接口以接收来自WebBrowser的事件。 Advise 方法将返回一个cookie ,该Cookie在你调用Unadvise 方法的时候携带上。

4.       实现 IDispatch::Invoke 以控制任何激发的事件。. (开发工具如 MFC ATL 能够容易为你做到.)

5.       当你不再接受事件,调用Unadvise, 并且传递cookie.

以上步骤如果采用VB和MFC /ATL等可能不很明显,但是当你采用标准C++创建应用程序的时候就应当很明显了.

以下 C++ 代码允许你在自动化IE的时候接收事件. 留意注释代码实现了哪一个步骤. 假定当你想连接事件时ConnectEvents 方法被调用,且当应用程序退出时候Exit 方法被调用. 同样的,类 CSomeClass 继承自IDispatch, m_pIE 数据成员为通过CoCreateInstance 方法创建的IE的实例

void CSomeClass::ConnectEvents()
{
   IConnectionPointContainer* pCPContainer;
 

   // Step 1: 获取连接点的指针.
   //
   HRESULT hr = m_pIE->QueryInterface(IID_IConnectionPointContainer, 
                                      (void**)&pCPContainer);
   if (SUCCEEDED(hr))
   {
      // m_pConnectionPoint is defined like this:
      // IConnectionPoint* m_pConnectionPoint;
 

      // Step 2: 选找连接点.
      //
      hr = pCPContainer->FindConnectionPoint(DIID_DWebBrowserEvents2, 
                                             &m_pConnectionPoint);
 

      if (SUCCEEDED(hr))
      {
         // Step 3: 实现连接点地事件接收
         //
         hr = m_pConnectionPoint->Advise(this, &m_dwCookie);
         if (FAILED(hr))
         {
            ::MessageBox(NULL, "Failed to Advise",
                         "C++ Event Sink", MB_OK);
         }
      }
 

      pCPContainer->Release();
   }
}
 

void CSomeClass::Exit()
{
   // Step 5: Unadvise. 注意m_pConnectionPoint 应当在CSomeClass的析构函数中释放
   //
   if (m_pConnectionPoint)
   {
      HRESULT hr = m_pConnectionPoint->Unadvise(m_dwCookie);
      if (FAILED(hr))
      {
         ::MessageBox(NULL, "Failed to Unadvise",
                      "C++ Event Sink", MB_OK);
      }
   }
}

注意此处少了step4:客户端的 IDispatch::Invoke 方法实现. 我将很快讨论此点. 每一次服务器激发事件会调用此. 当事件被激发,服务器传递事件的DISPID 到Invoke. 对于 Internet Explorer 5, 以下DISPIDs 定义于ExDispID.h 头文件.

·         DISPID_BEFORENAVIGATE2

  • DISPID_COMMANDSTATECHANGE
  • DISPID_DOCUMENTCOMPLETE
  • DISPID_DOWNLOADBEGIN
  • DISPID_DOWNLOADCOMPLETE
  • DISPID_NAVIGATECOMPLETE2
  • DISPID_NEWWINDOW2
  • DISPID_ONFULLSCREEN
  • DISPID_ONMENUBAR
  • DISPID_ONQUIT
  • DISPID_ONSTATUSBAR
  • DISPID_ONTHEATERMODE
  • DISPID_ONTOOLBAR
  • DISPID_ONVISIBLE
  • DISPID_PROGRESSCHANGE
  • DISPID_PROPERTYCHANGE
  • DISPID_STATUSTEXTCHANGE
  • DISPID_TITLECHANGE

现在我们返回讨论Invoke. 该方法有8个参数, 但我们将仅仅讨论其中的两个: dispidMemberpDispParams. (其余的参见MSDN中的IDispatch::Invoke.)

dispidMember 参数将告诉你哪一个事件被激发.如果客户应用程序接收来自Internet Explorer的事件, dispidMember 参数的值应当是DISPIDs 列表中的某个.

pDispParams 输入参数是指向容器结构的指针, 存储事件激发时的其他项. 传递到事件句柄的参数存储在pDispParams->rgvarg ,逆序存放. 举例来说, Internet Explorer 激发NavigateComplete2 事件如下所示:

NavigateComplete2(pDisp, URL)

Invoke 被调用, pDispParams->cArgs 将包含两个值, URL 参数在 pDispParams->rgvarg[0] 以及pDisp 参数存储在 pDispParams->rgvarg[1]. 这些就是COM次序传递参数给Invoke 方法的方式.

以下为 NavigateComplete2 事件的处理.注意采用ATL的CComVariant 处理从 VARIANTBSTR包装.

#include <strstrea.h>
STDMETHODIMP CSomeClass::Invoke(DISPID dispidMember,
                                REFIID riid,
                                LCID lcid, 
                                WORD wFlags,
                                DISPPARAMS* pDispParams,
                                VARIANT* pvarResult,
                                EXCEPINFO*  pExcepInfo,
                                UINT* puArgErr)
{
   USES_CONVERSION;
   strstream strEventInfo;
 

   if (!pDispParams)
      return E_INVALIDARG;
 

   switch (dispidMember)
   {
      // The parameters for this DISPID:
      // [0]: URL navigated to - VT_BYREF|VT_VARIANT
      // [1]: An object that evaluates to the top-level or frame
      //      WebBrowser object corresponding to the event. 
      //
      case DISPID_NAVIGATECOMPLETE2:
         // Check the argument's type.
         if (pDispParams->rgvarg[0].vt == (VT_BYREF|VT_VARIANT))
         {
            CComVariant varURL(*pDispParams->rgvarg[0].pvarVal);
            varURL.ChangeType(VT_BSTR);
 

            // strEventInfo is an object of type strstream.
            //
            strEventInfo << "NavigateComplete2: "
                         << OLE2T(vtURL.bstrVal)
                         << ends;
 

            ::MessageBox(NULL, strEventInfo.str(), "Invoke", MB_OK);
         }
         break;    
 

      default:
         break;
   }
 

   return S_OK;
}

ATL中接收事件

连同实现了缺省的COM 接口实现, ATL提供了两个函数—AtlAdviseAtlUnadvise—使得任何课连接对象的事件接收简单化.

AtlAdvise 函数告诉一个可连接对象客户想从此可连接对象接收事件.该函数封装实现接收事件的步骤1到3. AtlAdvise 理所当然省了大量的时间.就像IConnectionPoint::Advise 方法, AtlAdvise 返回一个cookie供你稍后调用 AtlUnadvise. AtlUnadvise 告诉可连接对象客户不再接收事件.

让我们行说吧, 举个例子, ATL应用程序自动化Internet Explorer, 所以你想知道任何IE激发的事件. 为了告知Internet Explorer客户想接收事件,发出对AtlAdvise的以下调用:

HRESULT hr = AtlAdvise(m_spInetExplorer, GetUnknown(),
                       DIID_DWebBrowserEvents2, &m_dwCookie);

四个参数传递给AtlAdvise. 第一个参数是指向可连接对象的IUnknown 接口的指针. m_spInetExplorer 数据成员是一个经过我们自动化当前运行的Internet Explorer实例的指针. 因为m_spInetExplorer 指向的对象直接或者间接继承自IUnknown, 编译器自动转换m_spInetExplorer 为当前运行的 IE实例的IUnknown 接口指针.

AtlAdvise 第二个参数必须指向提供事件的对象的IUnknown 接口. GetUnknown 函数返回此接口.记住,提供事件的类必须通过某种途径实现 IDispatch in.在此例子中,该类继承自 IDispatch.

第三个参数为你象接收的事件的IID, Internet Explorer 事件的可连接对象的IIS是 DIID_DWebBrowserEvents2.

最后一个参数指向DWORD的指针,该DWORD接收返回的Cookie. 该 cookie 将用于调用 AtlUnadvise.

客户必须实现 IDispatch::Invoke 以控制Internet Explorer 激发的事件. 当你的应用程序完成从IE接收事件, 只需要调用 call AtlUnadvise, 如下:

HRESULT hr = AtlUnadvise(m_spInetExplorer,
                         DIID_DWebBrowserEvents2, 
                         m_dwCookie);

Figure 7-3. ATLIEEvtSpy.

以下展示如何自动化IE:

hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, 
                      IID_IWebBrowser2, (void**)&m_spInetExplorer);
if (SUCCEEDED(hr))
{
   m_spInetExplorer->put_Visible(TRUE);
   m_spInetExplorer->GoHome();        
 

接下来, AtlAdvise 调用以接收事件, 如下:

hr = AtlAdvise(m_spInetExplorer, GetUnknown(),
               DIID_DWebBrowserEvents2, &m_dwCookie);

CIEEvtObj 类继承自IDispatch, 所以 CIEEvtObj 类可以作为事件接收对象. Invoke 实现控制事件. 每当Internet Explorer 激发一个事件, 在listBox中显示一个消息.以西为invoke的代码:

STDMETHODIMP CIEEvtObj::Invoke(DISPID dispidMember, 
                               REFIID riid, 
                               LCID lcid, 
                               WORD wFlags, 
                               DISPPARAMS* pDispParams, 
                               VARIANT* pvarResult,
                               EXCEPINFO* pExcepInfo,  
                               UINT* puArgErr)
{
   _ASSERT(m_spInetExplorer);
 

   USES_CONVERSION;
   strstream strEventInfo;
 

   if (!pDispParams)
      return E_INVALIDARG;
 

   switch (dispidMember)
   {
      //
      // The parameters for this DISPID are as follows:
      // [0]: Cancel flag  - VT_BYREF|VT_BOOL
      // [1]: HTTP headers - VT_BYREF|VT_VARIANT
      // [2]: Address of HTTP POST data  - VT_BYREF|VT_VARIANT 
      // [3]: Target frame name - VT_BYREF|VT_VARIANT 
      // [4]: Option flags - VT_BYREF|VT_VARIANT
      // [5]: URL to navigate to - VT_BYREF|VT_VARIANT
      // [6]: An object that evaluates to the top-level or frame
      //      WebBrowser object corresponding to the event 
      //
      case DISPID_BEFORENAVIGATE2:
         strEventInfo << "BeforeNavigate2: ";
 

         if (pDispParams->cArgs >= 5
            && pDispParams->rgvarg[5].vt == (VT_BYREF|VT_VARIANT))
         {
            CComVariant vtURL(*pDispParams->rgvarg[5].pvarVal);
            vtURL.ChangeType(VT_BSTR);
 

            strEventInfo << OLE2T(vtURL.bstrVal);
         }
         else
            strEventInfo << "NULL";
 

         strEventInfo << ends;
         break;
 

      //
      // The parameters for this DISPID:
      // [0]: Enabled state - VT_BOOL
      // [1]: Command identifier - VT_I4
      //
      case DISPID_COMMANDSTATECHANGE:
         strEventInfo << "CommandStateChange: ";
 

         if (pDispParams->cArgs == 0)
            strEventInfo << "NULL";
         else
         {
            if (pDispParams->cArgs > 1 
               && pDispParams->rgvarg[1].vt == VT_I4)
            {
               strEventInfo << "Command = " 
                            << pDispParams->rgvarg[1].lVal;
            }
 

            if (pDispParams->rgvarg[0].vt == VT_BOOL)
            {
               strEventInfo << ", Enabled = "
                      << ((pDispParams->rgvarg[0].boolVal == VARIANT_TRUE) 
                           ? "True" : "False");
            }
         }
 

         strEventInfo << ends;
         break;
 

      case DISPID_DOCUMENTCOMPLETE:
         strEventInfo << "DocumentComplete" << ends;
         break;
 

      case DISPID_DOWNLOADBEGIN:
         strEventInfo << "DownloadBegin" << ends;
         break;
 

      case DISPID_DOWNLOADCOMPLETE:
         strEventInfo << "DownloadComplete" << ends;
         break;
 

      //
      // The parameters for this DISPID:
      // [0]: URL navigated to - VT_BYREF|VT_VARIANT
      // [1]: An object that evaluates to the top-level or frame
      //      WebBrowser object corresponding to the event 
      //
      case DISPID_NAVIGATECOMPLETE2:
         if (pDispParams->rgvarg[0].vt == (VT_BYREF|VT_VARIANT))
         {
            CComVariant vtURL(*pDispParams->rgvarg[0].pvarVal);
            vtURL.ChangeType(VT_BSTR);
  
            strEventInfo << "NavigateComplete2: "
                         << OLE2T(vtURL.bstrVal)
                         << ends;
         }
         break;
 

      //
      // The parameters for this DISPID:
      // [0]: Maximum progress - VT_I4
      // [1]: Amount of total progress - VT_I4
      //
      case DISPID_PROGRESSCHANGE:
         strEventInfo << "ProgressChange: ";
 

         if (pDispParams->cArgs == 0)
            strEventInfo << "NULL";
         else
         {
            if (pDispParams->cArgs > 1 
               && pDispParams->rgvarg[1].vt == VT_I4)
            {
               strEventInfo << "Progress = " 
                            << pDispParams->rgvarg[1].lVal;
            }
 

            if (pDispParams->rgvarg[0].vt == VT_I4)
               strEventInfo << ", ProgressMax = " 
                            << pDispParams->rgvarg[0].lVal;
         }
 

         strEventInfo << ends;
         break;
 

      //
      // The parameter for this DISPID:
      // [0]: Name of property that changed - VT_BSTR
      //
      case DISPID_PROPERTYCHANGE:
         strEventInfo << "PropertyChange: ";
 

         if (pDispParams->cArgs > 0 
            && pDispParams->rgvarg[0].vt == VT_BSTR)
         {
            strEventInfo << OLE2T(pDispParams->rgvarg[0].bstrVal);
         }
         else
         {
            strEventInfo << "NULL";
         }
 

         strEventInfo << ends;
         break;
 

      //
      // The parameters for this DISPID:
      // [0]: New status bar text - VT_BSTR
      //
      case DISPID_STATUSTEXTCHANGE:
         LPOLESTR lpStatusText;
 

         m_spInetExplorer->get_StatusText(&lpStatusText);
         strEventInfo << "StatusTextChange: ";
 

         if (!strcmp(OLE2T(lpStatusText), ""))
            strEventInfo << "NULL";
         else
            strEventInfo << OLE2T(lpStatusText);
 

         strEventInfo << ends;
         break;
 

      case DISPID_NEWWINDOW2:
         strEventInfo << "NewWindow2" << ends;
         break;
 

      //
      // The parameter for this DISPID:
      // [0]: Document title - VT_BSTR
      //
      case DISPID_TITLECHANGE:
         strEventInfo << "TitleChange: ";
 

         if (pDispParams->cArgs > 0 
            && pDispParams->rgvarg[0].vt == VT_BSTR)
         {
            strEventInfo << OLE2T(pDispParams->rgvarg[0].bstrVal);
         }
         else
         {
            strEventInfo << "NULL";
         }
 

         strEventInfo << ends;
         break;
 

      // The user has told Internet Explorer to close.
      //
      case DISPID_ONQUIT:
         return Stop();
 

      default:
         // Note: This class acts only as an event sink, so
         // there's no reason to call the base class version of Invoke.
 

         strEventInfo << "Unknown Event" << dispidMember << ends;
         break;
   }
 

   AddEventToList(strEventInfo.str());
 

   return S_OK;
}

请注意此使用了标准C++ 库的 strstream 类来建立字符串.这么做是因为ATL 不提供像Cstring的类. 每一次从IE接收到事件,建立一个包含事件的名称和参数的字符串. 然后显示在列表框中.

退出时候调用AtlUnadvise:

STDMETHODIMP CIEEvtObj::Stop()
{
   if (m_spInetExplorer)
   {
      HRESULT hr = AtlUnadvise(m_spInetExplorer, 
                               DIID_DWebBrowserEvents2, 
                               m_dwCookie);
 

      if (FAILED(hr))
         ATLTRACE("Failed to Unadvise/n");
   }
 

   PostQuitMessage(0);
   return S_OK;
}

MFC中接收事件

MFC提供了数个宏使得你可以接收从自动化的对象或者宿主的控件的事件。在两种情况中, 接收事件的类必须直接或者间接继承自CCmdTarget. CCmdTarget 实现接收事件的IDispatch 接口. 另外, 你必须在你的应用中调用EnableAutomation 初始化包含在CCmdTarget 中的IDispatch.

MFC中自动化一个COM 对象时接收事件

mfc中接收事件很容易.全部要做的就是在代码中调用AfxConnectionAdvise 函数以通告连接点客户需要接收事件.当客户不许要接收事件,调用AfxConnectionUnadvise.  AfxConnectionAdvise AfxConnectionUnadvise 函数定义于afxctl.h 头文件。

AfxConnectionAdvise 函数查询连接点容器, 寻找可连接点,并且通告连接点. 函数的5个参数如下:

Table 7-3 Parameters of the AfxConnectionAdvise Function

Parameter

Description

pUnkSrc

指向激发事件的com对象的IUnknown 接口的指针. pUnkSrc 是由CoCreateInstance.建立的对象的指针

pUnkSink

指向事件接收的 IUnknown 接口

iid

连接点的IID. 例如对IE来说,是DIID_DWebBrowserEvents2.

bRefCount

传递 TRUE 表示建立连接点将导致pUnkSink 的引用将增加。FALSE 表示不会增加.

pdwCookie

表示此连接。由AfxConnectionAdvise 将传递给 AfxConnectionUnadvise

 

处理事件也很容易。记住MFC事件接收类必须继承自CCmdTarget. CCmdTarget 使用派遣映射检测当接收到事件时调用处理函数.你必须首先在头文件中声明派遣映射 然后再实现文件中 (.cpp) 实现. 幸运地, MFC提供了宏来帮助声明和处理派遣映射。.

为了定义派遣映射, 首先在声明接收事件类的头文件中简单定义DECLARE_DISPATCH_MAP. 这些宏声明派遣映射和CCmdTarget 访问的函数. 一旦你定义了派遣映射,你应当在实现文件中实现宏. 第一个宏放在BEGIN_DISPATCH_MAP 宏.它指定事件接收类的基础类.举例来说,如果事件类是CEventSink 继承自 CCmdTarget, BEGIN_DISPATCH_MAP 将看起来如下:

BEGIN_DISPATCH_MAP(CEventSink, CCmdTarget)

接下来用DISP_FUNCTION_ID来声明派遣ID。此宏的六个参数:

Table 7-4 Parameters of the DISP_FUNCTION_ID Macro

Parameter

Description

theClass

事件类的名称

szExternalName

函数的名字.

dispid

事件的DISPID

pfnMember

指向处理事件的成员函数.

vtRetval

成员函数的返回值类型,是VARENUM 的每局类型,定义于wtypes.h 头文件

vtsParams

空格分隔的参数类型的列表.

假设你想控制DownloadComplete 事件. 告诉CCmdTarget 你将控制处理DownloadComplete, 如下使用:

DISP_FUNCTION_ID(CIE5Events, "DownloadComplete",
                 DISPID_DOWNLOADCOMPLETE, OnDownloadComplete,
                 VT_EMPTY, VTS_NONE)

最终采用 END_DISPATCH_MAP宏关闭.完整如下:

BEGIN_DISPATCH_MAP(CEventSink, CCmdTarget)
   DISP_FUNCTION_ID(CIE5Events, "DownloadComplete",
                    DISPID_DOWNLOADCOMPLETE, OnDownloadComplete,
                    VT_EMPTY, VTS_NONE)
END_DISPATCH_MAP()

MFC中寄宿 ActiveX 控件时处理事件

这类似于处理COM对象的事件.主要区别在于你不需要通告或者解除通告连接点. CCmdTarget 未你控制了他.

在寄宿一个Activex控件情形中, CCmdTarget 使用事件接收宏代理派遣宏.就像你猜想的一样, MFC 提供初始化事件接收通告映射. 声明此宏类似声明派遣宏—派 DECLARE_EVENTSINK_MAP 宏存放在头文件中.另外的声明映射,  DECLARE_EVENTSINK_MAP 声明 CCmdTarget 访问映射的类

接下来在类中实现事件接收.开始于EGIN_EVENTSINK_MAP 宏.指定事件接收类的 和它的基类。举例,此处为实例:

BEGIN_EVENTSINK_MAP(CMyDlg, CDialog)

现在实用ON_EVENT*宏来处理是按接收.。大多数情形,你将使用带有5个参数的ON_EVENT.携带的参数如下:

ON_EVENT(CMyDlg, IDC_WEBBROWSER, DISPID_DOWNLOADCOMPLETE, 
         OnDownloadComplete, VTS_NONE)

如果你象多个成员函数处理此事件, 使用ON _EVENT_RANGE宏.

Table 7-5 Parameters of the ON_EVENT Macro

Parameter

Description

theClass

在那个类中接收事件

id

控件的资源ID

dispid

有控件激活的事件的 ID.

pfnHandler

事件的成员函数,用来处理事件句柄. 此函数应当有BOOL来型的返回值以及匹配事件的参数。当事件函数被处理则返回TRUE

vtsParams

 VTS_ constants 的类型

你引刚才用 END_EVENTSINK_MAP 宏.完整的定义如下:

BEGIN_EVENTSINK_MAP(CMyDlg, CDialog)
   ON_EVENT(CMyDlg, IDC_WEBBROWSER, DISPID_DOWNLOADCOMPLETE, 
            OnDownloadComplete, VTS_NONE)
END_EVENTSINK_MAP()

 

对于 DocumentComplete 事件,你应当如下声明:

// Declare the event sink map.  This declaration goes
// in the class declaration of CMFCIEEvtSpyDlg in the 
// MFCIEEvtSpyDlg.h header file.
//
DECLARE_EVENTSINK_MAP()
 

// Initialize the event sink map.  These macros
// go in the implementation file _ MFCIEEvtSpyDlg.cpp.
//
BEGIN_EVENTSINK_MAP(CMFCIEEvtSpyDlg, CDialog)
   ON_EVENT(CMFCIEEvtSpyDlg, IDC_WEBBROWSER, DISPID_DOCUMENTCOMPLETE,
            OnDocumentComplete, VTS_DISPATCH VTS_PVARIANT)
END_EVENTSINK_MAP()

Figure 7-4. MFCIEEvtSpy.

WebBrowser 控件基于对话框应用,你通常不需要插入默认的宏, 因为 ClassWizard 可为你做这一切.而在SDI或者MDI工程中,需要加上此宏。

现在事件接收映射已经声明, 每当WebBrowser 控件激发了DocumentComplete 事件, OnDocumentComplete 方法将被调用.在CMFCIEEvtSpyDlgOnDocumentComplete 方法中, 包含URL和事件名称的字符串被创建。之后字符串加入到列表框中展示WebBrowser 控件的事件.

以下代码解释如何接收处理DocumentComplete 事件.:

void CMFCIEEvtSpyDlg::OnDocumentComplete(LPDISPATCH pDisp, VARIANT* URL)
{
   USES_CONVERSION;
 

   CString strEvt("DocumentComplete: ");
   strEvt += OLE2T(URL->bstrVal);
 

   AddEventToList(WBListBox, strEvt);
}

当启动后,采用CoCreateInstance 创建的ie实例传递LSID_InternetExplorer接口..

以下为代码:

void CMFCIEEvtSpyDlg::OnStartIE() 
{
   if (m_pInetExplorer == NULL)  // Can start only one instance
   {
      // Create an instance of Internet Explorer. 
      //
      HRESULT hr = CoCreateInstance(CLSID_InternetExplorer,
                                    NULL,
                                    CLSCTX_LOCAL_SERVER, 
                                    IID_IWebBrowser2, 
                                    (void**)&m_pInetExplorer);
      if (SUCCEEDED(hr))
      {
         // Set up the event sink.
         //
         BOOL bAdvised = AfxConnectionAdvise(m_pInetExplorer, 
                                DIID_DWebBrowserEvents2,
                                m_pIE5Events->GetInterface(&IID_IUnknown),
                                TRUE, &m_dwCookie);
 

         // Disable the Start IE5 button so that the 
         // user knows that only one instance of 
         // Internet Explorer can be started at a time.
         //
         m_btnStartIE.EnableWindow(FALSE);
 

         // Make Internet Explorer visible and go home.
         //
         m_pInetExplorer->put_Visible(VARIANT_TRUE);
         m_pInetExplorer->GoHome();
      }
   }
}

为接收Internet Explorer 的事件,你应当声明派遣接口且在实现文中:

// Declare the dispatch map. This
// declaration is placed in the class declaration
// for the CIE5Events class, which is in the
// CIE5Events.h header file.
//
DECLARE_DISPATCH_MAP()
 

// Initialize the dispatch map in the
// implementation file for CIE5Events _ CIE5Events.cpp.
//
BEGIN_DISPATCH_MAP(CIE5Events, CCmdTarget)
   DISP_FUNCTION_ID(CIE5Events, "DocumentComplete", 
                    DISPID_DOCUMENTCOMPLETE, OnDocumentComplete, 
                    VT_EMPTY, VTS_DISPATCH VTS_PVARIANT)
END_DISPATCH_MAP()

现在无论如何接收到的自动化 Internet Explorer 的事件 DocumentComplete, OnDocumentComplete方法将被调用. OnDocumentComplete 方法创建一个包含事件名称和URL的字符串,且加入到列表框通告Internet Explorer事件发生.同样期它事件发生也会如此处理.此处为CIE5Events 类的OnDocumentComplete 方法代码:

void CIE5Events::OnDocumentComplete(LPDISPATCH pDisp, VARIANT* URL)
{
   USES_CONVERSION;
 

   CString strEvt("DocumentComplete: ");
   strEvt += OLE2T(URL->bstrVal);
 

   m_pParent->AddEventToList(CMFCIEEvtSpyDlg::IE5ListBox, strEvt);
}

 

 

 

 

 

 

 

 

 

发表于 @ 2005年07月31日 08:56:00|评论(6 )|编辑

新一篇: 关于WEB2.0的思考 | 旧一篇: 如何对webbrowser和IE编程(八)

评论

#TNT 发表于2005-08-04 23:37:00  IP: 61.186.252.*
这个需要我后面翻译的关于事件编程,请耐心等候。
#windcbf 发表于2005-08-02 21:17:00  IP: 61.186.252.*
为什么没有看到

如何对webbrowser和IE编程(一) 和 如何对webbrowser和IE编程(二) ?

是不是贴在其他地方了?
#TNT 发表于2005-08-02 23:01:00  IP: 61.186.252.*
在http://blog.csdn.net/shanhe/archive/2005/06/18/397373.aspx

http://blog.csdn.net/shanhe/archive/2005/06/18/398041.aspx
#wisent 发表于2005-08-04 19:51:00  IP: 61.186.252.*
您好,
我看了您的“如何对webbrowser和IE编程”的文章,对我很有用,谢谢。我现在是在一个对话框里面加了一个webbrowser控件进行浏览网页,但是点击了一个链接以后,会打开一个IE的窗口,我只想在webbrowser控件里面浏览网页,请问怎样才能解决这个问题,麻烦您指点一下,非常感谢!
#xiang 发表于2005-08-24 10:46:00  IP: 211.100.4.*
您好:
请问有没有完整的程序例子可以参考?谢谢!!
#lovemylove 发表于2005-11-17 14:25:00  IP: 219.144.166.*
拜读了你的关于IE编程的大作,十分钦佩,我现在正在用BHO开发一个客户端的网页修改程序,我已经实现了网页图片的替换,在对FLASH替换的时候发现替换不了,请问有什么办法能替换掉FLASH?如能指教,不胜感激!

 

 

如何对webbrowser和IE编程(十)收藏

新一篇: 一个哑巴(雅巴)的诞生! | 旧一篇: 说说百度和百度的股疯

Internet Explorer 5激发事件

如你所知, Internet Explorer像其他COM对象一样激发事件—通过连接点.但实际上Internet Explorer如何激发事件呢?每次 Internet Explorer需要向客户提供关于当前活动状态的信息, Internet Explorer 激发通过DWebBrowserEvents2 连接点激发一个事件. (之前到版本 4, Internet Explorer 通过 DWebBrowserEvents 接口激发事件.但到了版本4.x和5, Internet Explorer 通过 DWebBrowserEvents2 连接点.)

注意


如何领会到Internet Explorer加法那些事件?最佳途径是MSDN Online Web Workshop. 另外采用 OLE-COM Object Viewer

不像WebBrowser 控件 和 Internet Explorer的其他接口,是剑接口没有继承体系. DWebBrowserEvents 接口严格应用于Internet Explorer 3. 如果你正宿主WebBrowser 控件或者自动化Internet Explorer 5, 你可以通过此接口接收事件—但是不可挂接. DWebBrowserEvents2 接口包含的方法是为Internet Explorer 5定制的.用 DWebBrowserEvents2 替代DWebBrowserEvents ,你将有更多的控制能力. 所以不要忘记接口的最后面的2.

尽管DWebBrowserEvents2 是一个事件接口, 它其实就是像IWebBrowser2  一样的另外 COM 接口,所以它可以包含方法. (D 开头的命名是表示这是派遣接口.一个派遣接口是一个IDispatch 接口.但不同于普通接口, 派遣接口没有vtable.) 就像一个接口一样,派遣接口只不过提供一些函数的定义—他们并不真实实现.事件的实现由客户提供.举例来说,为了让WebBrowser 激发一个事件, 在DWebBrowserEvents2 接口中适当定义一些方法.这些方法由客户实现.但WebBrowser不直接调用这些方法.换句话讲, WebBrowser 并不调用DocumentComplete 方法.因为 DWebBrowserEvents2 是派遣接口, WebBrowser 通过IDispatch::Invoke调用客户的实现. 早期, 当WebBrowser 调用客户的Invoke 实现, WebBrowser 传递事件被激发的DISPID.

注意


某些工具如Visual Basic, MFC, ATL 提供Invoke实现

7-6 列出 WebBrowser 事件. (这些是WebBrowser 控件和Internet Explorer供有的事件,尽管有些事件仅仅用于自动化Internet Explorer).

注意


尽管有些方法为不包含2.尽管DWebBrowserEvents2 并非继承自WebBrowserEvents, DWebBrowserEvents2 还是匹配 DWebBrowserEvents 中被更改的,以免混淆。

注意表 7-6 重的参数有些值同样有 VARIANT_ TRUE VARIANT_FALSE. 如果你使用Visual C++,确信分派使用这些值不要使用 TRUE FALSE. 如果你使用Visual Basic, 它会自动帮助你转换,你可以比较True和False.

现在来仔细看看这些事件

Table 7-6 WebBrowser 事件

私有事件

描述

BeforeNavigate2

在导航之前发生. (该事件并不在不刷新页面的时候发生)

CommandStateChange

当命令状态改变时发生.该事件告诉你何时使能或者禁止 Back 以及Forward 菜单像或者按钮.

DocumentComplete

当整个文档完全完成装载时发生.如果你刷新页面, 此事件并不激发.

DownloadBegin

当一个下载项目开始时候发生 ,此事件也在你刷新(IWebBrowser2::Refresh.)时发生

DownloadComplete

党整个下载项目完成是发生该事件也发生在完成刷新页面.

NavigateComplete2

当整个导航完成. 该事件对应于 BeforeNavigate2.

NewWindow2

在一个新的窗口被创建以显示Web页或者其他资源的时候发生。譬如你在页面中以新建窗口的方式打开一个连接

OnFullScreen

FullScreen 属性被改变时候发生.此事件携带一个VARIANT_ BOOL类型的输入参数指示Internet Explorer 是否处于全屏(full-screen 模式 (VARIANT_TRUE) 或者处于普通模式(VARIANT_FALSE).

OnMenuBar

菜单条MenuBar 属性被改变的时候发生. 一个VARIANT_ BOOL类型输入参数指  Internet Explorer的菜单条属性是可见(VARIANT_TRUE) 或者隐藏 (VARIANT_ FALSE).

OnQuit

Internet Explorer正在退出时发生. 该事件当用户关闭浏览器或者调用 Quit 方法.

OnStatusBar

StatusBar 属性被改变的时候发生。事件携带VARIANT_ BOOL类型的输入参数 指示Internet Explorer的状态条是否可见 (VARIANT_TRUE) 或者隐藏 (VARIANT_FALSE).

OnTheaterMode

TheaterMode 属性被改变时发生. 事件携带VARIANT_ BOOL类型的输入参数 指示Internet Explorer的状态条是否可见 (VARIANT_TRUE) 或者隐藏 (VARIANT_FALSE).

OnToolBar

ToolBar属性被改变时发生. 事件携带VARIANT_ BOOL类型的输入参数 指示Internet Explorer的状态条是否可见 (VARIANT_TRUE) 或者隐藏 (VARIANT_FALSE).

OnVisible

WebBrowser将被显示或者隐藏时发生。. 事件携带VARIANT_ BOOL类型的输入参数 指示Internet Explorer的状态条是否可见 (VARIANT_TRUE) 或者隐藏 (VARIANT_FALSE).

ProgressChange

当下载进度被更新时候发生

PropertyChange

当属性改变时候发生。典型的, PutProperty 方法被调用时

StatusTextChange

Internet Explorer WebBrowser 控件改变状态条时候发生。即使webbrowser控件没有状态条。 StatusTextChange 给客户一个改变状态条的机会

TitleChange

.当文档对象的title可用或者改变的时候发生

BeforeNavigate2

BeforeNavigate2 就像字面上所说. 当Internet Explorer 导航到一个WEB页是激发; 因此, 当用户输入一个 URL, 点击 Back 或者 Forward 按钮, 或者处理一个导航时都会发生. BeforeNavigate2  也在WebBrowser 控件导航类方法调用时发生, 例如 Navigate, Navigate2, GoHome, 或者 GoSearch. 也许, 该事件不会在你刷新页面时发生. 如果页面上有帧, BeforeNavigate2 将像顶级窗口一样被激发. BeforeNavigate2 由7个输入参数, 见7-7.

Table 7-7 Input Parameters of the BeforeNavigate2 Event

Parameter

Description

pDisp

将发生导航的顶级窗口或者帧的Idispatch 接口的地址

URL

将导航至的URL

Flags

保留

TargetFrameName

显示资源的窗口或者帧的名字字符串,或者为NULL(如果没有命名)

PostData

HTTP POST 传输的数据地址

Headers

增加的将要发给服务器的 HTTP . 一般HTTP头指定其它的服务器要求。传送给服务器的数据类型、状态马等

Cancel

cancel 标志的地址. 设置为TRUE可取消导航

注意打所属参数匹配于Navigate 或者 Navigate2的调用参数.如果 BeforeNavigate2 由一个或者多个导航类调用激发,这些导航类的方法参数 传递到BeforeNavigate2 方法.

在事件的句柄函数中, 你可以使用Cancel 参数取消导航, 或者你可以是用pDisp 参数修改导航目的.设置Cancel 参数为 VARIANT_TRUE 可以取消导航,如果你想,你可以通过pDisp修改参数导航信息且导航到另外的位置.举例来说,如果我们向停止当前导航, 增加一些头信息,且导航到原先的URL.在Visual Basic, 我们可以如下代码实现:

Private Sub WebBrowser1_BeforeNavigate2(ByVal pDisp As Object, _
                                        URL As Variant, _
                                        Flags As Variant, _
                                        TargetFrameName As Variant, _
                                        PostData As Variant, _
                                        Headers As Variant, _
                                        Cancel As Boolean)
 

   If TypeName(pDisp) = "WebBrowser" And Headers = "" Then
      pDisp.Stop
      pDisp.Navigate URL, Flags, TargetFrameName, PostData, _
                     Headers + "MyHeaders"
      Cancel = True
   End If
End Sub

我们需要注意以上代码的几个重点.

首先你必须检查pDisp 的类型使之确定为WebBrowser. 当页面包含帧, pDisp 可能不是 WebBrowser 对象由此一些导航会导致错误.

其次你必须检查确信Headers 参数是空字符串以避免无限循环.记住BeforeNavigate2 每一次导航都会发生.因此如果你在BeforeNavigate2事件处理中调用Navigate, 另外一次 BeforeNavigate2 将被激活. 在前面的代码中, 仅仅在Headers 参数为空才调用Navigate 避免了无限循环. 如果Headers 参数是空, Navigate 将携带非空的 Headers 参数. 下一次BeforeNavigate2 北激活, Headers 将不为空; 此时,我们千万不可再次Navigate a,从而导致一个无限循环.

第三点你必须调用pDisp Stop 方法.如果你没做到这点,"about:NavigationCanceled" Web 页将会载你首次取消掉导航时被显示.

CommandStateChange

CommandStateChange 是当Internet Explorer 想通知一个应用程序WebBrowser 命令状态已经改变时激发.当检测到Forward和Back 菜单项和按钮禁止或者使能时激发此事件.

CommandStateChange 事件有2个参数, CommandEnable。 Command 输入参数将要改变状态的按钮的表示符,可以取值—CSC_NAVIGATEFORWARDCSC_NAVIGATEBACK分别表示是Forward按钮项和Back项,每次导航发生, CommandStateChange 事件发生并告诉你Forward 或者 Back 菜单项以及按钮将使能或禁止. 举例来说, 如果没有Web页在当前导航后发生, Command 的值应该为 CSC_NAVIGATEFORWARD, 并且Enable 参数将等于VARIANT_FALSE.

第二个参数, Enable,如果命令可用(使能) 则为VARIANT_TRUE,如果禁止则值为VARIANT_FALSE.

 

为说明问题,我们看看代码。一下代码为定义事件接收的宏声明:

 

// Event sink map declaration for WebBrowser 
// control events. This declaration goes in the
// header file for CMfcWebHostView _ MfcWebHostView.h.
//
DECLARE_EVENTSINK_MAP()
 

// Initialize the event sink map and handle the 
// CommandStateChange event.
BEGIN_EVENTSINK_MAP(CMfcWebHostView, CView)
   ON_EVENT(CMFCIEEvtSpyDlg, IDC_WEBBROWSER, DISPID_COMMANDSTATECHANGE,
            OnCommandStateChange, VTS_I4 VTS_BOOL)
END_EVENTSINK_MAP()

重要的一点是要通过WebBrowser控件的Create 方法来创建. ON_EVENT的第二个参数为你宿主的WebBrowser控件的IID.之前的例子中是NULL. 你必须为webbrowser控件声明一个ID 且用此 ID 创建实例.如果不这么做,事件将不会正确工作.

你可以在工程的任何文件定义此ID. (推荐在资源头文件resource.h.) 因为 最大的资源Id是32,779, 所以你可以如下定义WebBrowser 控件的ID:

#define IDC_WEBBROWSER  35000

此数高于resource.h中的任何资源ID, 因此可以确信ID 数字不会同采用ClassWizard添加的ID冲突.现在可以采用使用ID的 Create 方法来创建WebBrowser控件:

if (!m_webBrowser.Create(NULL, WS_CHILD|WS_VISIBLE,
                         CRect(), this, IDC_WEBBROWSER))
{
   return -1;
}

下一步声明OnCommandStateChange 方法,该方法将在WebBrowser控件激发CommandStateChange 事件时被调用.可声明 如下:

void OnCommandStateChange(long lCommand, BOOL bEnable);

OnCommandStateChange 方法的是现代码中, 设定表示Go Forward 或者 Go Back 想得导航菜单项的数据成员为使能或者禁止. 该书据成员将被UPDATE_COMMAND_UI 句柄使用。以下为OnCommandStateChange 方法实现:

void CMfcWebHostView::OnCommandStateChange(long lCommand, BOOL bEnable)
{
   switch(lCommand)
   {
      // Forward command
      //
 

      case CSC_NAVIGATEFORWARD:
         m_fForwardEnabled = bEnable;
         break;
 

      // Back command
      //
      case CSC_NAVIGATEBACK:
         m_fBackEnabled = bEnable;
         break;
 

      default:
         break;
   }
}

声明m_fForwardEnabledm_fBackEnabled 数据变量为保护成员,类型为BOOL.同样在构造函数中谁的些数据成员为TRUE。.

现在当Go Forward和Go Back的菜单 UPDATE_COMMAND_UI被处理, 你可以直接进行设置. 下为示例代码:

void CMfcWebHostView::OnUpdateNavigateGoForward(CCmdUI* pCmdUI) 
{
   pCmdUI->Enable(m_fForwardEnabled);
}
 

void CMfcWebHostView::OnUpdateNavigateGoBack(CCmdUI* pCmdUI) 
{
   pCmdUI->Enable(m_fBackEnabled);
}

DocumentComplete

当一个文档完整的完成下载Internet Explorer 激发DocumentComplete 事件. 仅仅当此事件激发后 文档对象才可安全使用.在一个无帧的Web页情形中文档对象是IHTMLDocument2 对象, 我们以后会讨论. 当文档对象准备好可用,他的状态为READYSTATE_COMPLETE.

关于 DocumentComplete 事件以西击点需要注意:

·         在没有帧的web页, DocumentComplete 事件在下载完成后激发一次.

  • 在多帧的web页,此事件激发多次.并非每一个帧激发一个事件, 但每一个帧激发DownloadBegin事件将会相应激发DocumentComplete 事件.
  • DocumentComplete又一个指向 IDispatch 的指针参数, 该参数指向激发此事件的窗口. 此窗口可以是帧中的窗口
  • 顶级帧在所有子帧激发了各自的DocumentComplete事件后激发自己的 DocumentComplete事件。 因此,,要看一个web页是否完整下载完成, 你需要从该事件的处理句柄中获取由事件产地过来的IDispatch 参数的IUnknown 接口。下一步,比较IUnknown 接口是否指向你正宿主的WebBrowser控件或者自动化的IE的实例的IUnknown 接口.如果这两个指针相同,这意味着全部HTML, 图片images, 控件,以及诸如此类在顶级帧或者子帧的全部对象元素都被下载了.

 

VB中实现以上四点及其容易.仅需要检查发送给事件的pDisp 参数事一个WebBrowser 对象. Visual Basic小心检查这些对象的 Iunknown否为同一个对象.此处为VB代码::

Private Sub WebBrowser1_DocumentComplete(ByVal pDisp As Object,
                                         URL As Variant)
   If (pDisp Is WebBrowser1.Object) Then
      MsgBox "The document is finished loading."
   End If
End Sub

实现以上四点在Visual C++ 应用程序里较困难一点,但你可以做到! 首先在DocumentComplete 事件的宏中如下声明:

ON_EVENT(CMfcWebHostView, IDC_WEBBROWSER, DISPID_DOCUMENTCOMPLETE,
         OnDocumentComplete, VTS_DISPATCH VTS_PVARIANT)

接下来声明OnDocumentComplete 方法作为事件处理句柄

void OnDocumentComplete(LPDISPATCH lpDispatch, VARIANT FAR* URL);

最后,实现该方法以检测 是否页面已经下载,我们得到我们宿主控制bBrowser 控件的IUnknown. (注意我们不是简单获取指向 IUnknown ,而是要调用GetControlUnknown 方法. GetControlUnknown方法返回的IUnknown 指针 实际上并不等于被宿主话的 WebBrowser控件的IUnknown. 那将返回IOleObject 接口指针.) 下一步, 获取IUnknown 指针,如果QueryInterface 查询得到的Dispatch 参数同Iunknown接口是同一对象,则页面完成整个下载。.

void CMfcWebHostView::OnDocumentComplete(LPDISPATCH lpDispatch,
                                         VARIANT FAR* URL)
{
   HRESULT   hr;
   LPUNKNOWN lpUnknown;
   LPUNKNOWN lpUnknownWB = NULL;
   LPUNKNOWN lpUnknownDC = NULL;
 

   lpUnknown = m_webBrowser.GetControlUnknown();
   ASSERT(lpUnknown);
 

   if (lpUnknown)
   {
      // Get the pointer to the IUnknown interface of the WebBrowser 
      // control being hosted. The pointer to the IUnknown returned from 
      // GetControlUnknown is not the pointer to the IUnknown of the 
      // WebBrowser control. It's actually a pointer to the IOleObject.
      // 
      hr = lpUnknown->QueryInterface(IID_IUnknown,
                                     (LPVOID*)&lpUnknownWB);
 

      ASSERT(SUCCEEDED(hr));
 

      if (FAILED(hr))
         return;
 

      // Get the pointer to the IUnknown of the object that fired this 
      // event.
      //
      hr = lpDispatch->QueryInterface(IID_IUnknown, 
                                      (LPVOID*)&lpUnknownDC);
 

      ASSERT(SUCCEEDED(hr));
 

      if (SUCCEEDED(hr) && lpUnknownWB == lpUnknownDC)
      {
         // The document has finished loading.
         //
         MessageBox("The document has finished loading.");
      }
 

      if (lpUnknownWB)
         lpUnknownWB->Release();
 

      if (lpUnknownDC)
         lpUnknownDC->Release();
   }
}

有一点需要注意上面的代码我们在GetControlUnknown 返回的IUnknown 接口指针使用时并没有进行Release ,因为b IUnknown 指针并没有在GetControlUnknown方法中 AddRef'. GetControlUnknown 方法仅仅返回一个IOleObject 数据成员的指针,该指针由控件站点类—CcontrolSite 操纵处理. 如果你释放了IUnknown 接口指针, 载你关闭应用程序时,一个访问违例将会发生,因为MFC 将试图在对象被删除时候多释放一次.

DownloadBegin

DownloadBegin 事件通知应用程序一个导航操作开始. 一般情况下该事件在BeforeNavigate2 事件之后激发, 除非导航操作在BeforeNavigate2 事件处理过程中被取消.容器应当显示动画或者忙指示当前正处于连接的DownloadBegin事件. 每一个 DownloadBegin 事件有一个相应的DownloadComplete 事件. 在刷新页面的情形中, DownloadBegin DownloadComplete 使唯一的被激发的导航事件.

DownloadComplete

DownloadComplete在一个导航操作完成时候发生, 停止, 或者失败. 不像 NavigateComplete2仅仅当成功导航才发生, DownloadComplete总是在道涵开始后激发.任何在DownloadBegin 中显示的动画或者忙指示将会在DownloadComplete 中停止.

NavigateComplete2

NavigateComplete2 事件在导航到一个超连接整个窗口或者帧集合的元素全部完成时候发生. 第一此事件发生表示文档document已经准备好.在此事件发生后, 你可以通过Document 属性存取文档(document)而不接收到错误.但是能够访问一个文档不意味着你访问文档使安全的.你可以在DocumentComplete 事件激发后安全访问文档.

档你需要访问document对象但是不需要访问文档内的元素,你可以在NavigateComplete2 事件中尽可能快的处理,例如当你在文打工通过高级宿主接口. NavigateComplete2 事件有2个参数—IDispatch of 代表激发事件的对象URL 为你需要导航到的URL.

NewWindow2

NewWindow2 档用户显示一个新窗口以进行新导航显示web页或者其他资源时发生.在WebBrowser控件响应柄进行预处理 (举例来说, 在响应window.open 方法).

NewWindow2 也在Navigate 或者 Navigate2 方法被调用且navOpenInNewWindow 标志被设定时发生. 档采用文件菜单中的New Window按钮时并不发生(Internet Explorer帧不是一个 HTML 帧; 它是帧窗口.) 因此, WebBrowser 对象不知道什么时候新窗口将被打开. 因为 NewWindow2 有时候很难使用, 所以我们来检查它的两个参数: ppDisp and Cancel.

ppDisp 参数是接口指针, 一般是接收新WebBrowser 或者 InternetExplorer 对象的IDispatch 接口指针, 是你能够创建一个Internet Explorer新实例以便能够控制来自你的应用程序导航产生的新窗口. 该实例开始为新建的, 隐藏的, (暂时)不可导航WebBrowser 或者 InternetExplorer 对象. 在NewWindow2事件句柄函数返回之前, InternetExplorer 对象激发NewWindow2 事件将配置新WebBrowser对象的导航目标位置.

另外参数, Cancel, 时取消(Cancel)标志的地址. 应用程序能够设定此参数为TRUE 以取消导航操作或者设定为FALSE 以允许新建窗口操作. 设定CancelTRUE 完全取消新建窗口操作和导航.

如果你不在NewWindow2 事件处理过程中作任何事, 新的 InternetExplorer 对象将自动建立. 一些原因你想控制NewWindow2 事件以便控制新建InternetExplorer 对象. 为什么? 因为你想限制Internet Explorer的实例数量,或者你想控制创建的实例的事件.

以下 NewWindow2 事件控制函数中; 建立了一个新的, 隐藏的, 不可导航的Internet Explorer实例; 并且设定ppDisp 参数指向新实例.如果你想,你可以加入任何接收新实例事件的代码.

void CMyEvtSink::NewWindow2(LPDISPATCH* ppDisp, BOOL* Cancel)
{
   // Note that m_pIE is a class member of type IWebBrowser2*.
   HRESULT hr = CoCreateInstance(CLSID_InternetExplorer, NULL, 
                                 CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, 
                                 (void**)&m_pIE);
   if (hr == S_OK)
      *ppDisp = (IDispatch*)pIE;
 

   // Do not set Cancel to TRUE. If you do,
   // the navigation will be completely canceled.
}

另外的原因控制NewWindow2 事件是由于你想你应用程序在用户选择在新窗口打开一个url时进行控制. 如果你不进行控制NewWindow2 事件, Internet Explorer 新实例将被创建.

以下为控制新建窗口的vb代码:

Private Sub WebBrowser1_NewWindow2(ppDisp As Object, Cancel As Boolean)
   Dim frmWB As Form1
   Set frmWB = New Form1
 

   Set ppDisp = frmWB.WebBrowser1.Object
   frmWB.Visible = True
 

   Set frmWB = Nothing
End Sub

在此NewWindow2事件代码中,档一个新常口需要被创建, 我们建立一个新的当前窗体Form1的拷贝. 在此表单窗体, 相当于Internet Explorer的新实例,将处理导航.

mfc中我们需要首先加入NewWindow2 事件的映射条目到视图类的事件映射宏. (不要忘记包含 ExDispID.h in, 那里有DISPID_NEWWINDOW2 定义.)

ON_EVENT(CMfcWebHostView, IDC_WEBBROWSER, DISPID_NEWWINDOW2,
         OnNewWindow2, VTS_PDISPATCH VTS_PBOOL)

下一步声明OnNewWindow2 方法:

void OnNewWindow2(LPDISPATCH* ppDisp, BOOL* Cancel);

最后实现OnNewWindow2 方法以创建一个新的MfcWebHost窗口实例:

void CMfcWebHostView::OnNewWindow2(LPDISPATCH FAR* ppDisp,
                                   BOOL FAR* Cancel)
{
   // Ensure that ppDisp is not NULL.
   // If it is NULL, you probably specified
   // VT_DISPATCH for the first parameter in 
   // the ON_EVENT macro for NewWindow2 in 
   // the event sink map. The correct parameter
   // type is VT_PDISPATCH.
   //
   ASSERT(ppDisp);
   if (!ppDisp)
      return;
 

   // Get a pointer to the application object
   // for this application.
   //
   CWinApp* pApp = AfxGetApp();
 

   // Get the correct document template.
   //
   CDocTemplate* pDocTemplate;
   POSITION pos = pApp->GetFirstDocTemplatePosition();
   pDocTemplate = pApp->GetNextDocTemplate(pos);
 

   ASSERT(pDocTemplate);
 

   // Create the new frame.
   CFrameWnd* pNewFrame = pDocTemplate->CreateNewFrame(GetDocument(),
                                            (CFrameWnd*)AfxGetMainWnd());
   ASSERT(pNewFrame);
 

   // Activate the frame, and set its active view.
   //
   pDocTemplate->InitialUpdateFrame(pNewFrame, NULL);
 

   CMfcWebHostView* pWbView = 
                    (CMfcWebHostView*)pNewFrame->GetActiveView();
   
   ASSERT(pWbView);
 

   *ppDisp = pWbView->m_webBrowser.GetApplication();
}

如果你在sid或者mdi应用程序中控制一个WebBrowser控件,实现OnNewWindow2 方法是复杂的且需要知道如何解决同文档模版如何工作. 或许, 如果你在一个给予对话框的应用程序控制一个WebBrowser控件是较为容易的.此处为示例:

void CMyDlg::OnNewWindow2(LPDISPATCH FAR* ppDisp, BOOL FAR* Cancel) 
{
   m_dlgNewWB = new CMyDlg;
   m_dlgNewWB->Create(IDD_MYDLG_DIALOG);
 

   *ppDisp = m_dlgNewWB->m_webBrowser.GetApplication();
}

记住当你完成打开的新对话框后删除(delete) m_dlgNewWB. 且不要在CMyDlg::OnInitDialog方法中导航, 因为这样代码将不会工作.

                 

 

ProgressChange

ProgressChange 事件通告你的应用程序下在操作状态已经更新. ProgressChange 有两个参数:

·         Progress. 总计有多少进度将被展示, 如果为-1 表示整个进度已经完成

  • ProgressMax.最大进度值

容器可通过此事件显示下载进度。

 

事件发生序列

下图展示了IE的事件发生序列.但这仅仅为不包含帧的普通网页浏览. (没有包含诸如 ProgressChange, CommandStateChange, OnToolBar, 等等事件.)不是所有事件都会被激发. 但是 BeforeNavigate2DocumentComplete 每次浏览都会被激发.

 

  

Figure 7-5. The sequence of events fired by the WebBrowser control during a typical navigation.

发表于 @ 2005年08月11日 22:28:00|评论(2 )|编辑

新一篇: 一个哑巴(雅巴)的诞生! | 旧一篇: 说说百度和百度的股疯

评论

#Huolima 发表于2005-08-27 21:03:00  IP: 211.100.4.*
楼主你实在太棒了!我辈不通英文的初级编程者向你致敬!
#bluesky 发表于2005-12-15 13:58:00  IP: 210.22.83.*
我正在尝试对ie,及WebBrowser进行编程,想实现在同一个窗口打开连接,也就是禁止在新窗口打开,希望一个WebBrowser实例可以hold所有的访问,希望在NewWindow2事件中对ppDisp进行一些设置实现
请楼主大大帮忙解决一下
0 0

相关博文

我的热门文章

img
取 消
img