您的位置:首页 > 其它

com初学心得小记

2006-06-05 00:09 260 查看

DLL是一群写lib写到郁闷的人发明的。
Com是一群写Dll写到郁闷的人发明的。
.net assembly是一群写com写到郁闷的人发明的....
----Akumas

某年某月某日,偶为了完成某同学未竟的事业,毅然向传说中浮云一般的Com发起挑战,虽然明知这是不可完成的任务,但是为了祖国的崛起,为了为中国的IT事业添砖加瓦,为了那美克星的爱与和平,俺还是决定自我牺牲一把。
突击了两周之后,终于有点不成样的半成品出来。经过我的实践证明,Com不愧是浮云级别的东西,而我也深刻的体会到,浮云的真实含义=编写繁琐+语法晦涩+资料难寻+设计不成熟。从现在的眼光来看,com有很多东西的设计已经落伍了,但是由于目前com在windows平台内的支持可谓是最广泛的,所以研究研究还是有点价值。以下是我在突击过程中遇到的小问题和解决,供日后回忆以及各位参考。
编写语言:c++
编译环境:vs2003

1、IDL和c++
原则上,com的接口应该是先编写IDL,由编译器生成头文件,然后由源文件去include这个头文件,不过实际上vc7默认的com project都是采用idl与头文件混合编写的方式。这种方式虽然用起来简便易懂,但是毕竟是在.h里,语法要求与纯IDL文件还不完全一样,有些语法不完全支持,所以参数设置的时候要特别小心,否则会出现IDL语法正确,但是编译报错的情况。比如如下方法声明:
HRESULT PutArray([in] SAFEARRAY(BSTR) inputArray);
虽然作为IDL语法是完全正确的,但是当idl混合到.h里的时候,这种语法就会编译报错,仅能使用
HRESULT PutArray([in] SAFEARRAY* inputArray);

2、接口中的自定义参数
由于Com设计时考虑了跨语言性,所以Com使用了一些通用的类型定义,以使编译后的com控件能够被各种语言所正确调用。而相对的,一些c++中常用的类型将不允许在com的接口定义中出现,如string(使用BSTR)、bool(使用VARIANT_BOOL)等。更多一层,如果用户希望在接口中使用自己的类作为自定义参数,那首先必须在控件库中定义该类的接口(实际上com中的类也肯定是先定义了接口的),然后使用该接口指定为参数的类型。
比如我们定义一个类CTranInfoCtrl继承了接口ITranInfoCtrl,则某方法如果需要使用CTranInfoCtrl作为参数,则需要如下定义:
HRESULT PutInfo([in]ITranInfoCtrl* tranInfo);

3、SAFEARRAY
自定义类型的数组对我来说,是玩com过程中最痛苦的一环,几乎所有找到的资料都语焉不详(强调:特别是baidu里找的资料基本没有参考价值...),为了让同学们不再重复我走过的弯路,特别在此说明如何实现自定义类型数组,并且将其正确导入c#中。
第一步,定义一个方法,以指定类型的安全数组为参数,类型暂定为BSTR。根据网上的资料,我们应该如此定义:
HRESULT PutArray([in] SAFEARRAY(BSTR) inputArray);
可惜,编译器报错,原因见前,我们修改定义如下:
HRESULT PutArray([in] SAFEARRAY* inputArray);
这次,编译ok了。但是这里我们仅声明了一个安全数组,但是控件并不知道该数组的类型,当然也就没达到我们的要求。
第二步,指定参数类型:
HRESULT PutArray([in, satype(BSTR)] SAFEARRAY* inputArray);
这里我们使用satype这个属性来指定safearray的参数类型。此外,虽然safearray可以自己描述数组大小,但是我们也可以使用size_is属性来标记数组大小(可以使用size_is(输入参数)来指定动态数组大小)。
第二步完成,我们此时将能成功编译一个Dll,将Dll导入c#之后,我们会发现一个问题,在导入类中,PutArray这个方法的参数不是我们期待的string[] inputArray,而是变成了System.Array inputArray。
这个问题是由于c#导入程序的默认参数在捣鬼,它在默认导入时使用了(/sysarray参数)。为了正确的导入safearray,比如手动的重新进行导入,在sdk目录下寻找到tlbimp.exe,我们将需要导入的com dll复制过来(假定名为array.dll),进入命令行,输入
tlbimp array.dll /out:array_imp.dll
就可生成我们需要的dll,将这个array_imp.dll引入到project,我们用对象查看器就能发现,现在的PutArray的参数已经变成了string[] inputArray。

4、接口类的实例化
通常一个com project中,对一个类我们会定义一个接口(Interface),比如我们想设计一个类Cobject,则会定义一个接口Iobject,由Cobject继承Iobject,而Com编译器实际上最后会自动生成一个完全实现类CobjectClass。而Cobject由于从Iobject继承了虚基类IDispatch和IUnknown,而且没有实现虚函数,所以Cobject这个类是无法实例化的。如果我们既想在代码中直接实例化Cobject的一个对象,又不想重新实现Cobject的虚函数,那我们可以使用com为我们提供的懒人接口CComObject。CComObject利用模版自动实现虚函数,并生成新的类实例,对我们来说非常方便,假设我们已经定义一个类CTranInfoCtrl,尚未实现虚寒树,但是又想使用它的对象,则使用方法如下:
CTranInfoCtrl* infoCtrl = new CComObject<CTranInfoCtrl>;
使用虽然简单,不过需要牢记,com对象生成后,不能直接调用delete进行删除,而是在实例化后调用AddRef()增加引用计数,使用完后用Release()减去计数并进行资源释放。如果不照此做的话,可能会导致不可预期的结果。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: