当前位置:编程学习 > C/C++ >>

有效的使用和设计COM智能指针—条款5:了解_com_ptr_t 的设计背后

条款5:了解_com_ptr_t 设计背后的历史原因
_com_ptr_t是微软在VC中的一个专有模版类。它封装了对IUnknown的QueryInterface()、AddRef()和Release()的操作,并提供自己的一些成员函数从而对COM接口指针进行操作。同时_com_ptr_t还简化了COM接口对引用计数的操作以及不同接口间的查询操作。

要使用_com_ptr_t这个智能指针,首先需要用_COM_SMARTPTR_TYPEDEF这个宏来声明特异化(Specialization)版本的_com_ptr_t 类别。之后则可以使用形如“接口名称+Ptr”这样的名称来定义此种接口类型的智能指针。例如:

view plaincopy to clipboardprint?_COM_SMARTPTR_TYPEDEF(ICalculator, __uuidof(ICalculator)); 
_COM_SMARTPTR_TYPEDEF(ICOMDebugger,__uuidof(ICOMDebugger)); 
HRESULT Calculaltor() 

    ICOMDebuggerPtr spDebugger = NULL; 
    ICalculatorPtr  spCalculator (CLSID_CALCULATOR); //构造函数可创建COM组件  
    int nSum = 0; 
    spCalculator->Add(1, 2, &nSum); 
         
    spDebugger = spCalculator;    //自动调用QueryInterface查询所需要的接口  
    spDebugger->GetRefCount(); 
     
return S_OK; 
}//无需手动调用Release(),接口会在智能指针析构时自动调用Release()。 
_COM_SMARTPTR_TYPEDEF(ICalculator, __uuidof(ICalculator));
_COM_SMARTPTR_TYPEDEF(ICOMDebugger,__uuidof(ICOMDebugger));
HRESULT Calculaltor()
{
    ICOMDebuggerPtr spDebugger = NULL;
    ICalculatorPtr  spCalculator (CLSID_CALCULATOR); //构造函数可创建COM组件
    int nSum = 0;
    spCalculator->Add(1, 2, &nSum);
       
    spDebugger = spCalculator;    //自动调用QueryInterface查询所需要的接口
    spDebugger->GetRefCount();
   
return S_OK;
}//无需手动调用Release(),接口会在智能指针析构时自动调用Release()。

 

_COM_SMARTPTR_TYPEDEF这个宏,一般放置于单独的头文件中。这样,只要include了此头文件的相关文件,都能使用名称为“接口名+Ptr”这种类型的智能指针。

这使得_com_ptr_t这套智能指针使用起来相对比较简单,编写代码时不存在一大堆针对模版的类型参数化过程。使用者也感觉不到模版的存在,用类似接口指针的方式即可使用此智能指针。

如果想探究_com_ptr_t这套智能指针的特异化过程是如何完成的,我们可以将特异化时候所用到的_COM_SMARTPTR_TYPEDEF这个宏展开:

view plaincopy to clipboardprint?typedef _com_ptr_t<_com_IIID<IMyInterface, __uuidof(IMyInterface)>> IMyInterfacePtr; 
typedef _com_ptr_t<_com_IIID<IMyInterface, __uuidof(IMyInterface)>> IMyInterfacePtr;
其中_com_IIID 的原型为:

view plaincopy to clipboardprint?template<typename _Interface, const IID* _IID /*= &__uuidof(_Interface)*/>  
class _com_IIID  
template<typename _Interface, const IID* _IID /*= &__uuidof(_Interface)*/>
class _com_IIID 
可以看出_com_IID这个类模版的功能是对IID和具体的类型进行封装,并把他们绑定在一起。_com_ptr_t则再会将此_com_IID参数化之后的类型作为类型参数的实参,从而构造一个特异化版本的智能指针类型。

另外值得一提的是,如果希望使用__uuidof这个vc专用的关键字,则需要在接口声明的时候加上形如:

view plaincopy to clipboardprint?__declspec(uuid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX")) 
__declspec(uuid("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"))
这样的语法。如下是ICalculator接口的声明:

view plaincopy to clipboardprint?interface __declspec(uuid("994D80AC-A5B1-430a-A3E9-2533100B87CE")) ICalculator : IUnknown 

    virtual HRESULT STDMETHODCALLTYPE Add( 
        const int nNum1,  
        const int nNum2,  
        int *pnSum 
    ) const = 0; 
     
    virtual HRESULT STDMETHODCALLTYPE Sub( 
        const int nMinuend, 
        const int nSubtrahend,  
        int *pnQuotient 
    ) const = 0; 
}; 
interface __declspec(uuid("994D80AC-A5B1-430a-A3E9-2533100B87CE")) ICalculator : IUnknown
{
    virtual HRESULT STDMETHODCALLTYPE Add(
        const int nNum1,
        const int nNum2,
        int *pnSum
    ) const = 0;
   
    virtual HRESULT STDMETHODCALLTYPE Sub(
        const int nMinuend,
        const int nSubtrahend,
        int *pnQuotient
    ) const = 0;
};
在_com_ptr_t 中封装了更多的功能性函数(如可以在构造智能指针的时候创建COM组件),并可以通过赋值运算符进行接口的查询。或许你会问为什么CComPtr不提供类似的操作。这个议题涉及到智能指针设计原则上的问题。我们会在“在设计原则中斟酌取舍”进行深入的讨论。

看完_com_ptr_t的一些基础用法后,让我们再来设想一种情况:如果我们有一个COM组件,但却拿不到他的头文件,那么在VC中应该如何操作他们呢?或许你认为拿不到头文件却要调用函数的情况不太可能发生,因为这样做你的代码无法通过编译。但事实是,缺少C/C++头文件这一现象却存在于大量的COM组件之中。

这些COM的设计者并非没有照顾到C/C++的程序员(很大程度上,他们也使用C++开发COM),而是他们使用了一种更好的方法来声明组件的接口——类型库。

类型库,是一种与语言无关、适合于解释性语言和宏语言使用C++头文件的等价物【1】。换而言之,C++和C语言中,我们的类型声明都用头文件来代替,而VB、delphi,则可以通过类型库来完成。

微软为VC提供的#import预处理命令,它能将一个类型库转换成等价的C/C++头文件。这样,开发者只需要发布一套类型库,则能在多种语言中定义出相应的接口了。

我们先可以用#import预处理命令来导入一个类型库,看看编译器帮我们完成了什么。我们以ADO为例,用#import预处理命令导入ADO类型库的源代码像是下面这样的:

view plaincopy to clipboardprint?#import "C:\Program Files\Common Files\System\ado\msado15.dll"  rename("EOF","rsEOF") 
#import "C:\Program Files\Common Files\System\ado\msado15.dll"  rename("EOF","rsEOF")
看上去有些复杂,而且和普通编译预处理命令形式上略有差别。但它却十分之方便,稍微编译一下这个程序,则会在相应的目录下输出msado15.tlh和msado15.tli两个文件。

msado15.tlh 包含了接口的声明,其内容看上去是下面这个样子的:

view plaincopy to clipboardprint?// Created by Microsoft (R) C/C++ Compiler Version 12.00.8168.0 (a2f27f36).  
//  
// d:\...\debug\msado15.tlh  
//  
// C++ source equivalent of Win32 type library C:\...\ado\msado15.dll  
// compiler-generated file created 08/22/11 at 14:19:31 - DO NOT EDIT!  
struct __declspec(uuid("00000512-0000-0010-8000-00aa006d2ea4")) 
/* dual interface */ _Collection; 
struct __declspec(uuid("00000513-0000-0010-8000-00aa006d2ea4")) 
/* dual interface */ _DynaCollection; 
struct __declspec(uuid("00000534-0000-0010-8000-00aa006d2ea4")) 
/* dual interface */ _ADO; 
str

补充:软件开发 , C语言 ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,