Comment : créer et utiliser des instances CComPtr et CComQIPtr
En programmation Windows classique, les bibliothèques sont souvent implémentée en tant qu'objets COM (ou plus précisément, comme serveurs COM). De nombreux composants du système d'exploitation Windows sont implémentés en tant que serveurs COM, et de nombreux collaborateurs fournissent des bibliothèques sous cette forme. Pour plus d'informations sur les bases de COM, consultez Component Object Model (COM).
Lorsque vous instanciez un Modèle d'objet composant (COM) (component object model), enregistrez le pointeur d'interface dans un pointeur intelligent COM, qui effectue un décompte de références en utilisant des appels à AddRef et Release dans le destructeur. Si vous utilisez la bibliothèque ATL (ATL) ou la bibliothèque MFC (MFC), utilisez le pointeur intelligent CComPtr. Si vous n'utilisez pas ATL ou MFC, utilisez _com_ptr_t. Étant donné qu'aucun COM n'équivalent à std::unique_ptr, utilisez les pointeurs intelligents à la fois pour les scénarios à un seul propriétaire et ceux à plusieurs propriétaire. CComPtr et ComQIPtr prennent en charge les opérations de déplacement qui ont des références de rvalue.
Exemple
L'exemple suivant montre comment utiliser CComPtr pour instancier un objet COM et obtenir des pointeurs sur ses interfaces. Notez que la fonction membre CComPtr::CoCreateInstance est utilisée pour créer l'objet COM, au lieu de la fonction Win32 qui porte le même nom.
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 et et ses parents font partie de ATL et sont définis dans atlcomcli.h. _com_ptr_t est déclaré dans comip.h. Le compilateur crée des spécialisations de _com_ptr_t lorsqu'il génère des classes wrapper pour les bibliothèques de types.
ATL fournit également CComQIPtr, qui a une syntaxe plus simple pour interroger un objet COM pour récupérer une interface supplémentaire. Toutefois, nous vous recommandons CComPtr car il fait tout ce que CComQIPtr peut faire et est sémantiquement plus cohérent avec les pointeurs d'interface COM bruts. Si vous utilisez CComPtr pour effectuer une requête à une interface, le nouveau pointeur d'interface est placé dans un paramètre de sortie. Si l'appel échoue, un HRESULT est retourné, qui est le modèle standard COM. Avec CComQIPtr, la valeur retournée est le pointeur lui-même, et si l'appel échoue, la valeur de retour HRESULT interne est inaccessible. Les deux lignes suivantes indiquent comment les mécanismes de gestion des erreurs dans CComPtr et CComQIPtr diffèrent.
// 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 fournit une spécialisation de l'IDispatch qui lui permet de stocker les pointeurs vers les composants d'automation COM et d'appeler des méthodes sur l'interface à l'aide de la liaison tardive. CComDispatchDriver est le typedef pour CComQIPtr<IDispatch, &IIDIDispatch>, qui peut être converti implicitement en CComPtr<IDispatch>. Par conséquent, lorsque l'un de ces trois nom apparaît dans le code, il correspond à CComPtr<IDispatch>. L'exemple suivant montre comment obtenir un pointeur vers le modèle objet Microsoft Word en utilisant 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();
}