Procedura: creare e utilizzare istanze CComPtr e CComQIPtr
In windows classic di programmazione, le librerie vengono spesso implementate come oggetti COM o più precisamente, come server COM. Molti componenti del sistema operativo Windows sono implementate come server COM e molti collaboratori forniscono le librerie nel form. Per informazioni sulle nozioni fondamentali su COM, vedere Component Object Model (COM).
Quando viene creata un'istanza di un oggetto di Component Object Model (COM), archiviare il puntatore a interfaccia in un puntatore intelligente COM, che esegue il conteggio dei riferimenti mediante chiamate a AddRef e a Release nel distruttore. Se si utilizza Active Template Library (ATL) o la libreria MFC (Microsoft Foundation classes (MFC), quindi utilizzare il puntatore intelligente di CComPtr. Se non si utilizzano ATL o MFC, quindi utilizzare _com_ptr_t. Poiché non è disponibile alcun equivalente COM a std::unique_ptr, utilizzare questi puntatori intelligenti per gli scenari con più proprietario che con un solo proprietario. Sia CComPtr che ComQIPtr supportano le operazioni di spostamento con riferimenti rvalue.
Esempio
Nell'esempio seguente viene illustrato come utilizzare CComPtr per creare un'istanza di un oggetto COM e ottenere i puntatori alle relative interfacce. Notare che la funzione membro di CComPtr::CoCreateInstance viene utilizzata per creare l'oggetto COM, anziché la funzione Win32 con lo stesso nome.
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 e i relativi parenti fa parte di ATL e sono definiti in atlcomcli.h. _com_ptr_t viene dichiarato in comip.h. Il compilatore crea le specializzazioni di _com_ptr_t quando vengono generate classi wrapper per le librerie dei tipi.
ATL fornisce inoltre CComQIPtr, che presenta una sintassi più semplice per eseguire una query su un oggetto COM per recuperare un'interfaccia aggiuntiva. Tuttavia, si consiglia CComPtr perché sono altro che CComQIPtr possa eseguire ed è semanticamente più coerente con i puntatori all'interfaccia COM non elaborati. Se si utilizza CComPtr per eseguire una query per un'interfaccia, il nuovo puntatore a interfaccia viene inserito in un parametro out. Se la chiamata ha esito negativo, un HRESULT viene restituito, ovvero il modello COM tipico. Con CComQIPtr, il valore restituito è il puntatore stesso e se la chiamata ha esito negativo, il valore restituito interno di HRESULT non è possibile accedervi. Le seguenti due righe mostrano come i meccanismi di gestione degli errori in CComPtr e in CComQIPtr differenze.
// 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 fornisce una specializzazione da IDispatch che consente di archiviare i puntatori ai componenti di automazione COM e richiamare i metodi dell'interfaccia utilizzando l'associazione tardiva. CComDispatchDriver è un typedef per CComQIPtr<IDispatch, &IIDIDispatch>, che costituisce implicitamente convertibili a CComPtr<IDispatch>. Pertanto, quando uno di questi tre nomi visualizzato nel codice, è equivalente a CComPtr<IDispatch>. Nell'esempio seguente viene illustrato come ottenere un puntatore al modello a oggetti di Microsoft Word utilizzando 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();
}