ATL - JavaScript混合编程

页面导航:首页 > 网络编程 > JavaScript > ATL - JavaScript混合编程

ATL - JavaScript混合编程

来源: 作者: 时间:2016-02-05 11:06 【

JavaScript混合编程- ATL最后更新日期:2014-5-10环境:Windows8 1 64bit英文版,Visual Studio 2013 Professional Update1英文版阅读前提:COM的基本概念 作者:Kagula内容简介

JavaScript混合- ATL

最后更新日期:2014-5-10

环境:Windows8.1 64bit英文版,Visual Studio 2013 Professional Update1英文版

前提:COM的基本概念

作者:Kagula

内容简介

ATL(ActiveTemplate Library)是微软为了简化COM编程提供的一套C++模板,这里介绍如何用ATL建立一个简单的轻量级COM服务供JavaScript脚本调用,使我们对ATL的使用有个概念。

Hello,World

使用Administrator账号启动VisualStudio否则注册COM服务会失败。

新建Solution命名为ATLTutorial1,[VisualC++]->[ATL Project]->[Application type:选择Dynamic LinkLibrary(DLL)]->[Support options:勾选Allow merging of proxy/stubcode]->[Finish],建立COM服务程序。

[Allow merging of proxy/stub code]选项,会把你的所有组件(二进制代码)打包到一个DLL文件中,proxy/stub指的是代理和存根。

你还需要为COM服务添加COM Class,[Class View]窗口中右键单击项目名称[ATLTutorial1]->[Add]->[Class]在弹出窗口中选择[ATL]->[ATLSimple Object]启动ATL Simple Object向导,在short name中输入HelloWorld, Threading model中选择Apartment,Aggregation选择No,Interface选择Dual,Support勾选Connection Points、IObjectWithSite(IE object support)两个选项,建立HelloWorld类。

这个向导[1]在IDL文件中暴露出你的HelloWorld类,[2]新建IHelloWorld类定义你的HelloWorld的接口[3]新建CHelloWorld类,实现你在IHelloWorld接口中定义的功能。在外部的客户程序是通过你在IDL文件HelloWorld类的uuid值来实例化你的类。

Interface选项

Interface的Dual选项,让你的Class继承IDispatch接口和自定义接口(CustomInterface),Dual即双接口的意思。IDispatch是由OLE自动化协议暴露出来的接口。Automation (IDispatch) 接口允许客户程序在运行的时候找出COM对象支持哪些属性和方法,并提供如何调用COM对象属性和方法的必要信息。由于客户程序编译的时候不需要知道COM对象的成员,这样脚本程序,比如说在IE(Internet Explorer)上运行的Java Script就可以调用你的COM对象了。 自定义接口(CustomInterface)使客户程序能够基于VTABLE调用你的对象,这比基于IDispatch的调用性能开销要少,下图是IDispatch接口的棒棒糖。

\

Threading Model(线程模型)

COM的核心是尽可能向客户程序hide对象的细节,客户程序只要知道如何调用COM对象就可以了,其中要hide的一个细节就是COM对象是否线程安全,所以Microsoft定义了Apartment(公寓)的概念来简化这个细节。

ThreadingModel(线程模型)Apartment(公寓)定义了COM对象的执行上下文,客户程序中的某根线程通过调用CoInitializa(或CoInitializeEx或OleInitialize)进入Apartment(公寓)初始化里面(全部) 的COM对象。COM要求客户,如果要通过接口指针调用COM对象中的方法,必须在Apartment(公寓)里进行。换言之,想要从当前线程中调用某个COM对象,要先用CoCreateInstance方法初始化这个COM对象, CoCreateInstance会根据你对象的线程模型(Threading Model属性)建立Apartment。一个Apartment(公寓)里可以有任意多个COM对象。

\

COM定义了两种Apartment(公寓),单线程公寓STA(Single-threadedapartment)和多线程公寓MTA(multi-threaded apartment)。

STA只允许一根线程访问你的COM对象所以是线程安全的,所以你COM对象的方法不会被多个线程同时访问,一个进程中可以有多个单线程公寓(STA),存放在里面的数据只会被单根线程访问。

MTA允许多线程访问你的COM对象,但是一个进程中只能有一个多线程公寓(MTA),存放在里面的数据会被多根线程调用。

Threading Model(线程模型)选择Apartment选项,指明我们的这个COM对象是线程不安全的,必须在初始化它的线程中调用,系统会为你的COM对象做好线程保护。但是,当多个进程调用你的DLL时,你得为COM对象的数据加锁了,这样才能防止数据同步问题。

Single or blank选项指示我们的COM类只能在客户的主线程中调用,你不需要为你的对象做同步工作,系统保证对你对象的调用时串行的。

Both选项告诉系统和客户我们的类是线程安全的,可以使用和客户程序一样的Apartment(公寓)类型,系统不会对你的对象进行线程保护,所以你必须为自己对象中的数据做好同步,由于避免了系统对整个对象做同步,所以提高了性能。

Free选项类似Both,但是要求系统把它放在多线程公寓中(MTA),放在MTA中的对象,它的方法可能会同时被多个线程调用。

Neutral选项告诉系统我们的Class要放在Neutral Apartment中。Neutral Apartment是COM+中出现的Apartment,使你的Class能被任意线程调用,而自己不需要做同步工作,但是一个进程中只能有一个Neutral Apartment。

ThreadingModel中常用的是Apartment选项和Both选项。

Support选项

Connecton Points选项,使你的COM类继承IConnectonPointImpl接口,并执行以下四个步骤,[1]在IDL(接口定义语言)文件中定义回调接口[2]使用ATL proxygenerator(代理生成器)创建proxy(代理)类[3]把创建的proxy(代理)类添加到你的COM对象[4]为你的COM对象connection point map(连接点映射),添加connection point(连接点)。

ISupportErrorInfo选项,让你的错误信息沿着调用路径往上正确传播,选择支持ISupportErrorInfo会让你的对象继承ISuportErrorInfoImpl接口,如果你要做个OLE Automation对象,必须继承这个接口进行差错处理。

IObjectWithSite(IE object support)选项,让你的对象能够在IE中,和它的容器(containersite)通讯,例如,Java Script调用了你写的COM对象,你的COM对象也能检索Java Script所在Web页面中的元素。

Aggregation选项

我们一般选择No,即不需要COM对象间的继承,如何使用Aggregation(聚合)可以参考下面的一篇资料。

《Aggregation explained》

http://www.codeproject.com/Articles/17455/Aggregation-explained

Attribute选项

一般不勾选它,即我们按照经典方式新建ATL对象,如果你经常需要编写ATL程序,可以研究下ATL属性(Attribute)编程,它通过一些关键词简化ATL 对象的开发,但是有Attribute属性的ATL对象间不能相互继承。

添加方法

在[Class View]中你可以看到向导为你生成了三个IHelloWorld,右键单击最上面的IHelloWorld,[Add]->[Add Function...]弹出添加方法窗口,添加SayHello方法,为方法添加“[in]BSTR msg”和“[out,retval] BSTR* result”两个参数,进入新添加的方法,并修改源代码(HelloWorld.cpp文件),修改后的样子如下。

#include

usingnamespacestd;

// CHelloWorld

STDMETHODIMPCHelloWorld::SayHello(BSTRmsg,BSTR*result)

{

wstringhead= L"收到来自JavaScript的信息=>";

wstringcontent=head+OLE2W(msg);

*result=W2BSTR(content.c_str());

returnS_OK;

}

传入字符串不需要带指针类型(BSTR类型),但是返回字符串给调用者,需要变量类型为指针型,即BSTR*类型。

为了让IE不要弹出警告窗口,,还需要为我们的CHelloWorld类添加IObjectSafetyImpl接口继承。双击[Class View]窗口中的CHelloWorld打开HelloWorld.h文件,找到public IObjectWithSiteImpl,在下面插入代码行

publicIObjectSafetyImpl,

找到“COM_INTERFACE_ENTRY(IObjectWithSite)”在下面插入

COM_INTERFACE_ENTRY(IObjectSafety)

按[F6]Build Solution,Visual Studio会自动注册你的ATL对象。

测试我们的第一个ATL对象

在[Class View]中双击你刚才新添加方法的IHelloWorld接口,打开ATLTutorial1.idl文件,这是COM对象的接口定义文件。 在“libraryATLTutorial1Lib”中找到COM对象类HelloWorld,coclass HelloWorld前面的uuid值就是HelloWorld对象在系统中的名称,我们在HTML中需要通过这个名称实例化这个对象。

IDL文件,library后面的ATLTutorial1Lib是我们ATL库的名称,在系统的COM对象列表中找到这个库,展开它后可以看见我们的COM类HelloWorld,进一步展开可以看到它继承了IHelloWorld接口。

右键单击[SolutionExplorer]中的Solution名称,为当前Solution添加test.htm文件,内容如下:

<scriptlanguage="JavaScript"type="text/javascript">

function testSayHelloMethod(){

var a= objHelloWorld.SayHello("My name iskagula!");

alert(a);

}

</script>

如果你要不借助Wizard(向导)添加、删除或修改HelloWorld类的方法,只需要修改ATLTutorial1.idl、HelloWorld.h、HelloWorld.cpp这三个文件就可以了。ATLTutorial1.idl定义你的方法如何暴露给外部,HelloWorld.h声明你的方法,HelloWorld.cpp实现你的方法。

以后想要发布DLL,建议你在当前Solution中添加SetupProject,制作安装文件非常方便。

从HTML跟踪你的代码

在[SolutionExplorer]中选中ATL项目名称,[Alt]+[Enter]快捷键打开项目属性页,[Configuration Properties]->[Debugging]->[Debugger to Launch]中选择[WebBrowser Debugger],在[HTTP URL]中填入你htm的位置,如果测试文件test.htm在D分区的MyProject目录下,可以填“file:///D:\MyProject\test.htm”,按[F5]就可以从HTML文件跟踪你ATL对象的源代码了。

从C#项目中跟踪

在你ATL项目所在的Solution中添加C#的Win32控制台项目,为项目添加[COM]->[TypeLibraries]->[ ATLTutorial1Lib]的引用,其中ATLTutorial1Lib是我们ATL库的库名,紧跟在ATLTutorial1.idl文件的library关键词后面,修改Program.cs文件内容如下:

staticvoid Main(string[] args)

{

ATLTutorial1Lib.HelloWorld hw =new ATLTutorial1Lib.HelloWorld();

Debug.WriteLine("===>"+hw.SayHello("abc"));

Console.ReadKey();

}

打开C#项目的属性页,勾选[Debug]->[EnableDebuggers]->[Enable native code debugging]。

打开Solution属性页,[CommonProperties]->[Project Dependencies]设置你C#的项目依赖于你的ATL项目。

把C#项目设置为启动项目,按[F5]启动Debug,就可以调试你ATL对象的代码,如果ATL代码做了修改,VisualStudio会先build你的ATL项目,再启动你的C#程序。

ATL还需要注意下面几个Class的使用

CComPtr

CComPtr是ATL提供的智能指针,用来减少编写释放对象所需要的代码,使代码更加简洁,示例代码如下:

//建立spDoc智能指针

CComPtr spDoc;

//sPDoc智能指针,得到对象

browser->get_Document((IDispatch**)&spDoc);

//使用spDoc智能指针

spDoc->get_Script(&pScript);

//代码段结束后,spDoc会自动释放所指向的对象,不需要你写其它额外的代码

CComBSTR

  BSTR类型是ATL的字符串类型,用来同外部传递字符串。标准BSTR是一个有长度前缀和null结束符的OLECHAR数组,ATL 类 CComBSTR 提供对 BSTR 数据类型的包装。有些COM对象的方法调用要求传入CComBSTR类的字符串对象,但是我们的C++代码一般用std::wstring类型的字符串,下面是 std::wstring类型和CComBSTR类型对象的互转。

//std::wstring的实例转CComBSTR实例

CComBSTR bstrMember(::W2BSTR(name.c_str()));//std::wstringname

//或则用下面的方式转

BSTRbs2 = SysAllocStringLen(name.data(), name.size());

CComBSTR bstrMember(bs2);

//CComBSTR实例转std::wstring实例

CComBSTRbs(L"ddd");

std::wstringws(bs, SysStringLen(bs));

CComBSTR类在超出范围后会自动释放它所分配的内存。

CComVariant

VARIANT是个struct对象,虽然可以容纳不同数据类型,但是在同JavaScript的混合编程中,主要用于传递用户对象。 CComVariant则是VARIANT的封装。具体可以参考

《VARIANT 与 CComVariant 的使用》

http://blog.csdn.net/tangaowen/article/details/6553305

当调用者不知道COM对象方法的参数类型时往往可以使用CComVariant类型的参数代替。

Q 如何从ATL中调用JavaScript中的方法?参考下面的链接

《ATL回调JavaScript》

http://blog.csdn.net/lee353086/article/details/8853820

Q如何从ATL对象中返回数组?参考下面的链接

《从ATL中返回字符串数组到JavaScript的示例》

http://blog.csdn.net/lee353086/article/details/7557333

Q 如何在ATL中检索HTML中的元素?参考下面的链接

《ATL中对DOM中的元素进行枚举的例子》

http://blog.csdn.net/lee353086/article/details/9342353

Q 如何把ATL发布成CAB包?参考下面的链接

《VS2013编写ATL简单对象在PHP中使用》

http://blog.csdn.net/sjg20010414/article/details/20045179

参考资料

[1]《Active TemplateLibrary》

http://resources.esri.com/help/9.3/arcgisdesktop/com/COM/VCpp/ATLDiscussion.htm

[2]《ATL and MFC changesand fixes in Visual Studio 2013》

http://blogs.msdn.com/b/vcblog/archive/2013/08/20/atl-and-mfc-changes-and-fixes-in-visual-studio-2013.x

[3]《ATL COM DesktopComponents》

http://msdn.microsoft.com/en-us/library/t9adwcde.aspx

[4]《IE11 EnhancedProtected Mode 解决BHO与高权限进程通信问题》

http://blog.csdn.net/yangjian8915/article/details/11812303

Tags:

文章评论

最 近 更 新
热 点 排 行
Js与CSS工具
代码转换工具

<