很早之前我写过一篇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就能解决问题了。

解决方案:直接上代码吧

 /* -------------------------------------------------------------------------
// FileName : calljs_helper.h
// Creator : linyehui
// Date : 2013/11/16 01:18:09
// Brief : 调用WebBrowser控件内嵌页上的JS函数,iframe中的也能调到
//
// $Id: $
// -----------------------------------------------------------------------*/
#ifndef __CALLJS_HELPER_H__
#define __CALLJS_HELPER_H__ // -------------------------------------------------------------------------
namespace calljs_helper
{
bool CallFunction(
CComPtr<IWebBrowser2> spIWebBrowser,
LPCTSTR lpFuncName,
const vector<wstring>& paramArray,
CComVariant * pVarResult = NULL,
bool bEnumFrame = true); } // namespace // -------------------------------------------------------------------------
// $Log: $ #endif /* __CALLJS_HELPER_H__ */
 /* -------------------------------------------------------------------------
// FileName : calljs_helper.cpp
// Creator : linyehui
// Date : 2013/11/16 01:18:14
// Brief : 调用WebBrowser控件内嵌页上的JS函数,iframe中的也能调到
//
// $Id: $
// -----------------------------------------------------------------------*/ #include "stdafx.h"
#include "calljs_helper.h" // ------------------------------------------------------------------------- CComPtr<IWebBrowser2> HtmlWindowToHtmlWebBrowser(CComPtr<IHTMLWindow2> spWindow)
{
ATLASSERT(spWindow != NULL);
CComQIPtr<IServiceProvider> spServiceProvider = spWindow;
if (spServiceProvider == NULL)
{
return CComPtr<IWebBrowser2>();
} CComPtr<IWebBrowser2> spWebBrws;
HRESULT hRes = spServiceProvider->QueryService(IID_IWebBrowserApp, IID_IWebBrowser2, (void**)&spWebBrws);
if (hRes != S_OK)
{
return CComPtr<IWebBrowser2>();
} return spWebBrws;
} // Converts a IHTMLWindow2 object to a IHTMLDocument2. Returns NULL in case of failure.
// It takes into account accessing the DOM across frames loaded from different domains.
CComPtr<IHTMLDocument2> HtmlWindowToHtmlDocument(CComPtr<IHTMLWindow2> spWindow)
{
ATLASSERT(spWindow != NULL);
CComPtr<IHTMLDocument2> spDocument;
HRESULT hRes = spWindow->get_document(&spDocument);
if ((S_OK == hRes) && (spDocument != NULL))
{
// The html document was properly retrieved.
return spDocument;
} // hRes could be E_ACCESSDENIED that means a security restriction that
// prevents scripting across frames that loads documents from different internet domains.
CComPtr<IWebBrowser2> spBrws = HtmlWindowToHtmlWebBrowser(spWindow);
if (spBrws == NULL)
{
return CComPtr<IHTMLDocument2>();
} // Get the document object from the IWebBrowser2 object.
CComPtr<IDispatch> spDisp;
hRes = spBrws->get_Document(&spDisp);
spDocument = spDisp;
return spDocument;
} bool CallFunctionInDocument(
CComPtr<IHTMLDocument2> spDocument2,
LPCTSTR lpFuncName,
const vector<wstring>& paramArray,
CComVariant * pVarResult)
{
if (!spDocument2)
return false; CComPtr<IDispatch> spScript;
if (FAILED(spDocument2->get_Script(&spScript))) { return false; } CComBSTR bstrMember(lpFuncName);
DISPID dispid = NULL;
HRESULT hr = spScript->GetIDsOfNames(IID_NULL, &bstrMember, , LOCALE_SYSTEM_DEFAULT, &dispid);
if (FAILED(hr)) { return false; } const int arraySize = paramArray.size(); DISPPARAMS dispparams;
memset(&dispparams, , sizeof dispparams);
dispparams.cArgs = arraySize;
dispparams.rgvarg = new VARIANT[dispparams.cArgs]; for (int i = ; i < arraySize; i++)
{
CComBSTR bstr = paramArray[arraySize - - i].c_str(); // back reading
bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
dispparams.rgvarg[i].vt = VT_BSTR;
}
dispparams.cNamedArgs = ; EXCEPINFO excepInfo;
memset(&excepInfo, , sizeof excepInfo);
CComVariant vaResult;
UINT nArgErr = (UINT)-; // initialize to invalid arg hr = spScript->Invoke(dispid, IID_NULL, , DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr); delete [] dispparams.rgvarg;
if (FAILED(hr)) { return false; } if (pVarResult) { *pVarResult = vaResult; } return true;
} void EnumFrame(
CComPtr<IHTMLDocument2> spIHTMLDocument2,
LPCTSTR lpFuncName,const vector<wstring>& paramArray,
CComVariant* pVarResult)
{
if ( !spIHTMLDocument2 )
return; CComPtr< IHTMLFramesCollection2 > spFramesCollection2;
spIHTMLDocument2->get_frames( &spFramesCollection2 ); long nFrameCount=;
HRESULT hr = spFramesCollection2->get_length( &nFrameCount );
if ( FAILED ( hr ) || == nFrameCount )
return; for(long i = ; i < nFrameCount; i++)
{
CComVariant vDispWin2;
hr = spFramesCollection2->item( &CComVariant(i), &vDispWin2 );
if ( FAILED ( hr ) )
continue; CComQIPtr< IHTMLWindow2 > spWin2 = vDispWin2.pdispVal;
if( !spWin2 )
continue; CComPtr < IHTMLDocument2 > spDoc2;
spDoc2 = HtmlWindowToHtmlDocument(spWin2);
if (!spDoc2)
continue; CallFunctionInDocument(spDoc2, lpFuncName, paramArray, pVarResult);
}
} bool calljs_helper::CallFunction(
CComPtr<IWebBrowser2> spIWebBrowser,
LPCTSTR lpFuncName,
const vector<wstring>& paramArray,
CComVariant * pVarResult,
bool bEnumFrame)
{
if (!spIWebBrowser)
return false; CComPtr<IDispatch> spDispDoc;
HRESULT hr = spIWebBrowser->get_Document(&spDispDoc);
if (FAILED(hr))
return false; CComQIPtr<IHTMLDocument2> spDocument2 = spDispDoc;
if (!spDocument2)
return false; CallFunctionInDocument(spDocument2, lpFuncName, paramArray, pVarResult); if (bEnumFrame)
{
EnumFrame(spDocument2, lpFuncName, paramArray, pVarResult);
} return true;
} // -------------------------------------------------------------------------
// $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.

WebBrowser内嵌页面的跨域调用问题的更多相关文章

  1. AJAX跨域调用相关知识-CORS和JSONP(引)

    AJAX跨域调用相关知识-CORS和JSONP 1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容. 但是我们项目开发过程中,经常 ...

  2. 子页面iframe跨域执行父页面定义的JS方法

    问题需求:父页面与子页面iframe跨域嵌套,子页面要触发父页面所定义的js方法.父子页面的数据传递. 下文中会用到一些文件:父页面: parent.html嵌在父页面的子iframe页面:child ...

  3. AJAX跨域调用ASP.NET MVC或者WebAPI服务的解决方案

    问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况. 重现方式 使用模板创建一个最简单的ASP.NET Web ...

  4. 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码】

    项目中常常用到搜索,特别是导航类的网站.自己做关键字搜索不太现实,直接调用百度的是最好的选择.使用jquery.ajax的jsonp方法可以异域调用到百度的js并拿到返回值,当然$.getScript ...

  5. 跨域调用webapi

    web端跨域调用webapi   在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webap ...

  6. 关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案

      作者:陈希章 时间:2014-7-3 问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况. 重现方式 使用模 ...

  7. 跨域调用webapi web端跨域调用webapi

    web端跨域调用webapi   在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webap ...

  8. jquery Ajax跨域调用WebServices方法

    由于公司需要开发一个手机页面,想提供给同事直接在手机上可以查询SAP资料.数据需要使用js调用webserver来获取. 因为初次使用Jquery调用Webserver,所以期间并不顺利.测试调用We ...

  9. web端跨域调用webapi

    在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webapi的程序,如下图所示: 由于微软已 ...

随机推荐

  1. JavaScript 工厂模式和订阅模式

    设计模式的好处: 代码规范 // 例如表单验证,两个 input ,一个用户名,一个密码 // 通常做法是 function checkUser(){ //..... } function check ...

  2. Laravel建站05--缓存、时间日期处理包

    缓存 Laravel 给多种缓存系统提供丰富而统一的 API,缓存配置信息位于 config/cache.php,在这个文件中你可以为你的应用程序指定默认的缓存驱动,Laravel 支持当前流行的缓存 ...

  3. CSS盒模型之三角形

    W3上介绍盒模型: 这里教程,但是太过于简单了,http://www.w3.org/community/webed/wiki/CSS/Training/Box_model. 如图,盒模型和背景属性控制 ...

  4. 服务管理-Apache

    WEB服务器介绍 web server 有两个意思: 一台负责提供网页的服务器,通过HTTP协议传给客户端(一般是指网页浏览器). 一个提供网页的服务器程序. 常见的WEB服务器 Apache是世界使 ...

  5. 每天一点儿Java--list

    import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.event.*; /** * ...

  6. Webkit JNI

    WebCoreFrameBridge.cpp BrowserFrame通过jni传下来的调用都会调用到WebCoreFrameBridge.cpp中的对应函数中,其他webkit的模块想回调信息给Br ...

  7. openjdk源码目录结构

    1 openjdk源码 http://hg.openjdk.java.net 选择jdk8u这个project, 然后选择jdk8u20这个repository. 2 目录结构 corba: comm ...

  8. 使用Primose方式解决异步编程回调的一些问题--animate动画的例子

    function animate(dis, time) { var def = $.Deferred(); $('.boll') .animate({ left: dis + 'px' }, time ...

  9. Boost智能指针——scoped_ptr

    boost::scoped_ptr和std::auto_ptr非常类似,是一个简单的智能指针,它能够保证在离开作用域后对象被自动释放. 上一段代码,以及其输出: #include <string ...

  10. platform_set_drvdata和platform_get_drvdata用法【转】

    本文转载自:http://www.cnblogs.com/wangxianzhen/archive/2013/04/09/3009530.html 在用到Linux设备驱动的platform框架时,常 ...