/** @file * MS COM / XPCOM Abstraction Layer - Smart COM pointer classes declaration. */ /* * Copyright (C) 2006-2020 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ #ifndef VBOX_INCLUDED_com_ptr_h #define VBOX_INCLUDED_com_ptr_h #ifndef RT_WITHOUT_PRAGMA_ONCE # pragma once #endif /* Make sure all the stdint.h macros are included - must come first! */ #ifndef __STDC_LIMIT_MACROS # define __STDC_LIMIT_MACROS #endif #ifndef __STDC_CONSTANT_MACROS # define __STDC_CONSTANT_MACROS #endif #ifdef VBOX_WITH_XPCOM # include #endif /* VBOX_WITH_XPCOM */ #include #include /* For bad_alloc. */ /** @defgroup grp_com_ptr Smart COM Pointer Classes * @ingroup grp_com * @{ */ #ifdef VBOX_WITH_XPCOM namespace com { // declare a couple of XPCOM helper methods (defined in glue/com.cpp) // so we don't have to include a ton of XPCOM implementation headers here HRESULT GlueCreateObjectOnServer(const CLSID &clsid, const char *serverName, const nsIID &id, void **ppobj); HRESULT GlueCreateInstance(const CLSID &clsid, const nsIID &id, void **ppobj); } #endif // VBOX_WITH_XPCOM /** * COM autopointer class which takes care of all required reference counting. * * This automatically calls the required basic COM methods on COM pointers * given to it: * * -- AddRef() gets called automatically whenever a new COM pointer is assigned * to the ComPtr instance (either in the copy constructor or by assignment); * * -- Release() gets called automatically by the destructor and when an existing * object gets released in assignment; * * -- QueryInterface() gets called automatically when COM pointers get converted * from one interface to another. * * Example usage: * * @code * * { * ComPtr pMachine = findMachine("blah"); // calls AddRef() * ComPtr pUnknown = pMachine; // calls QueryInterface() * } # ComPtr destructor of both instances calls Release() * * @endcode */ template class ComPtr { public: /** * Default constructor, sets up a NULL pointer. */ ComPtr() : m_p(NULL) { } /** * Destructor. Calls Release() on the contained COM object. */ ~ComPtr() { cleanup(); } /** * Copy constructor from another ComPtr of any interface. * * This calls QueryInterface(T) and can result in a NULL pointer if the input * pointer p does not support the ComPtr interface T. * * Does not call AddRef explicitly because if QueryInterface succeeded, then * the refcount will have been increased by one already. */ template ComPtr(const ComPtr &that) { m_p = NULL; if (!that.isNull()) that->QueryInterface(COM_IIDOF(T), (void **)&m_p); } /** * Specialization: copy constructor from another ComPtr. Calls AddRef(). */ ComPtr(const ComPtr &that) { copyFrom(that.m_p); } /** * Copy constructor from another interface pointer of any interface. * * This calls QueryInterface(T) and can result in a NULL pointer if the input * pointer p does not support the ComPtr interface T. * * Does not call AddRef explicitly because if QueryInterface succeeded, then * the refcount will have been increased by one already. */ template ComPtr(T2 *p) { m_p = NULL; if (p) p->QueryInterface(COM_IIDOF(T), (void **)&m_p); } /** * Specialization: copy constructor from a plain T * pointer. Calls AddRef(). */ ComPtr(T *that_p) { copyFrom(that_p); } /** * Assignment from another ComPtr of any interface. * * This calls QueryInterface(T) and can result in a NULL pointer if the input * pointer p does not support the ComPtr interface T. * * Does not call AddRef explicitly because if QueryInterface succeeded, then * the refcount will have been increased by one already. */ template ComPtr& operator=(const ComPtr &that) { return operator=((T2 *)that); } /** * Specialization of the previous: assignment from another ComPtr. * Calls Release() on the previous member pointer, if any, and AddRef() on the new one. */ ComPtr& operator=(const ComPtr &that) { return operator=((T *)that); } /** * Assignment from another interface pointer of any interface. * * This calls QueryInterface(T) and can result in a NULL pointer if the input * pointer p does not support the ComPtr interface T. * * Does not call AddRef explicitly because if QueryInterface succeeded, then * the refcount will have been increased by one already. */ template ComPtr& operator=(T2 *p) { cleanup(); if (p) p->QueryInterface(COM_IIDOF(T), (void **)&m_p); return *this; } /** * Specialization of the previous: assignment from a plain T * pointer. * Calls Release() on the previous member pointer, if any, and AddRef() on the new one. */ ComPtr& operator=(T *p) { cleanup(); copyFrom(p); return *this; } /** * Resets the ComPtr to NULL. Works like a NULL assignment except it avoids the templates. */ void setNull() { cleanup(); } /** * Returns true if the pointer is NULL. */ bool isNull() const { return (m_p == NULL); } /** * Returns true if the pointer is not NULL. */ bool isNotNull() const { return (m_p != NULL); } bool operator<(T *p) const { return m_p < p; } /** * Conversion operator, most often used to pass ComPtr instances as * parameters to COM method calls. */ operator T *() const { return m_p; } /** * Dereferences the instance (redirects the -> operator to the managed * pointer). */ T *operator->() const { return m_p; } /** * Special method which allows using a ComPtr as an output argument of a COM method. * The ComPtr will then accept the method's interface pointer without calling AddRef() * itself, since by COM convention this must has been done by the method which created * the object that is being accepted. * * The ComPtr destructor will then still invoke Release() so that the returned object * can get cleaned up properly. */ T **asOutParam() { cleanup(); return &m_p; } /** * Converts the contained pointer to a different interface * by calling QueryInterface() on it. * @param pp * @return */ template HRESULT queryInterfaceTo(T2 **pp) const { if (pp) { if (m_p) return m_p->QueryInterface(COM_IIDOF(T2), (void **)pp); *pp = NULL; return S_OK; } return E_INVALIDARG; } /** * Equality test operator. By COM definition, two COM objects are considered * equal if their IUnknown interface pointers are equal. */ template bool operator==(T2 *p) { IUnknown *p1 = NULL; bool fNeedsRelease1 = false; if (m_p) fNeedsRelease1 = (SUCCEEDED(m_p->QueryInterface(COM_IIDOF(IUnknown), (void **)&p1))); IUnknown *p2 = NULL; bool fNeedsRelease2 = false; if (p) fNeedsRelease2 = (SUCCEEDED(p->QueryInterface(COM_IIDOF(IUnknown), (void **)&p2))); bool f = p1 == p2; if (fNeedsRelease1) p1->Release(); if (fNeedsRelease2) p2->Release(); return f; } /** * Creates an in-process object of the given class ID and starts to * manage a reference to the created object in case of success. */ HRESULT createInprocObject(const CLSID &clsid) { HRESULT rc; T *obj = NULL; #ifndef VBOX_WITH_XPCOM rc = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, COM_IIDOF(T), (void **)&obj); #else /* VBOX_WITH_XPCOM */ using namespace com; rc = GlueCreateInstance(clsid, NS_GET_IID(T), (void **)&obj); #endif /* VBOX_WITH_XPCOM */ *this = obj; if (SUCCEEDED(rc)) obj->Release(); return rc; } /** * Creates a local (out-of-process) object of the given class ID and starts * to manage a reference to the created object in case of success. * * Note: In XPCOM, the out-of-process functionality is currently emulated * through in-process wrapper objects (that start a dedicated process and * redirect all object requests to that process). For this reason, this * method is fully equivalent to #createInprocObject() for now. */ HRESULT createLocalObject(const CLSID &clsid) { #ifndef VBOX_WITH_XPCOM HRESULT rc; T *obj = NULL; rc = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, COM_IIDOF(T), (void **)&obj); *this = obj; if (SUCCEEDED(rc)) obj->Release(); return rc; #else /* VBOX_WITH_XPCOM */ return createInprocObject(clsid); #endif /* VBOX_WITH_XPCOM */ } #ifdef VBOX_WITH_XPCOM /** * Creates an object of the given class ID on the specified server and * starts to manage a reference to the created object in case of success. * * @param serverName Name of the server to create an object within. */ HRESULT createObjectOnServer(const CLSID &clsid, const char *serverName) { T *obj = NULL; HRESULT rc = GlueCreateObjectOnServer(clsid, serverName, NS_GET_IID(T), (void **)&obj); *this = obj; if (SUCCEEDED(rc)) obj->Release(); return rc; } #endif protected: void copyFrom(T *p) { m_p = p; if (m_p) m_p->AddRef(); } void cleanup() { if (m_p) { m_p->Release(); m_p = NULL; } } public: // Do NOT access this member unless you really know what you're doing! T *m_p; }; /** * ComObjPtr is a more specialized variant of ComPtr designed to be used for implementation * objects. For example, use ComPtr for a client pointer that calls the interface * but ComObjPtr for a pointer to an implementation object. * * The methods behave the same except that ComObjPtr has the additional createObject() * method which allows for instantiating a new implementation object. * * Note: To convert a ComObjPtr to a ComObj you have * to query the interface. See the following example code for the IProgress * interface: * * @code * * { * ComObjPtr pProgress; // create the server side object * pProgress.createObject(); // ... * pProgress->init(...); // ... * ComPtr pProgress2; // create an interface pointer * pProgress.queryInterfaceTo(pProgress2.asOutParam()); // transfer the interface * } * * @endcode */ template class ComObjPtr : public ComPtr { public: ComObjPtr() : ComPtr() {} ComObjPtr(const ComObjPtr &that) : ComPtr(that) {} ComObjPtr(T *that_p) : ComPtr(that_p) {} ComObjPtr& operator=(const ComObjPtr &that) { ComPtr::operator=(that); return *this; } ComObjPtr& operator=(T *that_p) { ComPtr::operator=(that_p); return *this; } /** * Creates a new server-side object of the given component class and * immediately starts to manage a pointer to the created object (the * previous pointer, if any, is of course released when appropriate). * * @note Win32: when VBOX_COM_OUTOFPROC_MODULE is defined, the created * object doesn't increase the lock count of the server module, as it * does otherwise. * * @note In order to make it easier to use, this method does _not_ throw * bad_alloc, but instead returns E_OUTOFMEMORY. */ HRESULT createObject() { HRESULT hrc; #ifndef VBOX_WITH_XPCOM # ifdef VBOX_COM_OUTOFPROC_MODULE ATL::CComObjectNoLock *obj = NULL; try { obj = new ATL::CComObjectNoLock(); } catch (std::bad_alloc &) { obj = NULL; } if (obj) { obj->InternalFinalConstructAddRef(); try { hrc = obj->FinalConstruct(); } catch (std::bad_alloc &) { hrc = E_OUTOFMEMORY; } obj->InternalFinalConstructRelease(); if (FAILED(hrc)) { delete obj; obj = NULL; } } else hrc = E_OUTOFMEMORY; # else ATL::CComObject *obj = NULL; hrc = ATL::CComObject::CreateInstance(&obj); # endif #else /* VBOX_WITH_XPCOM */ ATL::CComObject *obj; # ifndef RT_EXCEPTIONS_ENABLED obj = new ATL::CComObject(); # else try { obj = new ATL::CComObject(); } catch (std::bad_alloc &) { obj = NULL; } # endif if (obj) { # ifndef RT_EXCEPTIONS_ENABLED hrc = obj->FinalConstruct(); # else try { hrc = obj->FinalConstruct(); } catch (std::bad_alloc &) { hrc = E_OUTOFMEMORY; } # endif if (FAILED(hrc)) { delete obj; obj = NULL; } } else hrc = E_OUTOFMEMORY; #endif /* VBOX_WITH_XPCOM */ *this = obj; return hrc; } }; /** @} */ #endif /* !VBOX_INCLUDED_com_ptr_h */