如何:创建和使用CComPtr和CComQIPtr实例

在传统 Windows 编程中,库通常实现为 COM 对象 (或更确切地说,作为 COM 服务器)。许多 Windows 操作系统组件作为 COM 服务器和多个参与者提供此窗体中的库。COM 的基本信息,请参阅Component Object Model (COM)

当实例化组件对象模型 (COM) 对象时,将接口指针存储在 COM 智能指针,它执行引用计数通过调用AddRefRelease在析构函数中。如果您使用活动模板库 (ATL) 或 Microsoft 基础类库 (MFC),然后使用CComPtr智能指针。如果您不使用 ATL 或 MFC,则请使用**_com_ptr_t**。因为没有任何 COM 等效于std::unique_ptr,单所有者和多个所有者方案中使用这些智能指针。两个CComPtrComQIPtr支持移动具有 rvalue 引用的操作。

示例

下面的示例演示如何使用CComPtr实例化一个 COM 对象,并获得其接口指针。请注意, CComPtr::CoCreateInstance成员函数用于创建 COM 对象,而不是 Win32 函数具有相同的名称。

void CComPtrDemo()
{

    HRESULT hr = CoInitialize(NULL);

    // Declare the smart pointer.
    CComPtr<IGraphBuilder> pGraph;

    // Use its member function CoCreateInstance to
    // create the COM object and obtain the IGraphBuilder pointer.
    hr = pGraph.CoCreateInstance(CLSID_FilterGraph);
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Use the overloaded -> operator to call the interface methods.
    hr = pGraph->RenderFile(L"C:\\Users\\Public\\Music\\Sample Music\\Sleep Away.mp3", NULL);
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Declare a second smart pointer and use it to 
    // obtain another interface from the object.
    CComPtr<IMediaControl> pControl;
    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Obtain a third interface.
    CComPtr<IMediaEvent> pEvent;
    hr = pGraph->QueryInterface(IID_PPV_ARGS(&pEvent));
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Use the second interface.
    hr = pControl->Run();
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Use the third interface.
    long evCode = 0;
    pEvent->WaitForCompletion(INFINITE, &evCode);

    CoUninitialize();

    // Let the smart pointers do all reference counting.
}

CComPtr和其亲戚是 ATL 的一部分,并在 atlcomcli.h 中定义。_com_ptr_t在 comip.h 中声明。编译器创建的专用化**_com_ptr_t**时,它会生成类型库的包装类。

ATL 还提供了CComQIPtr,它具有查询检索到的其他接口的 COM 对象的简单语法。但是,我们建议CComPtr ,因为它不一切的CComQIPtr可以执行,语义上与原始 COM 接口指针保持一致。如果您使用CComPtr为查询接口时,新的接口指针放在输出参数中。如果调用失败,则返回 HRESULT,这是典型的 COM 模式。与CComQIPtr、 返回值是其自身,鼠标指针,如果调用失败,无法访问内部的 HRESULT 返回值。下面两行显示方式的错误处理机制,在CComPtrCComQIPtr不同。

// CComPtr with error handling:
CComPtr<IMediaControl> pControl;
hr = pGraph->QueryInterface(IID_PPV_ARGS(&pControl));
if(FAILED(hr)){ /*... handle hr error*/ }

// CComQIPtr with error handling
CComQIPtr<IMediaEvent> pEvent = pControl;
if(!pEvent){ /*... handle NULL pointer error*/ }

// Use the second interface.
hr = pControl->Run();
if(FAILED(hr)){ /*... handle hr error*/ }

CComPtr提供 IDispatch,使它能够存储指向 COM 自动化组件并使用后期绑定调用的方法的接口上的专用化。CComDispatchDriver是的 typedef CComQIPtr<IDispatch, &IIDIDispatch>,这是隐式转换为CComPtr<IDispatch>。因此,当任何下列三个名称出现在代码中,它相当于CComPtr<IDispatch>。下面的示例演示如何获取一个指向 Microsoft Word 对象模型,使用CComPtr<IDispatch>

void COMAutomationSmartPointerDemo()
{

    CComPtr<IDispatch> pWord;
    CComQIPtr<IDispatch, &IID_IDispatch> pqi = pWord;
    CComDispatchDriver pDriver = pqi;

    HRESULT hr;
    _variant_t pOutVal;

    CoInitialize(NULL);
    hr = pWord.CoCreateInstance(L"Word.Application", NULL, CLSCTX_LOCAL_SERVER);    
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Make Word visible.
    hr = pWord.PutPropertyByName(_bstr_t("Visible"),  &_variant_t(1));
    if(FAILED(hr)){ /*... handle hr error*/ }

    // Get the Documents collection and store it in new CComPtr
    hr = pWord.GetPropertyByName(_bstr_t("Documents"), &pOutVal);
    if(FAILED(hr)){ /*... handle hr error*/ }

    CComPtr<IDispatch> pDocuments = pOutVal.pdispVal; 

    // Use Documents to open a document
    hr = pDocuments.Invoke1 (_bstr_t("Open"), &_variant_t("c:\\users\\public\\documents\\sometext.txt"),&pOutVal);
    if(FAILED(hr)){ /*... handle hr error*/ }

    CoUninitialize();
}

请参见

概念

智能指针(现代C++)