WebBrowser介绍——Javascript与C++互操作
WebBrowser控件是Microsoft提供的一個(gè)用于網(wǎng)頁(yè)瀏覽的客戶端控件,WebBrowser控件的使用相當(dāng)廣泛,例如很多郵件客戶端都是使用可編輯的WebBrowser控件作為寫郵件的工具,也有很多軟件用WebBrowser控件彈出網(wǎng)頁(yè),如qq的新聞首頁(yè)。
微軟的MFC和.NET都有WebBrowser控件,這兩個(gè)控件雖然容易上手,不過(guò)由于包裝的太好,所以很難深入。因此本文介紹的WebBrowser將不使用MFC和.NET,而是使用C++實(shí)現(xiàn)SDK的WebBrowser。
由于本文主要探討如何實(shí)現(xiàn)JavaScript與C++的互操作,對(duì)于如何使用SDK實(shí)現(xiàn)WebBrowser,本文不做詳細(xì)介紹,讀者可以參考以下這篇文章:
http://blog.csdn.net/norsd/archive/2008/09/13/2921389.aspx
不過(guò)盡管文章中介紹了SDK實(shí)現(xiàn)WebBrowser的要點(diǎn),卻沒(méi)有提供一個(gè)可以運(yùn)行的示例,如果要看到實(shí)際的運(yùn)行效果,可以下載以下這份源代碼,源代碼中也包括了互操作的演示:
SDK實(shí)現(xiàn)WebBrowser及演示代碼 [好文,推薦一下看不懂]1、C++調(diào)用WebBrowser中的全局函數(shù),變量等
(1) 從C++的角度看WebBrowser中的對(duì)象
WebBrowser中的對(duì)象大致可以分成兩類:DOM對(duì)象和使用Javascript創(chuàng)建的對(duì)象。但是無(wú)論是那種對(duì)象,從C++的角度來(lái)看,都是一些實(shí)現(xiàn)了IDispatch接口的對(duì)象,因此,如果用C++操作WebBrowser中的對(duì)象(全局函數(shù),變量,DOM)等,只需要通過(guò)IDispatch即可。
(2) 3個(gè)常用的函數(shù)
當(dāng)獲取了WebBrowser的對(duì)象的IDispatch接口后,就可以調(diào)用IDispatch的Invoke方法來(lái)調(diào)用對(duì)象的方法,獲取對(duì)象的屬性和設(shè)置對(duì)象的屬性。但是Invoke是通過(guò)ID判斷要調(diào)用指定對(duì)象的哪一個(gè)方法(或?qū)傩?#xff09;,因此在通過(guò)方法(或?qū)傩?#xff09;名稱調(diào)用對(duì)象的方法是,必須先調(diào)用IDispatch的GetIDsOfNames方法,將方法(或?qū)傩?#xff09;名轉(zhuǎn)換成ID,然后才能通過(guò)IDispatch的Invoke方法調(diào)用對(duì)象的方法。為了方便操作,封裝了三個(gè)函數(shù),分別用于調(diào)用WebBrowser的對(duì)象的方法,讀取對(duì)象的屬性,設(shè)置對(duì)象的屬性。
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)調(diào)用頁(yè)面的全局函數(shù)
在網(wǎng)頁(yè)中,所有的全局函數(shù)均是window的一個(gè)方法,因此,如果要調(diào)用全局函數(shù),首先要獲取到頁(yè)面的window對(duì)象,然后用InvokeMethod調(diào)用全局函數(shù),例如,假設(shè)頁(yè)面中有一個(gè)Test全局函數(shù):
<script language="javascript" type="text/javascript"> function Test() {alert("你調(diào)用了Test"); } </script>那么,您可以在C++中用以下代碼調(diào)用Test函數(shù):
VARIANT params[10]; VARIANT ret; //獲取頁(yè)面window IDispatch *pHtmlWindow = pBrowser->GetHtmlWindow(); //頁(yè)面全局函數(shù)Test實(shí)際上是window的Test方法, CWebBrowserBase::InvokeMethod(pHtmlWindow, L"Test", &ret, params, 0);(4)調(diào)用全局對(duì)象的方法
在網(wǎng)頁(yè)中,所有的全局變量均是window的一個(gè)屬性,因此,如果要調(diào)用變量的方法(或?qū)傩?#xff09;,首先要獲取到頁(yè)面的window對(duì)象,然后用GetProperty獲取到全局變量,然后就可以調(diào)用這個(gè)對(duì)象的方法,或讀寫其屬性。例如,假設(shè)頁(yè)面中有一個(gè)globalObject全局變量:
<script language="javascript" type="text/javascript"> function GlobalObject() {this.Test=function(){alert("你調(diào)用了GlobalObject.Test");} }var globalObject = new GlobalObject(); </script>那么,您可以使用一下代碼調(diào)用globalObject的Test方法:
VARIANT params[10]; VARIANT ret; //獲取頁(yè)面window IDispatch *pHtmlWindow = pBrowser->GetHtmlWindow(); //獲取globalObject CVariant globalObject; params[0].vt = VT_BSTR; params[0].bstrVal = L"globalObject"; CWebBrowserBase::GetProperty(pHtmlWindow, L"globalObject", &globalObject); //調(diào)用globalObject.Test CWebBrowserBase::InvokeMethod(globalObject.pdispVal, L"Test", &ret, params, 0);2、在網(wǎng)頁(yè)中調(diào)用客戶端的方法
上文我們已經(jīng)介紹了如何在C++中調(diào)用WebBrowser中的對(duì)象,接下來(lái),將介紹如何在頁(yè)面中調(diào)用客戶端中的函數(shù)和對(duì)象:
(1) 通過(guò)window.external調(diào)用
下面將示例如何通過(guò)window.external調(diào)用客戶端中的函數(shù),假設(shè)在C++中定義了一個(gè)名為CppCall的函數(shù):
void CppCall() {MessageBox(NULL, L"您調(diào)用了CppCall", L"提示(C++)", 0); }定義一個(gè)對(duì)象,并且實(shí)現(xiàn)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){//網(wǎng)頁(yè)調(diào)用window.external.CppCall時(shí),會(huì)調(diào)用這個(gè)方法獲取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){//網(wǎng)頁(yè)調(diào)用CppCall時(shí),或根據(jù)獲取到的ID調(diào)用Invoke方法CppCall();}return S_OK;} };定義類ClientCall后,就可以創(chuàng)建一個(gè)ClientCall的對(duì)象,傳遞給WebBrowser,使得網(wǎng)頁(yè)中可以通過(guò)window.external調(diào)用CppCall,要實(shí)現(xiàn)這些功能,WebBrowser需要實(shí)現(xiàn)IDocHostUIHandler接口,并重寫GetExternal方法以返回一個(gè)ClientCall對(duì)象:
ClientCall *pClientCall; pClientCall = new ClientCall();virtual HRESULT STDMETHODCALLTYPE GetExternal(IDispatch **ppDispatch) {//重寫GetExternal返回一個(gè)ClientCall對(duì)象*ppDispatch = pClientCall;return S_OK; }接下來(lái),就可以在網(wǎng)頁(yè)中調(diào)用了:
window.external.CppCall() (2)向網(wǎng)頁(yè)傳遞回調(diào)函數(shù)向網(wǎng)頁(yè)傳遞回調(diào)函數(shù)的一個(gè)典型應(yīng)用就是在客戶端中用C++處理DOM的事件(例如,處理按鈕的onclick事件),這里要注意的是,與C++不同的是,在網(wǎng)頁(yè)中,所謂的函數(shù),其實(shí)就是一個(gè)具有call方法的對(duì)象,因此,向網(wǎng)頁(yè)傳遞一個(gè)回調(diào)函數(shù),其實(shí)就是傳遞一個(gè)實(shí)現(xiàn)了call方法的對(duì)象,因此,我們必須定義一個(gè)C++類,并實(shí)現(xiàn)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 ){//令人費(fèi)解的是,網(wǎng)頁(yè)調(diào)用函數(shù)的call方法時(shí),沒(méi)有調(diào)用GetIDsOfNames獲取call的ID,而是直接調(diào)用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;} };接下來(lái),我們就可以使用JsFunction向網(wǎng)頁(yè)傳遞回調(diào),以下代碼用于處理按鈕的onclick事件:
static void _stdcall button1_onclick(LPVOID pParam) {MainForm *pMainForm = (MainForm*)pParam;MessageBox(pMainForm->hWnd, L"您點(diǎn)擊了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);總結(jié)
以上是生活随笔為你收集整理的WebBrowser介绍——Javascript与C++互操作的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: oracle提交数据按键,Oracle
- 下一篇: 矩阵 计算机应用,《计算机视觉算法:基于