VC与JavaScript交互(四) --- WebBrowser或CHtmlView中轻松屏蔽脚本错误(JavaScript)
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浏览器设置了脚本调试,也不会进行提示),经改造的代码如下:

1<html>
2<head>
3<script type="text/javascript" >
4
5function fnObjNotDefine(){
6 domethod();
7}
8
9function fnOnError(msg,url,lineno){
10 <!--
11 alert("window.onerror\n\n" +
12 "Error: " + msg + "\n" +
13 "URL: " + url + "\n" +
14 "Line: " + lineno);
15 return true; -->
16}
17window.onerror = fnOnError;
18MethodName.badcommand();
19
20function 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来做例子,具体代码如下:

1void 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操作即可。
最终代码为:

1void 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
31ScriptErrHandler.cpp文件:
32
33#include "StdAfx.h"
34
35#include "ScriptErrHandler.h"
36
37CScriptErrHandler::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
55CScriptErrHandler::~CScriptErrHandler(void)
56
57{
58
59 SysFreeString(m_bstrScript);
60
61}
62
63
64
65
66
67void 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
107void CScriptErrHandler::ShieldAllChildPages(CComPtr<IHTMLDocument2> &parentDoc)
108
109{
110
111 WalkAllChildPages(parentDoc);
112
113}
114
115
116
117void 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
4.3 How to Trap JScript Errors in Internet Explorer 4.01 and Earlier
http://support.microsoft.com/kb/183616/en-us
这个是成功代码
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);
}

禁止弹出窗口 给你的从CHtmlView派生而来的类加一个成员变量BOOL m_bPop;
在构造函数里给m_bPop初始化为:m_bPop = TRUE;
重载OnDownloadBegin虚函数,在其中加入:m_bPop = FALSE;
重载OnDownloadComplete虚函数,在其中加入:m_bPop = TRUE;
重载OnNewWindow2虚函数,在其中加入:
Cancel = m_bPop;
禁止脚本错误 在初始化的时候设置 SetSilent(TRUE);
VC与JavaScript交互(四) --- WebBrowser或CHtmlView中轻松屏蔽脚本错误(JavaScript)的更多相关文章
- VB.NET让webbrowser控件中JS脚本错误最新方法(2013-09-16)
最近也是在项目中遇到了webbrowser控件中想关闭JS脚本错误窗口的问题,所以经过多次测试,终于用一段高效实用的代码完美解决webbrowser控件中JS脚本错误窗口关闭的问题. 通过创建一个子线 ...
- VC与JavaScript交互(一) --- 如何实现
为什么要让VC与JavaScript交互? 1.有时候我们需要让自己的软件打开一个网页,来获取页面上的一些数据.这时,可以用mshtml解析HTML提取出数据,也可以向HTML文档动态写入我们准备好的 ...
- VC与JavaScript交互(一) ———— 怎样实现
为什么要让VC与JavaScript交互? 1.有时候我们须要让自己的软件打开一个网页.来获取页面上的一些数据. 这时,能够用mshtml解析HTML提取出数据.也能够向HTML文档动态写入我们准备好 ...
- VC与JavaScript交互(二) --- 调用JS函数
这一章,我们来动手实践VC调用JS函数. 我们动手写一个HTML,其中包含这样一段JS代码: //[html] <script type="text/javascript"& ...
- Qt webKit可以做什么(四)--实现本地QObject和JavaScript交互
Qt webKit可以做什么(四)--实现本地QObject和JavaScript交互 Qt webKit可以做什么(四)--实现本地QObject和JavaScript交互
- WPF加载HTML、WPF与JavaScript交互
目录 一.WebBrowser加载远程网页 二.WebBrowser加载本地网页,注:不可以加载本地样式CSS和脚本JS文件 三.WebBrowser隐藏网页的JavaScript错误 四.网页屏蔽鼠 ...
- Hybrid App: 看看第三方WebViewJavascriptBridge是如何来实现Native和JavaScript交互
一.简介 在前面两篇文章中已经介绍了Native与JavaScript交互的几种方式,依次是JavaScriptCore框架.UI组件UIWebView.WebKit框架,这几种方式都是苹果公司提供的 ...
- Android混合开发之WebView与Javascript交互
前言: 最近公司的App为了加快开发效率选择了一部分功能采用H5开发,从目前市面的大部分App来讲,大致分成Native App.Web App.Hybrid App三种方式,个人觉得目前以Hybri ...
- 移动端基于HTML模板和JSON数据的JavaScript交互
写本文之前,我正在做一个基于Tab页的订单中心: 每点击一个TAB标签,会请求对应状态的订单列表.之前的项目,我会在js里使用 + 连接符连接多个html内容: var html = ''; htm ...
随机推荐
- JMM & synchronized概述(转)
根据Java语言规范中的说明,JVM系统中存在一个主内存(Main Memory),Java中所有的变量存储在主内存中,对于所有的线程是共享的(相当于黑板,其他人都可以看到的).每个线程都有自己的工作 ...
- Servlet间的跳转
Forward 转向(Forward)是通过RequestDispatcher对象的forward(HTTPServletRequest req, HttpSerletRespon ...
- 玩转sublime(一)——玩转全局文件搜索/替换
这个快捷键好记,一般的搜索是Ctrl+f,多了一个Shift就是全局搜索
- ERP_Oracle Fusion Application新一代ERP介绍
2014-12-31 Created By BaoXinjian
- Codeforces Round #359 (Div. 2)C - Robbers' watch
C. Robbers' watch time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...
- POJ2226 Muddy Fields 二分匹配 最小顶点覆盖 好题
在一个n*m的草地上,.代表草地,*代表水,现在要用宽度为1,长度不限的木板盖住水, 木板可以重叠,但是所有的草地都不能被木板覆盖. 问至少需要的木板数. 这类题的建图方法: 把矩阵作为一个二分图,以 ...
- 从视频文件中读入数据-->将数据转换为灰度图-->对图像做canny边缘检测-->将这三个结构显示在一个图像中
//从视频文件中读入数据-->将数据转换为灰度图-->对图像做canny边缘检测-->将这三个结构显示在一个图像中 //作者:sandy //时间:2015-10-10 #inclu ...
- redis使用日志(二) 数据存储到redis
一段简短的代码,来展示如何把爬取内容写到redis里面: #! /usr/bin/env python # -*- coding=utf-8 -*- import requests import js ...
- JAVA类的构造方法
1,构造方法没有返回类型, 定义: []public] 方法名() {} 2,一个构造方法如果想调用同一类中的另一个构造方法,只能调用一个,并且要放在构造方法第一行 3,用this调用,如 publi ...
- js方法收藏
1.验证非负数字 //onfocusout="checkQty(this);" function checkQty(obj) { //排除0开头的非法输入 if (obj.valu ...