23.WebBrowser 高级特性调整

前面讲解了IE控件的常用方法,本节主要讨论如何调整WebBrowser 高级特性来完成我们想要的效果。需要注意的是严格来说,其实这里调整的都是WebBrowser控件的包容器(Host)窗口的特性

1.调整IE控件显示样式

最常见的就是禁用右键菜单及禁用滚动条和3D边缘。

这些都是包容器的特征,因此我们需要做的就是调整它的对应属性即可,有两种方法:

1.主动设置

	//设置浏览器窗口显示选项
	CComPtr<IAxWinAmbientDispatch> spHost;
	HRESULT hRet = wndIE.QueryHost(IID_IAxWinAmbientDispatch, (void**)&spHost);
	if(SUCCEEDED(hRet))
	{
		//禁用菜单
		hRet = spHost->put_AllowContextMenu(VARIANT_FALSE);
		ATLASSERT(SUCCEEDED(hRet));

		//禁用滚动条和3D边缘
		DWORD dwFlags = DOCHOSTUIFLAG_SCROLL_NO | DOCHOSTUIFLAG_NO3DBORDER;
		hRet = spHost->put_DocHostFlags(dwFlags);
		ATLASSERT(SUCCEEDED(hRet));
	}
这里的Flags还有很多可以选择,请自行查看MSDN。

2.被动设置

当WebBrowser需要显示时会询问我们应该如何处理界面元素的显示和行为,这时候我们可以告诉它我们的设置

很显然这个类似之前的SetExternalDispatch方法,该方法可参考atlwin.h中的实现。

对应我们调用SetExternalUIHandler方法来设置对应的IDocHostUIHandlerDispatch接口实现,然后在

ShowContextMenu方法中抑制右键菜单显示,

GetHostInfo中设置禁用滚动条和3D边缘等Flag,

还可以在GetExternal方法中设置JS调用C++的IDispatch接口

这里还有其它控制选项,就不一一演示,具体的演示稍后一起看,其它功能可以自行研究。


2.处理js错误

当IE发生错误时,会弹出提示窗,比如IE中运行如下html代码:

<html>
<head>
<script type="text/javascript" >

function fnObjNotDefine(){
    domethod();
}

function fnOnLoad(){
    //alert("on load!");
}
</script>
</head>
<body onload="fnOnLoad();">
<input type="button" value="function not defined" onclick="badcommand();">
<input type="button" value="object not defined" onclick="fnObjNotDefine();">
</body>
</html>

点击按钮,弹窗如下:


可以在如下位置控制是否显示提示框。



当我们嵌入IE控件使用时,一般都不希望弹出这个窗口,那么我们有如下两种方法处理

1.抑制通知

可以直接抑制WebBrowser一切通知弹窗消息,如下调用:

m_spWebBrowser->put_Silent(VARIANT_TRUE);
这会带来一个副作用—— 一些期望的窗口也没法弹出,比如我们内嵌的窗口需要处理网银通知时这样就弹不出来通知了。

2.拦截方法

之前说过脚本语言调用到C++层,肯定是通过自动化接口IDispatch接口来调用的,所以我们只要拦截脚本错误时的方法调用接口,这个很类似COM接口的Hook。最开始就说了,这里实际调整的都是包容器的特性,为了拦截这个WebBrowser控件的通知,必须实现Host的自定义接口ICustomDoc的SetUIHandler方法对应的接口,这个接口必须实现Host 的显示和命令响应,分别对应IDocHostUIHandler和IOleCommandTarget接口

对应的拦截处理如下:

	HRESULT STDMETHODCALLTYPE SetHandler(CComPtr<IWebBrowser2> spWebBrowser)  
	{  
		HRESULT result = E_NOINTERFACE;  
		CComPtr<IDispatch> spDisp;  
		HRESULT hr = spWebBrowser->get_Document(&spDisp);  
		if (SUCCEEDED(hr) && spDisp)  
		{  
			// If this is not an HTML document (e.g., it's a Word doc or a PDF), don't sink.  
			CComQIPtr<IHTMLDocument2, &IID_IHTMLDocument2> spHTML(spDisp);  
			if (spHTML)  
			{  
				// Get pointers to default interfaces  
				CComQIPtr<IOleObject, &IID_IOleObject> spOleObject(spDisp);  
				if (spOleObject)  
				{  
					CComPtr<IOleClientSite> spClientSite;  
					hr = spOleObject->GetClientSite(&spClientSite);  
					if (SUCCEEDED(hr) && spClientSite)  
					{  
						m_spDefaultDocHostUIHandler = spClientSite;  
						m_spDefaultOleCommandTarget = spClientSite;  
					}  
				}  

				// Set this class to be the IDocHostUIHandler  
				CComQIPtr<ICustomDoc, &IID_ICustomDoc> spCustomDoc(spDisp);  
				if (spCustomDoc)   
				{  
					spCustomDoc->SetUIHandler(this);  
					result = S_OK;  
				}  
			}  
		}   
		return result;  
	} 

和Hook一样,这里我们保存原来的接口m_spDefaultDocHostUIHandler和m_spDefaultOleCommandTarget,只在需要拦截的方法时调用自己操作,其他操作转向到默认实现即可,如下:

	STDMETHOD(Exec)(  
		/*[in]*/ const GUID *pguidCmdGroup,  
		/*[in]*/ DWORD nCmdID,  
		/*[in]*/ DWORD nCmdExecOpt,  
		/*[in]*/ VARIANTARG *pvaIn,  
		/*[in,out]*/ VARIANTARG *pvaOut)  
	{  
		if (nCmdID == OLECMDID_SHOWSCRIPTERROR)  
		{  
			// Don't show the error dialog, but  
			// continue running scripts on the page.  
			(*pvaOut).vt = VT_BOOL;  
			(*pvaOut).boolVal = VARIANT_TRUE;  
			return S_OK;  
		}  
		return m_spDefaultOleCommandTarget->Exec(pguidCmdGroup, nCmdID, nCmdExecOpt, pvaIn, pvaOut);  
	}

这里 我们拦截JS错误弹窗消息OLECMDID_SHOWSCRIPTERROR,自己处理不弹窗,其它交由默认处理

这样就完成了拦截操作,其实这里IDocHostUIHandler和上文的IDocHostUIHandlerDispatch实现很像,上文实现完全可以借鉴这里的处理。对应的实现已经封装成类,附件下载即可。


完整演示代码下载链接,注意加载目录htmls中的js文件进行测试

原创,转载请注明来自http://blog.csdn.net/wenzhou1219

相关文章
相关标签/搜索