原文地址: https://blog.csdn.net/zhongbin104/article/details/7555695
当WebBrowser控件(CDHtmlDialog自动创建了WebBrowser控件)加载的网页中含有错误Javascript代码时默认情况下控件会弹出错误信息提示对话框,相对于用户体验来说这样的提示完全不是开发人员想要的,针对这个问题有两个解决方案,一是完全屏蔽掉错误提示,二是控制错误的提示并且记录错误信息同时也可以控制出现错误后Javascript是否继续执行。
1. 错误全禁止
//禁止脚本错误提示
m_WebBrowser.put_Silent(TRUE);
在CDHtmlDialog::OnInitDialog()的代码中首先了创建WebBrowser控件,然后把控件的Browser对象赋值给m_pBrowserApp(这是CDHtmlDialog完成的不需要自己处理)。WebBrowser的put_Silent函数在官方给出的说明是禁用所有的对话框,但例外情况是它不会影响SSL安全认证需要的进示对话框。绝大多数情况下这就可以解决问题了,记得很久以前我遇到过一种情况就是虽然调用了put_Silent但是还是有极个别的js错误是无法屏蔽掉的依然会显示出来(在网页含有嵌套页面时会错误无法屏蔽,不知道是否还有其它情况),现在找不到这样的网页了,如果谁遇到这种情况了建议给我发上个URL让我也重温一下当年阳光灿烂的时刻。
2. 控制错误的提示并且记录错误信息同时也可以控制出现错误后Javascript是否继续执行。
这要比第一种方法复杂上许多,简短的来说就是自定义COleControlSite类并实现IOleCommandTarget接口,IOleCommandTarget接口是错误控制的关健,错误发生时会触发此接口的Exec函数并为nCmdID参数赋值为OLECMDID_SHOWSCRIPTERROR,这样就可以得到错误信息了。
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)
};
CMyControlSite.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(
/* [unique][in] */ const GUID __RPC_FAR *pguidCmdGroup,
/* [in] */ ULONG cCmds,
/* [out][in][size_is] */ OLECMD __RPC_FAR prgCmds[ ],
/* [unique][out][in] */ OLECMDTEXT __RPC_FAR *pCmdText
)
{
METHOD_PROLOGUE(CMyControlSite, OleCommandTarget)
return OLECMDERR_E_NOTSUPPORTED;
}
对话框头文件加入声明:
virtual BOOL CreateControlSite(COleControlContainer* pContainer,
COleControlSite** ppSite, UINT nID , REFCLSID clsid );
对应源文件:
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;
}