duilib c 与html交互,duilib中webbrowser实现js和c++互相调用的方法_赵克立博客_技术栈...

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及演示代码 [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全局函数:

{

alert("你调用了Test");

}

那么,您可以在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全局变量:

function GlobalObject()

{

this.Test=function()

{

alert("你调用了GlobalObject.Test");

}

}

var globalObject = new GlobalObject();

那么,您可以使用一下代码调用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 Methods

STDMETHODIMP 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 Methods

HRESULT _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 Methods

STDMETHODIMP 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 Methods

HRESULT _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,而是直接调用Invoke

return 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);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值