1.什么是javascript脚本错误
1.1 概述
JavaScript脚本错误包含“运行时错误”和“语法错误”。
1.2 JavaScript“语法错误”
JavaScript语法错误是指当 JavaScript语句违反了 JavaScript脚本语言的一条或多条语法规则时导致的错误。JavaScript语法错误发生在程序编译阶段,在开始运行该程序之前。
1.3 JavaScript“运行时错误”
JavaScript运行时错误是指当 JavaScript脚本试图执行一个系统不能运行的动作时导致的错误。当正在运行脚本、计算变量表达式、或者正在动态分配内存时出现 JavaScript运行时错误时。
2. 为什么要屏蔽javascript脚本错误?
由于开发海纳产品时,使用WebBrowser和CHtmlView来展示页面,进行填表等操作;但是由于打开的页面大多是其他用户的CMS页面,所以难免有些有脚本错误,于是决定要来屏蔽脚本错误,提升产品的易用性和友好性。
3. 怎么去屏蔽javascript脚本错误?
3.1 使用SetSilent函数
使用WebBrowser或CHtmlView的SetSilent函数可以达到屏蔽脚本错误的目的,不过这种情况,其它提示信息也都不显示了,例如使用alert进行的错误提示。
如果你觉得这样能满足你,那么推荐使用这种方法,简单啊!![]()
3.2 重载IOleCommandTarget的Exec函数
网上比较多资料都是说重载IOleCommandTarget中的Exec函数来进行屏蔽脚本错,定义如下:
HRESULT Exec( const GUID* pguidCmdGroup, DWORD nCmdID,
DWORD nCmdexecopt, VARIANTARG* pvaIn, VARIANTARG* pvaOut )
然后通过判断nCmdID是否等于OLECMDID_SHOWSCRIPTERROR(即报javascript脚本错误)来进行屏蔽;由于本人对COM和OLE的知识有限,琢磨了半天也没有想到怎么实现IOleCommandTarget接口中的Exec函数,然后跟我的WebBrowser或是HtmlView挂钩起来
,于是决定放弃这种方法,有兴趣的朋友可以查看参考资料的文章继续尝试一下。
3.3 另一种方法
不死心,继续在网上找,突然发现了一篇文章,介绍在html页面中,可以使用javascript的事件来进行javascript脚本错误的屏蔽,于是拷贝下来尝试,果然有用(即使IE浏览器设置了脚本调试,也不会进行提示
),经改造的代码如下:
<html>2
<head>3
<script type="text/javascript" >4

5
function fnObjNotDefine(){6
domethod();7
}8

9
function fnOnError(msg,url,lineno){10
<!--11
alert("window.onerror
" +12
"Error: " + msg + "
" +13
"URL: " + url + "
" +14
"Line: " + lineno);15
return true; -->16
}17
window.onerror = fnOnError;18
MethodName.badcommand();19

20
function fnOnLoad(){21
alert("on load!");22
}23
</script>24
</head>25
<body onload="fnOnLoad();">26
<input type="button" value="function not defined" onclick="badcommand();">27
<input type="button" value="object not defined" onclick="fnObjNotDefine();">28
</body>29
</html>30
通过查看javascript代码,发现是“重载”了window.onerror这个事件,只要它返回true,脚本错误就不显示了,估计这个就是Microsoft自己实现的截取javascript脚本错误信息的接口
![]()
,于是就想怎么把它插入到页面当中,其中有篇文章介绍说在OnDocumentComplete时来实现javascript的插入,经实践,这种方法是不行的;经过本人的不断尝试,发现在OnNavigateComplete2或OnNavigateComplete里实现javascript的注入是可行的,这两个函数只要实现一个就行,就看你用的是Navigate2还是Navigate来打开页面了。这里使用Navigate2来做例子,具体代码如下:
void CMyWebBrowser::OnNavigateComplete2(LPCTSTR strURL)2

3
{4

5
CComPtr<IDispatch> spDisp = GetHtmlDocument(); 6

7
if(spDisp != NULL) 8

9
{ 10

11
CComPtr<IHTMLDocument2> doc;12

13
spDisp->QueryInterface(IID_IHTMLDocument2, reinterpret_cast<void**>(&doc));14

15
if(doc != NULL)16

17
{ 18

19
IHTMLWindow2 * pIhtmlwindow2 = NULL;20

21
doc->get_parentWindow(&pIhtmlwindow2);22

23
if(pIhtmlwindow2 != NULL)24

25
{26

27
//屏蔽javascript脚本错误的javascript脚本28

29
CString strJavaScriptCode = "function fnOnError(msg,url,lineno){alert('script error:\n\nURL:'+url+'\n\nMSG:'+msg +'\n\nLine:'+lineno);return true;}window.onerror=fnOnError;";30

31
BSTR bstrScript = strJavaScriptCode.AllocSysString();32

33
CString strLanguage("JavaScript");34

35
BSTR bstrLanguage = strLanguage.AllocSysString();36

37
long lTime = 1 * 1000;38

39
long lTimeID = 0;40

41
VARIANT varLanguage;42

43
varLanguage.vt = VT_BSTR;44

45
varLanguage.bstrVal = bstrLanguage;46

47
VARIANT pRet;48

49
//把window.onerror函数插入入当前页面中去50

51
pIhtmlwindow2->execScript(bstrScript, bstrLanguage, &pRet);52

53
::SysFreeString(bstrScript);54

55
::SysFreeString(bstrLanguage);56

57
58

59
pIhtmlwindow2->Release();60

61
}62

63
}64

65
}66

67
}
其中,CMyWebBrowser是我自己继承了CHtmlView类的一个实现类, 这个函数可以在你的WebBrowser2或继承了CHtmlView类中实现,编写一个带有脚本错误的页面,打开进行浏览,是不是发现脚本错误被屏蔽了? 哈哈,实现起来也不麻烦。于是就把这个方法贴出来,供大家参考
。
另: 经测试,发现如果存在iframe嵌套的时候,嵌套的iframe中包含脚本错误,以上方法是不能屏蔽iframe中的脚本错误的,因为window.onerror只针对当前页面有效,因此需要在OnNavigateComplete2函数里加上对当前页面进行递归所有子页面,然后重复执行execScript操作即可。
最终代码为:
void CMyWebBrowser::OnNavigateComplete2(LPCTSTR strURL)2

3
{4

5
CComPtr<IDispatch> spDisp = GetHtmlDocument(); 6

7
if(spDisp != NULL) 8

9
{ 10

11
CComPtr<IHTMLDocument2> doc;12

13
spDisp->QueryInterface(IID_IHTMLDocument2, reinterpret_cast<void**>(&doc));14

15
if(doc != NULL)16

17
{ 18

19
CScriptErrHandler scriptHandler;20

21
scriptHandler.ShieldCurrPage(doc);22

23
scriptHandler.ShieldAllChildPages(doc);24

25
}26

27
}28

29
}30

31
ScriptErrHandler.cpp文件:32

33
#include "StdAfx.h"34

35
#include "ScriptErrHandler.h"36

37
CScriptErrHandler::CScriptErrHandler(void)38

39
{40

41
CString strJavaScriptCode = "function fnOnError(msg,url,lineno){alert('script error:\n\nURL:'+url"42

43
"+'\n\nMSG:'+msg +'\n\nLine:'+lineno+'\n\nframes:' + window.frames.length);return true;}window.onerror=fnOnError;";44

45
46

47
//屏蔽的脚本,可以改进为从文本里读取48

49
m_bstrScript = strJavaScriptCode.AllocSysString();50

51
}52

53
54

55
CScriptErrHandler::~CScriptErrHandler(void)56

57
{58

59
SysFreeString(m_bstrScript);60

61
}62

63
64

65
66

67
void CScriptErrHandler::ShieldCurrPage(CComPtr<IHTMLDocument2> &doc)68

69
{70

71
CComPtr<IHTMLWindow2> spIhtmlwindow2;72

73
doc->get_parentWindow(reinterpret_cast<IHTMLWindow2**>(&spIhtmlwindow2));74

75
if(spIhtmlwindow2 != NULL)76

77
{78

79
CString strLanguage("JavaScript");80

81
BSTR bstrLanguage = strLanguage.AllocSysString();82

83
long lTime = 1 * 1000;84

85
long lTimeID = 0;86

87
VARIANT varLanguage;88

89
varLanguage.vt = VT_BSTR;90

91
varLanguage.bstrVal = bstrLanguage;92

93
VARIANT pRet;94

95
//把window.onerror函数插入入当前页面中去96

97
spIhtmlwindow2->execScript(m_bstrScript, bstrLanguage, &pRet);98

99
::SysFreeString(bstrLanguage);100

101
}102

103
}104

105
106

107
void CScriptErrHandler::ShieldAllChildPages(CComPtr<IHTMLDocument2> &parentDoc)108

109
{110

111
WalkAllChildPages(parentDoc);112

113
}114

115
116

117
void CScriptErrHandler::WalkAllChildPages(CComPtr<IHTMLDocument2> &parentDoc)118

119
{120

121
CComPtr<IHTMLFramesCollection2> spFramesCol;122

123
HRESULT hr = parentDoc->get_frames(&spFramesCol);124

125
if(SUCCEEDED(hr) && spFramesCol != NULL)126

127
{128

129
long lSize = 0;130

131
hr = spFramesCol->get_length(&lSize);132

133
if (SUCCEEDED(hr))134

135
{136

137
for(int i=0; i<lSize; i++)138

139
{140

141
VARIANT frameRequested;142

143
VARIANT frameOut;144

145
frameRequested.vt = VT_UI4;146

147
frameRequested.lVal = i;148

149
hr = spFramesCol->item(&frameRequested, &frameOut);150

151
if(SUCCEEDED(hr) && frameOut.pdispVal != NULL)152

153
{154

155
CComPtr<IHTMLWindow2> spChildWindow;156

157
158

159
hr = frameOut.pdispVal->QueryInterface(IID_IHTMLWindow2,reinterpret_cast<void**>(&spChildWindow));160

161
if(SUCCEEDED(hr) && spChildWindow != NULL)162

163
{164

165
CComPtr<IHTMLDocument2> spChildDocument;166

167
hr = spChildWindow->get_document(reinterpret_cast<IHTMLDocument2**>(&spChildDocument));168

169
if(SUCCEEDED(hr) && spChildDocument != NULL)170

171
{172

173
ShieldCurrPage(spChildDocument);174

175
WalkAllChildPages(spChildDocument);176

177
}178

179
}180

181
frameOut.pdispVal->Release();182

183
}184

185
}186

187
}188

189
}190

191
}
目前存在的一个缺陷是OnNavigateComplete2会被调用多次,那么嵌入javascript的操作也会被执行多次(不知道会产生什么副作用,目前尚未发现);CMyWebBrowser从CHtmlView类继承,代码在VC2008和VC6.0下调试通过;若需要工程代码,请发送邮件到zhangqingping@hylanda.com 。
4. 参考资料
4.1 How to handle script errors as a WebBrowser control host
http://support.microsoft.com/default.aspx?scid=kb;en-us;261003
4.2 Script error notification is not sent to Exec method of WebBrowser Host
http://support.microsoft.com/kb/317024/en-us#top

这个是成功代码
void CZCSoftView::OnNavigateComplete2(LPCTSTR strURL)
{
// TODO: Add your specialized code here and/or call the base class
CComPtr<IDispatch> spDisp = GetHtmlDocument();
if(spDisp!=NULL)
{
CComPtr<IHTMLDocument2> doc;
spDisp->QueryInterface(IID_IHTMLDocument2, reinterpret_cast<void**>(&doc));
if(doc != NULL)
{
IHTMLWindow2 * pIhtmlwindow2 = NULL;
doc->get_parentWindow(&pIhtmlwindow2);
if(pIhtmlwindow2 != NULL)
{//"function fnOnError(msg,url,lineno){alert('script error:\n\nURL:'+url+'\n\nMSG:'+msg +'\n\nLine:'+lineno);return true;}window.onerror=fnOnError;";
//屏蔽javascript脚本错误的javascript脚本
CString strJavaScriptCode = "function fnOnError(){return true;}window.onerror=fnOnError;";
BSTR bstrScript = strJavaScriptCode.AllocSysString();
CString strLanguage("JavaScript");
BSTR bstrLanguage = strLanguage.AllocSysString();
long lTime = 1 * 1000;
long lTimeID = 0;
VARIANT varLanguage;
varLanguage.vt = VT_BSTR;
varLanguage.bstrVal = bstrLanguage;
VARIANT pRet;
//把window.onerror函数插入入当前页面中去
pIhtmlwindow2->execScript(bstrScript, bstrLanguage, &pRet);
::SysFreeString(bstrScript);
::SysFreeString(bstrLanguage);
pIhtmlwindow2->Release();
}
}
}
CHtmlView::OnNavigateComplete2(strURL);
}