【转】用系统默认浏览器打开Microsoft Web Browser控件触发的超链接

Microsoft Web Browser ActiveX 控件是一个基于IE内核的浏览器控件,利用它您可以在您的应用程序界面中方面地嵌入微型的浏览器。通过实现各种接口,您甚至可以利用它搭建起类似Maxthon那样强大的浏览器。当然,作为一般的简单应用,利用 MFC ActiveX 控件类生成向导生成的 CWebBrowser类已经足够用了。

  最近工作中有一个奇怪的需求:在用户点击 Microsoft Web Browser 控件中的超链接后,用用户机器上默认的浏览器(不一定是IE)打开那个超链接。我们都知道,默认情况下,从 Microsoft Web Browser 控件中点击一个超链接后,IE会在本窗口中载入超链接指向的网页或者在新的IE窗口/Tab页中打开这个链接(这取决于用户的配置和HTML中a元素的target参数),所以,要实现上述的需求,那么就必须做两件事情:

1、如果是指定在当前窗口中载入新内容,那么就要阻止这次载入,然后根据要载入的URL另行在默认浏览器中打开相应的内容

2、如果是指定在新窗口/Tab页中载入,那么要阻止这次打开,然后根据载入的URL另行在默认浏览器中打开相应的内容

  现在问题是如何取得即将被载入或打开的URL呢?

  Microsoft Web Browser 控件在发生一些行为的时候,会向它所在的顶层父窗口发送一些事件消息。父窗口通过监听这些事件可以知道 Microsoft Web Browser 控件即将干什么、已经干了什么。DISPID_BEFORENAVIGATE2 和 DISPID_NEWWINDOW2 这两个事件对于本文要实现的需求是很有用的。前者表示控件即将载入网页,后者表示控件即将打开新窗口。

  对于上述要处理的情况1,我们只要简单监听 DISPID_BEFORENAVIGATE2 就可以解决,因为控件会告诉我们它要载入的URL:

// 这个是为 CMyHTMLWnd 类中的 IDC_MYWEBCTRL 控件添加 MFC 消息映射

BEGIN_EVENTSINK_MAP(CMyHTMLWnd, CWnd)

ON_EVENT(CMyHTMLWnd, IDC_MYWEBCTRL, DISPID_BEFORENAVIGATE2, OnBeforeNavigate2, VTS_DISPATCH TS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PVARIANT VTS_PBOOL)

END_EVENTSINK_MAP() 

// 这个是 DISPID_BEFORENAVIGATE2 处理函数的定义形式

void OnBeforeNavigate2(LPDISPATCH pDisp, VARIANT FAR* URL, VARIANT FAR* Flags, VARIANT FAR* TargetFrameName, VARIANT FAR* PostData, VARIANT FAR* Headers, BOOL FAR* Cancel);

  注意到 OnBeforeNavigate2 函数中的 URL 参数就是要载入的 URL。而改 Cancel 参数指向的内容设置 TRUE 能让我们顺利撤销本次载入。

  那么情况二如何处理呢?

  我们监听 DISPID_NEWWINDOW2 事件,但是发现,这个事件只允许我们撤销本次打开或者指定在哪个 IDispatch 实例(您可以简单认为是那个浏览器控件)中打开这个 URL,而没有告诉我们这个要打开的 URL 是什么。

ON_EVENT(CMyHTMLWnd, IDC_MYWEBCTRL, DISPID_NEWWINDOW2, OnNewWindow2, VTS_PDISPATCH VTS_PBOOL)

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

  程序员总是聪明的,在聪明的程序员面前一切困难都是纸老虎  

  查看前辈们留下的代码,发现他们是这样处理的:在 DISPID_NEWWINDOW2 发生的时候,设置一个标记 Flag,然后将本次打开重定向到另一个隐藏的 Microsoft Web Browser 控件中,然后在那个控件的 DISPID_BEFORENAVIGATE2 事件处理函数中查看 Flag,如果 Flag 被打开,那么就表示这是一次打开窗口的操作,撤销之,同时也获得了 URL。由于不存在多线程 UI 的问题,这些事件的发生都是串行的,所以这个方法是正确的。

  但是,我觉得这个方法虽然达到了目的,看起来却很恶心。于是,我就试图寻求另外的方法。查看了微软的文档之后,我终于找到了一种相对更加优秀的方法。我的方法是这样的:实现 IDocHostUIHandler::TranslateUrl 接口,通过这个接口保存正在被处理的网页的 URL,同时监听DISPID_NEWWINDOW2 事件,一旦 DISPID_NEWWINDOW2 事件发生,那么通过 Cancel 参数阻止这个事件,另外,由于 DISPID_NEWWINDOW2 事件发生前,TranslateUrl 肯定会被先调用,那么可以认为现在保存着的 URL 就是这个要打开的窗口的 URL 了,因为同样,这些事件的发生都是串行的。

  另:通过查看微软的文档,其实还有一个非常好的事件 DISPID_NEWWINDOW3。这个事件带回来的信息非常全面,其中就包括了我们渴求的 URL,但是遗憾的是,这个事件要 Windows XP SP2 以上的系统才支持的。

转自:http://jgshining.me/blog/2008/06/open_url_with_default_browser/

posted @ 2011-09-02 12:11  iThinking  阅读(2109)  评论(0编辑  收藏