WebBrowser内嵌页面的跨域调用问题

很早之前我写过一篇Blog:网页通过External接口与WebBrowser交互,文中的交互其实只介绍了JS调用C++的部分,而C++调用JS由于微软自己的例子太多,那篇文章就没介绍,不过我最近遇到了一个新问题,和C++调用JS有关,所以重新梳理了下这块的逻辑,把之前的代码完善了下。

我遇到的问题:
内嵌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
 4 //    Date        :    2013/11/16 01:18:09
 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
  4 //    Date        :    2013/11/16 01:18:14
  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: $

 

完整的例子代码在:这里下载

另外再附上帮助我解决问题的参考资料:
[http://blog.csdn.net/skyremember/article/details/3422841 IHTMLWindow2的get_document方法有时候会返回E_ACCESSDENIED]
[http://blog.csdn.net/nvidiacuda/article/details/9300869 VC++实现浏览器自动填表]
[http://www.itdelphi.com/delphibbs/doc/2007/3845499.htm webbrowser控件的IHTMLDocument3 or2接口的iframe问题(非安全设置)]
[http://www.cnblogs.com/rainman/archive/2011/02/21/1960044.html window.name实现的跨域数据传输]
[http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html JavaScript跨域总结与解决办法]

The End.

posted @ 2013-11-16 02:27  linyehui  阅读(4854)  评论(0编辑  收藏  举报