我遇到的問題:
內嵌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: $
完整的例子代碼在:這裡下載