diff options
Diffstat (limited to 'include/VBox/com/ptr.h')
-rw-r--r-- | include/VBox/com/ptr.h | 569 |
1 files changed, 569 insertions, 0 deletions
diff --git a/include/VBox/com/ptr.h b/include/VBox/com/ptr.h new file mode 100644 index 00000000..613d2d08 --- /dev/null +++ b/include/VBox/com/ptr.h @@ -0,0 +1,569 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Smart COM pointer classes declaration. + */ + +/* + * Copyright (C) 2006-2022 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + +#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 <nsISupportsUtils.h> +#endif /* VBOX_WITH_XPCOM */ + +#include <VBox/com/defs.h> +#include <new> /* 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<IMachine> pMachine = findMachine("blah"); // calls AddRef() + * ComPtr<IUnknown> pUnknown = pMachine; // calls QueryInterface() + * } # ComPtr destructor of both instances calls Release() + * + * @endcode + */ +template <class T> +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 <class T2> + ComPtr(const ComPtr<T2> &that) + { + m_p = NULL; + if (!that.isNull()) + that->QueryInterface(COM_IIDOF(T), (void **)&m_p); + } + + /** + * Specialization: copy constructor from another ComPtr<T>. 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 <class T2> + 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 <class T2> + ComPtr& operator=(const ComPtr<T2> &that) + { + return operator=((T2 *)that); + } + + /** + * Specialization of the previous: assignment from another ComPtr<T>. + * 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 <class T2> + 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 <class T2> + 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 <class T2> + 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<IMachine> for a client pointer that calls the interface + * but ComObjPtr<Machine> 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<InterfaceImpl> to a ComObj<IInterface> you have + * to query the interface. See the following example code for the IProgress + * interface: + * + * @code + * + * { + * ComObjPtr<Progress> pProgress; // create the server side object + * pProgress.createObject(); // ... + * pProgress->init(...); // ... + * ComPtr<IProgress> pProgress2; // create an interface pointer + * pProgress.queryInterfaceTo(pProgress2.asOutParam()); // transfer the interface + * } + * + * @endcode + */ +template <class T> +class ComObjPtr : public ComPtr<T> +{ +public: + + ComObjPtr() + : ComPtr<T>() + {} + + ComObjPtr(const ComObjPtr &that) + : ComPtr<T>(that) + {} + + ComObjPtr(T *that_p) + : ComPtr<T>(that_p) + {} + + ComObjPtr& operator=(const ComObjPtr &that) + { + ComPtr<T>::operator=(that); + return *this; + } + + ComObjPtr& operator=(T *that_p) + { + ComPtr<T>::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<T> *obj = NULL; + try + { + obj = new ATL::CComObjectNoLock<T>(); + } + 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<T> *obj = NULL; + hrc = ATL::CComObject<T>::CreateInstance(&obj); +# endif +#else /* VBOX_WITH_XPCOM */ + ATL::CComObject<T> *obj; +# ifndef RT_EXCEPTIONS_ENABLED + obj = new ATL::CComObject<T>(); +# else + try + { + obj = new ATL::CComObject<T>(); + } + 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 */ + |