最近一个项目中要在对话框上使用WebBrowser控件进行页面浏览,但在开发过程中发现WebBrowser控件会在浏览一些页面的时候出现 JavaScript脚本错误,严重影响用户体验,而在IE和其他第三方浏览器中均没有这个现象。于是搜索一下发现原来可以通过下面的代码禁止这个错误提示:
- m_WebBrowser.put_Silent(TRUE);//禁止脚本错误提示
效果非常好,可以说立竿见影。但是随之问题又来了,在登录银行网站时会出现无法打开网页的错误,原来这个Silent把银行的选择证书窗口也给禁止了。
翻了翻MSDN,发现原来这个参数要么都不禁止,要么都禁止……这显然不符合要求
搜索引擎真是个好东西,就在准备放弃的时候发现这么一篇文章CDHtmlDialog探索----WebBrowser扩展和网页Javascript错误处理
这会儿真想对着thinkingfor鞠一个大大的躬,真的。
因为一直都搞不懂COM这个东西,所以就抱着试试看的想法把thinkingfor的代码加进工程一编译,除了少数头文件等改动以外,完美运行。
为了备忘也希望能给遇到相同问题的人一点帮助,故将编译通过的代码列出,环境vs2008 + xp
代码如下
//CMyControlSite.h
#pragma once
#include "afxocc.h"
#include "Mshtml.h"//应该加入这个头文件
#include "Mshtmhst.h"//这个也是
class CMyControlSite :public COleControlSite
{
public:
CMyControlSite(COleControlContainer *pCntr):COleControlSite(pCntr) {}
~CMyControlSite(void);
protected:
DECLARE_INTERFACE_MAP()
BEGIN_INTERFACE_PART(OleCommandTarget, IOleCommandTarget)
STDMETHOD(QueryStatus)(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD prgCmds[], OLECMDTEXT *pCmdText);
STDMETHOD(Exec)(const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut);
END_INTERFACE_PART(OleCommandTarget)
};
(OLECMDID 信息参看下篇博文)
// CMyControlSite.cpp<pre name="code" class="cpp"> #include "StdAfx.h" #include "MyControlSite.h" BEGIN_INTERFACE_MAP(CMyControlSite, COleControlSite) INTERFACE_PART(CMyControlSite, IID_IOleCommandTarget, OleCommandTarget) END_INTERFACE_MAP() CMyControlSite::~CMyControlSite(void) { } HRESULT CMyControlSite::XOleCommandTarget::Exec (const GUID* pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut ) { HRESULT hr = OLECMDERR_E_NOTSUPPORTED; //return S_OK; if (pguidCmdGroup && IsEqualGUID(*pguidCmdGroup, CGID_DocHostCommandHandler)) { switch (nCmdID) { case OLECMDID_SHOWSCRIPTERROR: { IHTMLDocument2* pDoc = NULL; IHTMLWindow2* pWindow = NULL; IHTMLEventObj* pEventObj = NULL; BSTR rgwszNames[5] = { SysAllocString(L"errLine"), SysAllocString(L"errCharacter"), SysAllocString(L"errCode"), SysAllocString(L"errMsg"), SysAllocString(L"errUrl") }; DISPID rgDispIDs[5]; VARIANT rgvaEventInfo[5]; DISPPARAMS params; BOOL fContinueRunningScripts = false; //修改此处为false禁止脚本错误提示 params.cArgs = 0; params.cNamedArgs = 0; hr = pvaIn->punkVal->QueryInterface(IID_IHTMLDocument2, (void **) &pDoc); hr = pDoc->get_parentWindow(&pWindow); pDoc->Release(); hr = pWindow->get_event(&pEventObj); for (int i = 0; i < 5; i++) { hr = pEventObj->GetIDsOfNames(IID_NULL, &rgwszNames[i], 1, LOCALE_SYSTEM_DEFAULT, &rgDispIDs[i]); hr = pEventObj->Invoke(rgDispIDs[i], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, &rgvaEventInfo[i], NULL, NULL); //可以在此记录错误信息 //必须使用SysFreeString来释放SysAllocString分配的内存,SysAllocString在分配的内存中记录了字符的长度 SysFreeString(rgwszNames[i]); } // At this point, you would normally alert the user with // the information about the error, which is now contained // in rgvaEventInfo[]. Or, you could just exit silently. (*pvaOut).vt = VT_BOOL; if (fContinueRunningScripts) { // 在页面中继续执行脚本 (*pvaOut).boolVal = VARIANT_TRUE; } else { // 停止在页面中执行脚本 (*pvaOut).boolVal = VARIANT_FALSE; } break; } default: hr =OLECMDERR_E_NOTSUPPORTED; break; } } else { hr = OLECMDERR_E_UNKNOWNGROUP; } return (hr); } ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::AddRef() { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) return pThis->ExternalAddRef(); } ULONG FAR EXPORT CMyControlSite::XOleCommandTarget::Release() { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) return pThis->ExternalRelease(); } HRESULT FAR EXPORT CMyControlSite::XOleCommandTarget::QueryInterface(REFIID riid, void **ppvObj) { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) HRESULT hr = (HRESULT)pThis->ExternalQueryInterface(&riid, ppvObj); return hr; } STDMETHODIMP CMyControlSite::XOleCommandTarget::QueryStatus( const GUID __RPC_FAR *pguidCmdGroup, ULONG cCmds, OLECMD __RPC_FAR prgCmds[ ], OLECMDTEXT __RPC_FAR *pCmdText ) { METHOD_PROLOGUE(CMyControlSite, OleCommandTarget) return OLECMDERR_E_NOTSUPPORTED; }
对话框头文件加入声明:
对应源文件:
BOOL CXDlg::CreateControlSite(COleControlContainer* pContainer,
COleControlSite** ppSite, UINT nID , REFCLSID clsid )
{
if(ppSite == NULL)
{
ASSERT(FALSE);
return FALSE;
}
CMyControlSite *pBrowserSite =
new CMyControlSite (pContainer);//
if (!pBrowserSite)
return FALSE;
*ppSite = pBrowserSite;
return TRUE;
}
此外,thinkingfor原文中为CDHtmlDialog,同样适用于CDialog。