v:* { }
o:* { }
w:* { }
.shape { }p.MsoNormal,li.MsoNormal,div.MsoNormal { margin: 0cm; margin-bottom: .0001pt; text-align: justify; font-size: 10.5pt; font-family: "Times New Roman", "serif" }
p.MsoHeader,li.MsoHeader,div.MsoHeader { margin: 0cm; margin-bottom: .0001pt; text-align: justify; border: none; padding: 0cm; font-size: 9.0pt; font-family: "Times New Roman", "serif" }
p.MsoFooter,li.MsoFooter,div.MsoFooter { margin: 0cm; margin-bottom: .0001pt; font-size: 9.0pt; font-family: "Times New Roman", "serif" }
a:visited,span.MsoHyperlinkFollowed { color: purple; text-decoration: underline }
span.SpellE { }
span.GramE { }
.MsoChpDefault { }
div.WordSection1 { }table.MsoNormalTable { font-size: 10.0pt; font-family: "Times New Roman", "serif" }

Javascript和BHO的相互调用简介

作者:magictong

Javascript调用BHO里面的函数

方案一、脚本扩展(window.external)

Window.external本身是浏览器提供的用来调用浏览器的外部方法的,譬如IE里面就提供了很多外部方法(参考[1])。如果我们想脚本能调用插件的函数,可以通过扩展这个接口的方法来实现(也就是脚本扩展)。

BHO实现要点

首先实现IDocHostUIHandler接口([3]),这个接口本身是IE暴露的一个界面替代接口(可以修改部分界面表现),通过ICustomDoc接口的SetUIHandler方法把IDocHostUIHandler设置到相应的页面上面,从而达到改变页面部分界面效果的作用,不过我们这里不用来改变界面,因此IDocHostUIHandler接口的大部分方法都可以简单实现,重点实现GetExternal方法即可。

GetExternal方法的实现也并不是很复杂,它只需要返回一个IDispatch接口即可,熟悉IE框架下面脚本和插件交互流程的童鞋应该很清楚返回这个接口的意义所在。因此这个地方我们需要实现一个继承自IDispatch接口的COM类,这里也是较为麻烦的一个地方。

实现说明

我们可以使用BHO类来实现IDocHostUIHandler接口即可,注意重点实现GetExternal方法,其实也很简单(注意:IDocHostUIHandler的大部分方法都可以返回一个S_OK即可)。

GetExternal的实现:

HRESULT STDMETHODCALLTYPECAdPromotionImp::GetExternal(IDispatch __RPC_FAR*__RPC_FAR *ppDispatch)

{

if(ppDispatch)

{

*ppDispatch = (IDispatch*)(new (std::nothrow) CJSObject());

}

return S_OK;

}

下面重点看一下这个CJSObject类的实现,它需要实现IDispatch接口,意味着你得把IUnknown接口也一并实现。IUnknown接口的实现就不详述了,看看IDispatch接口的四个方法的实现,GetTypeInfoCount和GetTypeInfo简单返回S_OK即可。另外两个方法的实现如下:

HRESULT STDMETHODCALLTYPECJSObject::GetIDsOfNames(

REFIID riid,

LPOLESTR __RPC_FAR*rgszNames,

UINT cNames,

LCID lcid,

DISPID __RPC_FAR*rgDispId)

{

if(!rgszNames || 0== cNames || NULL== rgDispId)

{

return S_OK;

}

*rgDispId = DISPID_UNKNOWN;

CComBSTR bstrRgszName(*rgszNames);

if (bstrRgszName== JS_CALL_FUN)

{

*rgDispId = DISP_ID_TESETSCRIPT;

return S_OK;

}

// 对于参数不正确,依然return S_OK 防止浏览器报脚本错误

return S_OK;

}

HRESULT STDMETHODCALLTYPECJSObject::Invoke(

DISPID dispIdMember,

REFIID riid,

LCID lcid,

WORD wFlags,

DISPPARAMS __RPC_FAR*pDispParams,

VARIANT __RPC_FAR*pVarResult,

EXCEPINFO __RPC_FAR*pExcepInfo,

UINT __RPC_FAR*puArgErr)

{

if(0 == (wFlags& DISPATCH_METHOD ))

{

// 对于参数错误的情况也return S_OK 防止浏览器报脚本错误

return S_OK;

}

if(dispIdMember ==DISP_ID_TESETSCRIPT)

{

::MessageBoxW(0, L"JS调用BHO函数TestScript()", L"TestScript", 0);

}

return S_OK;

}

要实现更复杂的函数就自己去想象吧……

另外,还得找一个地方去调用ICustomDoc接口的SetUIHandler方法哦!在哪里调用就看你的需求了而且要防止重入(在你感兴趣的网页需要且仅需调用一次)。

HRESULThr = E_FAIL;

CComPtr<IDispatch> spDoc;

CComQIPtr<IHTMLDocument2> spHTML;

m_spWebBrowser->get_Document(&spDoc);

spHTML= spDoc;

if(spHTML)

{

CComQIPtr<ICustomDoc, &IID_ICustomDoc>spCustomDoc;

hr = spHTML->QueryInterface(__uuidof(ICustomDoc), (void**)&spCustomDoc);

if (SUCCEEDED(hr) && spCustomDoc)

{

hr = spCustomDoc->SetUIHandler(this);

}

}

有副作用吗?

实现IDocHostUIHandler时要注意,为了避免副作用(因为你替换了系统原来的IDocHostUIHandler接口),在实现该接口的某些方法时为了避免内存泄漏,你应该把方法的调用总是转发给原来的IDocHostUIHandler接口,方法是在SetUIHandler之前先获取原来的IDocHostUIHandler接口并保存,然后在ShowUI和HideUI等方法里面转调源IDocHostUIHandler接口进行处理。

优缺点

在上面的讨论中零零碎碎有提到过,优点的话譬如实现相对来说比较简单等等,缺点就比较明显了副作用比较大,实现IDocHostUIHandler接口时要防止影响原始的功能(譬如另外一个插件也实现了这个接口),应用场景比较有限,另外安全性需要额外小心,因为函数名是固定的,需要保护好函数的参数等等。

测试

<html>

<head>

<script language='javascript'>

function call_external(){

try

{

window.external.TestScript();

}

catch(err)

{

alert(err.description);

}

}

</script>

</head>

<body onload="call_external();">

<center><div><span>HelloMagictong!!</span>

</div>

</center>

</body>

</html>

执行成功

方案二、导出类(属性注入)

上面的方法副作用很多,那么是否有其它的方法可以使用呢?答案是肯定的。还记得上面的方法里面在实现GetExternal时,返回了一个IDispatch接口给外部的脚本执行环境,对window.external空间进行了扩展的,导出类的方式其实也是类似的,但是扩展的是脚本的window空间,大概你可以这样认为,假如你导出了一个名字为KClass的对象给脚本的window空间,同时你的KClass对象实现了IDispatch接口,支持一个名为DoFunciton的函数,那么JS脚本里面就可以这样调用window.KClass.DoFunciton()。我们来看怎么实现。

在上面的方法中,我们已经把实现了IDispatch接口的类实现好了,就是CJSObject,可以直接拿来用,没有问题,关键看导出类的部分就可以了。

bool CAdPromotionImp::__ExtendJsWindow()

{

// 首先获取document2接口

CComPtr<IDispatch>spDispatch;

HRESULT hr = m_spWebBrowser->get_Document(&spDispatch);

if (!SUCCEEDED(hr) || !spDispatch)

return false;

CComQIPtr<IHTMLDocument2>pHTMLDoc(spDispatch);

if (!pHTMLDoc)

return false;

// 获取htmlwindow窗口

CComPtr<IHTMLWindow2>spWindow;

hr = pHTMLDoc->get_parentWindow(&spWindow);

if (!SUCCEEDED(hr) || !spWindow)

return false;

CComQIPtr<IDispatchEx>pHtmlWindow = spWindow;

if (!pHtmlWindow)

return false;

CComBSTR propName(L"KClass");

DISPID dispid= 0;

pHtmlWindow->GetDispID(propName, fdexNameEnsure,&dispid );

if (!SUCCEEDED(hr))

return false;

CComVariant varAdBho((IDispatch *)(new (std::nothrow) CJSObject()));

DISPPARAMS disParams= {&varAdBho, 0, 1, 0};

hr = pHtmlWindow->InvokeEx( dispid,LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUTREF, &disParams,NULL, NULL,NULL );

if (!SUCCEEDED(hr))

{

hr = pHtmlWindow->InvokeEx( dispid,LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &disParams,NULL, NULL,NULL );

}

if (!SUCCEEDED(hr))

{

return false;

}

return true;

}

有副作用吗?

基本没有。

优缺点

其实实现起来比上面的方法不会复杂。而且没有副作用,不会干扰其它插件或者浏览器的固有行为。导出类可以做命名随机和加密,可以降低命名冲突的可能性,同时也可以降低其它网页对插件的恶意攻击行为。另外需要注意的是,为了加强代码的健壮性,防止内存泄漏,建议调试时严格控制下导出类的声明周期。

测试

<html>

<head>

<script language='javascript'>

function call_external()

{

try

{

window.KClass.TestScript2();

}

catch(err)

{

alert(err.description);

}

}

</script>

</head>

<body onload="call_external();">

<center><div><span>HelloMagictong!!</span>

</div>

</center>

</body>

</html>

执行成功

方案三、Axtivex控件

这个东东生来的理由就是为了让脚本来调用它,扩展IE的功能,但是因为不在本文的讨论范围,因此不再讨论。

BHO调用Javascript里面的函数

这个相对来说简单一些,主要就是通过调用IHTMLWindow2接口的execScript来完成对JS脚本里面函数的调用。

看这个方法说明,很直观吧。

直接看代码吧。

bool CAdPromotionImp::__CallJSFunction()

{

CComPtr<IDispatch>spDispatch;

HRESULT hr = m_spWebBrowser->get_Document(&spDispatch);

if (!SUCCEEDED(hr) || !spDispatch)

return false;

CComQIPtr<IHTMLDocument2>pHTMLDoc(spDispatch);

if (!pHTMLDoc)

return false;

CComPtr<IHTMLWindow2>spWindow;

hr = pHTMLDoc->get_parentWindow(&spWindow);

if (!SUCCEEDED(hr) || !spWindow)

return false;

VARIANT out;

hr = spWindow->execScript(CComBSTR("whocallme_Function()"), CComBSTR(L"javascript"), &out);

if (SUCCEEDED(hr))

return true;

return false;

}

调用时机

这个就看你的需求了,另外一个需要注意的时,你得等待JS代码(譬如你调用的是一个JS函数)已经被IE加载完成了,BHO再调用才能成功。

测试

<html>

<head>

<script language='javascript'>

function whocallme_Function()

{

alert("BHO call JS function!");

}

</script>

</head>

<body>

<center><div><span>HelloMagictong!!</span>

</div>

</center>

</body>

</html>

执行成功

参考资料

[1] external object http://msdn.microsoft.com/en-us/library/ms535246%28VS.85%29.aspx

[2] Popup WindowBlocker http://www.codeproject.com/Articles/4003/Popup-Window-Blocker

[3] IDocHostUIHandler interface http://msdn.microsoft.com/en-us/library/aa753260(v=vs.85).aspx

[4] JS调用BHO http://www.cnblogs.com/dlbrant/p/3142887.html

[5] 一点编写 BHO 的思路 http://bbs.csdn.net/topics/370215253

Javascript和BHO的相互调用简介的更多相关文章

  1. JQuery javascript实现父子页面相互调用

    javascript实现父子页面相互调用 By:授客 QQ:1033553122 场景1 父页面调用子页面 如上图,在iframe子页面的<script>元素中,定义了taskStatus ...

  2. javascript与java的相互调用,纯java的javascript引擎rhino(转载)

    1.下载Rhino安装包,下载地址:官网http://www.mozilla.org/rhino. 2.rhino环境配置,把解压出来的js.jar文件加入到系统的环境变量classpath 3.在命 ...

  3. 开源项目ScriptGate,Delphi与JavaScript相互调用的神器

    ScriptGate是一个实现TWebBrowser上的JavaScript和Delphi代码相互调用的库,具体在这里:https://bitbucket.org/freeonterminate/sc ...

  4. Android和JavaScript相互调用的方法

    转载地址:http://www.jb51.net/article/77206.htm 这篇文章主要介绍了Android和JavaScript相互调用的方法,实例分析了Android的WebView执行 ...

  5. Android中通过WebView控件实现与JavaScript方法相互调用的地图应用

    在Android中通过WebView控件,可以实现要加载的页面与Android方法相互调用,我们要实现WebView中的addJavascriptInterface方法,这样html才能调用andro ...

  6. ASP.net关于C#代码与javaScript函数的相互调用

    C#代码与javaScript函数的相互调用 问:1.如何在JavaScript访问C#函数?2.如何在JavaScript访问C#变量?3.如何在C#中访问JavaScript的已有变量?4.如何在 ...

  7. android与javascript相互调用

    下面这一节来介绍android和javascript是怎么相互调用的,这样我们的UI界面设计起来就简单多了,而且UI设计起来也可以跨平台.现在有好多web app前台框架了,比如sencha和jque ...

  8. Hybrid App开发模式中, IOS/Android 和 JavaScript相互调用方式

    IOS:Objective-C 和 JavaScript 的相互调用 iOS7以前,iOS SDK 并没有原生提供 js 调用 native 代码的 API.但是 UIWebView 的一个 dele ...

  9. C#代码和javascript函数相互调用

    C#代码与javaScript函数的相互调用 问:1.如何在JavaScript访问C#函数?2.如何在JavaScript访问C#变量?3.如何在C#中访问JavaScript的已有变量?4.如何在 ...

随机推荐

  1. 粗浅看Struts2和Hibernate框架

    ----------------------------------------------------------------------------------------------[版权申明: ...

  2. 十大豪门推送sdk,哪个更适合你

    转自:http://jingyan.baidu.com/article/d621e8da0fd7042865913ff5.html 推送,使得开发者可以即时地向其应用程序的用户推送通知或者消息,与用户 ...

  3. 这是最好的时光 这是最坏的时光 v0.1.1.1

    这是最好的时光 这是最坏的时光 v0.1.1.1 1.2 学校的生活二三事之大学 话说上一回,扯了一下我青涩的少年往事,大家反响不一,有叫好的,有吐槽的,有字字码过的,也有一目十行的.我的心情也是随着 ...

  4. android M Launcher之LauncherModel (三)

    通过前两篇的分析,我们已经知道了LauncherModel的初始化及工作流程,如果您还不熟悉的话请看前两篇博文 android M Launcher之LauncherModel (一) android ...

  5. MacOS获取辅助功能权限控制鼠标点击事件

    昨晚玩一个模拟经营的游戏,由于升级太慢我就不停的种树卖树来换取经验值.不过重复点击10几分钟后,实在受不了.网上本来准备找个鼠标自动点击的软件用用.结果没找到趁手的.如是自己写了个. 自己设置需要点击 ...

  6. (Java)微信之个人公众账号开发(二)——接收并处理用户消息(下)

    接下来,我们再讲一下图文消息: 如图: 大家可以先从开发者文档中了解一下图文消息的一些参数: 如上图,用户回复4时,ipastor返回了几条图文消息,上图中属于多图文消息,当然还有单图文消息,图文消息 ...

  7. Dynamics CRM2016 业务流程之Task Flow(一)

    Task Flow 属于CRM移动端的特性,如果在项目实施中用不到CRM自带的APP或者对自APP不感冒的,那就没有往下看的必要了,移步吧. 该功能默认是不开启的,需要我们去系统设置中开启它,打勾,选 ...

  8. Solr 5.5.0 + tomcat 7.0.69 + zookeeper-3.4.6 Cloud部署

    Solr介绍:Solr是一个独立的企业级搜索应用服务器,Solr基于Lucene的全文搜索服务器,同时对其进行了扩展,提供了比Lucene更为丰富的查询语言,同时实现了可配置.可扩展并对查询性能进行了 ...

  9. Ubuntu下配置Telnet服务器

    1. 首先介绍linux中的守护进程 在Linux系统中有一个特殊的守护进程inetd(InterNET services Daemon),它用于Internet标准服务,通常在系统启动时启动.通过命 ...

  10. [ExtJS5学习笔记]第二十一节 Extjs5中使用config配置给ext.widget或者create方法传递参数

    本文地址:http://blog.csdn.net/sushengmiyan/article/details/39252805 官方例子:http://docs.sencha.com/extjs/5. ...