WebBrowser Customization上的MSDN文档说明了如何防止打开新窗口以及如何取消导航。在我的情况下,我的应用程序托管IWebBrowser2但我不希望用户导航到我的应用程序中的新页面。相反,我想在新的IE窗口中打开所有链接。所需的行为是:用户单击一个链接,然后打开一个带有该URL的新窗口。
一个类似的问题被问及并回答here而不是污染了回答的帖子,建议我开一个新的讨论。
相关帖子的成员建议我应该通过捕获DISPID_BEFORENAVIGATE2,设置取消标志,编写代码打开一个新窗口来做到这一点,但我发现浏览器控件得到了批次似乎是由主页上的脚本启动的BeforeNavigate2事件的。例如,amazon.com像疯狂一样触发BeforeNavigate2事件,并且它们不是链接调用的结果。
回复赞赏!
答案 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_NAVIGATECOMPLETE2
和DISPID_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:
在ClickEvents(IDispatch)中调用: