22.WebBrowser中JS和C++代码互相调用
来源:互联网 发布:淘宝怎么设置客服接待 编辑:程序博客网 时间:2024/05/01 21:04
利用WebBrowser控件我们可以利用各种Web界面库做出高大上的界面和炫酷的动画,扩展性也好,甚至可以实现界面实时升级。但是有一点问题,在WebBrowser内嵌的网页中如何访问本地计算机硬件呢?实时上,WebBrowser内嵌的网页中JS与本地C++代码可以相互调用,这样就可以最大程度利用C++强大的计算能力和与本地硬件通信。
在正式讲解前,需要指出的是为什么两者可以相互调用,其核心是IE控件是基于COM 的自动化IDispatch接口,这样只需要向IE控件发送命令即可完成C++调用JS,反之JS方法执行时,IE控件的实现可以调用外部指定的一个IDispatch接口,从而调用C++代码,记住这一点即可理解本文的所有内容。
1.C++调用JS
C++调用JS就是取到IE控件的对应接口,调用即可。
a)第一种方法
利用浏览器DOM中的Window窗口执行指定的js串,方法封装如下:
void CMainDlg::ExecScript1(LPCWSTR pszJs){if (!m_spWebBrowser){return;}CComPtr<IDispatch> spDispDoc;m_spWebBrowser->get_Document(&spDispDoc);if(!spDispDoc) {return;}CComPtr<IHTMLDocument2> spHtmlDoc;CComPtr<IHTMLWindow2> spHtmlWindow;HRESULT hr = spDispDoc->QueryInterface(IID_IHTMLDocument2,(void**)&spHtmlDoc);if (spHtmlDoc){if (SUCCEEDED(spHtmlDoc->get_parentWindow(&spHtmlWindow)) && spHtmlWindow){CComBSTR bstrJs = pszJs;CComBSTR bstrlan = L"javascript";VARIANT varRet;varRet.vt = VT_EMPTY;spHtmlWindow->execScript(bstrJs, bstrlan, &varRet);}}}调用如下:
void CMainDlg::OnScript1(UINT uNotifyCode, int nID, CWindow wndCtl){ExecScript1(L"window.alert(\'ExecScript1\')");}
b)第二种方法
获取IE控件中的JavaScript对象,执行它的方法,封装如下:
void CMainDlg::ExecScript2(){CComPtr<IDispatch> spDisp;HRESULT hr = m_spWebBrowser->get_Document(&spDisp);if (SUCCEEDED(hr)){CComQIPtr<IHTMLDocument2> spDoc2 = spDisp;if (spDoc2){CComDispatchDriver spScript;hr = spDoc2->get_Script(&spScript);if (SUCCEEDED(hr)){// {// CComVariant varRet; // spScript.Invoke0(L"test1", &varRet); // int a = 10;// }//--Add1{CComVariant var1 = 10, var2 = 20, varRet; spScript.Invoke2(L"Add1", &var1, &var2, &varRet); CString strVal;strVal.Format(L"%d", varRet.intVal);OutputDebugString(strVal.GetBuffer(0));}//--Add2{CComVariant var1 = 10, var2 = 20, varRet; spScript.Invoke2(L"Add2", &var1, &var2, &varRet); CComDispatchDriver spArray = varRet.pdispVal; //获取数组中元素个数,这个length在JS中是Array对象的属性CComVariant varArrayLen; spArray.GetPropertyByName(L"length", &varArrayLen); //获取数组中第0,1,2个元素的值: CComVariant varValue[3]; spArray.GetPropertyByName(L"0", &varValue[0]); spArray.GetPropertyByName(L"1", &varValue[1]); spArray.GetPropertyByName(L"2", &varValue[2]); CString strVal;strVal.Format(L"%d %d %d", varValue[0].intVal, varValue[1].intVal, varValue[2].intVal);OutputDebugString(strVal.GetBuffer(0));}//--Add3{CComVariant var1 = 10, var2 = 20, varRet; spScript.Invoke2(L"Add3", &var1, &var2, &varRet); CComDispatchDriver spData = varRet.pdispVal; CComVariant varValue1, varValue2; spData.GetPropertyByName(L"result", &varValue1); spData.GetPropertyByName(L"str", &varValue2); CString strVal;strVal.Format(L"%d %s", varValue1.intVal, varValue2.bstrVal);OutputDebugString(strVal.GetBuffer(0));}}}}}
代码处理了返回值是数组或结构体的方法可供参考。
要这样调用代码,还必须在html中定义对应的JS 函数,注意此时必须在对话框中输入对应的html网页地址,如下:function Add1(value1, value2) { window.alert('Add1'); return value1 + value2; } function Add2(value1, value2) { var array = new Array(); array[0] = value1; array[1] = value2; array[2] = value1 + value2; window.alert('Add2'); return array; } function Add3(value1, value2) { var data = new Object(); data.result = value1 + value2; data.str = "Hello,World!"; window.alert('Add3'); return data; } function test1(){ return function() { alert('test1'); }}test1方法返回一个函数类型,稍后我们再讨论它。
2.JS调用C++
如前文所说,js调用C++代码是因为IE控件实现时允许调用外部指定的IDispatch接口方法,俗称“打洞”,很容易理解,本来是各玩各的,结果现在打了个洞,可以从浏览器调到本地了,这个洞就是IDispatch接口。
js中调用外部接口方法是,通过window.external,如下:
<script type="text/javascript">function OnSayGoodBye() { window.alert(typeof window.external.SayGoodBye); window.alert(typeof window.document.getElementById); window.external.SayGoodBye(10,'DaGoodBye!');}</script> </head><body><p><button type="button" onclick="window.external.SayHello('DaHello!')">SayHello!</button></p><p><button type="button" onclick="OnSayGoodBye()">SayGoodBye!</button></p></body>这里两个按钮点击,都会调到我们的IDispatch接口上,那么怎么才能知道会调到我们的IDispatch接口上呢,答案是在InitDialog中初始化时注册下即可。
//设置浏览器内容回调接口wndIE.SetExternalDispatch((IDispatch*)(&m_ExternalObject));
其中m_ExternalObject就是我们自定义的IDispatch的实现,这这个类中需要不用类型库实现SayHello和SayGoodBye的响应如下:
HRESULT STDMETHODCALLTYPE CExternalObject::Invoke(/* [in] */ DISPID dispIdMember, /* [in] */ REFIID riid, /* [in] */ LCID lcid, /* [in] */ WORD wFlags, /* [out][in] */ DISPPARAMS *pDispParams, /* [out] */ VARIANT *pVarResult, /* [out] */ EXCEPINFO *pExcepInfo, /* [out] */ UINT *puArgErr){if (0==dispIdMember || (dispIdMember!=EXTFUNC_ID_HELLO && dispIdMember!=EXTFUNC_ID_GOODBYE) || 0==(DISPATCH_METHOD&wFlags || DISPATCH_PROPERTYGET&wFlags)) { return E_NOTIMPL; }if (pVarResult) { CComVariant var(true); *pVarResult = var; } //判断属性if (DISPATCH_PROPERTYGET&wFlags){return S_OK;}USES_CONVERSION; //调用本地方法switch (dispIdMember) { case EXTFUNC_ID_HELLO: if (pDispParams &&//参数数组有效 pDispParams->cArgs==1 &&//参数个数为1 pDispParams->rgvarg[0].vt==VT_BSTR &&//参数类型满足 pDispParams->rgvarg[0].bstrVal)//参数值有效 { CString strVal(OLE2T(pDispParams->rgvarg[0].bstrVal)); AtlMessageBox(NULL, strVal.GetBuffer(0), L"SayHello");} break; case EXTFUNC_ID_GOODBYE: if (pDispParams &&//参数数组有效 pDispParams->cArgs==2 &&//参数个数为2 pDispParams->rgvarg[1].vt==VT_I4 &&pDispParams->rgvarg[0].vt==VT_BSTR &&//参数类型满足 pDispParams->rgvarg[1].bstrVal &&pDispParams->rgvarg[0].bstrVal)//参数值有效 { CString strVal;strVal.Format(L"%d %s", pDispParams->rgvarg[1].bstrVal, pDispParams->rgvarg[0].bstrVal);AtlMessageBox(NULL, strVal.GetBuffer(0), L"SayGoodBye");} break; } return S_OK; }
这里我们判断对应的调用参数和类型,然后做出对应响应,即可完成对应本地调用。
值得注意的是在上文js代码中我们看到,
window.alert(typeof window.external.SayGoodBye); window.alert(typeof window.document.getElementById);我们分别输出自定义接口函数和浏览器自身函数类型对比,前者为boolean,后者为object,这是为什么?
看到上文C++代码中
//判断属性if (DISPATCH_PROPERTYGET&wFlags){return S_OK;}Disptach调用参数为DISPATCH_PROPERTYGET即为取当前函数的属性,这里我们并没有默认指明pVarResult的结果,所以才会出现使用默认值boolean。那么如果想让这个返回类型正确,应该返回什么呢?
因为JS是允许返回函数类型的,上文我们说了test1函数返回的是函数类型,因此我们在ExecScript2中的调用test1,断点看他返回的结果在C++中的布局即可。结果如下:
可以看到,这里的函数类型(object)对应C++中的IDispatch。结合JS中的函数对象特征,我们调整返回值,如下处理:
//判断属性if (DISPATCH_PROPERTYGET&wFlags){pVarResult->vt = VT_DISPATCH;pVarResult->pdispVal = this;return S_OK;}再次查看结果=object,满足需求,实际中有些js代码判断了待调用函数的类型,对于自定义的外部接口函数一定要注意这里的坑。
完整演示代码下载链接,注意加载目录htmls中的js文件进行测试
原创,转载请注明来自http://blog.csdn.net/wenzhou1219
- 22.WebBrowser中JS和C++代码互相调用
- Android和js代码互相调用
- C和C++中如何互相调用
- Js代码和Java代码之间的互相调用了
- Cordova2.0(既PhoneGap)中通过Plugin实现Java和JS代码互相调用
- Webview中js与本地java代码的互相调用
- Android JNI中C和JAVA代码之间的互相调用
- iOS的JS和OC代码互相调用
- 汇编和C代码之间的互相调用
- silverlight 和js互相调用
- as和js互相调用
- Android 和 js 互相调用
- android和js互相调用
- Android中WebView和JavaScript(JS)的互相调用
- iOS UIWebView中JS和OC互相调用
- C++ 和 C 互相调用
- .NET中WebBrowser控件内部页面的JS代码与外部C#代码的相互调用
- NET中WebBrowser控件内部页面的JS代码与外部C#代码的相互调用
- 理解与配置Android studio中的gradle
- 数据结构: 单链表排序
- “删除重复元素”:保留两个
- [队内测试Day10.22T1][bzoj1821]部落划分 Group
- JZ2440 第7章 内存管理单元 MMU
- 22.WebBrowser中JS和C++代码互相调用
- 从YOLOv1谈到YOLOv2(3)二代的准确度改进(上)
- 交叉熵是否非负?
- Android高效加载大图、多图解决方案,有效避免程序OOM 摘记
- [HDU] 5544 Ba Gua Zhen
- 网络学习——Unity3D的Time类(UnityEngine.Time)详解
- jsp九大内置对象,作用及方法
- Java多线程——线程间协作方式总结及使用示例
- 文章标题