Практическое руководство. Создание и использование экземпляров CComPtr и CComQIPtr
В классическом программировании Windows библиотеки часто реализуются как COM-объекты (или, более точно, как COM-серверы). Многие компоненты операционной системы Windows реализованы в виде COM-серверов, и большинство разработчиков предоставляют библиотеки в этой форме. Сведения об основах COM см. в разделе Component Object Model (COM).
При создании COM-объекта сохраните указатель на интерфейс в интеллектуальном указателе COM, который подсчитывает ссылки с помощью вызовов AddRef
и Release
в деструкторе. Если вы работаете с библиотекой ATL или библиотекой MFC, используйте интеллектуальный указатель CComPtr
. В противном случае используйте _com_ptr_t
. Поскольку эквивалент COM для std::unique_ptr
отсутствует, применяйте эти интеллектуальные указатели в сценариях с одним и несколькими владельцами. CComPtr
и ComQIPtr
поддерживают операции перемещения, имеющие ссылки rvalue.
Пример: CComPtr
В следующем примере показано, как использовать CComPtr
для создания COM-объекта и получения указателей на его интерфейсы. Обратите внимание, что для создания объекта используется функция-член CComPtr::CoCreateInstance
, а не функция 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
при создании классов-оболочек для библиотек типов.
Пример: CComQIPt
Кроме того, библиотека ATL предоставляет указатель CComQIPtr
с более простым синтаксисом запросов к COM-объекту для получения новых интерфейсов. Однако рекомендуется использовать CComPtr
, так как он поддерживает все возможности CComQIPtr
и семантически более совместим с необработанными указателями на интерфейс СОМ. Если запрос на интерфейс выполняется с помощью CComPtr
, указатель на новый интерфейс помещается в выходной параметр. Если вызов завершается ошибкой, возвращается значение HRESULT, которое является стандартным шаблоном COM. В случае с CComQIPtr
возвращаемым значением является сам указатель, и при сбое вызова внутреннее возвращаемое значение HRESULT будет недоступно. В следующих двух строках показано различие механизмов обработки ошибок 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*/ }
Пример: IDispatch
CComPtr
предоставляет специализацию для IDispatch, который позволяет сохранить указатели на компоненты автоматизации COM и вызывать методы в интерфейсе с помощью позднего связывания. CComDispatchDriver
является определением типа 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();
}
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по