ATL - JavaScript混合编程

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<string>

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<CHelloWorld>,在下面插入代码行

publicIObjectSafetyImpl<CHelloWorld,INTERFACESAFE_FOR_UNTRUSTED_CALLER|INTERFACESAFE_FOR_UNTRUSTED_DATA>,

找到“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文件,内容如下:

<HTML>

<HEAD>

<METANAME="GENERATOR"Content="MicrosoftVisual Studio">

<TITLE></TITLE>

</HEAD>

<BODY>

    <objectclassid="clsid:9147B0AF-2579-4E4F-87B0-FA2CAD630DD0"id="objHelloWorld"name="objHelloWorld"></object>

   

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

        function testSayHelloMethod(){

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

            alert(a);

        }

    </script>

 

    <inputtype=buttonvalue="测试我们的第一个ATL对象的方法"onclick="testSayHelloMethod()"/>

 

</BODY>

</HTML>

         如果你要不借助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<T>

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

   //建立spDoc智能指针

         CComPtr<IHTMLDocument2> 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.aspx

[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

[5]《[转]Internet Explorer已限制此网页运行可以访问计算机的脚本或ActiveX控件》

http://blog.sina.com.cn/s/blog_6d02ae330100w2ij.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

kagula086

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值