IWebBrowser2:如何强制链接在新窗口中打开?

时间:2010-05-27 21:54:59

标签: c++ internet-explorer com iwebbrowser2

WebBrowser Customization上的MSDN文档说明了如何防止打开新窗口以及如何取消导航。在我的情况下,我的应用程序托管IWebBrowser2但我不希望用户导航到我的应用程序中的新页面。相反,我想在新的IE窗口中打开所有链接。所需的行为是:用户单击一个链接,然后打开一个带有该URL的新窗口。

一个类似的问题被问及并回答here而不是污染了回答的帖子,建议我开一个新的讨论。

相关帖子的成员建议我应该通过捕获DISPID_BEFORENAVIGATE2,设置取消标志,编写代码打开一个新窗口来做到这一点,但我发现浏览器控件得到了批次似乎是由主页上的脚本启动的BeforeNavigate2事件的。例如,amazon.com像疯狂一样触发BeforeNavigate2事件,并且它们不是链接调用的结果。

回复赞赏!

5 个答案:

答案 0 :(得分:4)

很抱歉在这里回答我自己的问题,但希望这对其他人有帮助,我最终做的是直接使用IHTMLDocument而不是IWebBrowser。 IWebBrowser是IHTMLDocument的超集,IWebBrowser实现的导航模型不能按照我想要的程度进行自定义。

我实际上参与了MS开发人员支持,这种方法是他们的建议。他们说这就是Outlook用于基于HTML的电子邮件,这是我想要模仿的用户体验。他们还证实,没有可靠的方法来过滤用户操作产生的OnBeforeNavigate事件,这些事件来自脚本活动。

希望这可以帮助任何面临同样问题的人。移植代码以使用IHTMLDocument并不太难。如果您最终这样做,您可能还会发现自己正在寻找一种方法来确定文档何时完成加载。为此,请挂钩HTMLDocumentEvents而不是DWebBrowserEvents,并查找DISPID_HTMLDOCUMENTEVENTS_ONREADYSTATECHANGE事件。它没有告诉你准备状态是什么;你需要调用IHTMLDocument :: get_readyState并解析生成的字符串。高飞,但你去了。

答案 1 :(得分:2)

我在这里假设,但另一种方法可能是维持导航事件的计数,在DISPID_BEFORENAVIGATE2上递增计数器,并在出现DISPID_NAVIGATECOMPLETE2DISPID_NAVIGATEERROR时递减计数器。有了这个,你可以推测,只要你得到DISPID_BEFORENAVIGATE2并且你的计数器为零,它就是实际的用户导航/链接调用。

我不知道这种方法是否有效,或者这些方法是否是您需要的正确事件,但值得研究。

答案 2 :(得分:1)

您可以尝试使用其他方法,并物理添加属性target="_blank" 到渲染文档中的所有<a>标记。

此方法将涉及等待DISPID_DOCUMENTCOMPLETE,然后使用IHTMLDocument3::getElementsByTagName()获取所有锚元素。然后,您可以使用IHTMLElement::setAttribute()为每个人设置target="_blank"

答案 3 :(得分:0)

在我看来,你想要“在新的IE窗口中打开所有链接”,这意味着你想要在另一个进程中打开新窗口。最简单的方法:使用CreateObject("InternetExplorer.Application")方式(查看解决问题的另一个问题,与您的问题相反:InternetExplorer.Application object and cookie container)。通过这种方式,您将从应用程序中获得最佳隔离,并且点击链接的用户将获得IE中存在的所有可能性。您应该继续使用BeforeNavigate2事件来找出应该打开“新IE窗口”的时刻。

答案 4 :(得分:0)

您可以在文档完成之前绑定到onclick事件,同时使用IHTMLDocument2 :: put_onclick()在OnCreate()中创建浏览器:

#include <comutil.h>

ClickEvents<RootFrame> clickEvents;
_variant_t clickDispatch;
clickDispatch.vt = VT_DISPATCH;
clickDispatch.pdispVal = &clickEvents;

CComQIPtr<IDispatch> dispatch;
hr = webBrowser2->get_Document(&dispatch);
ASSERT_EXIT(SUCCEEDED(hr), "webBrowser->get_Document(&dispatch)");

CComQIPtr<IHTMLDocument2> htmlDocument2;
hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");

htmlDocument2->put_onclick(clickDispatch);

ClickEvents类实现IDispatch,你只需要实现Invoke方法,在休息时返回E_NOTIMPL:

HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
    DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
{
    HRESULT hr;

    CComQIPtr<IWebBrowser2> webBrowser2;
    hr = rootFrame->GetDlgControl(rootFrame->rootview.GetDlgCtrlID(), IID_IWebBrowser2, (void**) &webBrowser2);
    ASSERT_EXIT(SUCCEEDED(hr), "rootframe->GetDlgControl(IID_IWebBrowser2) failed");

    CComQIPtr<IDispatch> dispatch;
    hr = webBrowser2->get_Document(&dispatch);
    ASSERT_EXIT(SUCCEEDED(hr), "webBrowser2->get_Document(&dispatch)");

    CComQIPtr<IHTMLDocument2> htmlDocument2;
    hr = dispatch->QueryInterface(IID_IHTMLDocument2, (void**) &htmlDocument2);
    ASSERT_EXIT(SUCCEEDED(hr), "dispatch->QueryInterface(&htmlDocument2)");

    CComQIPtr<IHTMLWindow2> htmlWindow2;
    hr = htmlDocument2->get_parentWindow((IHTMLWindow2**) &htmlWindow2);
    ASSERT_EXIT(SUCCEEDED(hr), "htmlDocument2->get_parentWindow(&htmlWindow2)");

    CComQIPtr<IHTMLEventObj> htmlEvent;
    hr = htmlWindow2->get_event(&htmlEvent);
    ASSERT_EXIT(SUCCEEDED(hr), "htmlWindow2->get_event(&htmlEvent)");

    CComQIPtr<IHTMLElement> htmlElement;
    hr = htmlEvent->get_srcElement(&htmlElement);
    ASSERT_EXIT(SUCCEEDED(hr), "htmlEvent->get_srcElement(&htmlElement)");

    CComBSTR hrefAttr(L"href");
    VARIANT attrValue;
    VariantInit(&attrValue);
    hr = htmlElement->getAttribute(hrefAttr, 0 | 2, &attrValue); // 0 = case insensitive, 2 = return BSTR
    ASSERT_EXIT(SUCCEEDED(hr), "htmlElement->getAttribute()");

    wchar_t href[2084]; // maximum url length in IE, http://support.microsoft.com/kb/208427
    wcsncpy_s(href, _countof(href), attrValue.bstrVal, _TRUNCATE);

    if (!rootFrame->IsURLAllowed(href)) {

        VARIANT variant;
        variant.vt = VT_BOOL;
        variant.boolVal = VARIANT_FALSE;
        htmlEvent->put_returnValue(variant);

        ShellExecute(0, L"open", href, 0, 0, SW_SHOWNORMAL);
    }

    return S_OK;
}

正如您在查询某些界面后所看到的那样,我终于得到了被点击的元素,然后我调用我的根框架中定义的IsURLAllowed()来检查是否允许在当前的webbrowser窗口中打开url或是否使用默认浏览器打开它在用户的计算机上。

即使它们使用javascript附加到文档,也会处理所有链接。

对于表单的“onsubmit”事件也应该这样做。

我也认为我在javascript中有一个“window.location”重定向的解决方案,我还没有测试过,但我很快就会测试它,然后我会更新这个答案。您可以结合使用“onunload”和“onbeforeunload”事件以及DWebBrowserEvents2 :: BeforeNavigate2(),在调用onunload / onbeforeunload之后,您将知道用户正在离开当前页面,所以现在在BeforeNavigate2()中可以取消它。您可以使用IHTMLWindow2 :: put_onunload()和IHTMLWindow2 :: put_onbeforeunload()附加卸载事件。

请参阅下面“onclick”的完整解决方案的来源。

BrowserFrame中的AttachClickEvents:

http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/browser_frame.h?r=709d00b991b5#125

在ClickEvents(IDispatch)中调用:

http://code.google.com/p/phpdesktop/source/browse/phpdesktop-msie/msie/click_events.h?r=a5b0b350c933#132