zoukankan      html  css  js  c++  java
  • 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.

  • 相关阅读:
    JAVA数据库连接池
    数据库连接池总结 Proxool
    Linux下挂载ISO文件
    Runtime.getRuntime().addShutdownHook()的使用
    tomcat original passports setting
    JDBC之通过DatabaseMetaData对象了解数据库的信息
    linux下mysql远程登陆
    教您如何安全的应用 MySQL
    Eclipse中cvs的使用介绍
    WM_COPYDATA
  • 原文地址:https://www.cnblogs.com/linyehui/p/webbrowser-cross-domain-access.html
Copyright © 2011-2022 走看看