Практическое руководство. Создание и использование экземпляров CComPtr и CComQIPtr
В классическом Windows программирования, библиотеки часто реализуются в виде com-объекта (или более точно, как серверы модели COM). Многие компоненты операционной системы Windows реализованы как серверы модели COM и предоставляют несколько членов библиотеки в этой форме. Дополнительные сведения об основах COM см. в разделе Component Object Model (COM).
При создании экземпляра объекта (COM) модели COM, сохраните указатель интерфейса в интеллектуального указателя модели COM, который выполняет ссылку со счетчиком с помощью вызова AddRef и Release в деструкторе. Если используется библиотека шаблонных классов ATL (ATL) или библиотеки Microsoft Foundation Class (MFC), используйте интеллектуального указателя CComPtr. При отсутствии ATL или MFC следует использовать _com_ptr_t. Из-за отсутствия модели COM эквивалентно std::unique_ptr, эти интеллектуального указателя и сценариям одного владельца и несколько владельца. И CComPtr и поддержка ComQIPtr переходят операции, имеющие ссылки 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 невозможен. Следующие 2 линии показывают, как механизмы обработки ошибок в CComPtr и CComQIPtr отличаются.
// 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>. Следовательно, если какие-либо из этих 3 имени отображаются в коде, он эквивалентен 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();
}