WebBrowser控件是Microsoft提供的一个用于网页浏览的客户端控件,WebBrowser控件的使用相当广泛,例如很多邮件客户端都是使用可编辑的WebBrowser控件作为写邮件的工具,也有很多软件用WebBrowser控件弹出网页,如qq的新闻首页。

微软的MFC和.NET都有WebBrowser控件,这两个控件虽然容易上手,不过由于包装的太好,所以很难深入。因此本文介绍的WebBrowser将不使用MFC和.NET,而是使用C++实现SDK的WebBrowser。

由于本文主要探讨如何实现JavaScript与C++的互操作,对于如何使用SDK实现WebBrowser,本文不做详细介绍,读者可以参考以下这篇文章:

http://blog.csdn.net/norsd/archive/2008/09/13/2921389.aspx

不过尽管文章中介绍了SDK实现WebBrowser的要点,却没有提供一个可以运行的示例,如果要看到实际的运行效果,可以下载以下这份源代码,源代码中也包括了互操作的演示:

SDK实现WebBrowser及演示代码 [好文,推荐一下看不懂]

1、C++调用WebBrowser中的全局函数,变量等

(1) 从C++的角度看WebBrowser中的对象

WebBrowser中的对象大致可以分成两类:DOM对象和使用Javascript创建的对象。但是无论是那种对象,从C++的角度来看,都是一些实现了IDispatch接口的对象,因此,如果用C++操作WebBrowser中的对象(全局函数,变量,DOM)等,只需要通过IDispatch即可。

(2) 3个常用的函数

当获取了WebBrowser的对象的IDispatch接口后,就可以调用IDispatch的Invoke方法来调用对象的方法,获取对象的属性和设置对象的属性。但是Invoke是通过ID判断要调用指定对象的哪一个方法(或属性),因此在通过方法(或属性)名称调用对象的方法是,必须先调用IDispatch的GetIDsOfNames方法,将方法(或属性)名转换成ID,然后才能通过IDispatch的Invoke方法调用对象的方法。为了方便操作,封装了三个函数,分别用于调用WebBrowser的对象的方法,读取对象的属性,设置对象的属性。

DISPID CWebBrowserBase::FindId(IDispatch *pObj, LPOLESTR pName)
{DISPID id = 0;if(FAILED(pObj->GetIDsOfNames(IID_NULL,&pName,1,LOCALE_SYSTEM_DEFAULT,&id))) id = -1;return id;
}HRESULT CWebBrowserBase::InvokeMethod(IDispatch *pObj, LPOLESTR pName, VARIANT *pVarResult, VARIANT *p, int cArgs)
{DISPID dispid = FindId(pObj, pName);if(dispid == -1) return E_FAIL;DISPPARAMS ps;ps.cArgs = cArgs;ps.rgvarg = p;ps.cNamedArgs = 0;ps.rgdispidNamedArgs = NULL;return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &ps, pVarResult, NULL, NULL);
}HRESULT CWebBrowserBase::GetProperty(IDispatch *pObj, LPOLESTR pName, VARIANT *pValue)
{DISPID dispid = FindId(pObj, pName);if(dispid == -1) return E_FAIL;DISPPARAMS ps;ps.cArgs = 0;ps.rgvarg = NULL;ps.cNamedArgs = 0;ps.rgdispidNamedArgs = NULL;return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &ps, pValue, NULL, NULL);
}HRESULT CWebBrowserBase::SetProperty(IDispatch *pObj, LPOLESTR pName, VARIANT *pValue)
{DISPID dispid = FindId(pObj, pName);if(dispid == -1) return E_FAIL;DISPPARAMS ps;ps.cArgs = 1;ps.rgvarg = pValue;ps.cNamedArgs = 0;ps.rgdispidNamedArgs = NULL;return pObj->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &ps, NULL, NULL, NULL);
}

(3)调用页面的全局函数

在网页中,所有的全局函数均是window的一个方法,因此,如果要调用全局函数,首先要获取到页面的window对象,然后用InvokeMethod调用全局函数,例如,假设页面中有一个Test全局函数:

<script language="javascript" type="text/javascript">
function Test()
{alert("你调用了Test");
}
</script>

那么,您可以在C++中用以下代码调用Test函数:

VARIANT params[10];
VARIANT ret;
//获取页面window
IDispatch *pHtmlWindow = pBrowser->GetHtmlWindow();
//页面全局函数Test实际上是window的Test方法,
CWebBrowserBase::InvokeMethod(pHtmlWindow, L"Test", &ret, params, 0);

(4)调用全局对象的方法

在网页中,所有的全局变量均是window的一个属性,因此,如果要调用变量的方法(或属性),首先要获取到页面的window对象,然后用GetProperty获取到全局变量,然后就可以调用这个对象的方法,或读写其属性。例如,假设页面中有一个globalObject全局变量:

<script language="javascript" type="text/javascript">
function GlobalObject()
{this.Test=function(){alert("你调用了GlobalObject.Test");}
}var globalObject = new GlobalObject();
</script>

那么,您可以使用一下代码调用globalObject的Test方法:

VARIANT params[10];
VARIANT ret;
//获取页面window
IDispatch *pHtmlWindow = pBrowser->GetHtmlWindow();
//获取globalObject
CVariant globalObject;
params[0].vt = VT_BSTR;
params[0].bstrVal = L"globalObject";
CWebBrowserBase::GetProperty(pHtmlWindow, L"globalObject", &globalObject);
//调用globalObject.Test
CWebBrowserBase::InvokeMethod(globalObject.pdispVal, L"Test", &ret, params, 0);

2、在网页中调用客户端的方法

上文我们已经介绍了如何在C++中调用WebBrowser中的对象,接下来,将介绍如何在页面中调用客户端中的函数和对象:

(1) 通过window.external调用

下面将示例如何通过window.external调用客户端中的函数,假设在C++中定义了一个名为CppCall的函数:

void CppCall()
{MessageBox(NULL, L"您调用了CppCall", L"提示(C++)", 0);
}

定义一个对象,并且实现IDispatch接口:

class ClientCall:public IDispatch
{long _refNum;
public:ClientCall(){_refNum = 1;}~ClientCall(void){}
public:// IUnknown MethodsSTDMETHODIMP QueryInterface(REFIID iid,void**ppvObject){*ppvObject = NULL;if (iid == IID_IUnknown)    *ppvObject = this;else if (iid == IID_IDispatch)    *ppvObject = (IDispatch*)this;if(*ppvObject){AddRef();return S_OK;}return E_NOINTERFACE;}STDMETHODIMP_(ULONG) AddRef(){return ::InterlockedIncrement(&_refNum);}STDMETHODIMP_(ULONG) Release(){::InterlockedDecrement(&_refNum);if(_refNum == 0){delete this;}return _refNum;}// IDispatch MethodsHRESULT _stdcall GetTypeInfoCount(unsigned int * pctinfo) {return E_NOTIMPL;}HRESULT _stdcall GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo) {return E_NOTIMPL;}HRESULT _stdcall GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId ){if(lstrcmp(rgszNames[0], L"CppCall")==0){//网页调用window.external.CppCall时,会调用这个方法获取CppCall的ID*rgDispId = 100;}return S_OK;}HRESULT _stdcall Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS* pDispParams,VARIANT* pVarResult,EXCEPINFO* pExcepInfo,unsigned int* puArgErr){if(dispIdMember == 100){//网页调用CppCall时,或根据获取到的ID调用Invoke方法CppCall();}return S_OK;}
};

定义类ClientCall后,就可以创建一个ClientCall的对象,传递给WebBrowser,使得网页中可以通过window.external调用CppCall,要实现这些功能,WebBrowser需要实现IDocHostUIHandler接口,并重写GetExternal方法以返回一个ClientCall对象:

ClientCall *pClientCall;
pClientCall = new ClientCall();virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **ppDispatch)
{//重写GetExternal返回一个ClientCall对象*ppDispatch = pClientCall;return S_OK;
}

接下来,就可以在网页中调用了:

window.external.CppCall()

(2)向网页传递回调函数

向网页传递回调函数的一个典型应用就是在客户端中用C++处理DOM的事件(例如,处理按钮的onclick事件),这里要注意的是,与C++不同的是,在网页中,所谓的函数,其实就是一个具有call方法的对象,因此,向网页传递一个回调函数,其实就是传递一个实现了call方法的对象,因此,我们必须定义一个C++类,并实现IDispatch接口:

typedef void _stdcall JsFunction_Callback(LPVOID pParam);class JsFunction:public IDispatch
{long _refNum;JsFunction_Callback *m_pCallback;LPVOID m_pParam;
public:JsFunction(JsFunction_Callback *pCallback, LPVOID pParam){_refNum = 1;m_pCallback = pCallback;m_pParam = pParam;}~JsFunction(void){}
public:// IUnknown MethodsSTDMETHODIMP QueryInterface(REFIID iid,void**ppvObject){*ppvObject = NULL;if (iid == IID_IOleClientSite)    *ppvObject = (IOleClientSite*)this;else if (iid == IID_IUnknown)    *ppvObject = this;if(*ppvObject){AddRef();return S_OK;}return E_NOINTERFACE;}STDMETHODIMP_(ULONG) AddRef(){return ::InterlockedIncrement(&_refNum);}STDMETHODIMP_(ULONG) Release(){::InterlockedDecrement(&_refNum);if(_refNum == 0){delete this;}return _refNum;}// IDispatch MethodsHRESULT _stdcall GetTypeInfoCount(unsigned int * pctinfo) {return E_NOTIMPL;}HRESULT _stdcall GetTypeInfo(unsigned int iTInfo,LCID lcid,ITypeInfo FAR* FAR* ppTInfo) {return E_NOTIMPL;}HRESULT _stdcall GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId ){//令人费解的是,网页调用函数的call方法时,没有调用GetIDsOfNames获取call的ID,而是直接调用Invokereturn E_NOTIMPL;}HRESULT _stdcall Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS* pDispParams,VARIANT* pVarResult,EXCEPINFO* pExcepInfo,unsigned int* puArgErr){m_pCallback(m_pParam);return S_OK;}
};

接下来,我们就可以使用JsFunction向网页传递回调,以下代码用于处理按钮的onclick事件:

static void _stdcall button1_onclick(LPVOID pParam)
{MainForm *pMainForm = (MainForm*)pParam;MessageBox(pMainForm->hWnd, L"您点击了button1", L"提示(C++)", 0);
}VARIANT params[10];//获取window
IDispatch *pHtmlWindow = GetHtmlWindow();//获取document
CVariant document;
params[0].vt = VT_BSTR;
params[0].bstrVal = L"document";
GetProperty(pHtmlWindow, L"document", &document);//获取button1
CVariant button1;
params[0].vt = VT_BSTR;
params[0].bstrVal = L"button1";
InvokeMethod(document.pdispVal, L"getElementById", &button1, params, 1);//处理button1的onclick事件
params[0].vt = VT_DISPATCH;
params[0].pdispVal = new JsFunction(button1_onclick, this);
SetProperty(button1.pdispVal, L"onclick", params);

WebBrowser介绍——Javascript与C++互操作相关推荐

  1. C# webBrowser与javascript互调

    实现步骤: 一.新建一个窗体,加入webBrowser控件 控件名:webBrowser1 二.在窗体后台代码加入如下定义 [System.Runtime.InteropServices.ComVis ...

  2. 如何妥善处理WebBrowser对Javascript的错误问题,阻止JS弹出框,提高用户体验(原创)...

    由于项目需求,最近转战客户端,开始搞浏览器开发.众所周知,现在在微软平台上开发浏览器,最常用的方法就是扩展Webbrowser,但是首先要清楚的是,WebBrowser控件仅仅是对WebBrowser ...

  3. 简单介绍Javascript匿名函数和面向对象编程

    忙里偷闲,简单介绍一下Javascript中匿名函数和闭包函数以及面向对象编程.首先简单介绍一下Javascript中的密名函数. 在Javascript中函数有以下3中定义方式: 1.最常用的定义方 ...

  4. 全方位地介绍JavaScript开发中的各个主题《JavaScript编程全解》(好书分享更新中)

    JavaScript编程全解 作者: [日]井上诚一郎 / [日]土江拓郎 / [日]滨边将太 出版社: 人民邮电出版社 译者: 陈筱烟 内容简介  · · · · · · 本书全方位地介绍了Java ...

  5. 重新介绍JavaScript(JS教程)

    原文链接:https://developer.mozilla.org/en-US/docs/Web/JavaScript/A_re-introduction_to_JavaScript 为什么要重新介 ...

  6. 重新介绍 JavaScript(JS 教程)

    为什么会有这一篇"重新介绍"呢?因为 JavaScript 堪称世界上被人误解最深的编程语言.虽然常被嘲为"玩具语言",但在它看似简洁的外衣下,还隐藏着强大的语 ...

  7. 详细介绍javascript中的单体模式singleton(全面)

    一.单体模式描述 1.单体模式是javascript中最基本但又最有用的模式之一,他可能比其他任何模式都常用. 2.这种模式提供了一种将代码组织为一个逻辑单元的手段,这个逻辑单元中的代码可以通过单一的 ...

  8. 抽象方法示例_示例介绍JavaScript窗口方法

    抽象方法示例 窗口定位方法 (Window location Method) The window.location object can be used to get information on ...

  9. html怎么实现计算器功能键介绍,JavaScript实现网页计算器功能

    本文实例为大家分享了JavaScript实现网页计算器的具体代码,供大家参考,具体内容如下 要求:在网页上实现简单的计算器功能和界面 CSS样式代码: * { margin: 0; padding: ...

最新文章

  1. crontab的用法
  2. 直播 | 孙剑团队最新工作:用于物体检测的实例条件知识蒸馏 | NeurIPS 2021
  3. java 数据库中获取省市级json数据
  4. Java格式化日期和时间模式占位符
  5. flask框架中勾子函数的使用
  6. html5 选择列表,Html5添加基于列表的选择美化插件教程
  7. 载波聚合或双连接的方式进行_处理载波聚合及双连接的装置及方法与流程
  8. vue ui框架_Vue移动端UI框架指南
  9. gcc编译器参数使用及解决
  10. Linux 异步IO
  11. Android小游戏--2048
  12. tagcanvas.min.js 文字云
  13. 【算法小结】费马小定理
  14. flv视频转换成mp4格式怎么转?
  15. 《白帽子讲web安全》读书笔记_2021年7月16日(2)_第3篇 服务器端应用安全
  16. 关于流量分析软件brim最新安装方法
  17. mybatis大于号 小于号
  18. 背包算法设计(参考学习)
  19. 【数据挖掘】关联分析之先验(Apriori)原理与Apriori算法
  20. IP解析:含义、作用、格式、分类

热门文章

  1. php httphelper,C#的HttpHelper类post ,get
  2. android 北斗定位代码_大牛三步教你解决,BAT资深APP性能优化系列-卡顿定位问题,收藏哦
  3. [LeetCode] Flatten Binary Tree to Linked List
  4. UEditor使用报错Cannot set property 'innerHTML' of undefined
  5. 【代码笔记】iOS-removeFromSuper
  6. 安装rabbitMQ delayed-messaged
  7. 链表操作---面向过程--到---面型对象---到模板类
  8. 统计SQL2005中数据库中的每张表的记录数
  9. atitit.加入win 系统服务 bat批处理程序服务的法总结instsrv srvany java linux
  10. Linux网络技术管理