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就能解决问题了。
解决方案:直接上代码吧
/* -------------------------------------------------------------------------
// 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内嵌页面的跨域调用问题的更多相关文章
- AJAX跨域调用相关知识-CORS和JSONP(引)
AJAX跨域调用相关知识-CORS和JSONP 1.什么是跨域 跨域问题产生的原因,是由于浏览器的安全机制,JS只能访问与所在页面同一个域(相同协议.域名.端口)的内容. 但是我们项目开发过程中,经常 ...
- 子页面iframe跨域执行父页面定义的JS方法
问题需求:父页面与子页面iframe跨域嵌套,子页面要触发父页面所定义的js方法.父子页面的数据传递. 下文中会用到一些文件:父页面: parent.html嵌在父页面的子iframe页面:child ...
- AJAX跨域调用ASP.NET MVC或者WebAPI服务的解决方案
问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况. 重现方式 使用模板创建一个最简单的ASP.NET Web ...
- 使用jsonp跨域调用百度js实现搜索框智能提示,并实现鼠标和键盘对弹出框里候选词的操作【附源码】
项目中常常用到搜索,特别是导航类的网站.自己做关键字搜索不太现实,直接调用百度的是最好的选择.使用jquery.ajax的jsonp方法可以异域调用到百度的js并拿到返回值,当然$.getScript ...
- 跨域调用webapi
web端跨域调用webapi 在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webap ...
- 关于AJAX跨域调用ASP.NET MVC或者WebAPI服务的问题及解决方案
作者:陈希章 时间:2014-7-3 问题描述 当跨域(cross domain)调用ASP.NET MVC或者ASP.NET Web API编写的服务时,会发生无法访问的情况. 重现方式 使用模 ...
- 跨域调用webapi web端跨域调用webapi
web端跨域调用webapi 在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webap ...
- jquery Ajax跨域调用WebServices方法
由于公司需要开发一个手机页面,想提供给同事直接在手机上可以查询SAP资料.数据需要使用js调用webserver来获取. 因为初次使用Jquery调用Webserver,所以期间并不顺利.测试调用We ...
- web端跨域调用webapi
在做Web开发中,常常会遇到跨域的问题,到目前为止,已经有非常多的跨域解决方案. 通过自己的研究以及在网上看了一些大神的博客,写了一个Demo 首先新建一个webapi的程序,如下图所示: 由于微软已 ...
随机推荐
- Intel平台map
- Java IO 类
IO包中绝大部分的类都是由以下四个类直接或间接继承来的InputStream OutputStream Reader 还有Writer 其中InputStream和OutputStream代表输入流和 ...
- Android中Environment与StatFs获取系统/SDCard存储空间大小
近期想起Android开发的知识.好久没有使用了,都忘得几乎相同了,今天查看了一会资料往回捡捡,顺便写下来帮助一下须要的同学. 首先讲述一下Environment与StatFs这两个类,然后介绍它们的 ...
- Ipython基础功能
ipython:交互式的python命令行 直接在终端敲命令即可进入 安装:pip install ipython 使用:在终端敲“ipython” 与python解释器的使用方法一致 TAB键自动补 ...
- Zabbix 3.0安装
Server 1. rpm安装zabbix 3.0最新的epel源 rpm -ivh http://repo.zabbix.com/zabbix/3.0/rhel/7/x86_64/zabbix-re ...
- Appium python自动化测试系列之认识Appium(四)
4.1界面认识 在之前安装appium的时候说过我们有两种方法安装,也就有两种结果,一种是有界面的,一种是没有界面的,首先我们先讲一下有界面的,以及界面有哪些东西. 首先看第一幅图,如果你的是win ...
- wcf系列(一)--- 寄宿方式
一.自我寄宿(self-hosting) 1.wcf采用基于终结点(Endpoint)的通信手段:终结点由:地址(Address)+绑定(Binding)+契约(Contract)组成: Enpoi ...
- 九度OJ 1092:Fibonacci (递归)
时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:1923 解决:1378 题目描述: The Fibonacci Numbers{0,1,1,2,3,5,8,13,21,34,55...} ...
- Message-oriented middleware
en.wikipedia.org/wiki/Message-oriented_middleware Message-oriented middleware (MOM) is software or h ...
- 20170313 ABAP程序未激活状态下保存或激活进入debug
自动进入断点,没有设置的.FUNCTION RS_NEW_PROGRAM_INDEX. https://archive.sap.com/discussions/message/14132983 解决办 ...