ActiveX异步回调JavaScript

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/

时间: 2024-10-02 06:19:03

ActiveX异步回调JavaScript的相关文章

使用生成器展平异步回调结构,JS篇

1. 前言 2012 年的时候,我去详细了解过 Python 的 Tornado 框架中的 gen.py 这套工具,http://www.zouyesheng.com/generator-for-async.html ,因为觉得它用于异步环境的编程中实在太方便了,而且,适用性上几乎没有成本,你的定义部分代码完全不需要因为这套工具而作任何改动,这套工具完全是"使用时"的一种可选形式. 那时我想的就是,如果在遍地是 callback 的 Javascript 中也有这样的东西可用就好了.后

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

在上一篇中我们梳理了koa当中中间件的洋葱模型执行原理,并实现了一个可以让洋葱模型自动跑起来的流程管理函数.这一篇,我们再来研究一下koa当中异步回调同步化写法的原理,同样的,我们也会实现一个管理函数,是的我们能够通过同步化的写法来写异步回调函数. 1. 回调金字塔及理想中的解决方案 我们都知道javascript是一门单线程异步非阻塞语言.异步非阻塞当然是它的一个优点,但大量的异步操作必然涉及大量的回调函数,特别是当异步嵌套的时候,就会出现回调金字塔的问题,使得代码的可读性非常差.比如下面一个

node.js学习笔记(6) 异步回调

先来看一下国王的幸福生活: 在nodejs王国,国王有很多仆人. 早上,一个仆人叫醒了国王,问他有什么需要. 国王给他一份清单,上面列举了所有需要完成的任务,然后睡回笼觉去了. 当国王回去睡觉之后,仆人才离开国王,拿着清单,给其它的仆人一个个布置任务. 仆人们各自忙各自的去了,直到完成了自己的任务后,才回来把结果禀告给国王. 国王一次只召见一个人,其它的人就在外面排着队等着. 国王处理完这个结果后,可能给他布置一个新的任务,或者就直接让他走了,然后再召见下一个人. 等所有的结果都处理完了,国王就

jQuery的promise与deferred对象在异步回调中的作用_jquery

一.前言 为了让前端们从回调的地狱中回到天堂, jQuery 也引入了 Promise 的概念. Promise 是一种令代码异步行为更加优雅的抽象,有了它,我们就可以像写同步代码一样去写异步代码. jQuery 从1.5版本开始实现了 CommonJS Promise/A 规范这一重量级方案,不过没有严格按照规范进行实现,有一些API上的差异. 好,让我们来看看他们的特性吧( 本文示例基于jquery 1.8版本以上 ). 二.示例 以前写动画时,我们通常是这么干的: $('.animateE

ASP.NET服务器控件封装-【事件】-1.1【事件回发.异步回调】

最近,正在学习ASP.NET服务器控件封装相关的知识,把自己学到的和大家分 享下. 本次内容的概要如下: 1.事件以及为什么需要事件驱动机制: 2.回发的原理: 3.异步回调的原理: 4.事件回发的实现: 5.异步回调的实现. 了解了本次内容的概要,接下来就分节次说明了. 1.事件以及为什么需要事件驱动机制 在C#语言详解一书中对事件的定义是"事件是一种使对象或类能够提供通知的 成员",在这里换句话 说就是页面中已注册事件的对象能够对用户的操作进行捕获并处理.那么为什么 需要引用事件机

ASP.NET 2.0中控件的简单异步回调

虽然已经有了ASP.NET AJAX了,最近学习ASP.NET控件的时候,逐步理解了原始的控件异步回调(代码取自<ASP.NET 2.0 高级编程>): 首先,在Render事件中添加好一个事件 protected override void RenderContents(HtmlTextWriter output) { output.RenderBeginTag(HtmlTextWriterTag.Div); output.AddAttribute(HtmlTextWriterAttribu

微信扫码支付成功后未执行异步回调方法

问题描述 微信扫码支付成功后未执行异步回调方法 微信扫码支付成功后未执行异步回调方法,电脑页面也没有任何变化,是什么原因呢

Android异步回调中的UI同步性问题

Android程序编码过程中,回调无处不在.从最常见的Activity生命周期回调开始,到BroadcastReceiver.Service以及Sqlite等.Activity.BroadcastReceiver和Service这些基本组件的回调路径和过程也就是通常意义上所谓的"生命周期".同时,在处理具体的业务逻辑时,常常设计到不同线程之间的通信,如下载图片完成后通知 UI线程更新UI,凡此类场景,无论使用哪一种具体的线程间通信方式(Handler/Message.Handler/p

ActiveX组件与JavaScript交互

1.在COM组件中调用JavaScript函数// 连接点方式页面javascript脚本<object classid="CLSID:B568F111-DFE4-4944-B67F-0728AB2AB30F" id="testCom" VIEWASTEXT></object><script language="JavaScript" for="testCom" event="staTe