我遇到的問題:
內嵌IE浏覽器控件WebBrowser的內嵌頁host.html中使用iframe又嵌套了一個頁面iframe.html,iframe.html上有個JS方法,我用C++調用不到,而host.html上的JS方法可以正常調用到。
問題分析:
從JS來說,這是個跨域問題,host.html和iframe.html不在一個域內;
JS解決跨域問題的方案其實也是有的,但這個不是我們本文的重點,本文的重點是怎麼通過WebBrowser控件直接來解決這個“跨域調用”的問題。
問題的根本原因在於:
調用網頁的JS需要拿到IHTMLDocument2接口,而每個iframe都有自己對應的IHTMLDocument2,所以我們只要能拿到iframe對應的IHTMLDocument2就能解決問題了。
解決方案:直接上代碼吧
1 /* -------------------------------------------------------------------------
2 // FileName : calljs_helper.h
3 // Creator : linyehui 5 // Brief : 調用WebBrowser控件內嵌頁上的JS函數,iframe中的也能調到
6 //
7 // $Id: $
8 // -----------------------------------------------------------------------*/
9 #ifndef __CALLJS_HELPER_H__
10 #define __CALLJS_HELPER_H__
11
12 // -------------------------------------------------------------------------
13 namespace calljs_helper
14 {
15 bool CallFunction(
16 CComPtr<IWebBrowser2> spIWebBrowser,
17 LPCTSTR lpFuncName,
18 const vector<wstring>& paramArray,
19 CComVariant * pVarResult = NULL,
20 bool bEnumFrame = true);
21
22 } // namespace
23
24 // -------------------------------------------------------------------------
25 // $Log: $
26
27 #endif /* __CALLJS_HELPER_H__ */
1 /* -------------------------------------------------------------------------
2 // FileName : calljs_helper.cpp
3 // Creator : linyehui 5 // Brief : 調用WebBrowser控件內嵌頁上的JS函數,iframe中的也能調到
6 //
7 // $Id: $
8 // -----------------------------------------------------------------------*/
9
10 #include "stdafx.h"
11 #include "calljs_helper.h"
12
13 // -------------------------------------------------------------------------
14
15 CComPtr<IWebBrowser2> HtmlWindowToHtmlWebBrowser(CComPtr<IHTMLWindow2> spWindow)
16 {
17 ATLASSERT(spWindow != NULL);
18 CComQIPtr<IServiceProvider> spServiceProvider = spWindow;
19 if (spServiceProvider == NULL)
20 {
21 return CComPtr<IWebBrowser2>();
22 }
23
24 CComPtr<IWebBrowser2> spWebBrws;
25 HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws);
26 if (hRes != S_OK)
27 {
28 return CComPtr<IWebBrowser2>();
29 }
30
31 return spWebBrws;
32 }
33
34 // Converts a IHTMLWindow2 object to a IHTMLDocument2. Returns NULL in case of failure.
35 // It takes into account accessing the DOM across frames loaded from different domains.
36 CComPtr<IHTMLDocument2> HtmlWindowToHtmlDocument(CComPtr<IHTMLWindow2> spWindow)
37 {
38 ATLASSERT(spWindow != NULL);
39 CComPtr<IHTMLDocument2> spDocument;
40 HRESULT hRes = spWindow->get_document(&spDocument);
41 if ((S_OK == hRes) && (spDocument != NULL))
42 {
43 // The html document was properly retrieved.
44 return spDocument;
45 }
46
47 // hRes could be E_ACCESSDENIED that means a security restriction that
48 // prevents scripting across frames that loads documents from different internet domains.
49 CComPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(spWindow);
50 if (spBrws == NULL)
51 {
52 return CComPtr<IHTMLDocument2>();
53 }
54
55 // Get the document object from the IWebBrowser2 object.
56 CComPtr<IDispatch> spDisp;
57 hRes = spBrws->get_Document(&spDisp);
58 spDocument = spDisp;
59 return spDocument;
60 }
61
62 bool CallFunctionInDocument(
63 CComPtr<IHTMLDocument2> spDocument2,
64 LPCTSTR lpFuncName,
65 const vector<wstring>& paramArray,
66 CComVariant * pVarResult)
67 {
68 if (!spDocument2)
69 return false;
70
71 CComPtr<IDispatch> spScript;
72 if (FAILED(spDocument2->get_Script(&spScript))) { return false; }
73
74 CComBSTR bstrMember(lpFuncName);
75 DISPID dispid = NULL;
76 HRESULT hr = spScript->GetIDsOfNames(IID_NULL, &bstrMember, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
77 if (FAILED(hr)) { return false; }
78
79 const int arraySize = paramArray.size();
80
81 DISPPARAMS dispparams;
82 memset(&dispparams, 0, sizeof dispparams);
83 dispparams.cArgs = arraySize;
84 dispparams.rgvarg = new VARIANT[dispparams.cArgs];
85
86 for (int i = 0; i < arraySize; i++)
87 {
88 CComBSTR bstr = paramArray[arraySize - 1 - i].c_str(); // back reading
89 bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
90 dispparams.rgvarg[i].vt = VT_BSTR;
91 }
92 dispparams.cNamedArgs = 0;
93
94 EXCEPINFO excepInfo;
95 memset(&excepInfo, 0, sizeof excepInfo);
96 CComVariant vaResult;
97 UINT nArgErr = (UINT)-1; // initialize to invalid arg
98
99 hr = spScript->Invoke(dispid, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr);
100
101 delete [] dispparams.rgvarg;
102 if (FAILED(hr)) { return false; }
103
104 if (pVarResult) { *pVarResult = vaResult; }
105
106 return true;
107 }
108
109 void EnumFrame(
110 CComPtr<IHTMLDocument2> spIHTMLDocument2,
111 LPCTSTR lpFuncName,const vector<wstring>& paramArray,
112 CComVariant* pVarResult)
113 {
114 if ( !spIHTMLDocument2 )
115 return;
116
117 CComPtr< IHTMLFramesCollection2 > spFramesCollection2;
118 spIHTMLDocument2->get_frames( &spFramesCollection2 );
119
120 long nFrameCount=0;
121 HRESULT hr = spFramesCollection2->get_length( &nFrameCount );
122 if ( FAILED ( hr ) || 0 == nFrameCount )
123 return;
124
125 for(long i = 0; i < nFrameCount; i++)
126 {
127 CComVariant vDispWin2;
128 hr = spFramesCollection2->item( &CComVariant(i), &vDispWin2 );
129 if ( FAILED ( hr ) )
130 continue;
131
132 CComQIPtr< IHTMLWindow2 > spWin2 = vDispWin2.pdispVal;
133 if( !spWin2 )
134 continue;
135
136 CComPtr < IHTMLDocument2 > spDoc2;
137 spDoc2 = HtmlWindowToHtmlDocument(spWin2);
138 if (!spDoc2)
139 continue;
140
141 CallFunctionInDocument(spDoc2, lpFuncName, paramArray, pVarResult);
142 }
143 }
144
145 bool calljs_helper::CallFunction(
146 CComPtr<IWebBrowser2> spIWebBrowser,
147 LPCTSTR lpFuncName,
148 const vector<wstring>& paramArray,
149 CComVariant * pVarResult,
150 bool bEnumFrame)
151 {
152 if (!spIWebBrowser)
153 return false;
154
155 CComPtr<IDispatch> spDispDoc;
156 HRESULT hr = spIWebBrowser->get_Document(&spDispDoc);
157 if (FAILED(hr))
158 return false;
159
160 CComQIPtr<IHTMLDocument2> spDocument2 = spDispDoc;
161 if (!spDocument2)
162 return false;
163
164 CallFunctionInDocument(spDocument2, lpFuncName, paramArray, pVarResult);
165
166 if (bEnumFrame)
167 {
168 EnumFrame(spDocument2, lpFuncName, paramArray, pVarResult);
169 }
170
171 return true;
172 }
173
174 // -------------------------------------------------------------------------
175 // $Log: $
完整的例子代碼在:這裡下載