COM组件技术积累DOCWord下载.docx
- 文档编号:18838044
- 上传时间:2023-01-01
- 格式:DOCX
- 页数:50
- 大小:270.99KB
COM组件技术积累DOCWord下载.docx
《COM组件技术积累DOCWord下载.docx》由会员分享,可在线阅读,更多相关《COM组件技术积累DOCWord下载.docx(50页珍藏版)》请在冰豆网上搜索。
COM对象也具有
多态性,但这种多态性需要通过COM对象所具有的接口才能体现出来,就像C++对象的多态性需要通过其虚函数才能体现一样。
1.3COM对象和接口
COM提供的是面向对象的组件模型,COM组件提供给客户的是以对象形式封装起来的
实体。
客户程序和COM组件程序进行交互的实体是COM对象。
COM对象包括属性(也称为状态)和方法(也称为操作),对象的状态反映了对象的存在,也是区别于其他对象的要素;
而对象所提供的方法就是对象提供给外界的接口,客户必须通过接口才能获得对象的服务。
对于COM对象来说,接口是它与外界进行交互的唯一途径,因此,封装特性是COM对象的基本特征。
1.3.1COM对象标识——CLSID
COM组件的位置对于客户来说是透明的,因为客户并不直接去访问COM组件,客户程序通过一个全局标识符进行对象的创建和初始化工作,这个全局标识符就是CLSID。
CLSID结构定义上与GUID一致。
COM规范采用了128位全局唯一标示符GUID。
这是一个随机数。
手工创建128位GUID或者编写程序来产生GUID是件很麻烦的事。
为此,MicrosoftVisualC++提供了两个工具实现这样的目的:
UUIDGen.exe和GUIDGen.exe,前者是一个命令行程序,后者是一个基于对话框的应用程序。
另外,COM库为我们提供了以下API函数可以产生GUID:
HRESULTCoCreateGuid(GUID*pguid)
下面为示例工程中.rgs文件中CLSID的定义:
Math.Obj.1=s'
MyMathClass'
{
CLSID=s'
{3B28F0D6-D029-484B-80D7-A946EB20E9BD}'
}
将示例工程的COM组件成功注册后,我们可以根据组件的CLSID在系统的注册表编辑器中找到组件的注册信息,如图1。
图1系统注册表
1.3.2COM对象的数据类型
⏹HRESULT:
一个双字节的值,其最高位(bit)如果是0表示成功,1表示错误。
常见的HRESULT值:
HRESULT
值
含义
S_OK
0x00000000
成功
S_FALSE
0x00000001
函数成功执行完成,但返回时出现错误
E_INVALIDARG
0x80070057
参数有错误
E_OUTOFMEMORY
0x8007000E
内存申请错误
E_UNEXPECTED
0x8000FFFF
未知的异常
E_NOTIMPL
0x80004001
未实现功能
E_FAIL
0x80004005
没有详细说明的错误
E_POINTER
0x80004003
无效的指针
E_HANDLE
0x80070006
无效的句柄
E_ABORT
0x80004004
终止操作
E_ACCESSDENIED
0x80070005
访问被拒绝
E_NOINTERFACE
0x80004002
不支持接口
⏹UNICODE:
IDL字符串的标准形式,使用2个字节表示一个字符(unsignedshortint、WCHAR、_wchar_t、OLECHAR),不会出现乱码,UNICODE的范围是0x0000-0xFFFF共6万多个字符。
⏹BSTR:
一个OLECHAR*类型的Unicode字符串。
由于操作系统提供相应的API函数(如SysAllocString)来管理它以及一些默认的调度代码,因此BSTR实际上就是一个COM字符串,自带字符串长度信息。
API函数
函数作用
SysAllocString()
申请一个BSTR指针,并初始化为一个字符串
SysFreeString()
释放BSTR内存
SysAllocStringLen()
申请一个指定字符长度的BSTR指针,并初始化为一个字符串
SysAllocStringByteLen()
申请一个指定字节长度的BSTR指针,并初始化为一个字符串
SysReAllocStringLen()
重新申请BSTR指针
CString函数
AllocSysString()
从CString得到BSTR
SetSysString()
重新申请BSTR指针,并复制到CString中
CComBSTR函数
ATL的BSTR包装类,在atlbase.h中定义,具体查看MSDN
_bstr_t
C++对BSTR的封装,它的构造和析构函数分别调用SysAllocString和SysFreeString函数,其他操作是借用BSTRAPI函数。
⏹VARIANT:
具有跨语言的特性,它可以表示(存储)任意类型的数据,既包括了数据本身,也包含了数据的类型,定义在oaidl.h中。
structtagVARIANT{
VARTYPEvt;
Union{
shortiVal;
//VT_I2
longlVal;
//VT_I4
floatfltVal;
//VT_R4
doubledblVal;
//VT_R8
DATEdate;
//VT_DATE
BSTRbstrVal;
//VT_BSTR
…..
short*piVal;
//VT_BYREF|VT_I2
long*plVal;
//VT_BYREF|VT_I4
float*pfltVal;
//VT_BYREF|VT_R4
double*pdbVal;
//VT_BYREF|VT_R8
DATE*pdate;
//VT_BYREF|VT_DATE
BSTR*pbstrVal;
//VT_BYREF|VT_BSTR
};
typedeftagVARIANTVARIANT;
VARIANT使用示例:
VARIANTva;
:
:
VariantInit(&
va);
//初始化
Inta=2012;
Va.vt=VT_I4;
//指明long数据类型
Va.IVal=a;
//赋值
VariantClear();
Windows定义的VARIANT相关函数:
VariantInt——将变量初始化为VT_EMPTY;
VariantClear——消除并初始化VARIANT;
VariantChangeType——改变VARIANT的类型;
VariantCopy——释放与目标VARIANT相连的内存并赋值源VARIANT
COleVariant:
对VARIANT结构的封装
COleVariantv1(“Thisisatest”);
//直接构造
COleVariantv2=“Thisisatest”;
//结果时VT_BSTR类型,值为”Thisisatest”
COleVariantv3((long)2012);
COleVariantv4=(long)2012;
//结果时VT_I4类型,值为2012
_variant_t:
用于COM的VARIANT类
1.4描述性语言IDL和MIDL编译器
COM规范在采用OSF的DCE规范描述远程调用接口IDL(interfacedescriptionlanguage,
接口描述语言)的基础上,进行扩展形成了COM接口的描述语言。
接口描述语言提供了一种不依赖于任何语言的接口描述方法,因此,它可以成为组件程序和客户端程序之间的共同语言。
COM规范使用的IDL接口描述语言不仅可用于定义COM接口,同时还定义了一些常用的数据类型,也可以描述自定义的数据结构,对于接口成员函数,我们可以指定每个参数的类型、输入输出特性,甚至支持可变长度的数组的描述。
IDL中所有数据、方法、接口、类和库的特性都由属性信息来描述。
属性信息中由括号括起来,作为它们描述的对象的前缀。
1)in:
输入型参数,从调用者传递到被调用者,被调用者对输入型参数的更改不传回调用者。
2)out:
输出型参数,从被调用者返回调用者,而被调用者不关心参数的初始值。
3)In,out:
输入输出型参数在调用的时候传到被调用者,同时,被调用者可以对参数进行修改,这个修改在调用返回的时候会被复制回调用者。
(PS:
非指针类型一定是输入型参数。
输出型参数和输入输出型参数一定是指针类型)
4)retval:
返回一个与方法的物理HRESULT不相关的逻辑结果,与out一起使用,且只能有一个,放在参数的最后。
5)string:
参数所指向的是一个字符串类型参数,以Null终止。
6)size_is:
指针数组中元素个数由另一个参数说明。
7)length_is:
用来设置在序列化时需要复制的元素数量。
下面IDL示例为工程Math的IDL文件。
IDL中接口定义示例:
[
object,
uuid(CED2CE33-5419-49E0-AE72-CB1E4D2B0C8F),
oleautomation,
nonextensible,
pointer_default(unique)
]
interfaceIMyMath:
IUnknown{
[helpstring("
方法Add"
)]HRESULTAdd([in]Element*pElement,[out]DWORD*pValue);
[helpstring("
方法Operate"
)]HRESULTOperate([in]Element*pElement,functionfun,[out]DWORD*pValue);
方法Sum"
)]HRESULTSum([in]DWORDColCount,
[in]DWORDRowCount,
[in,size_is(ColCount*RowCount)]DWORD*pNums,
[out,retval]DWORD*pResult);
IDL中enum定义示例:
typedef
[
uuid(9CE9F449-3894-44AD-9F15-1DE67E915329),
version(1.0),
helpstring("
Enumoffunction"
)
]
enumfunction
fAdd=0,
fSub,
fMul
}function;
IDL中struct定义示例:
IDL中union定义示例:
[
uuid(994A75FF-6FC8-4802-AA42-4E04776BD521),
version(1.0),
helpstring(“NUMBER"
)
]
unionNUMBER{
[case
(1)]longi;
[case
(2)]]floatf;
}NUMBER;
IDL类定义示例:
uuid(12881436-9C8F-457E-851F-25CCD3F25D30),
libraryMathLib
{
importlib("
stdole2.tlb"
);
uuid(3B28F0D6-D029-484B-80D7-A946EB20E9BD)
coclassMyMath
[default]interfaceIMyMath;
interfaceIMyMath2;
};
MicrosoftVisualC++提供了MIDL工具,可以把IDL接口描述文件编译成C/C++兼容的接口描述文件(.h)和C文件(.c),可以被组件程序和客户程序所使用。
XX_i.h:
一个同C和C++兼容的,包含IDL中所描述的所有接口声明的头文件;
XX_i.c:
一个定义有IDL文件中所用的所有GUID的C文件
1.5IUnknown接口
COM定义的每一个接口都必须从IUnknow继承过来,其原因在于IUnknow接口提供了
非常重要的特性:
生存期控制和接口查询。
客户程序只能通过接口和COM对象进行通信,虽然客户程序可以不管对象内部的实现细节,但它要控制对象的存在与否。
1.5.1QueryInterface接口方法介绍
按照COM规范,一个COM对象可以实现多个接口,客户端程序可以在运行时刻对COM对象的接口进行询问,如果对象实现了该接口,则对象可以提供这样的接口服务。
QueryInterface函数的说明:
HRESULTQueryInterface([in]REFIIDiid,[ou]void**ppV);
函数的输入参数iid为接口标识符IID,输出参数ppv为查询得到的结果接口指针,如果没有实现iid所标识的接口,则输出参数ppv指向空(NULL)。
函数的返回值为一个32位的整数,反映了查询的结果,其含义有三种情况:
1)S_OK,查到了指定的接口,接口指针存放在ppv输出参数中;
2)E_NOINTERFACE,对象不支持所指定的接口,*ppv为NULL;
3)E_UNEXPECTED,发生了意外错误,*ppv为NULL。
对于调用QueryInterface函数,COM规范给出了以下一些规则:
1)对于同一个对象的不同接口指针,查询得到的IUnknown接口必须完全相同。
也就是说每个COM对象的IUnknown接口指针是唯一的。
2)接口自反性。
对于一个接口查询其自身总应该成功
3)接口对称性。
如果从一个接口指针查询到另一个接口指针,则从第二个接口指针再回到第一个接口指针必定成功,如:
IMyMath*pIMyMath=NULL;
HRESULThr=:
CoCreateInstance(CLSID_MyMath,NULL,CLSCTX_INPROC_SERVER,IID_IMyMath,(void**)&
pIMyMath);
IMyMath2*pIMyMath2=NULL;
hr=pIMyMath->
QueryInterface(IID_IMyMath2,(void**)&
pIMyMath2);
4)接口传递性。
如果从第一个接口指针查询到第二个接口指针,从第二个接口指针可以查询到第三个接口指针,则从第三个接口指针一定可以查询到第一个接口指针。
5)接口查询时间无关性。
如果某一个时刻可以查询到某一个接口指针,则以后任何时候再查询到同样的接口指针,一定可以查询成功。
1.5.2引用计数
IUnknown引入了“引用计数”(referencecounting)方法,可以有效地控制对象的生存周期,解决内存管理的问题。
COM对象通过引用计数来决定是否继续生存下去。
IUnknown的接口成员函数AddRef和Release分别完成引用计数的增1和减1操作。
如果一个COM对象实现了多个接口,则可以采用同样的计数技术,只要引用计数不为0,就表明该COM对象的客户仍然在使用它(前提是客户程序正确地操作了引用计数),它就继续生存下去;
反之,如果引用计数减到0,则表明客户不再使用该对象了,于是它就可以被清除。
⏹需要调用AddRef方法的情形:
1)当把一个非空指针写到局部变量中时
2)当被调用方把一个非空接口指针写到方法或者函数的[out]或者[in,out]参数中时
3)当被调用方返回一个非空接口指针作为函数的实际结果时
4)当把一个非空接口指针写到对象的一个数据成员中时
5)注意:
QueryInterface内含AddRef,不需要再调用
⏹需要调用Release方法的情形:
1)在改写一个非空局部变量或者数据成员之前
2)在离开非局部变量的作用域(scope)之前
3)在被调用方要改写方法或者函数的[in,out]参数,并且参数的初始值为非空时。
注意,对于传入的out参数,不需要释放
4)在改写一个对象的非空数据成员之前
5)在离开一个对象的析构函数之前,并且这时还有一个非空接口指针作为数据成员
⏹特殊情况
1)当调用方把一个非空接口指针通过[in]参数传递给一个函数或者方法时,既不需要调用AddRef,也不需要调用Release,因为在调用堆栈中,临时变量的生命周期只是“用于初始化形式参数”的表达式的生命周期的一个子集。
1.6重要概念:
套间
什么是套间?
根据《COM技术内幕》的观点,COM没有定义自己新的线程模型,而是
直接利用了Win32线程,或者说对其做了改造、包装。
《COM本质论》是这样定义的:
套间定义了一组对象的逻辑组合,这些对象共享一组并发性和冲入限制。
每个COM对象都属于某一个套间,一个套间可以包含多个COM对象。
MSDN上解释说,可以把进程中的组件对象想象为分成了很多组,每一组就是一个套间。
属于这个套间的线程,可以直接调用组件,不属于这个套间的线程,要通过代理才能调用组件。
最直接的说,COM库为了实现简化多线程编程的构想,提出了套间的概念。
套间是一个逻辑上的概念,它把Win32里的线程、组件等,按照一定的规则结合在一起,并且以此提供一种模式,用于多线程并发访问COM组件。
可以把套间看做COM对象的管理者,它通过调度,切换COM对象的执行环境,保证COM对象的多线程调用正常运行。
COM和线程不是包含关系,而是对应和关联关系。
1.6.1单进程套间STA
Single-threadedApartments,一个套间只关联一个线程,COM库保证对象只能由这个线程访问(通过对象的接口指针调用其他方法),其他线程不得直接访问这个对象(可以间接访问,但最终还是由这个线程访问)。
COM库实现了所有调用的同步,因为只有关联线程访问COM对象。
如果有N个调用同时并发,N-1个调用处于阻塞状态,如图2。
对象的状态(也就是对象的成员变量的值)肯定是正确变化的,不会出现线程访问冲突而导致对象状态错误。
图2STA实现过程
//创建一个STA套间并和当前线程关联
CoInitialize(NULL);
//或者
CoInitializeEx(NULL,COINIT_APARTMENTTHREADED);
……….
………
……
CoUninitialize();
上述代码创建了一个STA,然后套间把当前的线程和自己关联在一起,线程被标记为套间线程,只有这个线程能直接调用COM对象。
1.6.2多进程套间MTA
MultithreadedApartments,一个套间可以对应多个线程,COM对象可以被多个线程并发访问,如图3。
所以这个对象的作者必须在自己的代码中实现线程保护、同步工作,保证可以正确改变自己的状态。
图3MTA实现过程
//创建一个MTA套间并和当前线程关联
CoInitializeEx(NULL,COINIT_COINIT_MULTITHREADED);
………..
第一次如此调用的时候,会创建一个MTA,然后套间把当前线程和自己关联在一起,线程被标记为自由线程。
以后第二个线程再调用(同一个进程中)的时候,这个MTA会把第二个线程也关联在一起。
一个MTA可以关联多个线程。
所有的关联线程都可以调用套间中的组件。
这就涉及到同步问题,需要组件编写者解决。
这个对于作为业务逻辑组件或干后台服务的组件非常适合。
因为作为一个分布式的服务器,同一时间可能有几个服务请求到达,如果排队进行调用,那么将是不能想象的。
1.6.3NA套间
COM+为了进一步简化多线程编程,引入的中立线程套间概念。
NA/TNA/NAT,NeutralApartment/ThreadNeutralApartment/NeutralThreadedApartment。
这种套
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- COM 组件 技术 积累 DOC