ActiveX异步回调JavaScript

开发环境:VC6.0。

背景知识:COM/ActiveX/JavaScript/MFC/Thread

想必用过Ajax的童鞋们都知道xmlhttp这个东西吧,通过设定onreadystatechange属性,我们就可以指定他状态改变的回调函数,当状态改变时,ActiveX控件就会调用我们通过onreadystatechange属性制定的回调函数。从而就出现了Ajax给我们带来的精彩。关于Ajax的技术我们这里不做讨论,我们的目的就是实现像xmlhttp这样具有异步回调JavaScript功能的ocx控件来。

Let’s go!

1.      建立MFC ActiveX Control(方法略)

2.      在ClassWizard中添加属性callbackfunction属性,并为该属性生成get和set方法。我们将在ActiveX控件中开启线程,线程执行完后将调用通过该属性执行的JavaScript函数。在该实例中,通过callbackfunction属性指定的JavaScript函数必须是返回值是void的,并且含有一个short类型的参数的函数。

3.      我们需要一个方法来触发回调函数,添加方法Invoke包含一个short类型的参数param。在这个函数里将开启一个线程进行运算,然后返回计算结果。并把结果以回调函数的形式调用JavaScript的函数。

4.      在Invoke方法中开启线程。进行计算。线程同步的方法采用PostMessage自定义消息。这个很重要,否则的话,我们在线程中操作界面控件是不正确的。(我就是忘记了进行线程同步才多走了好多弯路)

#define WM_THREADFIREEVENT WM_USER+101

void f(void * r)

{

CThirdCtrl* p = (CThirdCtrl*)r;

Sleep(5000);

p->m_param +=10;

PostMessage(p->m_hWnd,WM_THREADFIREEVENT,(WPARAM)NULL,(LPARAM)NULL);

return;

}

void CThirdCtrl::invoke(short param)

{

m_param = param;

_beginthread(f, 0, (void*)(this));

}

5.      添加THREADFIREEVENT消息的消息映射函数:

ON_MESSAGE(WM_THREADFIREEVENT,OnFireEventForThread)

6.   实现函数OnFireEventForThread:

LRESULT CThirdCtrl::OnFireEventForThread(WPARAM wParam, LPARAM lParam)

{

//FireLengthyProcessDone();

InvokeScript ();

return TRUE;

}

7.  在实现InvokeScript前,先说一个重要的东西,就是OnSetClientSite这是一个CThirdCtrl的父类ColeControl的一个虚方法。我们需要重写他来获得IWebBrowser2指针,有了IWebBrowser2我们就可以为所欲为了。比方说获得document对象,获得html中的elements,设定他们的属性,调用方法。也可以执行页面中的JavaScript函数。

为获得顶层IWebBrowser2引用,从客户站点获取IServiceProvider接口并且执行一个QueryService 操作获取IID_IServiceProvider服务:SID_STopLevelBrowser (这在Shlguid.h中定义);对第二个IServiceProvider,执行一个QueryService获取IID_IWebBrowser2服务:SID_SWebBrowserApp.

上代码:

void CThirdCtrl::OnSetClientSite()

{

IOleClientSite*  pClientSite  =  GetClientSite();

HRESULT  hr  =  S_OK;

IServiceProvider  *isp,  *isp2  =  NULL;//用于导航DHTML对象层次,作用就是提供服务

if  (!pClientSite)

{

if(browser!=NULL)

{

browser->Release();

browser = NULL;

}

return;//  !S_OK;

}

else

{

hr  =  pClientSite->QueryInterface(IID_IServiceProvider,  reinterpret_cast<void  **>(&isp));

if  (FAILED(hr))

{

hr  =  S_OK;

goto  cleanup;

}

hr  =  isp->QueryService(SID_STopLevelBrowser,  IID_IServiceProvider,  reinterpret_cast<void  **>(&isp2));

if  (FAILED(hr))

{

hr  =  S_OK;

goto  cleanup;

}

//获得浏览器

hr  =  isp->QueryService(SID_SWebBrowserApp,  IID_IWebBrowser2,  reinterpret_cast<void  **>(&browser));

if  (FAILED(hr))

{

hr  =  S_OK;

goto  cleanup;

}

cleanup:

//  Free  resources.

if(isp!=NULL)

{

isp->Release();

isp = NULL;

}

if(isp2!=NULL)

{

isp2->Release();

isp2 = NULL;

}

return;//  hr;

}

return;//  hr;

}

同样的道理,如果我们是ATL做的ActiveX,则需要重写

STDMETHODIMP  CThirdCtrl::SetClientSite()

这个方法。

8.   下面就是最关键的InvokeScript函数的实现,我们在这里使用上面获取到的IWebBrowser2指针来获取document对象,然后获取Idispatch接口的script对象,然后调用Idispatch接口的Invoke方法。就可以调用JavaScript了。Idispatch接口真是强大啊。

废话少说,上代码:

void  CThirdCtrl::InvokeScript()

{

if(!browser)

{

if(browser!=NULL)

{

browser->Release();

browser = NULL;

}

return;

}

CComPtr<IHTMLDocument2> m_spDoc;

HRESULT hr = browser->get_Document((IDispatch**)&m_spDoc);

if(FAILED(hr))

throw("");

CComPtr<IDispatch> pScript;

hr = m_spDoc->get_Script(&pScript);

if(FAILED(hr))

throw("");

CComBSTR  bstrMember(m_callbackfunction);

DISPID  dispid;

hr=pScript->GetIDsOfNames(IID_NULL,&bstrMember,1,LOCALE_SYSTEM_DEFAULT,&dispid);

//  设置函数参数

DISPPARAMS  dispparams;

memset(&dispparams,0,sizeof(dispparams));

dispparams.cArgs = 1;//表示参数的计数。

dispparams.rgvarg = new VARIANT[dispparams.cArgs];//表示对参数数组的引用。

for(int i = 0; i < 1; i++)

{

//CComBSTR bstr = "111";  //  back  reading

//bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);

dispparams.rgvarg[i].iVal = m_param;

dispparams.rgvarg[i].vt = VT_I2;

}

dispparams.cNamedArgs =0;//表示命名参数的计数。

EXCEPINFO excepInfo;

memset(&excepInfo,0,sizeof(excepInfo));

CComVariant vaResult;

UINT nArgErr = (UINT)-1;  //  initialize  to  invalid  arg

hr = pScript->Invoke(dispid, IID_NULL,0,DISPATCH_METHOD,&dispparams,&vaResult,&excepInfo,&nArgErr);

}

这样,ActiveX控件就完成了。

9.      编写html页面代码。打开Microsoft ActiveX Control Pad,插入控件。然后编写JavaScript代码。

<HTML>

<HEAD>

<TITLE>New Page</TITLE>

</HEAD>

<BODY>

<SCRIPT LANGUAGE="JavaScript" >

function invoke()

{

Third1.callbackfunction = "callback";

Third1.invoke(2);

alert("begin invoke");

}

function callback(param)

{

alert(param);

}

</SCRIPT>

<OBJECT ID="Third1" WIDTH=100 HEIGHT=51

CLASSID="CLSID:E9D38528-0F4E-468B-858D-69905F16942F">

<PARAM NAME="_Version" VALUE="65536">

<PARAM NAME="_ExtentX" VALUE="2646">

<PARAM NAME="_ExtentY" VALUE="1323">

<PARAM NAME="_StockProps" VALUE="0">

</OBJECT>

<input type="button" value="test" onclick="invoke();" />

</BODY>

</HTML>

10.  测试:打开浏览器,打开test.html页面。点击“test“按钮,将会先显示对话框begin invoke,然后过5秒钟再显示对话框12。

11.  调试方法:我们可以直接调试浏览器。浏览器加载了控件,然后我们调用控件的方法,这时会自动触发我们在工程中设置的断点。在

project---settings---debug---executable for debug sessions设置浏览器的exe文件的路径。我用的世界之窗浏览器。所以值设置为:C:\Program Files\TheWorld\TheWorld.exe

如果你用IE浏览器,可设置为:C:\Program Files\Internet Explorer\iexplore.exe

说明:

1.   上述控件与xmlhttp不同的地方是callbackfunction我传的是一个字符串,而xmlhttp传的是一个JavaScript的函数指针。

2.   COM中的线程模型不在本文讨论范围之内。还有浏览器安全问题和打包CAB的问题也不在本文讨论范围之内。

参考:

http://vcfaq.mvps.org/com/1.htm

http://vcfaq.mvps.org/com/11.htm

http://support.microsoft.com/kb/q157437/

ActiveX异步回调JavaScript的更多相关文章

  1. ActiveX多线程回调JavaScript

    http://www.cnblogs.com/zdxster/archive/2011/01/27/1945872.html

  2. node 异步回调解决方法之yield

    先看如何使用 使用的npm包为genny,npm 安装genny,使用 node -harmony 文件(-harmony 为使用es6属性启动参数) 启动项目 var genny= require( ...

  3. State Threads——异步回调的线性实现

    State Threads——异步回调的线性实现 原文链接:http://coolshell.cn/articles/12012.html 本文的标题看起来有点拗口,其实State Threads库就 ...

  4. jquery.Deferred promise解决异步回调

    我们先来看一下编写AJAX编码经常遇到的几个问题: 1.由于AJAX是异步的,所有依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套,ajax等异步操作越多,嵌套层次就会越 ...

  5. 深入探析koa之异步回调处理篇

    在上一篇中我们梳理了koa当中中间件的洋葱模型执行原理,并实现了一个可以让洋葱模型自动跑起来的流程管理函数.这一篇,我们再来研究一下koa当中异步回调同步化写法的原理,同样的,我们也会实现一个管理函数 ...

  6. 前端入门20-JavaScript进阶之异步回调的执行时机

    声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...

  7. js同步-异步-回调

    出处:https://blog.csdn.net/u010297791/article/details/71158212(1)上面主要讲了同步和回调执行顺序的问题,接着我就举一个包含同步.异步.回调的 ...

  8. js异步回调Async/Await与Promise区别 新学习使用Async/Await

    Promise,我们了解到promise是ES6为解决异步回调而生,避免出现这种回调地狱,那么为何又需要Async/Await呢?你是不是和我一样对Async/Await感兴趣以及想知道如何使用,下面 ...

  9. c#线程之异步委托begininvoke、invoke、AsyncWaitHandle.WaitOne 、异步回调

    单靠自己看书学总是会走很多弯路,任何人也不列外,有些时候自己遇到的很多问题,其它别人在很久之前也可能遇到过,上网查查可以走很大捷径,对自己的学习有很大帮助,刚开始弄线程这块,一开始只是看书,很多东西都 ...

随机推荐

  1. hdu Hat's Fibonacci

    import java.math.BigInteger; import java.util.*; public class Main { public static void main(String ...

  2. tangible T4 Editor 2.2.3 plus modeling tools for VS 2012 扩展名

    tangible T4 Editor 2.2.3 plus modeling tools for VS 2012 扩展名 tangible T4 Editor 2.2.3 plus modeling ...

  3. error C2440

    error C2440: "初始化": 无法从"std::_List_const_iterator<std::_List_val<std::_List_sim ...

  4. autoprefixer安装或者里sass的$mixin处理浏览器前缀

    Autoprefixer是一个后处理程序,不象Sass以及Stylus之类的预处理器.它适用于普通的CSS,可以实现css3代码自动补全.也可以轻松跟Sass,LESS及Stylus集成,在CSS编译 ...

  5. Unity 关于属性的get/set

    学习Unity的可能多数是C#转过来的, 一进来的时候你会发现Unity编写代码,在一些视频或文章中.基本都没有用过get/set使用, 多数是public string name;这样写的公开字段, ...

  6. 模块工具类--utils

    File: js\utils.js/** * 模块工具类,用来初始化各模块视图.自定绑定事件以及其他辅助功能等 * @class Utils */Utils = (function() { var i ...

  7. RMAN数据库恢复 之归档模式有(无)备份-丢失数据文件的恢复

    1.归档模式有备份,丢失数据文件的恢复归档模式有备份,不管丢失什么数据文件,直接在RMAN下RESTOER--->RECOVER--->OPEN即可. RMAN> STARUP MO ...

  8. 【贪心】【Uva11292】 勇者斗恶龙

    直接用白书上的翻译吧 例题1  勇者斗恶龙(The Dragon of Loowater, UVa 11292) 你的王国里有一条n个头的恶龙,你希望雇一些骑士把它杀死(即砍掉所有头).村里有m个骑士 ...

  9. CentOS 6.5下Percona Xtrabackup的安装错误解决方案

    1.下载最新版的Xtracbackup 2.安装 yum install perl-DBIyum install perl-DBD-MySQLyum install perl-Time-HiResyu ...

  10. java学习之Date的使用

    Date使用,主要要注意将日期格式化,否则返回的是系统默认的格式.请自己查阅API文档. import java.util.*; import java.text.*; public class Te ...