1. 程式人生 > >VC與JavaScript互動(四) ———— WebBrowser或CHtmlView中輕鬆遮蔽指令碼錯誤(JavaScript)

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或CHtmlViewSetSilent函式可以達到遮蔽指令碼錯誤的目的,不過這種情況,其它提示資訊也都不顯示了,例如使用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的插入,經實踐,這種方法是不行的;經過本人的不斷嘗試,發現在OnNavigateComplete2OnNavigateComplete裡實現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