COM原理解析(1)

2 篇文章 0 订阅

IDL文件时COM技术实现的一个关键,在IDL当中定义接口的实现。每一个接口包含两个部分,第一个部分是用方括号括起来的属性。在所有的属性里面比较重要的是接口的IID,这是唯一标识COM接口的标识符,紧接着接口属性的定义,是整个接口的定义,所有的借口都是从IUnknown继承而来的——这样才能利用IUnknown的一些已经定义的特性,典型的定义如下所示:

<span style="font-size:14px;">	[
		object,
		uuid(CCA7F35D-DAF3-11D0-8C53-0080C73925BA),
        oleautomation,
		helpstring("IPoint Interface"),
		pointer_default(unique)
	]
	interface IPoint : IUnknown
	{
		HRESULT GetCoords([out] long *px, [out] long *py);
		HRESULT SetCoords([in] long x, [in] long y);
	};</span>

在定义了接口之后,就需要定义接口的实现了,也就是所谓的类的定义。类也有自身的特殊的ID被称为CLSID,不过属性的定义和接口属性的定义是类似的。当然所有的类的实现都有一个静态库文件的定义所包含,因为最后生成的COM组件毕竟是一个可加载的模块。静态库的属性定义如下面所示,其中有一点需要注意的是,所以在同一个静态库当中实现的类都和静态库的ID号码是一样的。

<span style="font-size:14px;">[
	uuid(CCA7F350-DAF3-11D0-8C53-0080C73925BA),
	version(1.0),
	helpstring("RectPoint 1.0 Type Library")
]
library RECTPOINTLib
{
	importlib("stdole32.tlb");
	importlib("stdole2.tlb");
	[
		uuid(CCA7F35E-DAF3-11D0-8C53-0080C73925BA),
		helpstring("Point Class")
	]
	coclass Point
	{
		[default] interface IPoint;
	};
};</span>

IDL文件只是COM实现的一半,另一半是真正的实现。而这两者维系起来的桥梁就是上面无处不在的UUID属性——对应不同的事物有不同的名称。作为一个可加载的模块,最重要的是找到两样东西,第一是模块的加载入口;第二是模块当中的全局变量——这些全局变量会在模块初始化最开始的时候就初始化了。所以,先看两个全局的变量,第一个是IGlobalInterfaceTable*g_pGIT这个指针提供进程当中套间之间的相互访问;另一个是CComModule_Module,这个全局变量使用一个数组来维护类的管理。当然这个数组和MFC当中的数组定义一样也是通过一组宏来实现的:

<span style="font-size:14px;">BEGIN_OBJECT_MAP(ObjectMap)
	OBJECT_ENTRY(CLSID_Point, CPoint)
	OBJECT_ENTRY(CLSID_Rect, CRect)
END_OBJECT_MAP()</span>
至于这个宏的展开分析,可以参考MFC当中宏的分析,然后在入口函数当中_Module会调用Init函数手工的将数组ObjectMap给填充到自身的数组当中。经过上面的处理之后,基本就可以从这个模块当中获取接口的信息了。然而,这时的COM还不具备自动配置的能力,程序员必须知道COM模块在那个地方才能得到模块当中的接口。所以还需要进一步的加工才能实现与机器无关性,这个时候就需要我们之前在IDL当中定义的一些ID了。通过相应的模块导出的DllRegisterServer函数实现将相应的ID和DLL模块的路径给加载到注册表当中,这个操作可以通过regsvr32小程序进行这个操作。在DllRegisterServer函数当中调用_Module类的RegisterServer函数完成——毕竟_Module类别包含一个数组,这个数组又具备相应的类别的信息。可以讲模块信息给保存到注册表,当然也希望有一个函数进行逆操作,这个函数就是DllUnregisterServer,当然这个函数也是通过调用_Module的UnregisterServer函数实现的。除此之外,COM接口还必须实现两个主要的功能,第一个是根据相应的调用获取实现接口的类,第二个是选择在适当的时机卸载这个模块。而这个功能的实现同样需要_Module的辅助:
<span style="font-size:14px;">STDAPI DllCanUnloadNow(void)
{
	return (_Module.GetLockCount()==0) ? S_OK : S_FALSE;
}

STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
	return _Module.GetClassObject(rclsid, riid, ppv);
}</span>
到目前为止,还只是讲解了一个辅助类别的作用。另一个类的指针g_pGIT主要用于处理套间的管理:
<span style="font-size:14px;">    HRESULT Globalize(Itf *pItf) {
        assert(g_pGIT != 0 && "GIT::Init not called");
        assert(m_dwCookie == 0 && "Attempt to Globalize invalidCookie");
        return g_pGIT->RegisterInterfaceInGlobal(pItf, *piid, &m_dwCookie);
    }</span><pre name="code" class="cpp">    HRESULT Unglobalize(void) {
        assert(g_pGIT != 0 && "GIT::Init not called");
        assert(m_dwCookie != 0 && "Attempt to Unglobalize invalid cookie");
        HRESULT hr = g_pGIT->RevokeInterfaceFromGlobal(m_dwCookie);
        m_dwCookie = 0;
        return hr;
    }
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值