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/
ActiveX异步回调JavaScript的更多相关文章
- ActiveX多线程回调JavaScript
http://www.cnblogs.com/zdxster/archive/2011/01/27/1945872.html
- node 异步回调解决方法之yield
先看如何使用 使用的npm包为genny,npm 安装genny,使用 node -harmony 文件(-harmony 为使用es6属性启动参数) 启动项目 var genny= require( ...
- State Threads——异步回调的线性实现
State Threads——异步回调的线性实现 原文链接:http://coolshell.cn/articles/12012.html 本文的标题看起来有点拗口,其实State Threads库就 ...
- jquery.Deferred promise解决异步回调
我们先来看一下编写AJAX编码经常遇到的几个问题: 1.由于AJAX是异步的,所有依赖AJAX返回结果的代码必需写在AJAX回调函数中.这就不可避免地形成了嵌套,ajax等异步操作越多,嵌套层次就会越 ...
- 深入探析koa之异步回调处理篇
在上一篇中我们梳理了koa当中中间件的洋葱模型执行原理,并实现了一个可以让洋葱模型自动跑起来的流程管理函数.这一篇,我们再来研究一下koa当中异步回调同步化写法的原理,同样的,我们也会实现一个管理函数 ...
- 前端入门20-JavaScript进阶之异步回调的执行时机
声明 本系列文章内容全部梳理自以下几个来源: <JavaScript权威指南> MDN web docs Github:smyhvae/web Github:goddyZhao/Trans ...
- js同步-异步-回调
出处:https://blog.csdn.net/u010297791/article/details/71158212(1)上面主要讲了同步和回调执行顺序的问题,接着我就举一个包含同步.异步.回调的 ...
- js异步回调Async/Await与Promise区别 新学习使用Async/Await
Promise,我们了解到promise是ES6为解决异步回调而生,避免出现这种回调地狱,那么为何又需要Async/Await呢?你是不是和我一样对Async/Await感兴趣以及想知道如何使用,下面 ...
- c#线程之异步委托begininvoke、invoke、AsyncWaitHandle.WaitOne 、异步回调
单靠自己看书学总是会走很多弯路,任何人也不列外,有些时候自己遇到的很多问题,其它别人在很久之前也可能遇到过,上网查查可以走很大捷径,对自己的学习有很大帮助,刚开始弄线程这块,一开始只是看书,很多东西都 ...
随机推荐
- Linux下ld搜索问题:ld: cannot find -l"XX"
ld命令行工具(链接库的一个工具)的搜索路径是-L指定的,库名是-l指定的. 比如: ld -L[dir] -l[mylib] --verbose 以上我用可视化的方法显示ld的搜索路径,其结果是居然 ...
- ListView.setOnItemClickListener无效
如果ListView中的单个Item的view中存在checkbox,button等view,会导致ListView.setOnItemClickListener无效, 事件会被子View捕获到,Li ...
- code 代码分析 及其解决方案
官网地址:http://msdn.microsoft.com/zh-cn/library/ms182135.aspx [FxCop.设计规则]11. 不应该使用默认参数 参考地址:http://blo ...
- HDU5441 Travel (离线操作+并查集)
Travel Time Limit: 1500/1000 MS (Java/Others) Memory Limit: 131072/131072 K (Java/Others)Total Su ...
- POJ1094 Sorting It All Out(拓扑排序)
Sorting It All Out Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 30110 Accepted: 10 ...
- 网易云课堂_C++开发入门到精通_章节8:设计模式
课时44设计模式简介 设计模式简介 面向对象设计的第一个原则:针对接口编程,而不是针对实现编程 接口->指针 实现->实例 若已存在一个类Class A,现在希望复用Class A,则有以 ...
- Java语言程序设计(基础篇) 第八章 多维数组
第八章 多维数组 8.2 二维数组的基础知识 二维数组中的元素通过行和列的下标来访问. 8.2.1 声明二维数组变量并创建二维数组 下面是二维数组的语法: 数据类型[][] 数组名; int[][] ...
- 【转】nand flash坏块管理OOB,BBT,ECC
0.NAND的操作管理方式 NAND FLASH的管理方式:以三星FLASH为例,一片Nand flash为一个设备(device),1 (Device) = xxxx (Blocks),1 ...
- Eclipse选中变量名,相同变量都变色显示
Eclipse选中变量名,相同变量都变色显示 java文件的设置"Window"-"preferences"-"Java"-"Ed ...
- 格而知之7:我所理解的Runtime(2)
消息发送(Messaging) 8.以上便是runtime相关的一些数据结构,接下来我们回看一开始的疑问: objc_msgSend()函数在执行的过程中是如何找到对应的类,找到对应的方法实现的呢? ...