diff options
Diffstat (limited to 'include/VBox/com')
-rw-r--r-- | include/VBox/com/AutoLock.h | 692 | ||||
-rw-r--r-- | include/VBox/com/ErrorInfo.h | 535 | ||||
-rw-r--r-- | include/VBox/com/EventQueue.h | 141 | ||||
-rw-r--r-- | include/VBox/com/Guid.h | 499 | ||||
-rw-r--r-- | include/VBox/com/Makefile.kup | 0 | ||||
-rw-r--r-- | include/VBox/com/MultiResult.h | 268 | ||||
-rw-r--r-- | include/VBox/com/NativeEventQueue.h | 151 | ||||
-rw-r--r-- | include/VBox/com/VirtualBox.h | 60 | ||||
-rw-r--r-- | include/VBox/com/array.h | 1776 | ||||
-rw-r--r-- | include/VBox/com/assert.h | 125 | ||||
-rw-r--r-- | include/VBox/com/com.h | 120 | ||||
-rw-r--r-- | include/VBox/com/defs.h | 595 | ||||
-rw-r--r-- | include/VBox/com/errorprint.h | 381 | ||||
-rw-r--r-- | include/VBox/com/list.h | 213 | ||||
-rw-r--r-- | include/VBox/com/listeners.h | 183 | ||||
-rw-r--r-- | include/VBox/com/microatl.h | 1360 | ||||
-rw-r--r-- | include/VBox/com/mtlist.h | 210 | ||||
-rw-r--r-- | include/VBox/com/ptr.h | 505 | ||||
-rw-r--r-- | include/VBox/com/string.h | 903 |
19 files changed, 8717 insertions, 0 deletions
diff --git a/include/VBox/com/AutoLock.h b/include/VBox/com/AutoLock.h new file mode 100644 index 00000000..71712373 --- /dev/null +++ b/include/VBox/com/AutoLock.h @@ -0,0 +1,692 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Automatic locks, implementation. + */ + +/* + * Copyright (C) 2006-2019 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_AutoLock_h +#define VBOX_INCLUDED_com_AutoLock_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/types.h> + + +/** @defgroup grp_com_autolock Automatic Locks + * @ingroup grp_com + * @{ + */ + +// macros for automatic lock validation; these will amount to nothing +// unless lock validation is enabled for the runtime +#if defined(RT_LOCK_STRICT) +# define VBOX_WITH_MAIN_LOCK_VALIDATION +# define COMMA_LOCKVAL_SRC_POS , RT_SRC_POS +# define LOCKVAL_SRC_POS_DECL RT_SRC_POS_DECL +# define COMMA_LOCKVAL_SRC_POS_DECL , RT_SRC_POS_DECL +# define LOCKVAL_SRC_POS_ARGS RT_SRC_POS_ARGS +# define COMMA_LOCKVAL_SRC_POS_ARGS , RT_SRC_POS_ARGS +#else +# define COMMA_LOCKVAL_SRC_POS +# define LOCKVAL_SRC_POS_DECL +# define COMMA_LOCKVAL_SRC_POS_DECL +# define LOCKVAL_SRC_POS_ARGS +# define COMMA_LOCKVAL_SRC_POS_ARGS +#endif + +namespace util +{ + +//////////////////////////////////////////////////////////////////////////////// +// +// Order classes for lock validation +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * IPRT now has a sophisticated system of run-time locking classes to validate + * locking order. Since the Main code is handled by simpler minds, we want + * compile-time constants for simplicity, and we'll look up the run-time classes + * in AutoLock.cpp transparently. These are passed to the constructors of the + * LockHandle classes. + */ +enum VBoxLockingClass +{ + LOCKCLASS_NONE = 0, + LOCKCLASS_WEBSERVICE = 1, // highest order: webservice locks + LOCKCLASS_VIRTUALBOXOBJECT = 2, // highest order within Main itself: VirtualBox object lock + LOCKCLASS_HOSTOBJECT = 3, // Host object lock + LOCKCLASS_LISTOFMACHINES = 4, // list of machines in VirtualBox object + LOCKCLASS_MACHINEOBJECT = 5, // Machine object lock + LOCKCLASS_SNAPSHOTOBJECT = 6, // snapshot object locks + // (the snapshots tree, including the child pointers in Snapshot, + // is protected by the normal Machine object lock) + LOCKCLASS_MEDIUMQUERY = 7, // lock used to protect Machine::queryInfo + LOCKCLASS_LISTOFMEDIA = 8, // list of media (hard disks, DVDs, floppies) in VirtualBox object + LOCKCLASS_LISTOFOTHEROBJECTS = 9, // any other list of objects + LOCKCLASS_OTHEROBJECT = 10, // any regular object member variable lock + LOCKCLASS_PROGRESSLIST = 11, // list of progress objects in VirtualBox; no other object lock + // may be held after this! + LOCKCLASS_OBJECTSTATE = 12 // object state lock (handled by AutoCaller classes) +}; + +void InitAutoLockSystem(); + +/** + * Check whether the current thread holds any locks in the given class + * + * @return true if any such locks are held, false otherwise. If the lock + * validator is not compiled in, always returns false. + * @param lockClass Which lock class to check. + */ +bool AutoLockHoldsLocksInClass(VBoxLockingClass lockClass); + +//////////////////////////////////////////////////////////////////////////////// +// +// LockHandle and friends +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Abstract base class for semaphore handles (RWLockHandle and WriteLockHandle). + * Don't use this directly, but this implements lock validation for them. + */ +class LockHandle +{ +public: + LockHandle() + {} + + virtual ~LockHandle() + {} + + /** + * Returns @c true if the current thread holds a write lock on this + * read/write semaphore. Intended for debugging only. + */ + virtual bool isWriteLockOnCurrentThread() const = 0; + + /** + * Returns @c true if the current thread holds a read lock on this + * read/write semaphore. Intended for debugging only as it isn't always + * accurate given @a fWannaHear. + */ + virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const = 0; + + /** + * Returns the current write lock level of this semaphore. The lock level + * determines the number of nested #lockWrite() calls on the given + * semaphore handle. + * + * Note that this call is valid only when the current thread owns a write + * lock on the given semaphore handle and will assert otherwise. + */ + virtual uint32_t writeLockLevel() const = 0; + + virtual void lockWrite(LOCKVAL_SRC_POS_DECL) = 0; + virtual void unlockWrite() = 0; + virtual void lockRead(LOCKVAL_SRC_POS_DECL) = 0; + virtual void unlockRead() = 0; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + virtual const char* describe() const = 0; +#endif + +private: + // prohibit copy + assignment + LockHandle(const LockHandle&); + LockHandle& operator=(const LockHandle&); +}; + +/** + * Full-featured read/write semaphore handle implementation. + * + * This is an auxiliary base class for classes that need full-featured + * read/write locking as described in the AutoWriteLock class documentation. + * Instances of classes inherited from this class can be passed as arguments to + * the AutoWriteLock and AutoReadLock constructors. + */ +class RWLockHandle : public LockHandle +{ +public: + RWLockHandle(VBoxLockingClass lockClass); + virtual ~RWLockHandle(); + + virtual bool isWriteLockOnCurrentThread() const; + virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const; + + virtual void lockWrite(LOCKVAL_SRC_POS_DECL); + virtual void unlockWrite(); + virtual void lockRead(LOCKVAL_SRC_POS_DECL); + virtual void unlockRead(); + + virtual uint32_t writeLockLevel() const; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + virtual const char* describe() const; +#endif + +private: + struct Data; + Data *m; + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(RWLockHandle); /* Shuts up MSC warning C4625. */ +}; + +/** + * Write-only semaphore handle implementation. + * + * This is an auxiliary base class for classes that need write-only (exclusive) + * locking and do not need read (shared) locking. This implementation uses a + * cheap and fast critical section for both lockWrite() and lockRead() methods + * which makes a lockRead() call fully equivalent to the lockWrite() call and + * therefore makes it pointless to use instahces of this class with + * AutoReadLock instances -- shared locking will not be possible anyway and + * any call to lock() will block if there are lock owners on other threads. + * + * Use with care only when absolutely sure that shared locks are not necessary. + */ +class WriteLockHandle : public LockHandle +{ +public: + WriteLockHandle(VBoxLockingClass lockClass); + virtual ~WriteLockHandle(); + virtual bool isWriteLockOnCurrentThread() const; + virtual bool isReadLockedOnCurrentThread(bool fWannaHear = true) const; + + virtual void lockWrite(LOCKVAL_SRC_POS_DECL); + virtual void unlockWrite(); + virtual void lockRead(LOCKVAL_SRC_POS_DECL); + virtual void unlockRead(); + virtual uint32_t writeLockLevel() const; + +#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION + virtual const char* describe() const; +#endif + +private: + struct Data; + Data *m; + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(WriteLockHandle); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// Lockable +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Lockable interface. + * + * This is an abstract base for classes that need read/write locking. Unlike + * RWLockHandle and other classes that makes the read/write semaphore a part of + * class data, this class allows subclasses to decide which semaphore handle to + * use. + */ +class Lockable +{ +public: + + /** + * Returns a pointer to a LockHandle used by AutoWriteLock/AutoReadLock + * for locking. Subclasses are allowed to return @c NULL -- in this case, + * the AutoWriteLock/AutoReadLock object constructed using an instance of + * such subclass will simply turn into no-op. + */ + virtual LockHandle *lockHandle() const = 0; + + /** + * Equivalent to <tt>#lockHandle()->isWriteLockOnCurrentThread()</tt>. + * Returns @c false if lockHandle() returns @c NULL. + */ + bool isWriteLockOnCurrentThread() + { + LockHandle *h = lockHandle(); + return h ? h->isWriteLockOnCurrentThread() : false; + } + + /** + * Equivalent to <tt>#lockHandle()->isReadLockedOnCurrentThread()</tt>. + * Returns @c false if lockHandle() returns @c NULL. + * @note Use with care, simple debug assertions and similar only. + */ + bool isReadLockedOnCurrentThread(bool fWannaHear = true) const + { + LockHandle *h = lockHandle(); + return h ? h->isReadLockedOnCurrentThread(fWannaHear) : false; + } +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoLockBase +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Abstract base class for all autolocks. + * + * This cannot be used directly. Use AutoReadLock or AutoWriteLock or AutoMultiWriteLock2/3 + * which directly and indirectly derive from this. + * + * In the implementation, the instance data contains a list of lock handles. + * The class provides some utility functions to help locking and unlocking + * them. + */ + +class AutoLockBase +{ +protected: + AutoLockBase(uint32_t cHandles + COMMA_LOCKVAL_SRC_POS_DECL); + AutoLockBase(uint32_t cHandles, + LockHandle *pHandle + COMMA_LOCKVAL_SRC_POS_DECL); + virtual ~AutoLockBase(); + + struct Data; + Data *m; + + virtual void callLockImpl(LockHandle &l) = 0; + virtual void callUnlockImpl(LockHandle &l) = 0; + + void callLockOnAllHandles(); + void callUnlockOnAllHandles(); + + void cleanup(); + +public: + void acquire(); + void release(); + +private: + // prohibit copy + assignment + AutoLockBase(const AutoLockBase&); + AutoLockBase& operator=(const AutoLockBase&); +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoReadLock +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Automatic read lock. Use this with a RWLockHandle to request a read/write + * semaphore in read mode. You can also use this with a WriteLockHandle but + * that makes little sense since they treat read mode like write mode. + * + * If constructed with a RWLockHandle or an instance of Lockable (which in + * practice means any VirtualBoxBase derivative), it autoamtically requests + * the lock in read mode and releases the read lock in the destructor. + */ +class AutoReadLock : public AutoLockBase +{ +public: + + /** + * Constructs a null instance that does not manage any read/write + * semaphore. + * + * Note that all method calls on a null instance are no-ops. This allows to + * have the code where lock protection can be selected (or omitted) at + * runtime. + */ + AutoReadLock(LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(LockHandle *aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(LockHandle &aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + &aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(const Lockable &aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + aLockable.lockHandle() + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a read lock. + */ + AutoReadLock(const Lockable *aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(1, + aLockable ? aLockable->lockHandle() : NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + virtual ~AutoReadLock(); + + virtual void callLockImpl(LockHandle &l); + virtual void callUnlockImpl(LockHandle &l); + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoReadLock); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoWriteLockBase +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Base class for all auto write locks. + * + * This cannot be used directly. Use AutoWriteLock or AutoMultiWriteLock2/3 + * which derive from this. + * + * It has some utility methods for subclasses. + */ +class AutoWriteLockBase : public AutoLockBase +{ +protected: + AutoWriteLockBase(uint32_t cHandles + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(cHandles + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + AutoWriteLockBase(uint32_t cHandles, + LockHandle *pHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoLockBase(cHandles, + pHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + virtual ~AutoWriteLockBase() + { } + + virtual void callLockImpl(LockHandle &l); + virtual void callUnlockImpl(LockHandle &l); + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLockBase); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoWriteLock +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * Automatic write lock. Use this with a RWLockHandle to request a read/write + * semaphore in write mode. There can only ever be one writer of a read/write + * semaphore: while the lock is held in write mode, no other writer or reader + * can request the semaphore and will block. + * + * If constructed with a RWLockHandle or an instance of Lockable (which in + * practice means any VirtualBoxBase derivative), it autoamtically requests + * the lock in write mode and releases the write lock in the destructor. + * + * When used with a WriteLockHandle, it requests the semaphore contained therein + * exclusively. + */ +class AutoWriteLock : public AutoWriteLockBase +{ +public: + + /** + * Constructs a null instance that does not manage any read/write + * semaphore. + * + * Note that all method calls on a null instance are no-ops. This allows to + * have the code where lock protection can be selected (or omitted) at + * runtime. + */ + AutoWriteLock(LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(LockHandle *aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(LockHandle &aHandle + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + &aHandle + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(const Lockable &aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + aLockable.lockHandle() + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(const Lockable *aLockable + COMMA_LOCKVAL_SRC_POS_DECL) + : AutoWriteLockBase(1, + aLockable ? aLockable->lockHandle() : NULL + COMMA_LOCKVAL_SRC_POS_ARGS) + { + acquire(); + } + + /** + * Constructs a new instance that will start managing the given read/write + * semaphore by requesting a write lock. + */ + AutoWriteLock(uint32_t cHandles, + LockHandle** pHandles + COMMA_LOCKVAL_SRC_POS_DECL); + + /** + * Release all write locks acquired by this instance through the #acquire() + * call and destroys the instance. + * + * Note that if there there are nested #acquire() calls without the + * corresponding number of #release() calls when the destructor is called, it + * will assert. This is because having an unbalanced number of nested locks + * is a program logic error which must be fixed. + */ + virtual ~AutoWriteLock() + { + cleanup(); + } + + void attach(LockHandle *aHandle); + + /** @see attach (LockHandle *) */ + void attach(LockHandle &aHandle) + { + attach(&aHandle); + } + + /** @see attach (LockHandle *) */ + void attach(const Lockable &aLockable) + { + attach(aLockable.lockHandle()); + } + + /** @see attach (LockHandle *) */ + void attach(const Lockable *aLockable) + { + attach(aLockable ? aLockable->lockHandle() : NULL); + } + + bool isWriteLockOnCurrentThread() const; + uint32_t writeLockLevel() const; + + bool isReadLockedOnCurrentThread(bool fWannaHear = true) const; + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoWriteLock); /* Shuts up MSC warning C4625. */ +}; + +//////////////////////////////////////////////////////////////////////////////// +// +// AutoMultiWriteLock* +// +//////////////////////////////////////////////////////////////////////////////// + +/** + * A multi-write-lock containing two other write locks. + * + */ +class AutoMultiWriteLock2 : public AutoWriteLockBase +{ +public: + AutoMultiWriteLock2(Lockable *pl1, + Lockable *pl2 + COMMA_LOCKVAL_SRC_POS_DECL); + AutoMultiWriteLock2(LockHandle *pl1, + LockHandle *pl2 + COMMA_LOCKVAL_SRC_POS_DECL); + + virtual ~AutoMultiWriteLock2() + { + cleanup(); + } + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock2); /* Shuts up MSC warning C4625. */ +}; + +/** + * A multi-write-lock containing three other write locks. + * + */ +class AutoMultiWriteLock3 : public AutoWriteLockBase +{ +public: + AutoMultiWriteLock3(Lockable *pl1, + Lockable *pl2, + Lockable *pl3 + COMMA_LOCKVAL_SRC_POS_DECL); + AutoMultiWriteLock3(LockHandle *pl1, + LockHandle *pl2, + LockHandle *pl3 + COMMA_LOCKVAL_SRC_POS_DECL); + + virtual ~AutoMultiWriteLock3() + { + cleanup(); + } + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock3); /* Shuts up MSC warning C4625. */ +}; + +/** + * A multi-write-lock containing four other write locks. + * + */ +class AutoMultiWriteLock4 : public AutoWriteLockBase +{ +public: + AutoMultiWriteLock4(Lockable *pl1, + Lockable *pl2, + Lockable *pl3, + Lockable *pl4 + COMMA_LOCKVAL_SRC_POS_DECL); + AutoMultiWriteLock4(LockHandle *pl1, + LockHandle *pl2, + LockHandle *pl3, + LockHandle *pl4 + COMMA_LOCKVAL_SRC_POS_DECL); + + virtual ~AutoMultiWriteLock4() + { + cleanup(); + } + +private: + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(AutoMultiWriteLock4); /* Shuts up MSC warning C4625. */ +}; + +} /* namespace util */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_AutoLock_h */ + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/include/VBox/com/ErrorInfo.h b/include/VBox/com/ErrorInfo.h new file mode 100644 index 00000000..eaaec190 --- /dev/null +++ b/include/VBox/com/ErrorInfo.h @@ -0,0 +1,535 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - ErrorInfo class declaration. + */ + +/* + * Copyright (C) 2006-2019 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_ErrorInfo_h +#define VBOX_INCLUDED_com_ErrorInfo_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "VBox/com/ptr.h" +#include "VBox/com/string.h" +#include "VBox/com/Guid.h" +#include "VBox/com/assert.h" + + +/** @defgroup grp_com_errinfo ErrorInfo Classes + * @ingroup grp_com + * @{ + */ + +COM_STRUCT_OR_CLASS(IProgress); +COM_STRUCT_OR_CLASS(IVirtualBoxErrorInfo); + +namespace com +{ + +/** + * General discussion: + * + * In COM all errors are stored on a per thread basis. In general this means + * only _one_ active error is possible per thread. A new error will overwrite + * the previous one. To prevent this use MultiResult or ErrorInfoKeeper (see + * below). The implementations in MSCOM/XPCOM differ slightly, but the details + * are handled by this glue code. + * + * We have different classes which are involved in the error management. I try + * to describe them separately to make clear what they are there for. + * + * ErrorInfo: + * + * This class is able to retrieve the per thread error and store it into its + * member variables. This class can also handle non-VirtualBox errors (like + * standard COM errors). + * + * ProgressErrorInfo: + * + * This is just a simple wrapper class to get the ErrorInfo stored within an + * IProgress object. That is the error which was stored when the progress + * object was in use and not an error produced by IProgress itself. + * + * IVirtualBoxErrorInfo: + * + * The VirtualBox interface class for accessing error information from Main + * clients. This class is also used for storing the error information in the + * thread context. + * + * ErrorInfoKeeper: + * + * A helper class which stores the current per thread info internally. After + * calling methods which may produce other errors it is possible to restore + * the previous error and therefore restore the situation before calling the + * other methods. + * + * MultiResult: + * + * Creating an instance of MultiResult turns error chain saving on. All errors + * which follow will be saved in a chain for later access. + * + * COMErrorInfo (Qt/Gui only): + * + * The Qt GUI does some additional work for saving errors. Because we create + * wrappers for _every_ COM call, it is possible to automatically save the + * error info after the execution. This allow some additional info like saving + * the callee. Please note that this error info is saved on the client side + * and therefore locally to the object instance. See COMBaseWithEI, + * COMErrorInfo and the generated COMWrappers.cpp in the GUI. + * + * Errors itself are set in VirtualBoxBase::setErrorInternal. First a + * IVirtualBoxErrorInfo object is created and the given error is saved within. + * If MultiResult is active the current per thread error is fetched and + * attached to the new created IVirtualBoxErrorInfo object. Next this object is + * set as the new per thread error. + * + * Some general hints: + * + * - Always use setError, especially when you are working in an asynchronous thread + * to indicate an error. Otherwise the error information itself will not make + * it into the client. + * + */ + +/** + * The ErrorInfo class provides a convenient way to retrieve error + * information set by the most recent interface method, that was invoked on + * the current thread and returned an unsuccessful result code. + * + * Once the instance of this class is created, the error information for + * the current thread is cleared. + * + * There is no sense to use instances of this class after the last + * invoked interface method returns a success. + * + * The class usage pattern is as follows: + * <code> + * IFoo *foo; + * ... + * HRESULT rc = foo->SomeMethod(); + * if (FAILED(rc)) { + * ErrorInfo info(foo); + * if (info.isFullAvailable()) { + * printf("error message = %ls\n", info.getText().raw()); + * } + * } + * </code> + * + * This class fetches error information using the IErrorInfo interface on + * Win32 (MS COM) or the nsIException interface on other platforms (XPCOM), + * or the extended IVirtualBoxErrorInfo interface when when it is available + * (i.e. a given IErrorInfo or nsIException instance implements it). + * Currently, IVirtualBoxErrorInfo is only available for VirtualBox components. + * + * ErrorInfo::isFullAvailable() and ErrorInfo::isBasicAvailable() determine + * what level of error information is available. If #isBasicAvailable() + * returns true, it means that only IErrorInfo or nsIException is available as + * the source of information (depending on the platform), but not + * IVirtualBoxErrorInfo. If #isFullAvailable() returns true, it means that all + * three interfaces are available. If both methods return false, no error info + * is available at all. + * + * Here is a table of correspondence between this class methods and + * and IErrorInfo/nsIException/IVirtualBoxErrorInfo attributes/methods: + * + * ErrorInfo IErrorInfo nsIException IVirtualBoxErrorInfo + * -------------------------------------------------------------------- + * getResultCode -- result resultCode + * getIID GetGUID -- interfaceID + * getComponent GetSource -- component + * getText GetDescription message text + * + * '--' means that this interface does not provide the corresponding portion + * of information, therefore it is useless to query it if only + * #isBasicAvailable() returns true. As it can be seen, the amount of + * information provided at the basic level, depends on the platform + * (MS COM or XPCOM). + */ +class ErrorInfo +{ +public: + + /** + * Constructs a new, "interfaceless" ErrorInfo instance that takes + * the error information possibly set on the current thread by an + * interface method of some COM component or by the COM subsystem. + * + * This constructor is useful, for example, after an unsuccessful attempt + * to instantiate (create) a component, so there is no any valid interface + * pointer available. + */ + explicit ErrorInfo() + : mIsBasicAvailable(false), + mIsFullAvailable(false), + mResultCode(S_OK), + mResultDetail(0), + m_pNext(NULL) + { + init(); + } + + ErrorInfo(IUnknown *pObj, const GUID &aIID) + : mIsBasicAvailable(false), + mIsFullAvailable(false), + mResultCode(S_OK), + mResultDetail(0), + m_pNext(NULL) + { + init(pObj, aIID); + } + + /** Specialization for the IVirtualBoxErrorInfo smart pointer */ + ErrorInfo(const ComPtr<IVirtualBoxErrorInfo> &aPtr) + : mIsBasicAvailable(false), mIsFullAvailable(false) + , mResultCode(S_OK), mResultDetail(0) + { init(aPtr); } + + /** + * Constructs a new ErrorInfo instance from the IVirtualBoxErrorInfo + * interface pointer. If this pointer is not NULL, both #isFullAvailable() + * and #isBasicAvailable() will return |true|. + * + * @param aInfo pointer to the IVirtualBoxErrorInfo interface that + * holds error info to be fetched by this instance + */ + ErrorInfo(IVirtualBoxErrorInfo *aInfo) + : mIsBasicAvailable(false), mIsFullAvailable(false) + , mResultCode(S_OK), mResultDetail(0) + { init(aInfo); } + + ErrorInfo(const ErrorInfo &x) + { + copyFrom(x); + } + + virtual ~ErrorInfo() + { + cleanup(); + } + + ErrorInfo& operator=(const ErrorInfo& x) + { + cleanup(); + copyFrom(x); + return *this; + } + + /** + * Returns whether basic error info is actually available for the current + * thread. If the instance was created from an interface pointer that + * supports basic error info and successfully provided it, or if it is an + * "interfaceless" instance and there is some error info for the current + * thread, the returned value will be true. + * + * See the class description for details about the basic error info level. + * + * The appropriate methods of this class provide meaningful info only when + * this method returns true (otherwise they simply return NULL-like values). + */ + bool isBasicAvailable() const + { + return mIsBasicAvailable; + } + + /** + * Returns whether full error info is actually available for the current + * thread. If the instance was created from an interface pointer that + * supports full error info and successfully provided it, or if it is an + * "interfaceless" instance and there is some error info for the current + * thread, the returned value will be true. + * + * See the class description for details about the full error info level. + * + * The appropriate methods of this class provide meaningful info only when + * this method returns true (otherwise they simply return NULL-like values). + */ + bool isFullAvailable() const + { + return mIsFullAvailable; + } + + /** + * Returns the COM result code of the failed operation. + */ + HRESULT getResultCode() const + { + return mResultCode; + } + + /** + * Returns the (optional) result detail code of the failed operation. + */ + LONG getResultDetail() const + { + return mResultDetail; + } + + /** + * Returns the IID of the interface that defined the error. + */ + const Guid& getInterfaceID() const + { + return mInterfaceID; + } + + /** + * Returns the name of the component that generated the error. + */ + const Bstr& getComponent() const + { + return mComponent; + } + + /** + * Returns the textual description of the error. + */ + const Bstr& getText() const + { + return mText; + } + + /** + * Returns the next error information object or @c NULL if there is none. + */ + const ErrorInfo* getNext() const + { + return m_pNext; + } + + /** + * Returns the name of the interface that defined the error + */ + const Bstr& getInterfaceName() const + { + return mInterfaceName; + } + + /** + * Returns the IID of the interface that returned the error. + * + * This method returns a non-null IID only if the instance was created + * using template \<class I\> ErrorInfo(I *i) or + * template \<class I\> ErrorInfo(const ComPtr<I> &i) constructor. + * + * @todo broken ErrorInfo documentation links, possibly misleading. + */ + const Guid& getCalleeIID() const + { + return mCalleeIID; + } + + /** + * Returns the name of the interface that returned the error + * + * This method returns a non-null name only if the instance was created + * using template \<class I\> ErrorInfo(I *i) or + * template \<class I\> ErrorInfo(const ComPtr<I> &i) constructor. + * + * @todo broken ErrorInfo documentation links, possibly misleading. + */ + const Bstr& getCalleeName() const + { + return mCalleeName; + } + + HRESULT getVirtualBoxErrorInfo(ComPtr<IVirtualBoxErrorInfo> &pVirtualBoxErrorInfo); + + /** + * Resets all collected error information. #isBasicAvailable() and + * #isFullAvailable will return @c true after this method is called. + */ + void setNull() + { + cleanup(); + } + +protected: + + ErrorInfo(bool /* aDummy */) + : mIsBasicAvailable(false), + mIsFullAvailable(false), + mResultCode(S_OK), + m_pNext(NULL) + { } + + void copyFrom(const ErrorInfo &x); + void cleanup(); + + void init(bool aKeepObj = false); + void init(IUnknown *aUnk, const GUID &aIID, bool aKeepObj = false); + void init(IVirtualBoxErrorInfo *aInfo); + + bool mIsBasicAvailable : 1; + bool mIsFullAvailable : 1; + + HRESULT mResultCode; + LONG mResultDetail; + Guid mInterfaceID; + Bstr mComponent; + Bstr mText; + + ErrorInfo *m_pNext; + + Bstr mInterfaceName; + Guid mCalleeIID; + Bstr mCalleeName; + + ComPtr<IUnknown> mErrorInfo; +}; + +/** + * A convenience subclass of ErrorInfo that, given an IProgress interface + * pointer, reads its errorInfo attribute and uses the returned + * IVirtualBoxErrorInfo instance to construct itself. + */ +class ProgressErrorInfo : public ErrorInfo +{ +public: + + /** + * Constructs a new instance by fetching error information from the + * IProgress interface pointer. If the progress object is not NULL, + * its completed attribute is true, resultCode represents a failure, + * and the errorInfo attribute returns a valid IVirtualBoxErrorInfo pointer, + * both #isFullAvailable() and #isBasicAvailable() will return true. + * + * @param progress the progress object representing a failed operation + */ + ProgressErrorInfo(IProgress *progress); +}; + +/** + * A convenience subclass of ErrorInfo that allows to preserve the current + * error info. Instances of this class fetch an error info object set on the + * current thread and keep a reference to it, which allows to restore it + * later using the #restore() method. This is useful to preserve error + * information returned by some method for the duration of making another COM + * call that may set its own error info and overwrite the existing + * one. Preserving and restoring error information makes sense when some + * method wants to return error information set by other call as its own + * error information while it still needs to make another call before return. + * + * Instead of calling #restore() explicitly you may let the object destructor + * do it for you, if you correctly limit the object's lifetime. + * + * The usage pattern is: + * <code> + * rc = foo->method(); + * if (FAILED(rc)) + * { + * ErrorInfoKeeper eik; + * ... + * // bar may return error info as well + * bar->method(); + * ... + * // no need to call #restore() explicitly here because the eik's + * // destructor will restore error info fetched after the failed + * // call to foo before returning to the caller + * return rc; + * } + * </code> + */ +class ErrorInfoKeeper : public ErrorInfo +{ +public: + + /** + * Constructs a new instance that will fetch the current error info if + * @a aIsNull is @c false (by default) or remain uninitialized (null) + * otherwise. + * + * @param aIsNull @c true to prevent fetching error info and leave + * the instance uninitialized. + */ + ErrorInfoKeeper(bool aIsNull = false) + : ErrorInfo(false), mForgot(aIsNull) + { + if (!aIsNull) + init(true /* aKeepObj */); + } + + /** + * Constructs a new instance from an ErrorInfo object, to inject a full + * error info created elsewhere. + * + * @param aInfo @c true to prevent fetching error info and leave + * the instance uninitialized. + */ + ErrorInfoKeeper(const ErrorInfo &aInfo) + : ErrorInfo(false), mForgot(false) + { + copyFrom(aInfo); + } + + /** + * Destroys this instance and automatically calls #restore() which will + * either restore error info fetched by the constructor or do nothing + * if #forget() was called before destruction. + */ + ~ErrorInfoKeeper() { if (!mForgot) restore(); } + + /** + * Tries to (re-)fetch error info set on the current thread. On success, + * the previous error information, if any, will be overwritten with the + * new error information. On failure, or if there is no error information + * available, this instance will be reset to null. + */ + void fetch() + { + setNull(); + mForgot = false; + init(true /* aKeepObj */); + } + + /** + * Restores error info fetched by the constructor and forgets it + * afterwards. Does nothing if the error info was forgotten by #forget(). + * + * @return COM result of the restore operation. + */ + HRESULT restore(); + + /** + * Forgets error info fetched by the constructor to prevent it from + * being restored by #restore() or by the destructor. + */ + void forget() { mForgot = true; } + + /** + * Forgets error info fetched by the constructor to prevent it from + * being restored by #restore() or by the destructor, and returns the + * stored error info object to the caller. + */ + ComPtr<IUnknown> takeError() { mForgot = true; return mErrorInfo; } + +private: + + bool mForgot : 1; +}; + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_ErrorInfo_h */ + diff --git a/include/VBox/com/EventQueue.h b/include/VBox/com/EventQueue.h new file mode 100644 index 00000000..0fd9ae00 --- /dev/null +++ b/include/VBox/com/EventQueue.h @@ -0,0 +1,141 @@ +/* $Id: EventQueue.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - Event queue class declaration. + */ + +/* + * Copyright (C) 2013-2019 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_EventQueue_h +#define VBOX_INCLUDED_com_EventQueue_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <list> + +#include <iprt/asm.h> +#include <iprt/critsect.h> + +#include <VBox/com/defs.h> +#include <VBox/com/assert.h> + + +/** @defgroup grp_com_evtqueue Event Queue Classes + * @ingroup grp_com + * @{ + */ + +namespace com +{ + +class EventQueue; + +/** + * Base class for all events. Intended to be subclassed to introduce new + * events and handlers for them. + * + * Subclasses usually reimplement virtual #handler() (that does nothing by + * default) and add new data members describing the event. + */ +class Event +{ +public: + + Event(void) : + mRefCount(0) { } + virtual ~Event(void) { AssertMsg(!mRefCount, + ("Reference count of event=%p not 0 on destruction (is %RU32)\n", + this, mRefCount)); } +public: + + uint32_t AddRef(void) { return ASMAtomicIncU32(&mRefCount); } + void Release(void) + { + Assert(mRefCount); + uint32_t cRefs = ASMAtomicDecU32(&mRefCount); + if (!cRefs) + delete this; + } + +protected: + + /** + * Event handler. Called in the context of the event queue's thread. + * Always reimplemented by subclasses + * + * @return reserved, should be NULL. + */ + virtual void *handler(void) { return NULL; } + + friend class EventQueue; + +protected: + + /** The event's reference count. */ + uint32_t mRefCount; +}; + +typedef std::list< Event* > EventQueueList; +typedef std::list< Event* >::iterator EventQueueListIterator; +typedef std::list< Event* >::const_iterator EventQueueListIteratorConst; + +/** + * Simple event queue. + */ +class EventQueue +{ +public: + + EventQueue(void); + virtual ~EventQueue(void); + +public: + + BOOL postEvent(Event *event); + int processEventQueue(RTMSINTERVAL cMsTimeout); + int processPendingEvents(size_t cNumEvents); + int interruptEventQueueProcessing(); + +private: + + /** Critical section for serializing access to this + * event queue. */ + RTCRITSECT mCritSect; + /** Number of concurrent users. At the moment we + * only support one concurrent user at a time when + calling processEventQueue(). */ + uint32_t mUserCnt; + /** Event semaphore for getting notified on new + * events being handled. */ + RTSEMEVENT mSemEvent; + /** The actual event queue, implemented as a list. */ + EventQueueList mEvents; + /** Shutdown indicator. */ + bool mShutdown; +}; + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_EventQueue_h */ + diff --git a/include/VBox/com/Guid.h b/include/VBox/com/Guid.h new file mode 100644 index 00000000..38a3b783 --- /dev/null +++ b/include/VBox/com/Guid.h @@ -0,0 +1,499 @@ +/* $Id: Guid.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - Guid class declaration. + */ + +/* + * Copyright (C) 2006-2019 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_Guid_h +#define VBOX_INCLUDED_com_Guid_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 + +#include "VBox/com/string.h" + +#include <iprt/uuid.h> + + +/** @defgroup grp_com_guid GUID Class + * @ingroup grp_com + * @{ + */ + +namespace com +{ + +typedef enum GuidState_t +{ + GUID_ZERO, + GUID_NORMAL, + GUID_INVALID +} GuidState_t; + +/** + * Helper class that represents the UUID type and hides platform-specific + * implementation details. + */ +class Guid +{ +public: + + Guid() + { + clear(); + } + + Guid(const Guid &that) + { + mUuid = that.mUuid; + mGuidState = that.mGuidState; + dbg_refresh(); + } + + Guid(const RTUUID &that) + { + mUuid = that; + updateState(); + dbg_refresh(); + } + + Guid(const GUID &that) + { + AssertCompileSize(GUID, sizeof(RTUUID)); + ::memcpy(&mUuid, &that, sizeof(GUID)); + updateState(); + dbg_refresh(); + } + + /** + * Construct a GUID from a string. + * + * @param that The UUID string. Can be with or without the curly + * brackets. Empty strings are translated to a zero + * GUID, and strings which are not confirming to + * valid GUID string representations are marked as + * invalid. + */ + Guid(const char *that) + { + initString(that); + } + + /** + * Construct a GUID from a BSTR. + * + * @param that The UUID BSTR. Can be with or without the curly + * brackets. Empty strings are translated to a zero + * GUID, and strings which are not confirming to + * valid GUID string representations are marked as + * invalid. + */ + Guid(CBSTR that) + { + initBSTR(that); + } + + /** + * Construct a GUID from a Utf8Str. + * + * @param that The UUID Utf8Str. Can be with or without the curly + * brackets. Empty strings are translated to a zero + * GUID, and strings which are not confirming to + * valid GUID string representations are marked as + */ + Guid(const Utf8Str &that) + { + initString(that.c_str()); + } + + /** + * Construct a GUID from a RTCString. + * + * @param that The UUID RTCString. Can be with or without the curly + * brackets. Empty strings are translated to a zero + * GUID, and strings which are not confirming to + * valid GUID string representations are marked as + */ + Guid(const RTCString &that) + { + initString(that.c_str()); + } + + /** + * Construct a GUID from a Bstr. + * + * @param that The UUID Bstr. Can be with or without the curly + * brackets. Empty strings are translated to a zero + * GUID, and strings which are not confirming to + * valid GUID string representations are marked as + */ + Guid(const Bstr &that) + { + initBSTR(that.raw()); + } + + Guid& operator=(const Guid &that) + { + mUuid = that.mUuid; + mGuidState = that.mGuidState; + dbg_refresh(); + return *this; + } + + Guid& operator=(const RTUUID &guid) + { + mUuid = guid; + updateState(); + dbg_refresh(); + return *this; + } + + Guid& operator=(const GUID &guid) + { + AssertCompileSize(GUID, sizeof(RTUUID)); + ::memcpy(&mUuid, &guid, sizeof(GUID)); + updateState(); + dbg_refresh(); + return *this; + } + + Guid& operator=(const char *str) + { + initString(str); + return *this; + } + + Guid& operator=(CBSTR str) + { + initBSTR(str); + return *this; + } + + Guid& operator=(const Utf8Str &str) + { + return operator=(str.c_str()); + } + + Guid& operator=(const RTCString &str) + { + return operator=(str.c_str()); + } + + Guid& operator=(const Bstr &str) + { + return operator=(str.raw()); + } + + void create() + { + ::RTUuidCreate(&mUuid); + mGuidState = GUID_NORMAL; + dbg_refresh(); + } + + void clear() + { + makeClear(); + dbg_refresh(); + } + + /** + * Convert the GUID to a string. + * + * @returns String object containing the formatted GUID. + * @throws std::bad_alloc + */ + Utf8Str toString() const + { + if (mGuidState == GUID_INVALID) + { + /* What to return in case of wrong Guid */ + return Utf8Str("00000000-0000-0000-0000-00000000000"); + } + + char buf[RTUUID_STR_LENGTH]; + ::memset(buf, '\0', sizeof(buf)); + ::RTUuidToStr(&mUuid, buf, sizeof(buf)); + + return Utf8Str(buf); + } + + /** + * Like toString, but encloses the returned string in curly brackets. + * + * @returns String object containing the formatted GUID in curly brackets. + * @throws std::bad_alloc + */ + Utf8Str toStringCurly() const + { + if (mGuidState == GUID_INVALID) + { + /* What to return in case of wrong Guid */ + return Utf8Str("{00000000-0000-0000-0000-00000000000}"); + } + + char buf[RTUUID_STR_LENGTH + 2]; + ::memset(buf, '\0', sizeof(buf)); + ::RTUuidToStr(&mUuid, buf + 1, sizeof(buf) - 2); + buf[0] = '{'; + buf[sizeof(buf) - 2] = '}'; + + return Utf8Str(buf); + } + + /** + * Convert the GUID to a string. + * + * @returns Bstr object containing the formatted GUID. + * @throws std::bad_alloc + */ + Bstr toUtf16() const + { + if (mGuidState == GUID_INVALID) + { + /* What to return in case of wrong Guid */ + return Bstr("00000000-0000-0000-0000-00000000000"); + } + + RTUTF16 buf[RTUUID_STR_LENGTH]; + ::memset(buf, '\0', sizeof(buf)); + ::RTUuidToUtf16(&mUuid, buf, RT_ELEMENTS(buf)); + + return Bstr(buf); + } + + bool isValid() const + { + return mGuidState != GUID_INVALID; + } + + bool isZero() const + { + return mGuidState == GUID_ZERO; + } + + bool operator==(const Guid &that) const { return ::RTUuidCompare(&mUuid, &that.mUuid) == 0; } + bool operator==(const RTUUID &guid) const { return ::RTUuidCompare(&mUuid, &guid) == 0; } + bool operator==(const GUID &guid) const { return ::RTUuidCompare(&mUuid, (PRTUUID)&guid) == 0; } + bool operator!=(const Guid &that) const { return !operator==(that); } + bool operator!=(const GUID &guid) const { return !operator==(guid); } + bool operator!=(const RTUUID &guid) const { return !operator==(guid); } + bool operator<(const Guid &that) const { return ::RTUuidCompare(&mUuid, &that.mUuid) < 0; } + bool operator<(const GUID &guid) const { return ::RTUuidCompare(&mUuid, (PRTUUID)&guid) < 0; } + bool operator<(const RTUUID &guid) const { return ::RTUuidCompare(&mUuid, &guid) < 0; } + + /** + * To directly copy the contents to a GUID, or for passing it as an input + * parameter of type (const GUID *), the compiler converts. */ + const GUID &ref() const + { + return *(const GUID *)&mUuid; + } + + /** + * To pass instances to printf-like functions. + */ + PCRTUUID raw() const + { + return (PCRTUUID)&mUuid; + } + +#if !defined(VBOX_WITH_XPCOM) + + /** To assign instances to OUT_GUID parameters from within the interface + * method. */ + const Guid &cloneTo(GUID *pguid) const + { + if (pguid) + ::memcpy(pguid, &mUuid, sizeof(GUID)); + return *this; + } + + /** To pass instances as OUT_GUID parameters to interface methods. */ + GUID *asOutParam() + { + return (GUID *)&mUuid; + } + +#else + + /** To assign instances to OUT_GUID parameters from within the + * interface method */ + const Guid &cloneTo(nsID **ppGuid) const + { + if (ppGuid) + *ppGuid = (nsID *)nsMemory::Clone(&mUuid, sizeof(nsID)); + + return *this; + } + + /** + * Internal helper class for asOutParam(). + * + * This takes a GUID reference in the constructor and copies the mUuid from + * the method to that instance in its destructor. + */ + class GuidOutParam + { + GuidOutParam(Guid &guid) + : ptr(0), + outer(guid) + { + outer.clear(); + } + + nsID *ptr; + Guid &outer; + GuidOutParam(const GuidOutParam &that); // disabled + GuidOutParam &operator=(const GuidOutParam &that); // disabled + public: + operator nsID**() { return &ptr; } + ~GuidOutParam() + { + if (ptr && outer.isZero()) + { + outer = *ptr; + outer.dbg_refresh(); + nsMemory::Free(ptr); + } + } + friend class Guid; + }; + + /** to pass instances as OUT_GUID parameters to interface methods */ + GuidOutParam asOutParam() { return GuidOutParam(*this); } + +#endif + + /** + * Static immutable empty (zero) object. May be used for comparison purposes. + */ + static const Guid Empty; + +private: + void makeClear() + { + ::RTUuidClear(&mUuid); + mGuidState = GUID_ZERO; + } + + void makeInvalid() + { + ::RTUuidClear(&mUuid); + mGuidState = GUID_INVALID; + } + + void updateState() + { + if (::RTUuidIsNull(&mUuid)) + mGuidState = GUID_ZERO; + else + mGuidState = GUID_NORMAL; + } + + void initString(const char *that) + { + if (!that || !*that) + { + makeClear(); + } + else + { + int rc = ::RTUuidFromStr(&mUuid, that); + if (RT_SUCCESS(rc)) + updateState(); + else + makeInvalid(); + } + dbg_refresh(); + } + + void initBSTR(CBSTR that) + { + if (!that || !*that) + { + makeClear(); + } + else + { + int rc = ::RTUuidFromUtf16(&mUuid, that); + if (RT_SUCCESS(rc)) + updateState(); + else + makeInvalid(); + } + dbg_refresh(); + } + + /** + * Refresh the debug-only UUID string. + * + * In debug code, refresh the UUID string representatino for debugging; + * must be called every time the internal uuid changes; compiles to nothing + * in release code. + */ + inline void dbg_refresh() + { +#ifdef DEBUG + switch (mGuidState) + { + case GUID_ZERO: + case GUID_NORMAL: + ::RTUuidToStr(&mUuid, mszUuid, RTUUID_STR_LENGTH); + break; + default: + ::memset(mszUuid, '\0', sizeof(mszUuid)); + ::RTStrCopy(mszUuid, sizeof(mszUuid), "INVALID"); + break; + } + m_pcszUUID = mszUuid; +#endif + } + + /** The UUID. */ + RTUUID mUuid; + + GuidState_t mGuidState; + +#ifdef DEBUG + /** String representation of mUuid for printing in the debugger. */ + char mszUuid[RTUUID_STR_LENGTH]; + /** Another string variant for the debugger, points to szUUID. */ + const char *m_pcszUUID; +#endif +}; + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_Guid_h */ + diff --git a/include/VBox/com/Makefile.kup b/include/VBox/com/Makefile.kup new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/include/VBox/com/Makefile.kup diff --git a/include/VBox/com/MultiResult.h b/include/VBox/com/MultiResult.h new file mode 100644 index 00000000..36afc5c7 --- /dev/null +++ b/include/VBox/com/MultiResult.h @@ -0,0 +1,268 @@ +/* $Id: MultiResult.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - MultiResult class declarations. + */ + +/* + * Copyright (C) 2008-2019 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_MultiResult_h +#define VBOX_INCLUDED_com_MultiResult_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "VBox/com/defs.h" +#include "VBox/com/string.h" + +#include <stdarg.h> + +/** @defgroup grp_com_mr MultiResult Classes + * @ingroup grp_com + * @{ + */ + +namespace com +{ + +/** + * "First worst" result type. + * + * Variables of this class are used instead of HRESULT variables when it is + * desirable to memorize the "first worst" result code instead of the last + * assigned one. In other words, an assignment operation to a variable of this + * class will succeed only if the result code to assign has worse severity. The + * following table demonstrate this (the first column lists the previous result + * code stored in the variable, the first row lists the new result code being + * assigned, 'A' means the assignment will take place, '> S_OK' means a warning + * result code): + * + * {{{ + * FAILED > S_OK S_OK + * FAILED - - - + * > S_OK A - - + * S_OK A A - + * + * }}} + * + * In practice, you will need to use a FWResult variable when you call some COM + * method B after another COM method A fails and want to return the result code + * of A even if B also fails, but want to return the failed result code of B if + * A issues a warning or succeeds. + */ +class FWResult +{ + +public: + + /** + * Constructs a new variable. Note that by default this constructor sets the + * result code to E_FAIL to make sure a failure is returned to the caller if + * the variable is never assigned another value (which is considered as the + * improper use of this class). + */ + FWResult (HRESULT aRC = E_FAIL) : mRC (aRC) {} + + FWResult &operator= (HRESULT aRC) + { + if ((FAILED (aRC) && !FAILED (mRC)) || + (mRC == S_OK && aRC != S_OK)) + mRC = aRC; + + return *this; + } + + operator HRESULT() const { return mRC; } + + HRESULT *operator&() { return &mRC; } + +private: + + HRESULT mRC; +}; + +/** + * The MultiResult class is a com::FWResult enhancement that also acts as a + * switch to turn on multi-error mode for VirtualBoxBase::setError() and + * VirtualBoxBase::setWarning() calls. + * + * When an instance of this class is created, multi-error mode is turned on + * for the current thread and the turn-on counter is increased by one. In + * multi-error mode, a call to setError() or setWarning() does not + * overwrite the current error or warning info object possibly set on the + * current thread by other method calls, but instead it stores this old + * object in the IVirtualBoxErrorInfo::next attribute of the new error + * object being set. + * + * This way, error/warning objects are stacked together and form a chain of + * errors where the most recent error is the first one retrieved by the + * calling party, the preceding error is what the + * IVirtualBoxErrorInfo::next attribute of the first error points to, and so + * on, up to the first error or warning occurred which is the last in the + * chain. See IVirtualBoxErrorInfo documentation for more info. + * + * When the instance of the MultiResult class goes out of scope and gets + * destroyed, it automatically decreases the turn-on counter by one. If + * the counter drops to zero, multi-error mode for the current thread is + * turned off and the thread switches back to single-error mode where every + * next error or warning object overwrites the previous one. + * + * Note that the caller of a COM method uses a non-S_OK result code to + * decide if the method has returned an error (negative codes) or a warning + * (positive non-zero codes) and will query extended error info only in + * these two cases. However, since multi-error mode implies that the method + * doesn't return control return to the caller immediately after the first + * error or warning but continues its execution, the functionality provided + * by the base com::FWResult class becomes very useful because it allows to + * preserve the error or the warning result code even if it is later assigned + * a S_OK value multiple times. See com::FWResult for details. + * + * Here is the typical usage pattern: + * @code + HRESULT Bar::method() + { + // assume multi-errors are turned off here... + + if (something) + { + // Turn on multi-error mode and make sure severity is preserved + MultiResult rc = foo->method1(); + + // return on fatal error, but continue on warning or on success + CheckComRCReturnRC (rc); + + rc = foo->method2(); + // no matter what result, stack it and continue + + // ... + + // return the last worst result code (it will be preserved even if + // foo->method2() returns S_OK. + return rc; + } + + // multi-errors are turned off here again... + + return S_OK; + } + * @endcode + * + * @note This class is intended to be instantiated on the stack, therefore + * You cannot create them using new(). Although it is possible to copy + * instances of MultiResult or return them by value, please never do + * that as it is breaks the class semantics (and will assert); + */ +class MultiResult : public FWResult +{ +public: + + /** + * @copydoc FWResult::FWResult() + */ + MultiResult (HRESULT aRC = E_FAIL) : FWResult (aRC) { incCounter(); } + + MultiResult (const MultiResult &aThat) : FWResult (aThat) + { + /* We need this copy constructor only for GCC that wants to have + * it in case of expressions like |MultiResult rc = E_FAIL;|. But + * we assert since the optimizer should actually avoid the + * temporary and call the other constructor directly instead. */ + AssertFailed(); + } + + ~MultiResult() { decCounter(); } + + MultiResult &operator= (HRESULT aRC) + { + FWResult::operator= (aRC); + return *this; + } + + MultiResult &operator= (const MultiResult & /* aThat */) + { + /* We need this copy constructor only for GCC that wants to have + * it in case of expressions like |MultiResult rc = E_FAIL;|. But + * we assert since the optimizer should actually avoid the + * temporary and call the other constructor directly instead. */ + AssertFailed(); + return *this; + } + + /** + * Returns true if multi-mode is enabled for the current thread (i.e. at + * least one MultiResult instance exists on the stack somewhere). + * @return + */ + static bool isMultiEnabled(); + +private: + + DECLARE_CLS_NEW_DELETE_NOOP(MultiResult); + + static void incCounter(); + static void decCounter(); + + static RTTLS sCounter; + + friend class MultiResultRef; +}; + +/** + * The MultiResultRef class is equivalent to MultiResult except that it takes + * a reference to the existing HRESULT variable instead of maintaining its own + * one. + */ +class MultiResultRef +{ +public: + + MultiResultRef (HRESULT &aRC) : mRC (aRC) { MultiResult::incCounter(); } + + ~MultiResultRef() { MultiResult::decCounter(); } + + MultiResultRef &operator= (HRESULT aRC) + { + /* Copied from FWResult */ + if ((FAILED (aRC) && !FAILED (mRC)) || + (mRC == S_OK && aRC != S_OK)) + mRC = aRC; + + return *this; + } + + operator HRESULT() const { return mRC; } + + HRESULT *operator&() { return &mRC; } + +private: + + DECLARE_CLS_NEW_DELETE_NOOP(MultiResultRef); + + HRESULT &mRC; +}; + + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_MultiResult_h */ + diff --git a/include/VBox/com/NativeEventQueue.h b/include/VBox/com/NativeEventQueue.h new file mode 100644 index 00000000..972f93f4 --- /dev/null +++ b/include/VBox/com/NativeEventQueue.h @@ -0,0 +1,151 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Event and EventQueue class declaration. + */ + +/* + * Copyright (C) 2006-2019 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_NativeEventQueue_h +#define VBOX_INCLUDED_com_NativeEventQueue_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#ifndef VBOX_WITH_XPCOM +# include <iprt/win/windows.h> +#else +# include <nsEventQueueUtils.h> +#endif + +#include <VBox/com/defs.h> +#include <VBox/com/assert.h> + + +/** @defgroup grp_com_evt Event and EventQueue Classes + * @ingroup grp_com + * @{ + */ + +namespace com +{ + +class MainEventQueue; + +/** + * Base class for all events. Intended to be subclassed to introduce new + * events and handlers for them. + * + * Subclasses usually reimplement virtual #handler() (that does nothing by + * default) and add new data members describing the event. + */ +class NativeEvent +{ +public: + + NativeEvent() {} + virtual ~NativeEvent() {}; + +protected: + + /** + * Event handler. Called in the context of the event queue's thread. + * Always reimplemented by subclasses + * + * @return reserved, should be NULL. + */ + virtual void *handler() { return NULL; } + + friend class NativeEventQueue; +}; + +/** + * Simple event queue. + * + * When using XPCOM, this will map onto the default XPCOM queue for the thread. + * So, if a queue is created on the main thread, it automatically processes + * XPCOM/IPC events while waiting. + * + * When using Windows, Darwin and OS/2, this will map onto the native thread + * queue/runloop. So, windows messages and what not will be processed while + * waiting for events. + * + * @note It is intentional that there is no way to retrieve arbitrary + * events and controlling their processing. There is no use case which + * warrants introducing the complexity of platform independent events. + */ +class NativeEventQueue +{ +public: + + NativeEventQueue(); + virtual ~NativeEventQueue(); + + BOOL postEvent(NativeEvent *event); + int processEventQueue(RTMSINTERVAL cMsTimeout); + int interruptEventQueueProcessing(); + int getSelectFD(); + static int init(); + static int uninit(); + static NativeEventQueue *getMainEventQueue(); + +#ifdef VBOX_WITH_XPCOM + already_AddRefed<nsIEventQueue> getIEventQueue() + { + return mEventQ.get(); + } +#else + static int dispatchMessageOnWindows(MSG const *pMsg, int rc); +#endif + +private: + static NativeEventQueue *sMainQueue; + +#ifndef VBOX_WITH_XPCOM + + /** The thread which the queue belongs to. */ + DWORD mThreadId; + /** Duplicated thread handle for MsgWaitForMultipleObjects. */ + HANDLE mhThread; + +#else // VBOX_WITH_XPCOM + + /** Whether it was created (and thus needs destroying) or if a queue already + * associated with the thread was used. */ + bool mEQCreated; + + /** Whether event processing should be interrupted. */ + bool mInterrupted; + + nsCOMPtr <nsIEventQueue> mEventQ; + nsCOMPtr <nsIEventQueueService> mEventQService; + + static void *PR_CALLBACK plEventHandler(PLEvent *self); + static void PR_CALLBACK plEventDestructor(PLEvent *self); + +#endif // VBOX_WITH_XPCOM +}; + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_NativeEventQueue_h */ + diff --git a/include/VBox/com/VirtualBox.h b/include/VBox/com/VirtualBox.h new file mode 100644 index 00000000..ea5e0420 --- /dev/null +++ b/include/VBox/com/VirtualBox.h @@ -0,0 +1,60 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - VirtualBox COM Library definitions. + * + * @note This is the main header file that COM/XPCOM clients include; however, + * it is only a wrapper around another platform-dependent include file + * that contains the real COM/XPCOM interface declarations. That other + * include file is generated automatically at build time from + * /src/VBox/Main/idl/VirtualBox.xidl, which contains all the VirtualBox + * interfaces; the include file is called VirtualBox.h on Windows hosts + * and VirtualBox_XPCOM.h on Linux hosts. The build process places it in + * out/{platform}/bin/sdk/include, from where it gets + * included by the rest of the VirtualBox code. + */ + +/* + * Copyright (C) 2006-2019 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_VirtualBox_h +#define VBOX_INCLUDED_com_VirtualBox_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +/* For XPCOM/C++ enum hack checks. */ +#include <iprt/assertcompile.h> + +/* Generated VirtualBox COM library definition file. */ +#if !defined(VBOXCOM_NOINCLUDE) +# if !defined(VBOX_WITH_XPCOM) +# include <VirtualBox.h> +# else +# define VBOX_WITH_XPCOM_CPP_ENUM_HACK +# include <VirtualBox_XPCOM.h> +# endif +#endif + +/* For convenience. */ +#include "VBox/com/defs.h" +#include "VBox/com/ptr.h" + +#endif /* !VBOX_INCLUDED_com_VirtualBox_h */ + diff --git a/include/VBox/com/array.h b/include/VBox/com/array.h new file mode 100644 index 00000000..8754a6f5 --- /dev/null +++ b/include/VBox/com/array.h @@ -0,0 +1,1776 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Safe array helper class declaration. + */ + +/* + * Copyright (C) 2006-2019 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_array_h +#define VBOX_INCLUDED_com_array_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + + +/** @defgroup grp_com_arrays COM/XPCOM Arrays + * @ingroup grp_com + * @{ + * + * The COM/XPCOM array support layer provides a cross-platform way to pass + * arrays to and from COM interface methods and consists of the com::SafeArray + * template and a set of ComSafeArray* macros part of which is defined in + * VBox/com/defs.h. + * + * This layer works with interface attributes and method parameters that have + * the 'safearray="yes"' attribute in the XIDL definition: + * @code + + <interface name="ISomething" ...> + + <method name="testArrays"> + <param name="inArr" type="long" dir="in" safearray="yes"/> + <param name="outArr" type="long" dir="out" safearray="yes"/> + <param name="retArr" type="long" dir="return" safearray="yes"/> + </method> + + </interface> + + * @endcode + * + * Methods generated from this and similar definitions are implemented in + * component classes using the following declarations: + * @code + + STDMETHOD(TestArrays)(ComSafeArrayIn(LONG, aIn), + ComSafeArrayOut(LONG, aOut), + ComSafeArrayOut(LONG, aRet)); + + * @endcode + * + * And the following function bodies: + * @code + + STDMETHODIMP Component::TestArrays(ComSafeArrayIn(LONG, aIn), + ComSafeArrayOut(LONG, aOut), + ComSafeArrayOut(LONG, aRet)) + { + if (ComSafeArrayInIsNull(aIn)) + return E_INVALIDARG; + if (ComSafeArrayOutIsNull(aOut)) + return E_POINTER; + if (ComSafeArrayOutIsNull(aRet)) + return E_POINTER; + + // Use SafeArray to access the input array parameter + + com::SafeArray<LONG> in(ComSafeArrayInArg(aIn)); + + for (size_t i = 0; i < in.size(); ++ i) + LogFlow(("*** in[%u]=%d\n", i, in[i])); + + // Use SafeArray to create the return array (the same technique is used + // for output array parameters) + + SafeArray<LONG> ret(in.size() * 2); + for (size_t i = 0; i < in.size(); ++ i) + { + ret[i] = in[i]; + ret[i + in.size()] = in[i] * 10; + } + + ret.detachTo(ComSafeArrayOutArg(aRet)); + + return S_OK; + } + + * @endcode + * + * Such methods can be called from the client code using the following pattern: + * @code + + ComPtr<ISomething> component; + + // ... + + com::SafeArray<LONG> in(3); + in[0] = -1; + in[1] = -2; + in[2] = -3; + + com::SafeArray<LONG> out; + com::SafeArray<LONG> ret; + + HRESULT rc = component->TestArrays(ComSafeArrayAsInParam(in), + ComSafeArrayAsOutParam(out), + ComSafeArrayAsOutParam(ret)); + + if (SUCCEEDED(rc)) + for (size_t i = 0; i < ret.size(); ++ i) + printf("*** ret[%u]=%d\n", i, ret[i]); + + * @endcode + * + * For interoperability with standard C++ containers, there is a template + * constructor that takes such a container as argument and performs a deep copy + * of its contents. This can be used in method implementations like this: + * @code + + STDMETHODIMP Component::COMGETTER(Values)(ComSafeArrayOut(int, aValues)) + { + // ... assume there is a |std::list<int> mValues| data member + + com::SafeArray<int> values(mValues); + values.detachTo(ComSafeArrayOutArg(aValues)); + + return S_OK; + } + + * @endcode + * + * The current implementation of the SafeArray layer supports all types normally + * allowed in XIDL as array element types (including 'wstring' and 'uuid'). + * However, 'pointer-to-...' types (e.g. 'long *', 'wstring *') are not + * supported and therefore cannot be used as element types. + * + * Note that for GUID arrays you should use SafeGUIDArray and + * SafeConstGUIDArray, customized SafeArray<> specializations. + * + * Also note that in order to pass input BSTR array parameters declared + * using the ComSafeArrayIn(IN_BSTR, aParam) macro to the SafeArray<> + * constructor using the ComSafeArrayInArg() macro, you should use IN_BSTR + * as the SafeArray<> template argument, not just BSTR. + * + * Arrays of interface pointers are also supported but they require to use a + * special SafeArray implementation, com::SafeIfacePointer, which takes the + * interface class name as a template argument (e.g. + * com::SafeIfacePointer\<IUnknown\>). This implementation functions + * identically to com::SafeArray. + */ + +#ifdef VBOX_WITH_XPCOM +# include <nsMemory.h> +#endif + +#include "VBox/com/defs.h" + +#if RT_GNUC_PREREQ(4, 6) || (defined(_MSC_VER) && (_MSC_VER >= 1600)) +/** @def VBOX_WITH_TYPE_TRAITS + * Type traits are a C++ 11 feature, so not available everywhere (yet). + * Only GCC 4.6 or newer and MSVC++ 16.0 (Visual Studio 2010) or newer. + */ +# define VBOX_WITH_TYPE_TRAITS +#endif + +#ifdef VBOX_WITH_TYPE_TRAITS +# include <type_traits> +#endif + +#include "VBox/com/ptr.h" +#include "VBox/com/assert.h" +#include "iprt/cpp/list.h" + +#ifdef VBOX_WITH_XPCOM + +/** + * Wraps the given com::SafeArray instance to generate an expression that is + * suitable for passing it to functions that take input safearray parameters + * declared using the ComSafeArrayIn macro. + * + * @param aArray com::SafeArray instance to pass as an input parameter. + */ +#define ComSafeArrayAsInParam(aArray) \ + (PRUint32)(aArray).size(), (aArray).__asInParam_Arr((aArray).raw()) + +/** + * Wraps the given com::SafeArray instance to generate an expression that is + * suitable for passing it to functions that take output safearray parameters + * declared using the ComSafeArrayOut macro. + * + * @param aArray com::SafeArray instance to pass as an output parameter. + */ +#define ComSafeArrayAsOutParam(aArray) \ + (aArray).__asOutParam_Size(), (aArray).__asOutParam_Arr() + +#else /* !VBOX_WITH_XPCOM */ + +#define ComSafeArrayAsInParam(aArray) (aArray).__asInParam() + +#define ComSafeArrayAsOutParam(aArray) (aArray).__asOutParam() + +#endif /* !VBOX_WITH_XPCOM */ + +/** + * + */ +namespace com +{ + +#ifdef VBOX_WITH_XPCOM + +//////////////////////////////////////////////////////////////////////////////// + +/** + * Provides various helpers for SafeArray. + * + * @param T Type of array elements. + */ +template<typename T> +struct SafeArrayTraits +{ +protected: + + /** Initializes memory for aElem. */ + static void Init(T &aElem) { aElem = (T)0; } + + /** Initializes memory occupied by aElem. */ + static void Uninit(T &aElem) { RT_NOREF(aElem); } + + /** Creates a deep copy of aFrom and stores it in aTo. */ + static void Copy(const T &aFrom, T &aTo) { aTo = aFrom; } + +public: + + /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard (that + * in particular forbid casts of 'char **' to 'const char **'). Then initial + * reason for this magic is that XPIDL declares input strings + * (char/PRUnichar pointers) as const but doesn't do so for pointers to + * arrays. */ + static T *__asInParam_Arr(T *aArr) { return aArr; } + static T *__asInParam_Arr(const T *aArr) { return const_cast<T *>(aArr); } +}; + +template<typename T> +struct SafeArrayTraits<T *> +{ + // Arbitrary pointers are not supported +}; + +template<> +struct SafeArrayTraits<PRUnichar *> +{ +protected: + + static void Init(PRUnichar * &aElem) { aElem = NULL; } + + static void Uninit(PRUnichar * &aElem) + { + if (aElem) + { + ::SysFreeString(aElem); + aElem = NULL; + } + } + + static void Copy(const PRUnichar * aFrom, PRUnichar * &aTo) + { + AssertCompile(sizeof(PRUnichar) == sizeof(OLECHAR)); + aTo = aFrom ? ::SysAllocString((const OLECHAR *)aFrom) : NULL; + } + +public: + + /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard */ + static const PRUnichar **__asInParam_Arr(PRUnichar **aArr) + { + return const_cast<const PRUnichar **>(aArr); + } + static const PRUnichar **__asInParam_Arr(const PRUnichar **aArr) { return aArr; } +}; + +template<> +struct SafeArrayTraits<const PRUnichar *> +{ +protected: + + static void Init(const PRUnichar * &aElem) { aElem = NULL; } + static void Uninit(const PRUnichar * &aElem) + { + if (aElem) + { + ::SysFreeString(const_cast<PRUnichar *>(aElem)); + aElem = NULL; + } + } + + static void Copy(const PRUnichar * aFrom, const PRUnichar * &aTo) + { + AssertCompile(sizeof(PRUnichar) == sizeof(OLECHAR)); + aTo = aFrom ? ::SysAllocString((const OLECHAR *)aFrom) : NULL; + } + +public: + + /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard */ + static const PRUnichar **__asInParam_Arr(const PRUnichar **aArr) { return aArr; } +}; + +template<> +struct SafeArrayTraits<nsID *> +{ +protected: + + static void Init(nsID * &aElem) { aElem = NULL; } + + static void Uninit(nsID * &aElem) + { + if (aElem) + { + ::nsMemory::Free(aElem); + aElem = NULL; + } + } + + static void Copy(const nsID * aFrom, nsID * &aTo) + { + if (aFrom) + { + aTo = (nsID *) ::nsMemory::Alloc(sizeof(nsID)); + if (aTo) + *aTo = *aFrom; + } + else + aTo = NULL; + } + + /* This specification is also reused for SafeConstGUIDArray, so provide a + * no-op Init() and Uninit() which are necessary for SafeArray<> but should + * be never called in context of SafeConstGUIDArray. */ + + static void Init(const nsID * &aElem) { NOREF(aElem); AssertFailed(); } + static void Uninit(const nsID * &aElem) { NOREF(aElem); AssertFailed(); } + +public: + + /** Magic to workaround strict rules of par. 4.4.4 of the C++ standard. */ + static const nsID **__asInParam_Arr(nsID **aArr) + { + return const_cast<const nsID **>(aArr); + } + static const nsID **__asInParam_Arr(const nsID **aArr) { return aArr; } +}; + +#else /* !VBOX_WITH_XPCOM */ + +//////////////////////////////////////////////////////////////////////////////// + +struct SafeArrayTraitsBase +{ +protected: + + static SAFEARRAY *CreateSafeArray(VARTYPE aVarType, SAFEARRAYBOUND *aBound) + { return SafeArrayCreate(aVarType, 1, aBound); } +}; + +/** + * Provides various helpers for SafeArray. + * + * @param T Type of array elements. + * + * Specializations of this template must provide the following methods: + * + // Returns the VARTYPE of COM SafeArray elements to be used for T + static VARTYPE VarType(); + + // Returns the number of VarType() elements necessary for aSize + // elements of T + static ULONG VarCount(size_t aSize); + + // Returns the number of elements of T that fit into the given number of + // VarType() elements (opposite to VarCount(size_t aSize)). + static size_t Size(ULONG aVarCount); + + // Creates a deep copy of aFrom and stores it in aTo + static void Copy(ULONG aFrom, ULONG &aTo); + */ +template<typename T> +struct SafeArrayTraits : public SafeArrayTraitsBase +{ +protected: + + // Arbitrary types are treated as passed by value and each value is + // represented by a number of VT_Ix type elements where VT_Ix has the + // biggest possible bitness necessary to represent T w/o a gap. COM enums + // fall into this category. + + static VARTYPE VarType() + { +#ifdef VBOX_WITH_TYPE_TRAITS + if ( std::is_integral<T>::value + && !std::is_signed<T>::value) + { + if (sizeof(T) % 8 == 0) return VT_UI8; + if (sizeof(T) % 4 == 0) return VT_UI4; + if (sizeof(T) % 2 == 0) return VT_UI2; + return VT_UI1; + } +#endif + if (sizeof(T) % 8 == 0) return VT_I8; + if (sizeof(T) % 4 == 0) return VT_I4; + if (sizeof(T) % 2 == 0) return VT_I2; + return VT_I1; + } + + /* + * Fallback method in case type traits (VBOX_WITH_TYPE_TRAITS) + * are not available. Always returns unsigned types. + */ + static VARTYPE VarTypeUnsigned() + { + if (sizeof(T) % 8 == 0) return VT_UI8; + if (sizeof(T) % 4 == 0) return VT_UI4; + if (sizeof(T) % 2 == 0) return VT_UI2; + return VT_UI1; + } + + static ULONG VarCount(size_t aSize) + { + if (sizeof(T) % 8 == 0) return (ULONG)((sizeof(T) / 8) * aSize); + if (sizeof(T) % 4 == 0) return (ULONG)((sizeof(T) / 4) * aSize); + if (sizeof(T) % 2 == 0) return (ULONG)((sizeof(T) / 2) * aSize); + return (ULONG)(sizeof(T) * aSize); + } + + static size_t Size(ULONG aVarCount) + { + if (sizeof(T) % 8 == 0) return (size_t)(aVarCount * 8) / sizeof(T); + if (sizeof(T) % 4 == 0) return (size_t)(aVarCount * 4) / sizeof(T); + if (sizeof(T) % 2 == 0) return (size_t)(aVarCount * 2) / sizeof(T); + return (size_t) aVarCount / sizeof(T); + } + + static void Copy(T aFrom, T &aTo) { aTo = aFrom; } +}; + +template<typename T> +struct SafeArrayTraits<T *> +{ + // Arbitrary pointer types are not supported +}; + +/* Although the generic SafeArrayTraits template would work for all integers, + * we specialize it for some of them in order to use the correct VT_ type */ + +template<> +struct SafeArrayTraits<LONG> : public SafeArrayTraitsBase +{ +protected: + + static VARTYPE VarType() { return VT_I4; } + static ULONG VarCount(size_t aSize) { return (ULONG)aSize; } + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount; } + + static void Copy(LONG aFrom, LONG &aTo) { aTo = aFrom; } +}; + +template<> +struct SafeArrayTraits<ULONG> : public SafeArrayTraitsBase +{ +protected: + + static VARTYPE VarType() { return VT_UI4; } + static ULONG VarCount(size_t aSize) { return (ULONG)aSize; } + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount; } + + static void Copy(ULONG aFrom, ULONG &aTo) { aTo = aFrom; } +}; + +template<> +struct SafeArrayTraits<LONG64> : public SafeArrayTraitsBase +{ +protected: + + static VARTYPE VarType() { return VT_I8; } + static ULONG VarCount(size_t aSize) { return (ULONG)aSize; } + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount; } + + static void Copy(LONG64 aFrom, LONG64 &aTo) { aTo = aFrom; } +}; + +template<> +struct SafeArrayTraits<ULONG64> : public SafeArrayTraitsBase +{ +protected: + + static VARTYPE VarType() { return VT_UI8; } + static ULONG VarCount(size_t aSize) { return (ULONG)aSize; } + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount; } + + static void Copy(ULONG64 aFrom, ULONG64 &aTo) { aTo = aFrom; } +}; + +template<> +struct SafeArrayTraits<BSTR> : public SafeArrayTraitsBase +{ +protected: + + static VARTYPE VarType() { return VT_BSTR; } + static ULONG VarCount(size_t aSize) { return (ULONG)aSize; } + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount; } + + static void Copy(BSTR aFrom, BSTR &aTo) + { + aTo = aFrom ? ::SysAllocString((const OLECHAR *)aFrom) : NULL; + } +}; + +template<> +struct SafeArrayTraits<GUID> : public SafeArrayTraitsBase +{ +protected: + + /* Use the 64-bit unsigned integer type for GUID */ + static VARTYPE VarType() { return VT_UI8; } + + /* GUID is 128 bit, so we need two VT_UI8 */ + static ULONG VarCount(size_t aSize) + { + AssertCompileSize(GUID, 16); + return (ULONG)(aSize * 2); + } + + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount / 2; } + + static void Copy(GUID aFrom, GUID &aTo) { aTo = aFrom; } +}; + +/** + * Helper for SafeArray::__asOutParam() that automatically updates m.raw after a + * non-NULL m.arr assignment. + */ +class OutSafeArrayDipper +{ + OutSafeArrayDipper(SAFEARRAY **aArr, void **aRaw) + : arr(aArr), raw(aRaw) { Assert(*aArr == NULL && *aRaw == NULL); } + + SAFEARRAY **arr; + void **raw; + + template<class, class> friend class SafeArray; + +public: + + ~OutSafeArrayDipper() + { + if (*arr != NULL) + { + HRESULT rc = SafeArrayAccessData(*arr, raw); + AssertComRC(rc); + } + } + + operator SAFEARRAY **() { return arr; } +}; + +#endif /* !VBOX_WITH_XPCOM */ + +//////////////////////////////////////////////////////////////////////////////// + +/** + * The SafeArray class represents the safe array type used in COM to pass arrays + * to/from interface methods. + * + * This helper class hides all MSCOM/XPCOM specific implementation details and, + * together with ComSafeArrayIn, ComSafeArrayOut and ComSafeArrayRet macros, + * provides a platform-neutral way to handle safe arrays in the method + * implementation. + * + * When an instance of this class is destroyed, it automatically frees all + * resources occupied by individual elements of the array as well as by the + * array itself. However, when the value of an element is manually changed + * using #operator[] or by accessing array data through the #raw() pointer, it is + * the caller's responsibility to free resources occupied by the previous + * element's value. + * + * Also, objects of this class do not support copy and assignment operations and + * therefore cannot be returned from functions by value. In other words, this + * class is just a temporary storage for handling interface method calls and not + * intended to be used to store arrays as data members and such -- you should + * use normal list/vector classes for that. + * + * @note The current implementation supports only one-dimensional arrays. + * + * @note This class is not thread-safe. + */ +template<typename T, class Traits = SafeArrayTraits<T> > +class SafeArray : public Traits +{ +public: + + /** + * Creates a null array. + */ + SafeArray() {} + + /** + * Creates a new array of the given size. All elements of the newly created + * array initialized with null values. + * + * @param aSize Initial number of elements in the array. + * + * @note If this object remains null after construction it means that there + * was not enough memory for creating an array of the requested size. + * The constructor will also assert in this case. + */ + SafeArray(size_t aSize) { resize(aSize); } + + /** + * Weakly attaches this instance to the existing array passed in a method + * parameter declared using the ComSafeArrayIn macro. When using this call, + * always wrap the parameter name in the ComSafeArrayInArg macro call like + * this: + * <pre> + * SafeArray safeArray(ComSafeArrayInArg(aArg)); + * </pre> + * + * Note that this constructor doesn't take the ownership of the array. In + * particular, it means that operations that operate on the ownership (e.g. + * #detachTo()) are forbidden and will assert. + * + * @param aArg Input method parameter to attach to. + */ + SafeArray(ComSafeArrayIn(T, aArg)) + { + if (aArg) + { +#ifdef VBOX_WITH_XPCOM + + m.size = aArgSize; + m.arr = aArg; + m.isWeak = true; + +#else /* !VBOX_WITH_XPCOM */ + + SAFEARRAY *arg = aArg; + + AssertReturnVoid(arg->cDims == 1); + + VARTYPE vt; + HRESULT rc = SafeArrayGetVartype(arg, &vt); + AssertComRCReturnVoid(rc); +# ifndef VBOX_WITH_TYPE_TRAITS + AssertMsgReturnVoid( + vt == VarType() + || vt == VarTypeUnsigned(), + ("Expected vartype %d or %d, got %d.\n", + VarType(), VarTypeUnsigned(), vt)); +# else /* !VBOX_WITH_TYPE_TRAITS */ + AssertMsgReturnVoid( + vt == VarType(), + ("Expected vartype %d, got %d.\n", + VarType(), vt)); +# endif + rc = SafeArrayAccessData(arg, (void HUGEP **)&m.raw); + AssertComRCReturnVoid(rc); + + m.arr = arg; + m.isWeak = true; + +#endif /* !VBOX_WITH_XPCOM */ + } + } + + /** + * Creates a deep copy of the given standard C++ container that stores + * T objects. + * + * @param aCntr Container object to copy. + * + * @tparam C Standard C++ container template class (normally deduced from + * @c aCntr). + */ + template<template<typename, typename> class C, class A> + SafeArray(const C<T, A> & aCntr) + { + resize(aCntr.size()); + AssertReturnVoid(!isNull()); + + size_t i = 0; + for (typename C<T, A>::const_iterator it = aCntr.begin(); + it != aCntr.end(); ++ it, ++ i) +#ifdef VBOX_WITH_XPCOM + SafeArray::Copy(*it, m.arr[i]); +#else + Copy(*it, m.raw[i]); +#endif + } + + /** + * Creates a deep copy of the given standard C++ map that stores T objects + * as values. + * + * @param aMap Map object to copy. + * + * @tparam C Standard C++ map template class (normally deduced from + * @a aMap). + * @tparam L Standard C++ compare class (deduced from @a aMap). + * @tparam A Standard C++ allocator class (deduced from @a aMap). + * @tparam K Map key class (deduced from @a aMap). + */ + template<template<typename, typename, typename, typename> + class C, class L, class A, class K> + SafeArray(const C<K, T, L, A> & aMap) + { + typedef C<K, T, L, A> Map; + + resize(aMap.size()); + AssertReturnVoid(!isNull()); + + int i = 0; + for (typename Map::const_iterator it = aMap.begin(); + it != aMap.end(); ++ it, ++ i) +#ifdef VBOX_WITH_XPCOM + Copy(it->second, m.arr[i]); +#else + Copy(it->second, m.raw[i]); +#endif + } + + /** + * Destroys this instance after calling #setNull() to release allocated + * resources. See #setNull() for more details. + */ + virtual ~SafeArray() { setNull(); } + + /** + * Returns @c true if this instance represents a null array. + */ + bool isNull() const { return m.arr == NULL; } + + /** + * Returns @c true if this instance does not represents a null array. + */ + bool isNotNull() const { return m.arr != NULL; } + + /** + * Resets this instance to null and, if this instance is not a weak one, + * releases any resources occupied by the array data. + * + * @note This method destroys (cleans up) all elements of the array using + * the corresponding cleanup routine for the element type before the + * array itself is destroyed. + */ + virtual void setNull() { m.uninit(); } + + /** + * Returns @c true if this instance is weak. A weak instance doesn't own the + * array data and therefore operations manipulating the ownership (e.g. + * #detachTo()) are forbidden and will assert. + */ + bool isWeak() const { return m.isWeak; } + + /** Number of elements in the array. */ + size_t size() const + { +#ifdef VBOX_WITH_XPCOM + if (m.arr) + return m.size; + return 0; +#else + if (m.arr) + return Size(m.arr->rgsabound[0].cElements); + return 0; +#endif + } + + /** + * Appends a copy of the given element at the end of the array. + * + * The array size is increased by one by this method and the additional + * space is allocated as needed. + * + * This method is handy in cases where you want to assign a copy of the + * existing value to the array element, for example: + * <tt>Bstr string; array.push_back(string);</tt>. If you create a string + * just to put it in the array, you may find #appendedRaw() more useful. + * + * @param aElement Element to append. + * + * @return @c true on success and @c false if there is not enough + * memory for resizing. + */ + bool push_back(const T &aElement) + { + if (!ensureCapacity(size() + 1)) + return false; + +#ifdef VBOX_WITH_XPCOM + SafeArray::Copy(aElement, m.arr[m.size]); + ++ m.size; +#else + Copy(aElement, m.raw[size() - 1]); +#endif + return true; + } + + /** + * Appends an empty element at the end of the array and returns a raw + * pointer to it suitable for assigning a raw value (w/o constructing a + * copy). + * + * The array size is increased by one by this method and the additional + * space is allocated as needed. + * + * Note that in case of raw assignment, value ownership (for types with + * dynamically allocated data and for interface pointers) is transferred to + * the safe array object. + * + * This method is handy for operations like + * <tt>Bstr("foo").detachTo(array.appendedRaw());</tt>. Don't use it as + * an l-value (<tt>array.appendedRaw() = SysAllocString(L"tralala");</tt>) + * since this doesn't check for a NULL condition; use #resize() instead. If + * you need to assign a copy of the existing value instead of transferring + * the ownership, look at #push_back(). + * + * @return Raw pointer to the added element or NULL if no memory. + */ + T *appendedRaw() + { + if (!ensureCapacity(size() + 1)) + return NULL; + +#ifdef VBOX_WITH_XPCOM + SafeArray::Init(m.arr[m.size]); + ++ m.size; + return &m.arr[m.size - 1]; +#else + /* nothing to do here, SafeArrayCreate() has performed element + * initialization */ + return &m.raw[size() - 1]; +#endif + } + + /** + * Resizes the array preserving its contents when possible. If the new size + * is larger than the old size, new elements are initialized with null + * values. If the new size is less than the old size, the contents of the + * array beyond the new size is lost. + * + * @param aNewSize New number of elements in the array. + * @return @c true on success and @c false if there is not enough + * memory for resizing. + */ + bool resize(size_t aNewSize) + { + if (!ensureCapacity(aNewSize)) + return false; + +#ifdef VBOX_WITH_XPCOM + + if (m.size < aNewSize) + { + /* initialize the new elements */ + for (size_t i = m.size; i < aNewSize; ++ i) + SafeArray::Init(m.arr[i]); + } + + /** @todo Fix this! */ + m.size = (PRUint32)aNewSize; +#else + /* nothing to do here, SafeArrayCreate() has performed element + * initialization */ +#endif + return true; + } + + /** + * Reinitializes this instance by preallocating space for the given number + * of elements. The previous array contents is lost. + * + * @param aNewSize New number of elements in the array. + * @return @c true on success and @c false if there is not enough + * memory for resizing. + */ + bool reset(size_t aNewSize) + { + m.uninit(); + return resize(aNewSize); + } + + /** + * Returns a pointer to the raw array data. Use this raw pointer with care + * as no type or bound checking is done for you in this case. + * + * @note This method returns @c NULL when this instance is null. + * @see #operator[] + */ + T *raw() + { +#ifdef VBOX_WITH_XPCOM + return m.arr; +#else + return m.raw; +#endif + } + + /** + * Const version of #raw(). + */ + const T *raw() const + { +#ifdef VBOX_WITH_XPCOM + return m.arr; +#else + return m.raw; +#endif + } + + /** + * Array access operator that returns an array element by reference. A bit + * safer than #raw(): asserts and returns an invalid reference if this + * instance is null or if the index is out of bounds. + * + * @note For weak instances, this call will succeed but the behavior of + * changing the contents of an element of the weak array instance is + * undefined and may lead to a program crash on some platforms. + */ + T &operator[] (size_t aIdx) + { + AssertReturn(m.arr != NULL, *((T *)NULL)); + AssertReturn(aIdx < size(), *((T *)NULL)); +#ifdef VBOX_WITH_XPCOM + return m.arr[aIdx]; +#else + AssertReturn(m.raw != NULL, *((T *)NULL)); + return m.raw[aIdx]; +#endif + } + + /** + * Const version of #operator[] that returns an array element by value. + */ + const T operator[] (size_t aIdx) const + { + AssertReturn(m.arr != NULL, *((T *)1)); + AssertReturn(aIdx < size(), *((T *)1)); +#ifdef VBOX_WITH_XPCOM + return m.arr[aIdx]; +#else + AssertReturn(m.raw != NULL, *((T *)NULL)); + return m.raw[aIdx]; +#endif + } + + /** + * Creates a copy of this array and stores it in a method parameter declared + * using the ComSafeArrayOut macro. When using this call, always wrap the + * parameter name in the ComSafeArrayOutArg macro call like this: + * <pre> + * safeArray.cloneTo(ComSafeArrayOutArg(aArg)); + * </pre> + * + * @note It is assumed that the ownership of the returned copy is + * transferred to the caller of the method and he is responsible to free the + * array data when it is no longer needed. + * + * @param aArg Output method parameter to clone to. + */ + virtual const SafeArray &cloneTo(ComSafeArrayOut(T, aArg)) const + { + /// @todo Implement me! +#ifdef VBOX_WITH_XPCOM + NOREF(aArgSize); + NOREF(aArg); +#else + NOREF(aArg); +#endif + AssertFailedReturn(*this); + } + + void cloneTo(SafeArray<T>& aOther) const + { + aOther.reset(size()); + aOther.initFrom(*this); + } + + + /** + * Transfers the ownership of this array's data to the specified location + * declared using the ComSafeArrayOut macro and makes this array a null + * array. When using this call, always wrap the parameter name in the + * ComSafeArrayOutArg macro call like this: + * <pre> + * safeArray.detachTo(ComSafeArrayOutArg(aArg)); + * </pre> + * + * Detaching the null array is also possible in which case the location will + * receive NULL. + * + * @note Since the ownership of the array data is transferred to the + * caller of the method, he is responsible to free the array data when it is + * no longer needed. + * + * @param aArg Location to detach to. + */ + virtual SafeArray &detachTo(ComSafeArrayOut(T, aArg)) + { + AssertReturn(!m.isWeak, *this); + +#ifdef VBOX_WITH_XPCOM + + AssertReturn(aArgSize != NULL, *this); + AssertReturn(aArg != NULL, *this); + + *aArgSize = m.size; + *aArg = m.arr; + + m.isWeak = false; + m.size = 0; + m.arr = NULL; + +#else /* !VBOX_WITH_XPCOM */ + + AssertReturn(aArg != NULL, *this); + *aArg = m.arr; + + if (m.raw) + { + HRESULT rc = SafeArrayUnaccessData(m.arr); + AssertComRCReturn(rc, *this); + m.raw = NULL; + } + + m.isWeak = false; + m.arr = NULL; + +#endif /* !VBOX_WITH_XPCOM */ + + return *this; + } + + /** + * Returns a copy of this SafeArray as RTCList<T>. + */ + RTCList<T> toList() + { + RTCList<T> list(size()); + for (size_t i = 0; i < size(); ++i) +#ifdef VBOX_WITH_XPCOM + list.append(m.arr[i]); +#else + list.append(m.raw[i]); +#endif + return list; + } + + inline void initFrom(const com::SafeArray<T> & aRef); + inline void initFrom(const T* aPtr, size_t aSize); + + // Public methods for internal purposes only. + +#ifdef VBOX_WITH_XPCOM + + /** Internal function. Never call it directly. */ + PRUint32 *__asOutParam_Size() { setNull(); return &m.size; } + + /** Internal function Never call it directly. */ + T **__asOutParam_Arr() { Assert(isNull()); return &m.arr; } + +#else /* !VBOX_WITH_XPCOM */ + + /** Internal function Never call it directly. */ + SAFEARRAY * __asInParam() { return m.arr; } + + /** Internal function Never call it directly. */ + OutSafeArrayDipper __asOutParam() + { setNull(); return OutSafeArrayDipper(&m.arr, (void **)&m.raw); } + +#endif /* !VBOX_WITH_XPCOM */ + + static const SafeArray Null; + +protected: + + DECLARE_CLS_COPY_CTOR_ASSIGN_NOOP(SafeArray); + + /** + * Ensures that the array is big enough to contain aNewSize elements. + * + * If the new size is greater than the current capacity, a new array is + * allocated and elements from the old array are copied over. The size of + * the array doesn't change, only the capacity increases (which is always + * greater than the size). Note that the additionally allocated elements are + * left uninitialized by this method. + * + * If the new size is less than the current size, the existing array is + * truncated to the specified size and the elements outside the new array + * boundary are freed. + * + * If the new size is the same as the current size, nothing happens. + * + * @param aNewSize New size of the array. + * + * @return @c true on success and @c false if not enough memory. + */ + bool ensureCapacity(size_t aNewSize) + { + AssertReturn(!m.isWeak, false); + +#ifdef VBOX_WITH_XPCOM + + /* Note: we distinguish between a null array and an empty (zero + * elements) array. Therefore we never use zero in malloc (even if + * aNewSize is zero) to make sure we get a non-null pointer. */ + + if (m.size == aNewSize && m.arr != NULL) + return true; + + /* Allocate in 16-byte pieces. */ + size_t newCapacity = RT_MAX((aNewSize + 15) / 16 * 16, 16); + + if (m.capacity != newCapacity) + { + T *newArr = (T *)nsMemory::Alloc(RT_MAX(newCapacity, 1) * sizeof(T)); + AssertReturn(newArr != NULL, false); + + if (m.arr != NULL) + { + if (m.size > aNewSize) + { + /* Truncation takes place, uninit exceeding elements and + * shrink the size. */ + for (size_t i = aNewSize; i < m.size; ++ i) + SafeArray::Uninit(m.arr[i]); + + /** @todo Fix this! */ + m.size = (PRUint32)aNewSize; + } + + /* Copy the old contents. */ + memcpy(newArr, m.arr, m.size * sizeof(T)); + nsMemory::Free((void *)m.arr); + } + + m.arr = newArr; + } + else + { + if (m.size > aNewSize) + { + /* Truncation takes place, uninit exceeding elements and + * shrink the size. */ + for (size_t i = aNewSize; i < m.size; ++ i) + SafeArray::Uninit(m.arr[i]); + + /** @todo Fix this! */ + m.size = (PRUint32)aNewSize; + } + } + + /** @todo Fix this! */ + m.capacity = (PRUint32)newCapacity; + +#else + + SAFEARRAYBOUND bound = { VarCount(aNewSize), 0 }; + HRESULT rc; + + if (m.arr == NULL) + { + m.arr = CreateSafeArray(VarType(), &bound); + AssertReturn(m.arr != NULL, false); + } + else + { + SafeArrayUnaccessData(m.arr); + + rc = SafeArrayRedim(m.arr, &bound); + AssertComRCReturn(rc == S_OK, false); + } + + rc = SafeArrayAccessData(m.arr, (void HUGEP **)&m.raw); + AssertComRCReturn(rc, false); + +#endif + return true; + } + + struct Data + { + Data() + : isWeak(false) +#ifdef VBOX_WITH_XPCOM + , capacity(0), size(0), arr(NULL) +#else + , arr(NULL), raw(NULL) +#endif + {} + + ~Data() { uninit(); } + + void uninit() + { +#ifdef VBOX_WITH_XPCOM + + if (arr) + { + if (!isWeak) + { + for (size_t i = 0; i < size; ++ i) + SafeArray::Uninit(arr[i]); + + nsMemory::Free((void *)arr); + } + else + isWeak = false; + + arr = NULL; + } + + size = capacity = 0; + +#else /* !VBOX_WITH_XPCOM */ + + if (arr) + { + if (raw) + { + SafeArrayUnaccessData(arr); + raw = NULL; + } + + if (!isWeak) + { + HRESULT rc = SafeArrayDestroy(arr); + AssertComRCReturnVoid(rc); + } + else + isWeak = false; + + arr = NULL; + } + +#endif /* !VBOX_WITH_XPCOM */ + } + + bool isWeak : 1; + +#ifdef VBOX_WITH_XPCOM + PRUint32 capacity; + PRUint32 size; + T *arr; +#else + SAFEARRAY *arr; + T *raw; +#endif + }; + + Data m; +}; + +/* Few fast specializations for primitive array types */ +template<> +inline void com::SafeArray<BYTE>::initFrom(const com::SafeArray<BYTE> & aRef) +{ + size_t sSize = aRef.size(); + resize(sSize); + ::memcpy(raw(), aRef.raw(), sSize); +} +template<> +inline void com::SafeArray<BYTE>::initFrom(const BYTE* aPtr, size_t aSize) +{ + resize(aSize); + ::memcpy(raw(), aPtr, aSize); +} + + +template<> +inline void com::SafeArray<SHORT>::initFrom(const com::SafeArray<SHORT> & aRef) +{ + size_t sSize = aRef.size(); + resize(sSize); + ::memcpy(raw(), aRef.raw(), sSize * sizeof(SHORT)); +} +template<> +inline void com::SafeArray<SHORT>::initFrom(const SHORT* aPtr, size_t aSize) +{ + resize(aSize); + ::memcpy(raw(), aPtr, aSize * sizeof(SHORT)); +} + +template<> +inline void com::SafeArray<USHORT>::initFrom(const com::SafeArray<USHORT> & aRef) +{ + size_t sSize = aRef.size(); + resize(sSize); + ::memcpy(raw(), aRef.raw(), sSize * sizeof(USHORT)); +} +template<> +inline void com::SafeArray<USHORT>::initFrom(const USHORT* aPtr, size_t aSize) +{ + resize(aSize); + ::memcpy(raw(), aPtr, aSize * sizeof(USHORT)); +} + +template<> +inline void com::SafeArray<LONG>::initFrom(const com::SafeArray<LONG> & aRef) +{ + size_t sSize = aRef.size(); + resize(sSize); + ::memcpy(raw(), aRef.raw(), sSize * sizeof(LONG)); +} +template<> +inline void com::SafeArray<LONG>::initFrom(const LONG* aPtr, size_t aSize) +{ + resize(aSize); + ::memcpy(raw(), aPtr, aSize * sizeof(LONG)); +} + + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef VBOX_WITH_XPCOM + +/** + * Version of com::SafeArray for arrays of GUID. + * + * In MS COM, GUID arrays store GUIDs by value and therefore input arrays are + * represented using |GUID *| and out arrays -- using |GUID **|. In XPCOM, + * GUID arrays store pointers to nsID so that input arrays are |const nsID **| + * and out arrays are |nsID ***|. Due to this difference, it is impossible to + * work with arrays of GUID on both platforms by simply using com::SafeArray + * <GUID>. This class is intended to provide some level of cross-platform + * behavior. + * + * The basic usage pattern is basically similar to com::SafeArray<> except that + * you use ComSafeGUIDArrayIn* and ComSafeGUIDArrayOut* macros instead of + * ComSafeArrayIn* and ComSafeArrayOut*. Another important nuance is that the + * raw() array type is different (nsID **, or GUID ** on XPCOM and GUID * on MS + * COM) so it is recommended to use operator[] instead which always returns a + * GUID by value. + * + * Note that due to const modifiers, you cannot use SafeGUIDArray for input GUID + * arrays. Please use SafeConstGUIDArray for this instead. + * + * Other than mentioned above, the functionality of this class is equivalent to + * com::SafeArray<>. See the description of that template and its methods for + * more information. + * + * Output GUID arrays are handled by a separate class, SafeGUIDArrayOut, since + * this class cannot handle them because of const modifiers. + */ +class SafeGUIDArray : public SafeArray<nsID *> +{ +public: + + typedef SafeArray<nsID *> Base; + + class nsIDRef + { + public: + + nsIDRef(nsID * &aVal) : mVal(aVal) {} + + operator const nsID &() const { return mVal ? *mVal : *Empty; } + operator nsID() const { return mVal ? *mVal : *Empty; } + + const nsID *operator&() const { return mVal ? mVal : Empty; } + + nsIDRef &operator= (const nsID &aThat) + { + if (mVal == NULL) + Copy(&aThat, mVal); + else + *mVal = aThat; + return *this; + } + + private: + + nsID * &mVal; + + static const nsID *Empty; + + friend class SafeGUIDArray; + }; + + /** See SafeArray<>::SafeArray(). */ + SafeGUIDArray() {} + + /** See SafeArray<>::SafeArray(size_t). */ + SafeGUIDArray(size_t aSize) : Base(aSize) {} + + /** + * Array access operator that returns an array element by reference. As a + * special case, the return value of this operator on XPCOM is an nsID (GUID) + * reference, instead of an nsID pointer (the actual SafeArray template + * argument), for compatibility with the MS COM version. + * + * The rest is equivalent to SafeArray<>::operator[]. + */ + nsIDRef operator[] (size_t aIdx) + { + Assert(m.arr != NULL); + Assert(aIdx < size()); + return nsIDRef(m.arr[aIdx]); + } + + /** + * Const version of #operator[] that returns an array element by value. + */ + const nsID &operator[] (size_t aIdx) const + { + Assert(m.arr != NULL); + Assert(aIdx < size()); + return m.arr[aIdx] ? *m.arr[aIdx] : *nsIDRef::Empty; + } +}; + +/** + * Version of com::SafeArray for const arrays of GUID. + * + * This class is used to work with input GUID array parameters in method + * implementations. See SafeGUIDArray for more details. + */ +class SafeConstGUIDArray : public SafeArray<const nsID *, + SafeArrayTraits<nsID *> > +{ +public: + + typedef SafeArray<const nsID *, SafeArrayTraits<nsID *> > Base; + + /** See SafeArray<>::SafeArray(). */ + SafeConstGUIDArray() {} + + /* See SafeArray<>::SafeArray(ComSafeArrayIn(T, aArg)). */ + SafeConstGUIDArray(ComSafeGUIDArrayIn(aArg)) + : Base(ComSafeGUIDArrayInArg(aArg)) {} + + /** + * Array access operator that returns an array element by reference. As a + * special case, the return value of this operator on XPCOM is nsID (GUID) + * instead of nsID *, for compatibility with the MS COM version. + * + * The rest is equivalent to SafeArray<>::operator[]. + */ + const nsID &operator[] (size_t aIdx) const + { + AssertReturn(m.arr != NULL, **((const nsID * *)1)); + AssertReturn(aIdx < size(), **((const nsID * *)1)); + return *m.arr[aIdx]; + } + +private: + + /* These are disabled because of const. */ + bool reset(size_t aNewSize) { NOREF(aNewSize); return false; } +}; + +#else /* !VBOX_WITH_XPCOM */ + +typedef SafeArray<GUID> SafeGUIDArray; +typedef SafeArray<const GUID, SafeArrayTraits<GUID> > SafeConstGUIDArray; + +#endif /* !VBOX_WITH_XPCOM */ + +//////////////////////////////////////////////////////////////////////////////// + +#ifdef VBOX_WITH_XPCOM + +template<class I> +struct SafeIfaceArrayTraits +{ +protected: + + static void Init(I * &aElem) { aElem = NULL; } + static void Uninit(I * &aElem) + { + if (aElem) + { + aElem->Release(); + aElem = NULL; + } + } + + static void Copy(I * aFrom, I * &aTo) + { + if (aFrom != NULL) + { + aTo = aFrom; + aTo->AddRef(); + } + else + aTo = NULL; + } + +public: + + /* Magic to workaround strict rules of par. 4.4.4 of the C++ standard. */ + static I **__asInParam_Arr(I **aArr) { return aArr; } + static I **__asInParam_Arr(const I **aArr) { return const_cast<I **>(aArr); } +}; + +#else /* !VBOX_WITH_XPCOM */ + +template<class I> +struct SafeIfaceArrayTraits +{ +protected: + + static VARTYPE VarType() { return VT_DISPATCH; } + static ULONG VarCount(size_t aSize) { return (ULONG)aSize; } + static size_t Size(ULONG aVarCount) { return (size_t)aVarCount; } + + static void Copy(I * aFrom, I * &aTo) + { + if (aFrom != NULL) + { + aTo = aFrom; + aTo->AddRef(); + } + else + aTo = NULL; + } + + static SAFEARRAY *CreateSafeArray(VARTYPE aVarType, SAFEARRAYBOUND *aBound) + { + NOREF(aVarType); + return SafeArrayCreateEx(VT_DISPATCH, 1, aBound, (PVOID)&COM_IIDOF(I)); + } +}; + +#endif /* !VBOX_WITH_XPCOM */ + +//////////////////////////////////////////////////////////////////////////////// + +/** + * Version of com::SafeArray for arrays of interface pointers. + * + * Except that it manages arrays of interface pointers, the usage of this class + * is identical to com::SafeArray. + * + * @param I Interface class (no asterisk). + */ +template<class I> +class SafeIfaceArray : public SafeArray<I *, SafeIfaceArrayTraits<I> > +{ +public: + + typedef SafeArray<I *, SafeIfaceArrayTraits<I> > Base; + + /** + * Creates a null array. + */ + SafeIfaceArray() {} + + /** + * Creates a new array of the given size. All elements of the newly created + * array initialized with null values. + * + * @param aSize Initial number of elements in the array. Must be greater + * than 0. + * + * @note If this object remains null after construction it means that there + * was not enough memory for creating an array of the requested size. + * The constructor will also assert in this case. + */ + SafeIfaceArray(size_t aSize) { Base::resize(aSize); } + + /** + * Weakly attaches this instance to the existing array passed in a method + * parameter declared using the ComSafeArrayIn macro. When using this call, + * always wrap the parameter name in the ComSafeArrayOutArg macro call like + * this: + * <pre> + * SafeArray safeArray(ComSafeArrayInArg(aArg)); + * </pre> + * + * Note that this constructor doesn't take the ownership of the array. In + * particular, this means that operations that operate on the ownership + * (e.g. #detachTo()) are forbidden and will assert. + * + * @param aArg Input method parameter to attach to. + */ + SafeIfaceArray(ComSafeArrayIn(I *, aArg)) + { + if (aArg) + { +#ifdef VBOX_WITH_XPCOM + + Base::m.size = aArgSize; + Base::m.arr = aArg; + Base::m.isWeak = true; + +#else /* !VBOX_WITH_XPCOM */ + + SAFEARRAY *arg = aArg; + + AssertReturnVoid(arg->cDims == 1); + + VARTYPE vt; + HRESULT rc = SafeArrayGetVartype(arg, &vt); + AssertComRCReturnVoid(rc); + AssertMsgReturnVoid(vt == VT_UNKNOWN || vt == VT_DISPATCH, + ("Expected vartype VT_UNKNOWN or VT_DISPATCH, got %d.\n", + vt)); + GUID guid; + rc = SafeArrayGetIID(arg, &guid); + AssertComRCReturnVoid(rc); + AssertMsgReturnVoid(InlineIsEqualGUID(COM_IIDOF(I), guid) || arg->rgsabound[0].cElements == 0 /* IDispatch if empty */, + ("Expected IID {%RTuuid}, got {%RTuuid}.\n", &COM_IIDOF(I), &guid)); + + rc = SafeArrayAccessData(arg, (void HUGEP **)&m.raw); + AssertComRCReturnVoid(rc); + + m.arr = arg; + m.isWeak = true; + +#endif /* !VBOX_WITH_XPCOM */ + } + } + + /** + * Creates a deep copy of the given standard C++ container that stores + * interface pointers as objects of the ComPtr\<I\> class. + * + * @param aCntr Container object to copy. + * + * @tparam C Standard C++ container template class (normally deduced from + * @c aCntr). + * @tparam A Standard C++ allocator class (deduced from @c aCntr). + * @tparam OI Argument to the ComPtr template (deduced from @c aCntr). + */ + template<template<typename, typename> class C, class A, class OI> + SafeIfaceArray(const C<ComPtr<OI>, A> & aCntr) + { + typedef C<ComPtr<OI>, A> List; + + Base::resize(aCntr.size()); + AssertReturnVoid(!Base::isNull()); + + int i = 0; + for (typename List::const_iterator it = aCntr.begin(); + it != aCntr.end(); ++ it, ++ i) +#ifdef VBOX_WITH_XPCOM + this->Copy(*it, Base::m.arr[i]); +#else + Copy(*it, Base::m.raw[i]); +#endif + } + + /** + * Creates a deep copy of the given standard C++ container that stores + * interface pointers as objects of the ComObjPtr\<I\> class. + * + * @param aCntr Container object to copy. + * + * @tparam C Standard C++ container template class (normally deduced from + * @c aCntr). + * @tparam A Standard C++ allocator class (deduced from @c aCntr). + * @tparam OI Argument to the ComObjPtr template (deduced from @c aCntr). + */ + template<template<typename, typename> class C, class A, class OI> + SafeIfaceArray(const C<ComObjPtr<OI>, A> & aCntr) + { + typedef C<ComObjPtr<OI>, A> List; + + Base::resize(aCntr.size()); + AssertReturnVoid(!Base::isNull()); + + int i = 0; + for (typename List::const_iterator it = aCntr.begin(); + it != aCntr.end(); ++ it, ++ i) +#ifdef VBOX_WITH_XPCOM + SafeIfaceArray::Copy(*it, Base::m.arr[i]); +#else + Copy(*it, Base::m.raw[i]); +#endif + } + + /** + * Creates a deep copy of the given standard C++ map whose values are + * interface pointers stored as objects of the ComPtr\<I\> class. + * + * @param aMap Map object to copy. + * + * @tparam C Standard C++ map template class (normally deduced from + * @c aCntr). + * @tparam L Standard C++ compare class (deduced from @c aCntr). + * @tparam A Standard C++ allocator class (deduced from @c aCntr). + * @tparam K Map key class (deduced from @c aCntr). + * @tparam OI Argument to the ComPtr template (deduced from @c aCntr). + */ + template<template<typename, typename, typename, typename> + class C, class L, class A, class K, class OI> + SafeIfaceArray(const C<K, ComPtr<OI>, L, A> & aMap) + { + typedef C<K, ComPtr<OI>, L, A> Map; + + Base::resize(aMap.size()); + AssertReturnVoid(!Base::isNull()); + + int i = 0; + for (typename Map::const_iterator it = aMap.begin(); + it != aMap.end(); ++ it, ++ i) +#ifdef VBOX_WITH_XPCOM + SafeIfaceArray::Copy(it->second, Base::m.arr[i]); +#else + Copy(it->second, Base::m.raw[i]); +#endif + } + + /** + * Creates a deep copy of the given standard C++ map whose values are + * interface pointers stored as objects of the ComObjPtr\<I\> class. + * + * @param aMap Map object to copy. + * + * @tparam C Standard C++ map template class (normally deduced from + * @c aCntr). + * @tparam L Standard C++ compare class (deduced from @c aCntr). + * @tparam A Standard C++ allocator class (deduced from @c aCntr). + * @tparam K Map key class (deduced from @c aCntr). + * @tparam OI Argument to the ComObjPtr template (deduced from @c aCntr). + */ + template<template<typename, typename, typename, typename> + class C, class L, class A, class K, class OI> + SafeIfaceArray(const C<K, ComObjPtr<OI>, L, A> & aMap) + { + typedef C<K, ComObjPtr<OI>, L, A> Map; + + Base::resize(aMap.size()); + AssertReturnVoid(!Base::isNull()); + + int i = 0; + for (typename Map::const_iterator it = aMap.begin(); + it != aMap.end(); ++ it, ++ i) +#ifdef VBOX_WITH_XPCOM + SafeIfaceArray::Copy(it->second, Base::m.arr[i]); +#else + Copy(it->second, Base::m.raw[i]); +#endif + } + + void setElement(size_t iIdx, I* obj) + { +#ifdef VBOX_WITH_XPCOM + SafeIfaceArray::Copy(obj, Base::m.arr[iIdx]); +#else + Copy(obj, Base::m.raw[iIdx]); +#endif + } +}; + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_array_h */ + diff --git a/include/VBox/com/assert.h b/include/VBox/com/assert.h new file mode 100644 index 00000000..9262dcdd --- /dev/null +++ b/include/VBox/com/assert.h @@ -0,0 +1,125 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Assertion macros for COM/XPCOM. + */ + +/* + * Copyright (C) 2006-2019 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_assert_h +#define VBOX_INCLUDED_com_assert_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <iprt/assert.h> + +/** @defgroup grp_com_assert Assertion Macros for COM/XPCOM + * @ingroup grp_com + * @{ + */ + + +/** + * Asserts that the COM result code is succeeded in strict builds. + * In non-strict builds the result code will be NOREF'ed to kill compiler warnings. + * + * @param hrc The COM result code + */ +#define AssertComRC(hrc) \ + do { AssertMsg(SUCCEEDED(hrc), ("COM RC = %Rhrc (0x%08X)\n", hrc, hrc)); NOREF(hrc); } while (0) + +/** + * Same as AssertComRC, except the caller already knows we failed. + * + * @param hrc The COM result code + */ +#define AssertComRCFailed(hrc) \ + do { AssertMsgFailed(("COM RC = %Rhrc (0x%08X)\n", hrc, hrc)); NOREF(hrc); } while (0) + +/** + * A special version of AssertComRC that returns the given expression + * if the result code is failed. + * + * @param hrc The COM result code + * @param RetExpr The expression to return + */ +#define AssertComRCReturn(hrc, RetExpr) \ + AssertMsgReturn(SUCCEEDED(hrc), ("COM RC = %Rhrc (0x%08X)\n", hrc, hrc), RetExpr) + +/** + * A special version of AssertComRC that returns the given result code + * if it is failed. + * + * @param hrc The COM result code + */ +#define AssertComRCReturnRC(hrc) \ + AssertMsgReturn(SUCCEEDED(hrc), ("COM RC = %Rhrc (0x%08X)\n", hrc, hrc), hrc) + +/** + * A special version of AssertComRC that returns if the result code is failed. + * + * @param hrc The COM result code + */ +#define AssertComRCReturnVoid(hrc) \ + AssertMsgReturnVoid(SUCCEEDED(hrc), ("COM RC = %Rhrc (0x%08X)\n", hrc, hrc)) + +/** + * A special version of AssertComRC that evaluates the given expression and + * breaks if the result code is failed. + * + * @param hrc The COM result code + * @param PreBreakExpr The expression to evaluate on failure. + */ +#define AssertComRCBreak(hrc, PreBreakExpr) \ + if (!SUCCEEDED(hrc)) { AssertComRCFailed(hrc); PreBreakExpr; break; } else do {} while (0) + +/** + * A special version of AssertComRC that evaluates the given expression and + * throws it if the result code is failed. + * + * @param hrc The COM result code + * @param ThrowMeExpr The expression which result to be thrown on failure. + */ +#define AssertComRCThrow(hrc, ThrowMeExpr) \ + do { if (SUCCEEDED(hrc)) { /*likely*/} else { AssertComRCFailed(hrc); throw (ThrowMeExpr); } } while (0) + +/** + * A special version of AssertComRC that just breaks if the result code is + * failed. + * + * @param hrc The COM result code + */ +#define AssertComRCBreakRC(hrc) \ + if (!SUCCEEDED(hrc)) { AssertComRCFailed(hrc); break; } else do {} while (0) + +/** + * A special version of AssertComRC that just throws @a hrc if the result code + * is failed. + * + * @param hrc The COM result code + */ +#define AssertComRCThrowRC(hrc) \ + do { if (SUCCEEDED(hrc)) { /*likely*/ } else { AssertComRCFailed(hrc); throw hrc; } } while (0) + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_assert_h */ + diff --git a/include/VBox/com/com.h b/include/VBox/com/com.h new file mode 100644 index 00000000..88db0c1c --- /dev/null +++ b/include/VBox/com/com.h @@ -0,0 +1,120 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - COM initialization / shutdown. + */ + +/* + * Copyright (C) 2005-2019 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_com_h +#define VBOX_INCLUDED_com_com_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include "VBox/com/defs.h" + +/** @defgroup grp_com MS COM / XPCOM Abstraction Layer + * @{ + */ + +namespace com +{ + +/** + * Initializes the COM runtime. + * Must be called on the main thread, before any COM activity in any thread, and by any thread + * willing to perform COM operations. + * + * @param fGui if call is performed on the GUI thread + * @param fAutoRegUpdate if to do auto MS COM registration updates. + * @return COM result code + */ +HRESULT Initialize(bool fGui = false, bool fAutoRegUpdate = true); + +/** + * Shuts down the COM runtime. + * Must be called on the main thread before termination. + * No COM calls may be made in any thread after this method returns. + */ +HRESULT Shutdown(); + +/** + * Resolves a given interface ID to a string containing the interface name. + * If, for some reason, the given IID cannot be resolved to a name, a NULL + * string is returned. A non-NULL string returned by this function must be + * freed using SysFreeString(). + * + * @param aIID ID of the interface to get a name for + * @param aName Resolved interface name or @c NULL on error + */ +void GetInterfaceNameByIID(const GUID &aIID, BSTR *aName); + +/** + * Returns the VirtualBox user home directory. + * + * On failure, this function will return a path that caused a failure (or + * NULL if the failure is not path-related). + * + * On success, this function will try to create the returned directory if it + * doesn't exist yet. This may also fail with the corresponding status code. + * + * If @a aDirLen is smaller than RTPATH_MAX then there is a great chance that + * this method will return VERR_BUFFER_OVERFLOW. + * + * @param aDir Buffer to store the directory string in UTF-8 encoding. + * @param aDirLen Length of the supplied buffer including space for the + * terminating null character, in bytes. + * @param fCreateDir Flag whether to create the returned directory on success if it + * doesn't exist. + * @return VBox status code. + */ +int GetVBoxUserHomeDirectory(char *aDir, size_t aDirLen, bool fCreateDir = true); + +/** + * Creates a release log file, used both in VBoxSVC and in API clients. + * + * @param pcszEntity Human readable name of the program. + * @param pcszLogFile Name of the release log file. + * @param fFlags Logger instance flags. + * @param pcszGroupSettings Group logging settings. + * @param pcszEnvVarBase Base environment variable name for the logger. + * @param fDestFlags Logger destination flags. + * @param cMaxEntriesPerGroup Limit for log entries per group. UINT32_MAX for no limit. + * @param cHistory Number of old log files to keep. + * @param uHistoryFileTime Maximum amount of time to put in a log file. + * @param uHistoryFileSize Maximum size of a log file before rotating. + * @param pErrInfo Where to return extended error information. + * Optional. + * + * @return VBox status code. + */ +int VBoxLogRelCreate(const char *pcszEntity, const char *pcszLogFile, + uint32_t fFlags, const char *pcszGroupSettings, + const char *pcszEnvVarBase, uint32_t fDestFlags, + uint32_t cMaxEntriesPerGroup, uint32_t cHistory, + uint32_t uHistoryFileTime, uint64_t uHistoryFileSize, + PRTERRINFO pErrInfo); + +} /* namespace com */ + +/** @} */ +#endif /* !VBOX_INCLUDED_com_com_h */ + diff --git a/include/VBox/com/defs.h b/include/VBox/com/defs.h new file mode 100644 index 00000000..b01c236a --- /dev/null +++ b/include/VBox/com/defs.h @@ -0,0 +1,595 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Common Definitions. + */ + +/* + * Copyright (C) 2006-2019 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_defs_h +#define VBOX_INCLUDED_com_defs_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 + +#if defined (RT_OS_OS2) + +# if defined(RT_MAX) && RT_MAX != 22 +# undef RT_MAX +# define REDEFINE_RT_MAX +# endif +# undef RT_MAX + +/* Make sure OS/2 Toolkit headers are pulled in to have BOOL/ULONG/etc. typedefs + * already defined in order to be able to redefine them using #define. */ +# define INCL_BASE +# define INCL_PM +# include <os2.h> + +/* OS/2 Toolkit defines TRUE and FALSE */ +# undef FALSE +# undef TRUE + +/* */ +# undef RT_MAX +# ifdef REDEFINE_RT_MAX +# define RT_MAX(Value1, Value2) ( (Value1) >= (Value2) ? (Value1) : (Value2) ) +# endif + +#endif /* defined(RT_OS_OS2) */ + +/* Include iprt/types.h (which also includes iprt/types.h) now to make sure iprt + * gets to stdint.h first, otherwise a system/xpcom header might beat us and + * we'll be without the macros that are optional in C++. */ +#include <iprt/types.h> + + + +/** @defgroup grp_com_defs Common Definitions + * @ingroup grp_com + * @{ + */ + +#if !defined(VBOX_WITH_XPCOM) + +#ifdef RT_OS_WINDOWS + +// Windows COM +///////////////////////////////////////////////////////////////////////////// + +# include <iprt/win/objbase.h> +# ifndef VBOX_COM_NO_ATL + +/* Do not use actual ATL, just provide a superficial lookalike ourselves. */ +# include <VBox/com/microatl.h> +# endif /* VBOX_COM_NO_ATL */ + +# define NS_DECL_ISUPPORTS +# define NS_IMPL_ISUPPORTS1_CI(a, b) + +/* these are XPCOM only, one for every interface implemented */ +# define NS_DECL_ISUPPORTS + +/** Returns @c true if @a rc represents a warning result code */ +# define SUCCEEDED_WARNING(rc) (SUCCEEDED(rc) && (rc) != S_OK) + +/** Tests is a COM result code indicates that the process implementing the + * interface is dead. + * + * COM status codes: + * 0x800706ba - RPC_S_SERVER_UNAVAILABLE. Killed before call was made. + * 0x800706be - RPC_S_CALL_FAILED. Killed after call was made. + * 0x800706bf - RPC_S_CALL_FAILED_DNE. Not observed, but should be + * matter of timing. + * 0x80010108 - RPC_E_DISCONNECTED. Observed deregistering + * python event listener. + * 0x800706b5 - RPC_S_UNKNOWN_IF. Observed deregistering python + * event listener + */ +#define FAILED_DEAD_INTERFACE(rc) \ + ( (rc) == HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) \ + || (rc) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED) \ + || (rc) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED_DNE) \ + || (rc) == RPC_E_DISCONNECTED \ + ) + +/** Immutable BSTR string */ +typedef const OLECHAR *CBSTR; + +/** Input BSTR argument of interface method declaration. */ +#define IN_BSTR BSTR + +/** Input GUID argument of interface method declaration. */ +#define IN_GUID GUID +/** Output GUID argument of interface method declaration. */ +#define OUT_GUID GUID * + +/** Makes the name of the getter interface function (n must be capitalized). */ +#define COMGETTER(n) get_##n +/** Makes the name of the setter interface function (n must be capitalized). */ +#define COMSETTER(n) put_##n + +/** + * Declares an input safearray parameter in the COM method implementation. Also + * used to declare the COM attribute setter parameter. Corresponds to either of + * the following XIDL definitions: + * <pre> + * <param name="arg" ... dir="in" safearray="yes"/> + * ... + * <attribute name="arg" ... safearray="yes"/> + * </pre> + * + * The method implementation should use the com::SafeArray helper class to work + * with parameters declared using this define. + * + * @param aType Array element type. + * @param aArg Parameter/attribute name. + */ +#define ComSafeArrayIn(aType, aArg) SAFEARRAY *aArg + +/** + * Expands to @c true if the given input safearray parameter is a "null pointer" + * which makes it impossible to use it for reading safearray data. + */ +#define ComSafeArrayInIsNull(aArg) ((aArg) == NULL) + +/** + * Wraps the given parameter name to generate an expression that is suitable for + * passing the parameter to functions that take input safearray parameters + * declared using the ComSafeArrayIn macro. + * + * @param aArg Parameter name to wrap. The given parameter must be declared + * within the calling function using the ComSafeArrayIn macro. + */ +#define ComSafeArrayInArg(aArg) aArg + +/** + * Declares an output safearray parameter in the COM method implementation. Also + * used to declare the COM attribute getter parameter. Corresponds to either of + * the following XIDL definitions: + * <pre> + * <param name="arg" ... dir="out" safearray="yes"/> + * <param name="arg" ... dir="return" safearray="yes"/> + * ... + * <attribute name="arg" ... safearray="yes"/> + * </pre> + * + * The method implementation should use the com::SafeArray helper class to work + * with parameters declared using this define. + * + * @param aType Array element type. + * @param aArg Parameter/attribute name. + */ +#define ComSafeArrayOut(aType, aArg) SAFEARRAY **aArg + +/** + * Expands to @c true if the given output safearray parameter is a "null + * pointer" which makes it impossible to use it for returning a safearray. + */ +#define ComSafeArrayOutIsNull(aArg) ((aArg) == NULL) + +/** + * Wraps the given parameter name to generate an expression that is suitable for + * passing the parameter to functions that take output safearray parameters + * declared using the ComSafeArrayOut marco. + * + * @param aArg Parameter name to wrap. The given parameter must be declared + * within the calling function using the ComSafeArrayOut macro. + */ +#define ComSafeArrayOutArg(aArg) aArg + +/** + * Version of ComSafeArrayIn for GUID. + * @param aArg Parameter name to wrap. + */ +#define ComSafeGUIDArrayIn(aArg) SAFEARRAY *aArg + +/** + * Version of ComSafeArrayInIsNull for GUID. + * @param aArg Parameter name to wrap. + */ +#define ComSafeGUIDArrayInIsNull(aArg) ComSafeArrayInIsNull(aArg) + +/** + * Version of ComSafeArrayInArg for GUID. + * @param aArg Parameter name to wrap. + */ +#define ComSafeGUIDArrayInArg(aArg) ComSafeArrayInArg(aArg) + +/** + * Version of ComSafeArrayOut for GUID. + * @param aArg Parameter name to wrap. + */ +#define ComSafeGUIDArrayOut(aArg) SAFEARRAY **aArg + +/** + * Version of ComSafeArrayOutIsNull for GUID. + * @param aArg Parameter name to wrap. + */ +#define ComSafeGUIDArrayOutIsNull(aArg) ComSafeArrayOutIsNull(aArg) + +/** + * Version of ComSafeArrayOutArg for GUID. + * @param aArg Parameter name to wrap. + */ +#define ComSafeGUIDArrayOutArg(aArg) ComSafeArrayOutArg(aArg) + +/** + * Gets size of safearray parameter. + * @param aArg Parameter name. + */ +#define ComSafeArraySize(aArg) ((aArg) == NULL ? 0 : (aArg)->rgsabound[0].cElements) + +/** + * Apply RT_NOREF_PV to a safearray parameter. + * @param aArg Parameter name. + */ +#define ComSafeArrayNoRef(aArg) RT_NOREF_PV(aArg) + +/** + * Returns the const reference to the IID (i.e., |const GUID &|) of the given + * interface. + * + * @param I interface class + */ +#define COM_IIDOF(I) __uuidof(I) + +/** + * For using interfaces before including the interface definitions. This will + * deal with XPCOM using 'class' and COM using 'struct' when defining + * interfaces. + * + * @param I interface name. + */ +#define COM_STRUCT_OR_CLASS(I) struct I + +#else /* defined(RT_OS_WINDOWS) */ + +#error "VBOX_WITH_XPCOM must be defined on a platform other than Windows!" + +#endif /* defined(RT_OS_WINDOWS) */ + +#else /* !defined(VBOX_WITH_XPCOM) */ + +// XPCOM +///////////////////////////////////////////////////////////////////////////// + +#if defined(RT_OS_DARWIN) || (defined(QT_VERSION) && (QT_VERSION >= 0x040000)) + /* CFBase.h defines these & + * qglobal.h from Qt4 defines these */ +# undef FALSE +# undef TRUE +#endif /* RT_OS_DARWIN || QT_VERSION */ + +#include <nsID.h> + +#define HRESULT nsresult +#define SUCCEEDED NS_SUCCEEDED +#define FAILED NS_FAILED + +#define SUCCEEDED_WARNING(rc) (NS_SUCCEEDED(rc) && (rc) != NS_OK) + +#define FAILED_DEAD_INTERFACE(rc) ( (rc) == NS_ERROR_ABORT \ + || (rc) == NS_ERROR_CALL_FAILED \ + ) + +#define IUnknown nsISupports + +#define BOOL PRBool +#define BYTE PRUint8 +#define SHORT PRInt16 +#define USHORT PRUint16 +#define LONG PRInt32 +#define ULONG PRUint32 +#define LONG64 PRInt64 +#define ULONG64 PRUint64 +/* XPCOM has only 64bit floats */ +#define FLOAT PRFloat64 +#define DOUBLE PRFloat64 + +#define FALSE PR_FALSE +#define TRUE PR_TRUE + +#define OLECHAR wchar_t + +/* note: typedef to semantically match BSTR on Win32 */ +typedef PRUnichar *BSTR; +typedef const PRUnichar *CBSTR; +typedef BSTR *LPBSTR; + +/** Input BSTR argument the interface method declaration. */ +#define IN_BSTR CBSTR + +/** + * Type to define a raw GUID variable (for members use the com::Guid class + * instead). + */ +#define GUID nsID +/** Input GUID argument the interface method declaration. */ +#define IN_GUID const nsID & +/** Output GUID argument the interface method declaration. */ +#define OUT_GUID nsID ** + +/** Makes the name of the getter interface function (n must be capitalized). */ +#define COMGETTER(n) Get##n +/** Makes the name of the setter interface function (n must be capitalized). */ +#define COMSETTER(n) Set##n + +/* safearray input parameter macros */ +#define ComSafeArrayIn(aType, aArg) PRUint32 aArg##Size, aType *aArg +#define ComSafeArrayInIsNull(aArg) ((aArg) == NULL) +#define ComSafeArrayInArg(aArg) aArg##Size, aArg + +/* safearray output parameter macros */ +#define ComSafeArrayOut(aType, aArg) PRUint32 *aArg##Size, aType **aArg +#define ComSafeArrayOutIsNull(aArg) ((aArg) == NULL) +#define ComSafeArrayOutArg(aArg) aArg##Size, aArg + +/* safearray input parameter macros for GUID */ +#define ComSafeGUIDArrayIn(aArg) PRUint32 aArg##Size, const nsID **aArg +#define ComSafeGUIDArrayInIsNull(aArg) ComSafeArrayInIsNull(aArg) +#define ComSafeGUIDArrayInArg(aArg) ComSafeArrayInArg(aArg) + +/* safearray output parameter macros for GUID */ +#define ComSafeGUIDArrayOut(aArg) PRUint32 *aArg##Size, nsID ***aArg +#define ComSafeGUIDArrayOutIsNull(aArg) ComSafeArrayOutIsNull(aArg) +#define ComSafeGUIDArrayOutArg(aArg) ComSafeArrayOutArg(aArg) + +/** safearray size */ +#define ComSafeArraySize(aArg) ((aArg) == NULL ? 0 : (aArg##Size)) + +/** NOREF a COM safe array argument. */ +#define ComSafeArrayNoRef(aArg) RT_NOREF2(aArg, aArg##Size) + +/* CLSID and IID for compatibility with Win32 */ +typedef nsCID CLSID; +typedef nsIID IID; + +/* OLE error codes */ +#define S_OK ((nsresult)NS_OK) +#define E_UNEXPECTED NS_ERROR_UNEXPECTED +#define E_NOTIMPL NS_ERROR_NOT_IMPLEMENTED +#define E_OUTOFMEMORY NS_ERROR_OUT_OF_MEMORY +#define E_INVALIDARG NS_ERROR_INVALID_ARG +#define E_NOINTERFACE NS_ERROR_NO_INTERFACE +#define E_POINTER NS_ERROR_NULL_POINTER +#define E_ABORT NS_ERROR_ABORT +#define E_FAIL NS_ERROR_FAILURE +/* Note: a better analog for E_ACCESSDENIED would probably be + * NS_ERROR_NOT_AVAILABLE, but we want binary compatibility for now. */ +#define E_ACCESSDENIED ((nsresult)0x80070005L) + +#define STDMETHOD(a) NS_IMETHOD a +#define STDMETHODIMP NS_IMETHODIMP +#define STDMETHOD_(ret, meth) NS_IMETHOD_(ret) meth + +#define COM_IIDOF(I) NS_GET_IID(I) + +#define COM_STRUCT_OR_CLASS(I) class I + +/* helper functions */ +extern "C" +{ +BSTR SysAllocString(const OLECHAR *sz); +BSTR SysAllocStringByteLen(char *psz, unsigned int len); +BSTR SysAllocStringLen(const OLECHAR *pch, unsigned int cch); +void SysFreeString(BSTR bstr); +int SysReAllocString(BSTR *pbstr, const OLECHAR *psz); +int SysReAllocStringLen(BSTR *pbstr, const OLECHAR *psz, unsigned int cch); +unsigned int SysStringByteLen(BSTR bstr); +unsigned int SysStringLen(BSTR bstr); +} + +#ifndef VBOX_COM_NO_ATL + +namespace ATL +{ + +#define ATL_NO_VTABLE +#define DECLARE_CLASSFACTORY(a) +#define DECLARE_CLASSFACTORY_SINGLETON(a) +#define DECLARE_REGISTRY_RESOURCEID(a) +#define DECLARE_NOT_AGGREGATABLE(a) +#define DECLARE_PROTECT_FINAL_CONSTRUCT() +#define BEGIN_COM_MAP(a) +#define COM_INTERFACE_ENTRY(a) +#define COM_INTERFACE_ENTRY2(a,b) +#define END_COM_MAP() NS_DECL_ISUPPORTS +#define COM_INTERFACE_ENTRY_AGGREGATE(a,b) + +/* A few very simple ATL emulator classes to provide + * FinalConstruct()/FinalRelease() functionality with XPCOM. */ + +class CComMultiThreadModel +{ +}; + +template <class DummyThreadModel> class CComObjectRootEx +{ +public: + HRESULT FinalConstruct() + { + return S_OK; + } + void FinalRelease() + { + } +}; + +template <class Base> class CComObject : public Base +{ +public: + virtual ~CComObject() { this->FinalRelease(); } +}; + +} /* namespace ATL */ + + +/** + * 'Constructor' for the component class. + * This constructor, as opposed to NS_GENERIC_FACTORY_CONSTRUCTOR, + * assumes that the component class is derived from the CComObjectRootEx<> + * template, so it calls FinalConstruct() right after object creation + * and ensures that FinalRelease() will be called right before destruction. + * The result from FinalConstruct() is returned to the caller. + */ +#define NS_GENERIC_FACTORY_CONSTRUCTOR_WITH_RC(_InstanceClass) \ +static NS_IMETHODIMP \ +_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ + void **aResult) \ +{ \ + nsresult rv; \ + \ + *aResult = NULL; \ + if (NULL != aOuter) { \ + rv = NS_ERROR_NO_AGGREGATION; \ + return rv; \ + } \ + \ + ATL::CComObject<_InstanceClass> *inst = new ATL::CComObject<_InstanceClass>(); \ + if (NULL == inst) { \ + rv = NS_ERROR_OUT_OF_MEMORY; \ + return rv; \ + } \ + \ + NS_ADDREF(inst); /* protect FinalConstruct() */ \ + rv = inst->FinalConstruct(); \ + if (NS_SUCCEEDED(rv)) \ + rv = inst->QueryInterface(aIID, aResult); \ + NS_RELEASE(inst); \ + \ + return rv; \ +} + +/** + * 'Constructor' that uses an existing getter function that gets a singleton. + * The getter function must have the following prototype: + * nsresult _GetterProc(_InstanceClass **inst) + * This constructor, as opposed to NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR, + * lets the getter function return a result code that is passed back to the + * caller that tries to instantiate the object. + * NOTE: assumes that getter does an AddRef - so additional AddRef is not done. + */ +#define NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR_WITH_RC(_InstanceClass, _GetterProc) \ +static NS_IMETHODIMP \ +_InstanceClass##Constructor(nsISupports *aOuter, REFNSIID aIID, \ + void **aResult) \ +{ \ + nsresult rv; \ + \ + _InstanceClass *inst = NULL; /* initialized to shut up gcc */ \ + \ + *aResult = NULL; \ + if (NULL != aOuter) { \ + rv = NS_ERROR_NO_AGGREGATION; \ + return rv; \ + } \ + \ + rv = _GetterProc(&inst); \ + if (NS_FAILED(rv)) \ + return rv; \ + \ + /* sanity check */ \ + if (NULL == inst) \ + return NS_ERROR_OUT_OF_MEMORY; \ + \ + /* NS_ADDREF(inst); */ \ + if (NS_SUCCEEDED(rv)) { \ + rv = inst->QueryInterface(aIID, aResult); \ + } \ + NS_RELEASE(inst); \ + \ + return rv; \ +} + +#endif /* !VBOX_COM_NO_ATL */ + +#endif /* !defined(VBOX_WITH_XPCOM) */ + +/** + * Declares a wchar_t string literal from the argument. + * Necessary to overcome MSC / GCC differences. + * @param s expression to stringify + */ +#if defined(_MSC_VER) +# define WSTR_LITERAL(s) L#s +#elif defined(__GNUC__) +# define WSTR_LITERAL(s) L""#s +#else +# error "Unsupported compiler!" +#endif + +namespace com +{ + +#ifndef VBOX_COM_NO_ATL + +// use this macro to implement scriptable interfaces +#ifdef RT_OS_WINDOWS +#define VBOX_SCRIPTABLE_IMPL(iface) \ + public ATL::IDispatchImpl<iface, &IID_##iface, &LIBID_VirtualBox, \ + kTypeLibraryMajorVersion, kTypeLibraryMinorVersion> + +#define VBOX_SCRIPTABLE_DISPATCH_IMPL(iface) \ + STDMETHOD(QueryInterface)(REFIID riid, void **ppObj) \ + { \ + if (riid == IID_##iface) \ + { \ + *ppObj = (iface *)this; \ + AddRef(); \ + return S_OK; \ + } \ + if (riid == IID_IUnknown) \ + { \ + *ppObj = (IUnknown *)this; \ + AddRef(); \ + return S_OK; \ + } \ + if (riid == IID_IDispatch) \ + { \ + *ppObj = (IDispatch *)this; \ + AddRef(); \ + return S_OK; \ + } \ + *ppObj = NULL; \ + return E_NOINTERFACE; \ + } +#else +#define VBOX_SCRIPTABLE_IMPL(iface) \ + public iface +#define VBOX_SCRIPTABLE_DISPATCH_IMPL(iface) +#endif + +#endif /* !VBOX_COM_NO_ATL */ + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_defs_h */ + diff --git a/include/VBox/com/errorprint.h b/include/VBox/com/errorprint.h new file mode 100644 index 00000000..b4579942 --- /dev/null +++ b/include/VBox/com/errorprint.h @@ -0,0 +1,381 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Error Reporting. + * + * Error printing macros using shared functions defined in shared glue code. + * Use these CHECK_* macros for efficient error checking around calling COM + * methods. + */ + +/* + * Copyright (C) 2009-2019 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_errorprint_h +#define VBOX_INCLUDED_com_errorprint_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/com/ErrorInfo.h> + + +/** @defgroup grp_com_error_reporting Error Reporting + * @ingroup grp_com + * @{ + */ + +namespace com +{ + +// shared prototypes; these are defined in shared glue code and are +// compiled only once for all front-ends +void GluePrintErrorInfo(const com::ErrorInfo &info); +void GluePrintErrorContext(const char *pcszContext, const char *pcszSourceFile, uint32_t uLine); +void GluePrintRCMessage(HRESULT rc); +void GlueHandleComError(ComPtr<IUnknown> iface, const char *pcszContext, HRESULT rc, const char *pcszSourceFile, uint32_t uLine); +void GlueHandleComErrorProgress(ComPtr<IProgress> progress, const char *pcszContext, HRESULT rc, + const char *pcszSourceFile, uint32_t uLine); + +/** + * Extended macro that implements all the other CHECK_ERROR2XXX macros. + * + * Calls the method of the given interface and checks the return status code. + * If the status indicates failure, as much information as possible is reported + * about the error, including current source file and line. + * + * After reporting an error, the statement |stmtError| is executed. + * + * This macro family is intended for command line tools like VBoxManage, but + * could also be handy for debugging. + * + * @param type For defining @a hrc locally inside the the macro + * expansion, pass |HRESULT| otherwise |RT_NOTHING|. + * @param hrc Name of the HRESULT variable to assign the result of the + * method call to. + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @param stmtError Statement to be executed after reporting failures. This + * can be a |break| or |return| statement, if so desired. + * + * @remarks Unlike CHECK_ERROR, CHECK_ERROR_RET and family, this macro family + * does not presuppose a |rc| variable but instead either let the user + * specify the variable to use or employs a local variable |hrcCheck| + * within its own scope. + * + * @sa CHECK_ERROR2, CHECK_ERROR2I, CHECK_ERROR2_STMT, CHECK_ERROR2I_STMT, + * CHECK_ERROR2_BREAK, CHECK_ERROR2I_BREAK, CHECK_ERROR2_RET, + * CHECK_ERROR2I_RET + */ +#define CHECK_ERROR2_EX(type, hrc, iface, method, stmtError) \ + if (1) { \ + type hrc = iface->method; \ + if (SUCCEEDED(hrc)) \ + { /*likely*/ } \ + else \ + { \ + com::GlueHandleComError(iface, #method, (hrc), __FILE__, __LINE__); \ + stmtError; \ + } \ + } else do { /* nothing */ } while (0) + + +/** + * Calls the given method of the given interface and then checks if the return + * value (COM result code) indicates a failure. If so, prints the failed + * function/line/file, the description of the result code and attempts to + * query the extended error information on the current thread (using + * com::ErrorInfo) if the interface reports that it supports error information. + * + * Used by command line tools or for debugging and assumes the |HRESULT rc| + * variable is accessible for assigning in the current scope. + * @sa CHECK_ERROR2, CHECK_ERROR2I + */ +#define CHECK_ERROR(iface, method) \ + do { \ + rc = iface->method; \ + if (FAILED(rc)) \ + com::GlueHandleComError(iface, #method, rc, __FILE__, __LINE__); \ + } while (0) +/** + * Simplified version of CHECK_ERROR2_EX, no error statement or type necessary. + * + * @param hrc Name of the HRESULT variable to assign the result of the + * method call to. + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @sa CHECK_ERROR2I, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2(hrc, iface, method) CHECK_ERROR2_EX(RT_NOTHING, hrc, iface, method, (void)1) +/** + * Simplified version of CHECK_ERROR2_EX that uses an internal variable + * |hrcCheck| for holding the result and have no error statement. + * + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @sa CHECK_ERROR2, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2I(iface, method) CHECK_ERROR2_EX(HRESULT, hrcCheck, iface, method, (void)1) + + +/** + * Same as CHECK_ERROR except that it also executes the statement |stmt| on + * failure. + * @sa CHECK_ERROR2_STMT, CHECK_ERROR2I_STMT + */ +#define CHECK_ERROR_STMT(iface, method, stmt) \ + do { \ + rc = iface->method; \ + if (FAILED(rc)) \ + { \ + com::GlueHandleComError(iface, #method, rc, __FILE__, __LINE__); \ + stmt; \ + } \ + } while (0) +/** + * Simplified version of CHECK_ERROR2_EX (no @a hrc type). + * + * @param hrc Name of the HRESULT variable to assign the result of the + * method call to. + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @param stmt Statement to be executed after reporting failures. + * @sa CHECK_ERROR2I_STMT, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2_STMT(hrc, iface, method, stmt) CHECK_ERROR2_EX(RT_NOTHING, hrc, iface, method, stmt) +/** + * Simplified version of CHECK_ERROR2_EX that uses an internal variable + * |hrcCheck| for holding the result. + * + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @param stmt Statement to be executed after reporting failures. + * @sa CHECK_ERROR2_STMT, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2I_STMT(iface, method, stmt) CHECK_ERROR2_EX(HRESULT, hrcCheck, iface, method, stmt) + + +/** + * Does the same as CHECK_ERROR(), but executes the |break| statement on + * failure. + * @sa CHECK_ERROR2_BREAK, CHECK_ERROR2I_BREAK + */ +#ifdef __GNUC__ +# define CHECK_ERROR_BREAK(iface, method) \ + __extension__ \ + ({ \ + rc = iface->method; \ + if (FAILED(rc)) \ + { \ + com::GlueHandleComError(iface, #method, rc, __FILE__, __LINE__); \ + break; \ + } \ + }) +#else +# define CHECK_ERROR_BREAK(iface, method) \ + if (1) \ + { \ + rc = iface->method; \ + if (FAILED(rc)) \ + { \ + com::GlueHandleComError(iface, #method, rc, __FILE__, __LINE__); \ + break; \ + } \ + } \ + else do {} while (0) +#endif +/** + * Simplified version of CHECK_ERROR2_EX that executes the |break| statement + * after error reporting (no @a hrc type). + * + * @param hrc The result variable (type HRESULT). + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @sa CHECK_ERROR2I_BREAK, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2_BREAK(hrc, iface, method) CHECK_ERROR2_EX(RT_NOTHING, hrc, iface, method, break) +/** + * Simplified version of CHECK_ERROR2_EX that executes the |break| statement + * after error reporting and that uses an internal variable |hrcCheck| for + * holding the result. + * + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @sa CHECK_ERROR2_BREAK, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2I_BREAK(iface, method) CHECK_ERROR2_EX(HRESULT, hrcCheck, iface, method, break) +/** + * Simplified version of CHECK_ERROR2_EX that executes the |stmt;break| + * statements after error reporting and that uses an internal variable + * |hrcCheck| for holding the result. + * + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @param stmt Statement to be executed after reporting failures. + * @sa CHECK_ERROR2_BREAK, CHECK_ERROR2_EX + */ +#define CHECK_ERROR2I_BREAK_STMT(iface, method, stmt) CHECK_ERROR2_EX(HRESULT, hrcCheck, iface, method, stmt; break) + + +/** + * Does the same as CHECK_ERROR(), but executes the |return ret| statement on + * failure. + * @sa CHECK_ERROR2_RET, CHECK_ERROR2I_RET + */ +#define CHECK_ERROR_RET(iface, method, ret) \ + do { \ + rc = iface->method; \ + if (FAILED(rc)) \ + { \ + com::GlueHandleComError(iface, #method, rc, __FILE__, __LINE__); \ + return (ret); \ + } \ + } while (0) +/** + * Simplified version of CHECK_ERROR2_EX that executes the |return (rcRet)| + * statement after error reporting. + * + * @param hrc The result variable (type HRESULT). + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @param rcRet What to return on failure. + */ +#define CHECK_ERROR2_RET(hrc, iface, method, rcRet) CHECK_ERROR2_EX(RT_NOTHING, hrc, iface, method, return (rcRet)) +/** + * Simplified version of CHECK_ERROR2_EX that executes the |return (rcRet)| + * statement after error reporting and that uses an internal variable |hrcCheck| + * for holding the result. + * + * @param iface The interface pointer (can be a smart pointer object). + * @param method The method to invoke together with the parameters. + * @param rcRet What to return on failure. Use |hrcCheck| to return + * the status code of the method call. + */ +#define CHECK_ERROR2I_RET(iface, method, rcRet) CHECK_ERROR2_EX(HRESULT, hrcCheck, iface, method, return (rcRet)) + + +/** + * Check the progress object for an error and if there is one print out the + * extended error information. + * @remarks Requires HRESULT variable named @a rc. + */ +#define CHECK_PROGRESS_ERROR(progress, msg) \ + do { \ + LONG iRc; \ + rc = progress->COMGETTER(ResultCode)(&iRc); \ + if (FAILED(rc) || FAILED(iRc)) \ + { \ + if (SUCCEEDED(rc)) rc = iRc; else iRc = rc; \ + RTMsgError msg; \ + com::GlueHandleComErrorProgress(progress, __PRETTY_FUNCTION__, iRc, __FILE__, __LINE__); \ + } \ + } while (0) + +/** + * Does the same as CHECK_PROGRESS_ERROR(), but executes the |break| statement + * on failure. + * @remarks Requires HRESULT variable named @a rc. + */ +#ifdef __GNUC__ +# define CHECK_PROGRESS_ERROR_BREAK(progress, msg) \ + __extension__ \ + ({ \ + LONG iRc; \ + rc = progress->COMGETTER(ResultCode)(&iRc); \ + if (FAILED(rc) || FAILED(iRc)) \ + { \ + if (SUCCEEDED(rc)) rc = iRc; else iRc = rc; \ + RTMsgError msg; \ + com::GlueHandleComErrorProgress(progress, __PRETTY_FUNCTION__, iRc, __FILE__, __LINE__); \ + break; \ + } \ + }) +#else +# define CHECK_PROGRESS_ERROR_BREAK(progress, msg) \ + if (1) \ + { \ + LONG iRc; \ + rc = progress->COMGETTER(ResultCode)(&iRc); \ + if (FAILED(rc) || FAILED(iRc)) \ + { \ + if (SUCCEEDED(rc)) rc = iRc; else iRc = rc; \ + RTMsgError msg; \ + com::GlueHandleComErrorProgress(progress, __PRETTY_FUNCTION__, iRc, __FILE__, __LINE__); \ + break; \ + } \ + } \ + else do {} while (0) +#endif + +/** + * Does the same as CHECK_PROGRESS_ERROR(), but executes the |return ret| + * statement on failure. + */ +#define CHECK_PROGRESS_ERROR_RET(progress, msg, ret) \ + do { \ + LONG iRc; \ + HRESULT hrcCheck = progress->COMGETTER(ResultCode)(&iRc); \ + if (SUCCEEDED(hrcCheck) && SUCCEEDED(iRc)) \ + { /* likely */ } \ + else \ + { \ + RTMsgError msg; \ + com::GlueHandleComErrorProgress(progress, __PRETTY_FUNCTION__, \ + SUCCEEDED(hrcCheck) ? iRc : hrcCheck, __FILE__, __LINE__); \ + return (ret); \ + } \ + } while (0) + +/** + * Asserts the given expression is true. When the expression is false, prints + * a line containing the failed function/line/file; otherwise does nothing. + */ +#define ASSERT(expr) \ + do { \ + if (!(expr)) \ + { \ + RTPrintf ("[!] ASSERTION FAILED at line %d: %s\n", __LINE__, #expr); \ + Log (("[!] ASSERTION FAILED at line %d: %s\n", __LINE__, #expr)); \ + } \ + } while (0) + + +/** + * Does the same as ASSERT(), but executes the |return ret| statement if the + * expression to assert is false; + * @remarks WARNING! @a expr is evalutated TWICE! + */ +#define ASSERT_RET(expr, ret) \ + do { ASSERT(expr); if (!(expr)) return (ret); } while (0) + +/** + * Does the same as ASSERT(), but executes the |break| statement if the + * expression to assert is false; + * @remarks WARNING! @a expr is evalutated TWICE! + */ +#define ASSERT_BREAK(expr, ret) \ + if (1) { ASSERT(expr); if (!(expr)) break; } else do {} while (0) + +} /* namespace com */ + + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_errorprint_h */ + diff --git a/include/VBox/com/list.h b/include/VBox/com/list.h new file mode 100644 index 00000000..d84f28cc --- /dev/null +++ b/include/VBox/com/list.h @@ -0,0 +1,213 @@ +/* $Id: list.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - List classes declaration. + */ + +/* + * Copyright (C) 2011-2019 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_list_h +#define VBOX_INCLUDED_com_list_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/com/ptr.h> +#include <VBox/com/string.h> +#include <VBox/com/array.h> +#include <iprt/cpp/list.h> + + +/** @defgroup grp_com_list List Classes + * @ingroup grp_com + * @{ + */ + +/** + * Specialized list class for using with com::ComPtr<C> + * + * @note This is necessary cause ComPtr<IFACE> has a size of 8. + */ +template <typename C> +class RTCList< ComPtr<C> >: public RTCListBase< ComPtr<C>, ComPtr<C>*, false> +{ + /* Traits */ + typedef ComPtr<C> T; + typedef T *ITYPE; + static const bool MT = false; + typedef RTCListBase<T, ITYPE, MT> BASE; + +public: + /** + * Creates a new list. + * + * This preallocates @a cCapacity elements within the list. + * + * @param cCapacity The initial capacity the list has. + * @throws std::bad_alloc + */ + RTCList(size_t cCapacity = BASE::kDefaultCapacity) + : BASE(cCapacity) {} + + /* Define our own new and delete. */ + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** + * Specialized list class for using with com::ComObjPtr<C> + * + * @note: This is necessary cause ComObjPtr<IFACE> has a size of 8. + */ +template <typename C> +class RTCList< ComObjPtr<C> >: public RTCListBase< ComObjPtr<C>, ComObjPtr<C>*, false> +{ + /* Traits */ + typedef ComObjPtr<C> T; + typedef T *ITYPE; + static const bool MT = false; + typedef RTCListBase<T, ITYPE, MT> BASE; + +public: + /** + * Creates a new list. + * + * This preallocates @a cCapacity elements within the list. + * + * @param cCapacity The initial capacity the list has. + * @throws std::bad_alloc + */ + RTCList(size_t cCapacity = BASE::kDefaultCapacity) + : BASE(cCapacity) {} + + /* Define our own new and delete. */ + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** + * Specialized list class for using with com::Utf8Str. + * + * The class offers methods for importing com::SafeArray's of com::Bstr's. + */ +template <> +class RTCList<com::Utf8Str>: public RTCListBase<com::Utf8Str, com::Utf8Str*, false> +{ + /* Traits */ + typedef com::Utf8Str T; + typedef T *ITYPE; + static const bool MT = false; + typedef RTCListBase<T, ITYPE, MT> BASE; + +public: + /** + * Creates a new list. + * + * This preallocates @a cCapacity elements within the list. + * + * @param cCapacity The initial capacity the list has. + * @throws std::bad_alloc + */ + RTCList(size_t cCapacity = BASE::kDefaultCapacity) + : BASE(cCapacity) {} + + /** + * Creates a copy of another list. + * + * The other list will be fully copied and the capacity will be the same as + * the size of the other list. The com::Bstr's are silently converted to + * com::Utf8Str's. + * + * @param other The list to copy. + * @throws std::bad_alloc + */ + RTCList(ComSafeArrayIn(IN_BSTR, other)) + { + com::SafeArray<IN_BSTR> sfaOther(ComSafeArrayInArg(other)); + size_t const cElementsOther = sfaOther.size(); + resizeArray(cElementsOther); + m_cElements = cElementsOther; + for (size_t i = 0; i < cElementsOther; ++i) + RTCListHelper<T, ITYPE>::set(m_pArray, i, T(sfaOther[i])); + } + + /** + * Creates a copy of another list. + * + * The other list will be fully copied and the capacity will be the same as + * the size of the other list. The com::Bstr's are silently converted to + * com::Utf8Str's. + * + * @param other The list to copy. + * @throws std::bad_alloc + */ + RTCList(const com::SafeArray<IN_BSTR> &other) + : BASE(other.size()) + { + for (size_t i = 0; i < m_cElements; ++i) + RTCListHelper<T, ITYPE>::set(m_pArray, i, T(other[i])); + } + + /** + * Copy the items of the other list into this list. All previous items of + * this list are deleted. + * + * @param other The list to copy. + * @return a reference to this list. + * @throws std::bad_alloc + */ + RTCListBase<T, ITYPE, MT> &operator=(const com::SafeArray<IN_BSTR> &other) + { + m_guard.enterWrite(); + + /* Values cleanup */ + RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements); + + /* Copy */ + size_t cElementsOther = other.size(); + if (cElementsOther != m_cCapacity) + resizeArrayNoErase(cElementsOther); + m_cElements = cElementsOther; + for (size_t i = 0; i < cElementsOther; ++i) + RTCListHelper<T, ITYPE>::set(m_pArray, i, T(other[i])); + + m_guard.leaveWrite(); + return *this; + } + + /** + * Implicit conversion to a RTCString list. + * + * This allows the usage of the RTCString::join method with this list type. + * + * @return a converted const reference to this list. + */ + operator const RTCList<RTCString, RTCString*>&() + { + return *reinterpret_cast<RTCList<RTCString, RTCString*> *>(this); + } + + /* Define our own new and delete. */ + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_list_h */ + diff --git a/include/VBox/com/listeners.h b/include/VBox/com/listeners.h new file mode 100644 index 00000000..37f0e2bb --- /dev/null +++ b/include/VBox/com/listeners.h @@ -0,0 +1,183 @@ +/* $Id: listeners.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - Listener helpers. + */ + +/* + * Copyright (C) 2010-2019 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_listeners_h +#define VBOX_INCLUDED_com_listeners_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/com/com.h> +#include <VBox/com/VirtualBox.h> + + +/** @defgroup grp_com_listeners Listener Helpers + * @ingroup grp_com + * @{ + */ + + +#ifdef VBOX_WITH_XPCOM +# define NS_IMPL_QUERY_HEAD_INLINE() \ +NS_IMETHODIMP QueryInterface(REFNSIID aIID, void **aInstancePtr) \ +{ \ + NS_ASSERTION(aInstancePtr, "QueryInterface requires a non-NULL destination!"); \ + nsISupports *foundInterface; + +# define NS_INTERFACE_MAP_BEGIN_INLINE() NS_IMPL_QUERY_HEAD_INLINE() + +# define NS_IMPL_QUERY_INTERFACE1_INLINE(a_i1) \ + NS_INTERFACE_MAP_BEGIN_INLINE() \ + NS_INTERFACE_MAP_ENTRY(a_i1) \ + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, a_i1) \ + NS_INTERFACE_MAP_END +#endif + +template <class T, class TParam = void *> +class ListenerImpl : + public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>, + VBOX_SCRIPTABLE_IMPL(IEventListener) +{ + T* mListener; + +#ifdef RT_OS_WINDOWS + /* FTM stuff */ + ComPtr<IUnknown> m_pUnkMarshaler; +#else + nsAutoRefCnt mRefCnt; + NS_DECL_OWNINGTHREAD +#endif + +public: + ListenerImpl() + { + } + + virtual ~ListenerImpl() + { + } + + HRESULT init(T* aListener, TParam param) + { + mListener = aListener; + return mListener->init(param); + } + + HRESULT init(T* aListener) + { + mListener = aListener; + return mListener->init(); + } + + void uninit() + { + if (mListener) + { + mListener->uninit(); + delete mListener; + mListener = 0; + } + } + + HRESULT FinalConstruct() + { +#ifdef RT_OS_WINDOWS + return CoCreateFreeThreadedMarshaler(this, &m_pUnkMarshaler.m_p); +#else + return S_OK; +#endif + } + + void FinalRelease() + { + uninit(); +#ifdef RT_OS_WINDOWS + m_pUnkMarshaler.setNull(); +#endif + } + + T* getWrapped() + { + return mListener; + } + + DECLARE_NOT_AGGREGATABLE(ListenerImpl) + + DECLARE_PROTECT_FINAL_CONSTRUCT() + +#ifdef RT_OS_WINDOWS + BEGIN_COM_MAP(ListenerImpl) + COM_INTERFACE_ENTRY(IEventListener) + COM_INTERFACE_ENTRY2(IDispatch, IEventListener) + COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pUnkMarshaler.m_p) + END_COM_MAP() +#else + NS_IMETHOD_(nsrefcnt) AddRef(void) + { + NS_PRECONDITION(PRInt32(mRefCnt) >= 0, "illegal refcnt"); + nsrefcnt count; + count = PR_AtomicIncrement((PRInt32*)&mRefCnt); + NS_LOG_ADDREF(this, count, "ListenerImpl", sizeof(*this)); + return count; + } + + NS_IMETHOD_(nsrefcnt) Release(void) + { + nsrefcnt count; + NS_PRECONDITION(0 != mRefCnt, "dup release"); + count = PR_AtomicDecrement((PRInt32 *)&mRefCnt); + NS_LOG_RELEASE(this, count, "ListenerImpl"); + if (0 == count) { + mRefCnt = 1; /* stabilize */ + /* enable this to find non-threadsafe destructors: */ + /* NS_ASSERT_OWNINGTHREAD(_class); */ + NS_DELETEXPCOM(this); + return 0; + } + return count; + } + + NS_IMPL_QUERY_INTERFACE1_INLINE(IEventListener) +#endif + + + STDMETHOD(HandleEvent)(IEvent * aEvent) + { + VBoxEventType_T aType = VBoxEventType_Invalid; + aEvent->COMGETTER(Type)(&aType); + return mListener->HandleEvent(aType, aEvent); + } +}; + +#ifdef VBOX_WITH_XPCOM +# define VBOX_LISTENER_DECLARE(klazz) NS_DECL_CLASSINFO(klazz) +#else +# define VBOX_LISTENER_DECLARE(klazz) +#endif + +/** @} */ +#endif /* !VBOX_INCLUDED_com_listeners_h */ + diff --git a/include/VBox/com/microatl.h b/include/VBox/com/microatl.h new file mode 100644 index 00000000..6e744a36 --- /dev/null +++ b/include/VBox/com/microatl.h @@ -0,0 +1,1360 @@ +/** @file + * ATL lookalike, just the tiny subset we actually need. + */ + +/* + * Copyright (C) 2016-2019 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_microatl_h +#define VBOX_INCLUDED_com_microatl_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/cdefs.h> /* VBOX_STRICT */ +#include <iprt/assert.h> +#include <iprt/critsect.h> +#include <iprt/errcore.h> /* RT_FAILURE() */ + +#include <iprt/win/windows.h> + +#include <new> + + +namespace ATL +{ + +#define ATL_NO_VTABLE __declspec(novtable) + +class CAtlModule; +__declspec(selectany) CAtlModule *_pAtlModule = NULL; + +class CComModule; +__declspec(selectany) CComModule *_pModule = NULL; + +typedef HRESULT (WINAPI FNCREATEINSTANCE)(void *pv, REFIID riid, void **ppv); +typedef FNCREATEINSTANCE *PFNCREATEINSTANCE; +typedef HRESULT (WINAPI FNINTERFACEMAPHELPER)(void *pv, REFIID riid, void **ppv, DWORD_PTR dw); +typedef FNINTERFACEMAPHELPER *PFNINTERFACEMAPHELPER; +typedef void (__stdcall FNATLTERMFUNC)(void *pv); +typedef FNATLTERMFUNC *PFNATLTERMFUNC; + +struct _ATL_TERMFUNC_ELEM +{ + PFNATLTERMFUNC pfn; + void *pv; + _ATL_TERMFUNC_ELEM *pNext; +}; + +struct _ATL_INTMAP_ENTRY +{ + const IID *piid; // interface ID + DWORD_PTR dw; + PFNINTERFACEMAPHELPER pFunc; // NULL: end of array, 1: offset based map entry, other: function pointer +}; + +#define COM_SIMPLEMAPENTRY ((ATL::PFNINTERFACEMAPHELPER)1) + +#define DECLARE_CLASSFACTORY_EX(c) typedef ATL::CComCreator<ATL::CComObjectNoLock<c> > _ClassFactoryCreatorClass; +#define DECLARE_CLASSFACTORY() DECLARE_CLASSFACTORY_EX(ATL::CComClassFactory) +#define DECLARE_CLASSFACTORY_SINGLETON(o) DECLARE_CLASSFACTORY_EX(ATL::CComClassFactorySingleton<o>) +#define DECLARE_AGGREGATABLE(c) \ +public: \ + typedef ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<c> >, ATL::CComCreator<ATL::CComAggObject<c> > > _CreatorClass; +#define DECLARE_NOT_AGGREGATABLE(c) \ +public: \ + typedef ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<c> >, ATL::CComFailCreator<CLASS_E_NOAGGREGATION> > _CreatorClass; + +#define DECLARE_PROTECT_FINAL_CONSTRUCT() \ + void InternalFinalConstructAddRef() \ + { \ + InternalAddRef(); \ + } \ + void InternalFinalConstructRelease() \ + { \ + InternalRelease(); \ + } + +#define BEGIN_COM_MAP(c) \ +public: \ + typedef c _ComClass; \ + HRESULT _InternalQueryInterface(REFIID iid, void **ppvObj) throw() \ + { \ + return InternalQueryInterface(this, _GetEntries(), iid, ppvObj); \ + } \ + const static ATL::_ATL_INTMAP_ENTRY *WINAPI _GetEntries() throw() \ + { \ + static const ATL::_ATL_INTMAP_ENTRY _aInterfaces[] = \ + { + +#define COM_INTERFACE_ENTRY(c) \ + { &__uuidof(c), (DWORD_PTR)(static_cast<c *>((_ComClass *)8))-8, COM_SIMPLEMAPENTRY }, + +#define COM_INTERFACE_ENTRY2(c, c2) \ + { &__uuidof(c), (DWORD_PTR)(static_cast<c *>(static_cast<c2 *>((_ComClass *)8)))-8, COM_SIMPLEMAPENTRY }, + +#define COM_INTERFACE_ENTRY_AGGREGATE(iid, pUnk) \ + { &iid, (DWORD_PTR)RT_UOFFSETOF(_ComClass, pUnk), _Delegate}, + +#define END_COM_MAP() \ + { NULL, 0, NULL} \ + }; \ + return _aInterfaces; \ + } \ + virtual ULONG STDMETHODCALLTYPE AddRef(void) throw() = 0; \ + virtual ULONG STDMETHODCALLTYPE Release(void) throw() = 0; \ + STDMETHOD(QueryInterface)(REFIID, void **) throw() = 0; + +struct _ATL_OBJMAP_ENTRY +{ + const CLSID *pclsid; + PFNCREATEINSTANCE pfnGetClassObject; + PFNCREATEINSTANCE pfnCreateInstance; + IUnknown *pCF; + DWORD dwRegister; +}; + +#define BEGIN_OBJECT_MAP(o) static ATL::_ATL_OBJMAP_ENTRY o[] = { +#define END_OBJECT_MAP() {NULL, NULL, NULL, NULL, 0}}; +#define OBJECT_ENTRY(clsid, c) {&clsid, c::_ClassFactoryCreatorClass::CreateInstance, c::_CreatorClass::CreateInstance, NULL, 0 }, + + +class CComCriticalSection +{ +public: + CComCriticalSection() throw() + { + memset(&m_CritSect, 0, sizeof(m_CritSect)); + } + ~CComCriticalSection() + { + } + HRESULT Lock() throw() + { + RTCritSectEnter(&m_CritSect); + return S_OK; + } + HRESULT Unlock() throw() + { + RTCritSectLeave(&m_CritSect); + return S_OK; + } + HRESULT Init() throw() + { + HRESULT hrc = S_OK; + if (RT_FAILURE(RTCritSectInit(&m_CritSect))) + hrc = E_FAIL; + return hrc; + } + + HRESULT Term() throw() + { + RTCritSectDelete(&m_CritSect); + return S_OK; + } + + RTCRITSECT m_CritSect; +}; + +template <class TLock> class CComCritSectLock +{ +public: + CComCritSectLock(CComCriticalSection &cs, bool fInitialLock = true) : + m_cs(cs), + m_fLocked(false) + { + if (fInitialLock) + { + HRESULT hrc = Lock(); + if (FAILED(hrc)) + throw hrc; + } + } + + ~CComCritSectLock() throw() + { + if (m_fLocked) + Unlock(); + } + + HRESULT Lock() + { + Assert(!m_fLocked); + HRESULT hrc = m_cs.Lock(); + if (FAILED(hrc)) + return hrc; + m_fLocked = true; + return S_OK; + } + + void Unlock() throw() + { + Assert(m_fLocked); + m_cs.Unlock(); + m_fLocked = false; + } + + +private: + TLock &m_cs; + bool m_fLocked; + + CComCritSectLock(const CComCritSectLock&) throw(); // Do not call. + CComCritSectLock &operator=(const CComCritSectLock &) throw(); // Do not call. +}; + +class CComFakeCriticalSection +{ +public: + HRESULT Lock() throw() + { + return S_OK; + } + HRESULT Unlock() throw() + { + return S_OK; + } + HRESULT Init() throw() + { + return S_OK; + } + HRESULT Term() throw() + { + return S_OK; + } +}; + +class CComAutoCriticalSection : public CComCriticalSection +{ +public: + CComAutoCriticalSection() + { + HRESULT hrc = CComCriticalSection::Init(); + if (FAILED(hrc)) + throw hrc; + } + ~CComAutoCriticalSection() throw() + { + CComCriticalSection::Term(); + } +private : + HRESULT Init() throw(); // Do not call. + HRESULT Term() throw(); // Do not call. +}; + +class CComAutoDeleteCriticalSection : public CComCriticalSection +{ +public: + CComAutoDeleteCriticalSection(): m_fInit(false) + { + } + + ~CComAutoDeleteCriticalSection() throw() + { + if (!m_fInit) + return; + m_fInit = false; + CComCriticalSection::Term(); + } + + HRESULT Init() throw() + { + Assert(!m_fInit); + HRESULT hrc = CComCriticalSection::Init(); + if (SUCCEEDED(hrc)) + m_fInit = true; + return hrc; + } + + HRESULT Lock() + { + Assert(m_fInit); + return CComCriticalSection::Lock(); + } + + HRESULT Unlock() + { + Assert(m_fInit); + return CComCriticalSection::Unlock(); + } + +private: + HRESULT Term() throw(); + bool m_fInit; +}; + + +class CComMultiThreadModelNoCS +{ +public: + static ULONG WINAPI Increment(LONG *pL) throw() + { + return InterlockedIncrement(pL); + } + static ULONG WINAPI Decrement(LONG *pL) throw() + { + return InterlockedDecrement(pL); + } + typedef CComFakeCriticalSection AutoCriticalSection; + typedef CComFakeCriticalSection AutoDeleteCriticalSection; + typedef CComMultiThreadModelNoCS ThreadModelNoCS; +}; + +class CComMultiThreadModel +{ +public: + static ULONG WINAPI Increment(LONG *pL) throw() + { + return InterlockedIncrement(pL); + } + static ULONG WINAPI Decrement(LONG *pL) throw() + { + return InterlockedDecrement(pL); + } + typedef CComAutoCriticalSection AutoCriticalSection; + typedef CComAutoDeleteCriticalSection AutoDeleteCriticalSection; + typedef CComMultiThreadModelNoCS ThreadModelNoCS; +}; + +class ATL_NO_VTABLE CAtlModule +{ +public: + static GUID m_LibID; + CComCriticalSection m_csStaticDataInitAndTypeInfo; + + CAtlModule() throw() + { + // One instance only per linking namespace! + AssertMsg(!_pAtlModule, ("CAtlModule: trying to create more than one instance per linking namespace\n")); + + fInit = false; + + m_cLock = 0; + m_pTermFuncs = NULL; + _pAtlModule = this; + + if (FAILED(m_csStaticDataInitAndTypeInfo.Init())) + { + AssertMsgFailed(("CAtlModule: failed to init critsect\n")); + return; + } + fInit = true; + } + + void Term() throw() + { + if (!fInit) + return; + + // Call all term functions. + if (m_pTermFuncs) + { + _ATL_TERMFUNC_ELEM *p = m_pTermFuncs; + _ATL_TERMFUNC_ELEM *pNext; + while (p) + { + p->pfn(p->pv); + pNext = p->pNext; + delete p; + p = pNext; + } + m_pTermFuncs = NULL; + } + m_csStaticDataInitAndTypeInfo.Term(); + fInit = false; + } + + virtual ~CAtlModule() throw() + { + Term(); + } + + virtual LONG Lock() throw() + { + return CComMultiThreadModel::Increment(&m_cLock); + } + + virtual LONG Unlock() throw() + { + return CComMultiThreadModel::Decrement(&m_cLock); + } + + virtual LONG GetLockCount() throw() + { + return m_cLock; + } + + HRESULT AddTermFunc(PFNATLTERMFUNC pfn, void *pv) + { + _ATL_TERMFUNC_ELEM *pNew = new(std::nothrow) _ATL_TERMFUNC_ELEM; + if (!pNew) + return E_OUTOFMEMORY; + pNew->pfn = pfn; + pNew->pv = pv; + CComCritSectLock<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo, false); + HRESULT hrc = lock.Lock(); + if (SUCCEEDED(hrc)) + { + pNew->pNext = m_pTermFuncs; + m_pTermFuncs = pNew; + } + else + { + delete pNew; + AssertMsgFailed(("CComModule::AddTermFunc: failed to lock critsect\n")); + } + return hrc; + } + +protected: + bool fInit; + LONG m_cLock; + _ATL_TERMFUNC_ELEM *m_pTermFuncs; +}; + +__declspec(selectany) GUID CAtlModule::m_LibID = {0x0, 0x0, 0x0, {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0} }; + +struct _ATL_COM_MODULE +{ + HINSTANCE m_hInstTypeLib; + CComCriticalSection m_csObjMap; +}; + +#ifndef _delayimp_h +extern "C" IMAGE_DOS_HEADER __ImageBase; +#endif + +class CAtlComModule : public _ATL_COM_MODULE +{ +public: + static bool m_fInitFailed; + CAtlComModule() throw() + { + m_hInstTypeLib = reinterpret_cast<HINSTANCE>(&__ImageBase); + + if (FAILED(m_csObjMap.Init())) + { + AssertMsgFailed(("CAtlComModule: critsect init failed\n")); + m_fInitFailed = true; + return; + } + } + + ~CAtlComModule() + { + Term(); + } + + void Term() + { + m_csObjMap.Term(); + } +}; + +__declspec(selectany) bool CAtlComModule::m_fInitFailed = false; +__declspec(selectany) CAtlComModule _AtlComModule; + +template <class T> class ATL_NO_VTABLE CAtlModuleT : public CAtlModule +{ +public: + CAtlModuleT() throw() + { + T::InitLibId(); + } + + static void InitLibId() throw() + { + } +}; + +/** + * + * This class not _not_ be statically instantiated as a global variable! It may + * use VBoxRT before it's initialized otherwise, messing up logging and whatnot. + * + * When possible create the instance inside the TrustedMain() or main() as a + * stack variable. In DLLs use 'new' to instantiate it in the DllMain function. + */ +class CComModule : public CAtlModuleT<CComModule> +{ +public: + CComModule() + { + // One instance only per linking namespace! + AssertMsg(!_pModule, ("CComModule: trying to create more than one instance per linking namespace\n")); + _pModule = this; + m_pObjMap = NULL; + } + + ~CComModule() + { + } + + _ATL_OBJMAP_ENTRY *m_pObjMap; + HRESULT Init(_ATL_OBJMAP_ENTRY *p, HINSTANCE h, const GUID *pLibID = NULL) throw() + { + RT_NOREF1(h); + + if (pLibID) + m_LibID = *pLibID; + + // Go over the object map to do some sanity checking, making things + // crash early if something is seriously busted. + _ATL_OBJMAP_ENTRY *pEntry; + if (p != (_ATL_OBJMAP_ENTRY *)-1) + { + m_pObjMap = p; + if (m_pObjMap) + { + pEntry = m_pObjMap; + while (pEntry->pclsid) + pEntry++; + } + } + return S_OK; + } + + void Term() throw() + { + _ATL_OBJMAP_ENTRY *pEntry; + if (m_pObjMap) + { + pEntry = m_pObjMap; + while (pEntry->pclsid) + { + if (pEntry->pCF) + pEntry->pCF->Release(); + pEntry->pCF = NULL; + pEntry++; + } + } + + CAtlModuleT<CComModule>::Term(); + } + + HRESULT GetClassObject(REFCLSID rclsid, REFIID riid, void **ppv) throw() + { + *ppv = NULL; + HRESULT hrc = S_OK; + + if (m_pObjMap) + { + const _ATL_OBJMAP_ENTRY *pEntry = m_pObjMap; + + while (pEntry->pclsid) + { + if (pEntry->pfnGetClassObject && rclsid == *pEntry->pclsid) + { + if (!pEntry->pCF) + { + CComCritSectLock<CComCriticalSection> lock(_AtlComModule.m_csObjMap, false); + hrc = lock.Lock(); + if (FAILED(hrc)) + { + AssertMsgFailed(("CComModule::GetClassObject: failed to lock critsect\n")); + break; + } + + if (!pEntry->pCF) + { + hrc = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (void **)&pEntry->pCF); + } + } + + if (pEntry->pCF) + { + hrc = pEntry->pCF->QueryInterface(riid, ppv); + } + break; + } + pEntry++; + } + } + + return hrc; + } + + // For EXE only: register all class factories with COM. + HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags) throw() + { + HRESULT hrc = S_OK; + _ATL_OBJMAP_ENTRY *pEntry; + if (m_pObjMap) + { + pEntry = m_pObjMap; + while (pEntry->pclsid && SUCCEEDED(hrc)) + { + if (pEntry->pfnGetClassObject) + { + IUnknown *p; + hrc = pEntry->pfnGetClassObject(pEntry->pfnCreateInstance, __uuidof(IUnknown), (void **)&p); + if (SUCCEEDED(hrc)) + hrc = CoRegisterClassObject(*(pEntry->pclsid), p, dwClsContext, dwFlags, &pEntry->dwRegister); + if (p) + p->Release(); + } + pEntry++; + } + } + return hrc; + } + // For EXE only: revoke all class factories with COM. + HRESULT RevokeClassObjects() throw() + { + HRESULT hrc = S_OK; + _ATL_OBJMAP_ENTRY *pEntry; + if (m_pObjMap != NULL) + { + pEntry = m_pObjMap; + while (pEntry->pclsid && SUCCEEDED(hrc)) + { + if (pEntry->dwRegister) + hrc = CoRevokeClassObject(pEntry->dwRegister); + pEntry++; + } + } + return hrc; + } +}; + + +template <class T> class CComCreator +{ +public: + static HRESULT WINAPI CreateInstance(void *pv, REFIID riid, void **ppv) + { + AssertReturn(ppv, E_POINTER); + *ppv = NULL; + HRESULT hrc = E_OUTOFMEMORY; + T *p = new(std::nothrow) T(pv); + if (p) + { + p->SetVoid(pv); + p->InternalFinalConstructAddRef(); + hrc = p->_AtlInitialConstruct(); + if (SUCCEEDED(hrc)) + hrc = p->FinalConstruct(); + p->InternalFinalConstructRelease(); + if (SUCCEEDED(hrc)) + hrc = p->QueryInterface(riid, ppv); + if (FAILED(hrc)) + delete p; + } + return hrc; + } +}; + +template <HRESULT hrc> class CComFailCreator +{ +public: + static HRESULT WINAPI CreateInstance(void *, REFIID, void **ppv) + { + AssertReturn(ppv, E_POINTER); + *ppv = NULL; + + return hrc; + } +}; + +template <class T1, class T2> class CComCreator2 +{ +public: + static HRESULT WINAPI CreateInstance(void *pv, REFIID riid, void **ppv) + { + AssertReturn(ppv, E_POINTER); + + return !pv ? T1::CreateInstance(NULL, riid, ppv) : T2::CreateInstance(pv, riid, ppv); + } +}; + +template <class Base> class CComObjectCached : public Base +{ +public: + CComObjectCached(void * = NULL) + { + } + virtual ~CComObjectCached() + { + // Catch refcount screwups by setting refcount to -(LONG_MAX/2). + m_iRef = -(LONG_MAX/2); + FinalRelease(); + } + STDMETHOD_(ULONG, AddRef)() throw() + { + // If you get errors about undefined InternalAddRef then Base does not + // derive from CComObjectRootEx. + ULONG l = InternalAddRef(); + if (l == 2) + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + _pAtlModule->Lock(); + } + return l; + } + STDMETHOD_(ULONG, Release)() throw() + { + // If you get errors about undefined InternalRelease then Base does not + // derive from CComObjectRootEx. + ULONG l = InternalRelease(); + if (l == 0) + delete this; + else if (l == 1) + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + _pAtlModule->Unlock(); + } + return l; + } + STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw() + { + // If you get errors about undefined _InternalQueryInterface then + // double check BEGIN_COM_MAP in the class definition. + return _InternalQueryInterface(iid, ppvObj); + } + static HRESULT WINAPI CreateInstance(CComObjectCached<Base> **pp) throw() + { + AssertReturn(pp, E_POINTER); + *pp = NULL; + + HRESULT hrc = E_OUTOFMEMORY; + CComObjectCached<Base> *p = new(std::nothrow) CComObjectCached<Base>(); + if (p) + { + p->SetVoid(NULL); + p->InternalFinalConstructAddRef(); + hrc = p->_AtlInitialConstruct(); + if (SUCCEEDED(hrc)) + hrc = p->FinalConstruct(); + p->InternalFinalConstructRelease(); + if (FAILED(hrc)) + delete p; + else + *pp = p; + } + return hrc; + } +}; + +template <class Base> class CComObjectNoLock : public Base +{ +public: + CComObjectNoLock(void * = NULL) + { + } + virtual ~CComObjectNoLock() + { + // Catch refcount screwups by setting refcount to -(LONG_MAX/2). + m_iRef = -(LONG_MAX/2); + FinalRelease(); + } + STDMETHOD_(ULONG, AddRef)() throw() + { + // If you get errors about undefined InternalAddRef then Base does not + // derive from CComObjectRootEx. + return InternalAddRef(); + } + STDMETHOD_(ULONG, Release)() throw() + { + // If you get errors about undefined InternalRelease then Base does not + // derive from CComObjectRootEx. + ULONG l = InternalRelease(); + if (l == 0) + delete this; + return l; + } + STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw() + { + // If you get errors about undefined _InternalQueryInterface then + // double check BEGIN_COM_MAP in the class definition. + return _InternalQueryInterface(iid, ppvObj); + } +}; + +class CComTypeInfoHolder +{ + /** @todo implement type info caching, making stuff more efficient - would we benefit? */ +public: + const GUID *m_pGUID; + const GUID *m_pLibID; + WORD m_iMajor; + WORD m_iMinor; + ITypeInfo *m_pTInfo; + + HRESULT GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) + { + if (iTInfo != 0) + return DISP_E_BADINDEX; + return GetTI(lcid, ppTInfo); + } + HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *pwszNames, UINT cNames, LCID lcid, DISPID *pDispID) + { + RT_NOREF1(riid); /* should be IID_NULL */ + HRESULT hrc = FetchTI(lcid); + if (m_pTInfo) + hrc = m_pTInfo->GetIDsOfNames(pwszNames, cNames, pDispID); + return hrc; + } + HRESULT Invoke(IDispatch *p, DISPID DispID, REFIID riid, LCID lcid, WORD iFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) + { + RT_NOREF1(riid); /* should be IID_NULL */ + HRESULT hrc = FetchTI(lcid); + if (m_pTInfo) + hrc = m_pTInfo->Invoke(p, DispID, iFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + return hrc; + } +private: + static void __stdcall Cleanup(void *pv) + { + AssertReturnVoid(pv); + CComTypeInfoHolder *p = (CComTypeInfoHolder *)pv; + if (p->m_pTInfo != NULL) + p->m_pTInfo->Release(); + p->m_pTInfo = NULL; + } + + HRESULT GetTI(LCID lcid) + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + Assert(m_pLibID && m_pGUID); + if (m_pTInfo) + return S_OK; + CComCritSectLock<CComCriticalSection> lock(_pAtlModule->m_csStaticDataInitAndTypeInfo, false); + HRESULT hrc = lock.Lock(); + ITypeLib *pTypeLib = NULL; + Assert(*m_pLibID != GUID_NULL); + hrc = LoadRegTypeLib(*m_pLibID, m_iMajor, m_iMinor, lcid, &pTypeLib); + if (SUCCEEDED(hrc)) + { + ITypeInfo *pTypeInfo; + hrc = pTypeLib->GetTypeInfoOfGuid(*m_pGUID, &pTypeInfo); + if (SUCCEEDED(hrc)) + { + ITypeInfo2 *pTypeInfo2; + if (SUCCEEDED(pTypeInfo->QueryInterface(__uuidof(ITypeInfo2), (void **)&pTypeInfo2))) + { + pTypeInfo->Release(); + pTypeInfo = pTypeInfo2; + } + m_pTInfo = pTypeInfo; + _pAtlModule->AddTermFunc(Cleanup, (void *)this); + } + pTypeLib->Release(); + } + return hrc; + } + HRESULT GetTI(LCID lcid, ITypeInfo **ppTInfo) + { + AssertReturn(ppTInfo, E_POINTER); + HRESULT hrc = S_OK; + if (!m_pTInfo) + hrc = GetTI(lcid); + if (m_pTInfo) + { + m_pTInfo->AddRef(); + hrc = S_OK; + } + *ppTInfo = m_pTInfo; + return hrc; + } + HRESULT FetchTI(LCID lcid) + { + if (!m_pTInfo) + return GetTI(lcid); + return S_OK; + } +}; + +template <class ThreadModel> class CComObjectRootEx +{ +public: + typedef ThreadModel _ThreadModel; + CComObjectRootEx() + { + m_iRef = 0L; + } + ~CComObjectRootEx() + { + } + ULONG InternalAddRef() + { + Assert(m_iRef != -1L); + return ThreadModel::Increment(&m_iRef); + } + ULONG InternalRelease() + { +#ifdef VBOX_STRICT + LONG c = ThreadModel::Decrement(&m_iRef); + AssertMsg(c >= -(LONG_MAX / 2), /* See ~CComObjectNoLock, ~CComObject & ~CComAggObject. */ + ("Release called on object which has been already destroyed!\n")); + return c; +#else + return ThreadModel::Decrement(&m_iRef); +#endif + } + ULONG OuterAddRef() + { + return m_pOuterUnknown->AddRef(); + } + ULONG OuterRelease() + { + return m_pOuterUnknown->Release(); + } + HRESULT OuterQueryInterface(REFIID iid, void **ppvObject) + { + return m_pOuterUnknown->QueryInterface(iid, ppvObject); + } + HRESULT _AtlInitialConstruct() + { + return m_CritSect.Init(); + } + void Lock() + { + m_CritSect.Lock(); + } + void Unlock() + { + m_CritSect.Unlock(); + } + void SetVoid(void *) + { + } + void InternalFinalConstructAddRef() + { + } + void InternalFinalConstructRelease() + { + Assert(m_iRef == 0); + } + HRESULT FinalConstruct() + { + return S_OK; + } + void FinalRelease() + { + } + static HRESULT WINAPI InternalQueryInterface(void *pThis, const _ATL_INTMAP_ENTRY *pEntries, REFIID iid, void **ppvObj) + { + AssertReturn(pThis, E_INVALIDARG); + AssertReturn(pEntries, E_INVALIDARG); + AssertReturn(ppvObj, E_POINTER); + *ppvObj = NULL; + if (iid == IID_IUnknown) + { + // For IUnknown use first interface, must be simple map entry. + Assert(pEntries->pFunc == COM_SIMPLEMAPENTRY); + IUnknown *pObj = (IUnknown *)((INT_PTR)pThis + pEntries->dw); + pObj->AddRef(); + *ppvObj = pObj; + return S_OK; + } + while (pEntries->pFunc) + { + if (iid == *pEntries->piid) + { + if (pEntries->pFunc == COM_SIMPLEMAPENTRY) + { + IUnknown *pObj = (IUnknown *)((INT_PTR)pThis + pEntries->dw); + pObj->AddRef(); + *ppvObj = pObj; + return S_OK; + } + else + return pEntries->pFunc(pThis, iid, ppvObj, pEntries->dw); + } + pEntries++; + } + return E_NOINTERFACE; + } + static HRESULT WINAPI _Delegate(void *pThis, REFIID iid, void **ppvObj, DWORD_PTR dw) + { + AssertPtrReturn(pThis, E_NOINTERFACE); + IUnknown *pObj = *(IUnknown **)((DWORD_PTR)pThis + dw); + // If this assertion fails then the object has a delegation with a NULL + // object pointer, which is highly unusual often means that the pointer + // was not set up correctly. Check the COM interface map of the class + // for bugs with initializing. + AssertPtrReturn(pObj, E_NOINTERFACE); + return pObj->QueryInterface(iid, ppvObj); + } + + union + { + LONG m_iRef; + IUnknown *m_pOuterUnknown; + }; +private: + typename ThreadModel::AutoDeleteCriticalSection m_CritSect; +}; + +template <class Base> class CComObject : public Base +{ +public: + CComObject(void * = NULL) throw() + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + _pAtlModule->Lock(); + } + virtual ~CComObject() throw() + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + // Catch refcount screwups by setting refcount to -(LONG_MAX/2). + m_iRef = -(LONG_MAX/2); + FinalRelease(); + _pAtlModule->Unlock(); + } + STDMETHOD_(ULONG, AddRef)() + { + // If you get errors about undefined InternalAddRef then Base does not + // derive from CComObjectRootEx. + return InternalAddRef(); + } + STDMETHOD_(ULONG, Release)() + { + // If you get errors about undefined InternalRelease then Base does not + // derive from CComObjectRootEx. + ULONG l = InternalRelease(); + if (l == 0) + delete this; + return l; + } + STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw() + { + // If you get errors about undefined _InternalQueryInterface then + // double check BEGIN_COM_MAP in the class definition. + return _InternalQueryInterface(iid, ppvObj); + } + + static HRESULT WINAPI CreateInstance(CComObject<Base> **pp) throw() + { + AssertReturn(pp, E_POINTER); + *pp = NULL; + + HRESULT hrc = E_OUTOFMEMORY; + CComObject<Base> *p = new(std::nothrow) CComObject<Base>(); + if (p) + { + p->InternalFinalConstructAddRef(); + hrc = p->_AtlInitialConstruct(); + if (SUCCEEDED(hrc)) + hrc = p->FinalConstruct(); + p->InternalFinalConstructRelease(); + if (FAILED(hrc)) + { + delete p; + p = NULL; + } + } + *pp = p; + return hrc; + } +}; + +template <class T, const IID *piid, const GUID *pLibID, WORD iMajor = 1, WORD iMinor = 0> class ATL_NO_VTABLE IDispatchImpl : public T +{ +public: + // IDispatch + STDMETHOD(GetTypeInfoCount)(UINT *pcTInfo) + { + if (!pcTInfo) + return E_POINTER; + *pcTInfo = 1; + return S_OK; + } + STDMETHOD(GetTypeInfo)(UINT cTInfo, LCID lcid, ITypeInfo **ppTInfo) + { + return tih.GetTypeInfo(cTInfo, lcid, ppTInfo); + } + STDMETHOD(GetIDsOfNames)(REFIID riid, LPOLESTR *pwszNames, UINT cNames, LCID lcid, DISPID *pDispID) + { + return tih.GetIDsOfNames(riid, pwszNames, cNames, lcid, pDispID); + } + STDMETHOD(Invoke)(DISPID DispID, REFIID riid, LCID lcid, WORD iFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) + { + return tih.Invoke((IDispatch *)this, DispID, riid, lcid, iFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + } +protected: + static CComTypeInfoHolder tih; + static HRESULT GetTI(LCID lcid, ITypeInfo **ppTInfo) + { + return tih.GetTI(lcid, ppTInfo); + } +}; + +template <class T, const IID *piid, const GUID *pLibID, WORD iMajor, WORD iMinor> CComTypeInfoHolder IDispatchImpl<T, piid, pLibID, iMajor, iMinor>::tih = { piid, pLibID, iMajor, iMinor, NULL }; + + +template <class Base> class CComContainedObject : public Base +{ +public: + CComContainedObject(void *pv) + { + m_pOuterUnknown = (IUnknown *)pv; + } + + STDMETHOD_(ULONG, AddRef)() throw() + { + return OuterAddRef(); + } + STDMETHOD_(ULONG, Release)() throw() + { + return OuterRelease(); + } + STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw() + { + return OuterQueryInterface(iid, ppvObj); + } +}; + +template <class Aggregated> class CComAggObject : + public IUnknown, + public CComObjectRootEx<typename Aggregated::_ThreadModel::ThreadModelNoCS> +{ +public: + CComAggObject(void *pv) : + m_Aggregated(pv) + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + _pAtlModule->Lock(); + } + virtual ~CComAggObject() + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + // Catch refcount screwups by setting refcount to -(LONG_MAX/2). + m_iRef = -(LONG_MAX/2); + FinalRelease(); + _pAtlModule->Unlock(); + } + HRESULT _AtlInitialConstruct() + { + HRESULT hrc = m_Aggregated._AtlInitialConstruct(); + if (SUCCEEDED(hrc)) + { + hrc = CComObjectRootEx<typename Aggregated::_ThreadModel::ThreadModelNoCS>::_AtlInitialConstruct(); + } + return hrc; + } + HRESULT FinalConstruct() + { + CComObjectRootEx<Aggregated::_ThreadModel::ThreadModelNoCS>::FinalConstruct(); + return m_Aggregated.FinalConstruct(); + } + void FinalRelease() + { + CComObjectRootEx<Aggregated::_ThreadModel::ThreadModelNoCS>::FinalRelease(); + m_Aggregated.FinalRelease(); + } + + STDMETHOD_(ULONG, AddRef)() + { + return InternalAddRef(); + } + STDMETHOD_(ULONG, Release)() + { + ULONG l = InternalRelease(); + if (l == 0) + delete this; + return l; + } + STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) + { + AssertReturn(ppvObj, E_POINTER); + *ppvObj = NULL; + + HRESULT hrc = S_OK; + if (iid == __uuidof(IUnknown)) + { + *ppvObj = (void *)(IUnknown *)this; + AddRef(); + } + else + hrc = m_Aggregated._InternalQueryInterface(iid, ppvObj); + return hrc; + } + static HRESULT WINAPI CreateInstance(LPUNKNOWN pUnkOuter, CComAggObject<Aggregated> **pp) + { + AssertReturn(pp, E_POINTER); + *pp = NULL; + + HRESULT hrc = E_OUTOFMEMORY; + CComAggObject<Aggregated> *p = new(std::nothrow) CComAggObject<Aggregated>(pUnkOuter); + if (p) + { + p->SetVoid(NULL); + p->InternalFinalConstructAddRef(); + hrc = p->_AtlInitialConstruct(); + if (SUCCEEDED(hrc)) + hrc = p->FinalConstruct(); + p->InternalFinalConstructRelease(); + if (FAILED(hrc)) + delete p; + else + *pp = p; + } + return hrc; + } + + CComContainedObject<Aggregated> m_Aggregated; +}; + +class CComClassFactory: + public IClassFactory, + public CComObjectRootEx<CComMultiThreadModel> +{ +public: + BEGIN_COM_MAP(CComClassFactory) + COM_INTERFACE_ENTRY(IClassFactory) + END_COM_MAP() + + virtual ~CComClassFactory() + { + } + + // IClassFactory + STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj) + { + Assert(m_pfnCreateInstance); + HRESULT hrc = E_POINTER; + if (ppvObj) + { + *ppvObj = NULL; + if (pUnkOuter && riid != __uuidof(IUnknown)) + { + AssertMsgFailed(("CComClassFactory: cannot create an aggregated object other than IUnknown\n")); + hrc = CLASS_E_NOAGGREGATION; + } + else + hrc = m_pfnCreateInstance(pUnkOuter, riid, ppvObj); + } + return hrc; + } + + STDMETHOD(LockServer)(BOOL fLock) + { + AssertMsg(_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n")); + if (fLock) + _pAtlModule->Lock(); + else + _pAtlModule->Unlock(); + return S_OK; + } + + // Set creator for use by the factory. + void SetVoid(void *pv) + { + m_pfnCreateInstance = (PFNCREATEINSTANCE)pv; + } + + PFNCREATEINSTANCE m_pfnCreateInstance; +}; + +template <class T> class CComClassFactorySingleton : public CComClassFactory +{ +public: + CComClassFactorySingleton() : + m_hrc(S_OK), + m_pObj(NULL) + { + } + virtual ~CComClassFactorySingleton() + { + if (m_pObj) + m_pObj->Release(); + } + // IClassFactory + STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **pvObj) + { + HRESULT hrc = E_POINTER; + if (ppvObj) + { + *ppvObj = NULL; + // Singleton factories do not support aggregation. + AssertReturn(!pUnkOuter, CLASS_E_NOAGGREGATION); + + // Test if singleton is already created. Do it outside the lock, + // relying on atomic checks. Remember the inherent race! + if (SUCCEEDED(m_hrc) && !m_pObj) + { + Lock(); + // Make sure that the module is in use, otherwise the + // module can terminate while we're creating a new + // instance, which leads to strange errors. + LockServer(true); + __try + { + // Repeat above test to avoid races when multiple threads + // want to create a singleton simultaneously. + if (SUCCEEDED(m_hrc) && !m_pObj) + { + CComObjectCached<T> *p; + m_hrc = CComObjectCached<T>::CreateInstance(&p); + if (SUCCEEDED(m_hrc)) + { + m_hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj); + if (FAILED(m_hrc)) + { + delete p; + } + } + } + } + __finally + { + Unlock(); + LockServer(false); + } + } + if (SUCCEEDED(m_hrc)) + { + hrc = m_pObj->QueryInterface(riid, ppvObj); + } + else + { + hrc = m_hrc; + } + } + return hrc; + } + HRESULT m_hrc; + IUnknown *m_pObj; +}; + + +template <class T, const CLSID *pClsID = &CLSID_NULL> class CComCoClass +{ +public: + DECLARE_CLASSFACTORY() + DECLARE_AGGREGATABLE(T) + static const CLSID& WINAPI GetObjectCLSID() + { + return *pClsID; + } + template <class Q> + static HRESULT CreateInstance(Q **pp) + { + return T::_CreatorClass::CreateInstance(NULL, __uuidof(Q), (void **)pp); + } +}; + +} /* namespace ATL */ + +#endif /* !VBOX_INCLUDED_com_microatl_h */ + diff --git a/include/VBox/com/mtlist.h b/include/VBox/com/mtlist.h new file mode 100644 index 00000000..e4cd952b --- /dev/null +++ b/include/VBox/com/mtlist.h @@ -0,0 +1,210 @@ +/* $Id: mtlist.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - Thread-safe list classes declaration. + */ + +/* + * Copyright (C) 2011-2019 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_mtlist_h +#define VBOX_INCLUDED_com_mtlist_h +#ifndef RT_WITHOUT_PRAGMA_ONCE +# pragma once +#endif + +#include <VBox/com/ptr.h> +#include <VBox/com/string.h> +#include <VBox/com/array.h> +#include <iprt/cpp/mtlist.h> + + +/** @defgroup grp_com_mtlist Thread-safe List Classes + * @ingroup grp_com + * @{ + */ + +/** + * Specialized thread-safe list class for using with com::ComPtr<C> + * + * @note: This is necessary cause ComPtr<IFACE> has a size of 8. + */ +template <typename C> +class RTCMTList< ComPtr<C> >: public RTCListBase< ComPtr<C>, ComPtr<C>*, true> +{ + /* Traits */ + typedef ComPtr<C> T; + typedef T *ITYPE; + static const bool MT = true; + typedef RTCListBase<T, ITYPE, MT> BASE; + +public: + /** + * Creates a new list. + * + * This preallocates @a cCapacity elements within the list. + * + * @param cCapacity The initial capacity the list has. + * @throws std::bad_alloc + */ + RTCMTList(size_t cCapacity = BASE::kDefaultCapacity) + : BASE(cCapacity) {} + + /* Define our own new and delete. */ + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** + * Specialized thread-safe list class for using with com::ComObjPtr<C> + * + * @note: This is necessary cause ComObjPtr<IFACE> has a size of 8. + */ +template <typename C> +class RTCMTList< ComObjPtr<C> >: public RTCListBase< ComObjPtr<C>, ComObjPtr<C>*, true> +{ + /* Traits */ + typedef ComObjPtr<C> T; + typedef T *ITYPE; + static const bool MT = true; + typedef RTCListBase<T, ITYPE, MT> BASE; + +public: + /** + * Creates a new list. + * + * This preallocates @a cCapacity elements within the list. + * + * @param cCapacity The initial capacity the list has. + * @throws std::bad_alloc + */ + RTCMTList(size_t cCapacity = BASE::kDefaultCapacity) + : BASE(cCapacity) {} + + /* Define our own new and delete. */ + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** + * Specialized thread-safe list class for using with com::Utf8Str. + * + * The class offers methods for importing com::SafeArray's of com::Bstr's. + */ +template <> +class RTCMTList<com::Utf8Str>: public RTCListBase<com::Utf8Str, com::Utf8Str *, true> +{ + /* Traits */ + typedef com::Utf8Str T; + typedef T *ITYPE; + static const bool MT = true; + typedef RTCListBase<T, ITYPE, MT> BASE; + +public: + /** + * Creates a new list. + * + * This preallocates @a cCapacity elements within the list. + * + * @param cCapacity The initial capacity the list has. + * @throws std::bad_alloc + */ + RTCMTList(size_t cCapacity = BASE::kDefaultCapacity) + : BASE(cCapacity) {} + + /** + * Creates a copy of another list. + * + * The other list will be fully copied and the capacity will be the same as + * the size of the other list. The com::Bstr's are silently converted to + * com::Utf8Str's. + * + * @param other The list to copy. + * @throws std::bad_alloc + */ + RTCMTList(ComSafeArrayIn(IN_BSTR, other)) + { + com::SafeArray<IN_BSTR> sfaOther(ComSafeArrayInArg(other)); + size_t const cElementsOther = sfaOther.size(); + resizeArray(cElementsOther); + m_cElements = cElementsOther; + for (size_t i = 0; i < cElementsOther; ++i) + RTCListHelper<T, ITYPE>::set(m_pArray, i, T(sfaOther[i])); + } + + /** + * Creates a copy of another list. + * + * The other list will be fully copied and the capacity will be the same as + * the size of the other list. The com::Bstr's are silently converted to + * com::Utf8Str's. + * + * @param other The list to copy. + * @throws std::bad_alloc + */ + RTCMTList(const com::SafeArray<IN_BSTR> &other) + : BASE(other.size()) + { + for (size_t i = 0; i < m_cElements; ++i) + RTCListHelper<T, ITYPE>::set(m_pArray, i, T(other[i])); + } + + /** + * Copy the items of the other list into this list. All previous items of + * this list are deleted. + * + * @param other The list to copy. + * @return a reference to this list. + * @throws std::bad_alloc + */ + RTCListBase<T, ITYPE, MT> &operator=(const com::SafeArray<IN_BSTR> &other) + { + m_guard.enterWrite(); + /* Values cleanup */ + RTCListHelper<T, ITYPE>::eraseRange(m_pArray, 0, m_cElements); + /* Copy */ + if (other.size() != m_cCapacity) + resizeArrayNoErase(other.size()); + m_cElements = other.size(); + for (size_t i = 0; i < other.size(); ++i) + RTCListHelper<T, ITYPE>::set(m_pArray, i, T(other[i])); + m_guard.leaveWrite(); + + return *this; + } + + /** + * Implicit conversion to a RTCString list. + * + * This allows the usage of the RTCString::join method with this list type. + * + * @return a converted const reference to this list. + */ + operator const RTCMTList<RTCString, RTCString*>&() + { + return *reinterpret_cast<RTCMTList<RTCString, RTCString*> *>(this); + } + + /* Define our own new and delete. */ + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_mtlist_h */ + diff --git a/include/VBox/com/ptr.h b/include/VBox/com/ptr.h new file mode 100644 index 00000000..aef02508 --- /dev/null +++ b/include/VBox/com/ptr.h @@ -0,0 +1,505 @@ +/** @file + * MS COM / XPCOM Abstraction Layer - Smart COM pointer classes declaration. + */ + +/* + * Copyright (C) 2006-2019 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 <nsISupportsUtils.h> +#endif /* VBOX_WITH_XPCOM */ + +#include <VBox/com/defs.h> + + +/** @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. + */ + HRESULT createObject() + { + HRESULT rc; +#ifndef VBOX_WITH_XPCOM +# ifdef VBOX_COM_OUTOFPROC_MODULE + ATL::CComObjectNoLock<T> *obj = new ATL::CComObjectNoLock<T>(); + if (obj) + { + obj->InternalFinalConstructAddRef(); + rc = obj->FinalConstruct(); + obj->InternalFinalConstructRelease(); + } + else + rc = E_OUTOFMEMORY; +# else + ATL::CComObject<T> *obj = NULL; + rc = ATL::CComObject<T>::CreateInstance(&obj); +# endif +#else /* VBOX_WITH_XPCOM */ + ATL::CComObject<T> *obj = new ATL::CComObject<T>(); + if (obj) + rc = obj->FinalConstruct(); + else + rc = E_OUTOFMEMORY; +#endif /* VBOX_WITH_XPCOM */ + *this = obj; + return rc; + } +}; + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_ptr_h */ + diff --git a/include/VBox/com/string.h b/include/VBox/com/string.h new file mode 100644 index 00000000..17c3861e --- /dev/null +++ b/include/VBox/com/string.h @@ -0,0 +1,903 @@ +/* $Id: string.h $ */ +/** @file + * MS COM / XPCOM Abstraction Layer - Smart string classes declaration. + */ + +/* + * Copyright (C) 2006-2019 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_string_h +#define VBOX_INCLUDED_com_string_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 + +#if defined(VBOX_WITH_XPCOM) +# include <nsMemory.h> +#endif + +#include "VBox/com/defs.h" +#include "VBox/com/assert.h" + +#include <iprt/mem.h> +#include <iprt/utf16.h> +#include <iprt/cpp/ministring.h> + + +/** @defgroup grp_com_str Smart String Classes + * @ingroup grp_com + * @{ + */ + +namespace com +{ + +class Utf8Str; + +// global constant in glue/string.cpp that represents an empty BSTR +extern const BSTR g_bstrEmpty; + +/** + * String class used universally in Main for COM-style Utf-16 strings. + * + * Unfortunately COM on Windows uses UTF-16 everywhere, requiring conversions + * back and forth since most of VirtualBox and our libraries use UTF-8. + * + * To make things more obscure, on Windows, a COM-style BSTR is not just a + * pointer to a null-terminated wide character array, but the four bytes (32 + * bits) BEFORE the memory that the pointer points to are a length DWORD. One + * must therefore avoid pointer arithmetic and always use SysAllocString and + * the like to deal with BSTR pointers, which manage that DWORD correctly. + * + * For platforms other than Windows, we provide our own versions of the Sys* + * functions in Main/xpcom/helpers.cpp which do NOT use length prefixes though + * to be compatible with how XPCOM allocates string parameters to public + * functions. + * + * The Bstr class hides all this handling behind a std::string-like interface + * and also provides automatic conversions to RTCString and Utf8Str instances. + * + * The one advantage of using the SysString* routines is that this makes it + * possible to use it as a type of member variables of COM/XPCOM components and + * pass their values to callers through component methods' output parameters + * using the #cloneTo() operation. Also, the class can adopt (take ownership + * of) string buffers returned in output parameters of COM methods using the + * #asOutParam() operation and correctly free them afterwards. + * + * Starting with VirtualBox 3.2, like Utf8Str, Bstr no longer differentiates + * between NULL strings and empty strings. In other words, Bstr("") and + * Bstr(NULL) behave the same. In both cases, Bstr allocates no memory, + * reports a zero length and zero allocated bytes for both, and returns an + * empty C wide string from raw(). + * + * @note All Bstr methods ASSUMES valid UTF-16 or UTF-8 input strings. + * The VirtualBox policy in this regard is to validate strings coming + * from external sources before passing them to Bstr or Utf8Str. + */ +class Bstr +{ +public: + + Bstr() + : m_bstr(NULL) + { } + + Bstr(const Bstr &that) + { + copyFrom((const OLECHAR *)that.m_bstr); + } + + Bstr(CBSTR that) + { + copyFrom((const OLECHAR *)that); + } + +#if defined(VBOX_WITH_XPCOM) + Bstr(const wchar_t *that) + { + AssertCompile(sizeof(wchar_t) == sizeof(OLECHAR)); + copyFrom((const OLECHAR *)that); + } +#endif + + Bstr(const RTCString &that) + { + copyFrom(that.c_str()); + } + + Bstr(const char *that) + { + copyFrom(that); + } + + Bstr(const char *a_pThat, size_t a_cchMax) + { + copyFromN(a_pThat, a_cchMax); + } + + ~Bstr() + { + setNull(); + } + + Bstr& operator=(const Bstr &that) + { + cleanup(); + copyFrom((const OLECHAR *)that.m_bstr); + return *this; + } + + Bstr& operator=(CBSTR that) + { + cleanup(); + copyFrom((const OLECHAR *)that); + return *this; + } + +#if defined(VBOX_WITH_XPCOM) + Bstr& operator=(const wchar_t *that) + { + cleanup(); + copyFrom((const OLECHAR *)that); + return *this; + } +#endif + + Bstr& setNull() + { + cleanup(); + return *this; + } + +#ifdef _MSC_VER +# if _MSC_VER >= 1400 + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +# endif +#else + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +#endif + + /** Case sensitivity selector. */ + enum CaseSensitivity + { + CaseSensitive, + CaseInsensitive + }; + + /** + * Compares the member string to str. + * @param str + * @param cs Whether comparison should be case-sensitive. + * @return + */ + int compare(CBSTR str, CaseSensitivity cs = CaseSensitive) const + { + if (cs == CaseSensitive) + return ::RTUtf16Cmp((PRTUTF16)m_bstr, (PRTUTF16)str); + return ::RTUtf16LocaleICmp((PRTUTF16)m_bstr, (PRTUTF16)str); + } + + int compare(BSTR str, CaseSensitivity cs = CaseSensitive) const + { + return compare((CBSTR)str, cs); + } + + int compare(const Bstr &that, CaseSensitivity cs = CaseSensitive) const + { + return compare(that.m_bstr, cs); + } + + bool operator==(const Bstr &that) const { return !compare(that.m_bstr); } + bool operator==(CBSTR that) const { return !compare(that); } + bool operator==(BSTR that) const { return !compare(that); } + bool operator!=(const Bstr &that) const { return !!compare(that.m_bstr); } + bool operator!=(CBSTR that) const { return !!compare(that); } + bool operator!=(BSTR that) const { return !!compare(that); } + bool operator<(const Bstr &that) const { return compare(that.m_bstr) < 0; } + bool operator<(CBSTR that) const { return compare(that) < 0; } + bool operator<(BSTR that) const { return compare(that) < 0; } + bool operator<=(const Bstr &that) const { return compare(that.m_bstr) <= 0; } + bool operator<=(CBSTR that) const { return compare(that) <= 0; } + bool operator<=(BSTR that) const { return compare(that) <= 0; } + bool operator>(const Bstr &that) const { return compare(that.m_bstr) > 0; } + bool operator>(CBSTR that) const { return compare(that) > 0; } + bool operator>(BSTR that) const { return compare(that) > 0; } + bool operator>=(const Bstr &that) const { return compare(that.m_bstr) >= 0; } + bool operator>=(CBSTR that) const { return compare(that) >= 0; } + bool operator>=(BSTR that) const { return compare(that) >= 0; } + + /** + * Compares this string to an UTF-8 C style string. + * + * @retval 0 if equal + * @retval -1 if this string is smaller than the UTF-8 one. + * @retval 1 if the UTF-8 string is smaller than this. + * + * @param a_pszRight The string to compare with. + * @param a_enmCase Whether comparison should be case-sensitive. + */ + int compareUtf8(const char *a_pszRight, CaseSensitivity a_enmCase = CaseSensitive) const; + + /** Java style compare method. + * @returns true if @a a_pszRight equals this string. + * @param a_pszRight The (UTF-8) string to compare with. */ + bool equals(const char *a_pszRight) const { return compareUtf8(a_pszRight, CaseSensitive) == 0; } + + /** Java style case-insensitive compare method. + * @returns true if @a a_pszRight equals this string. + * @param a_pszRight The (UTF-8) string to compare with. */ + bool equalsIgnoreCase(const char *a_pszRight) const { return compareUtf8(a_pszRight, CaseInsensitive) == 0; } + + /** Java style compare method. + * @returns true if @a a_rThat equals this string. + * @param a_rThat The other Bstr instance to compare with. */ + bool equals(const Bstr &a_rThat) const { return compare(a_rThat.m_bstr, CaseSensitive) == 0; } + /** Java style case-insensitive compare method. + * @returns true if @a a_rThat equals this string. + * @param a_rThat The other Bstr instance to compare with. */ + bool equalsIgnoreCase(const Bstr &a_rThat) const { return compare(a_rThat.m_bstr, CaseInsensitive) == 0; } + + /** Java style compare method. + * @returns true if @a a_pThat equals this string. + * @param a_pThat The native const BSTR to compare with. */ + bool equals(CBSTR a_pThat) const { return compare(a_pThat, CaseSensitive) == 0; } + /** Java style case-insensitive compare method. + * @returns true if @a a_pThat equals this string. + * @param a_pThat The native const BSTR to compare with. */ + bool equalsIgnoreCase(CBSTR a_pThat) const { return compare(a_pThat, CaseInsensitive) == 0; } + + /** Java style compare method. + * @returns true if @a a_pThat equals this string. + * @param a_pThat The native BSTR to compare with. */ + bool equals(BSTR a_pThat) const { return compare(a_pThat, CaseSensitive) == 0; } + /** Java style case-insensitive compare method. + * @returns true if @a a_pThat equals this string. + * @param a_pThat The native BSTR to compare with. */ + bool equalsIgnoreCase(BSTR a_pThat) const { return compare(a_pThat, CaseInsensitive) == 0; } + + /** + * Returns true if the member string has no length. + * This is true for instances created from both NULL and "" input strings. + * + * @note Always use this method to check if an instance is empty. Do not + * use length() because that may need to run through the entire string + * (Bstr does not cache string lengths). + */ + bool isEmpty() const { return m_bstr == NULL || *m_bstr == 0; } + + /** + * Returns true if the member string has a length of one or more. + * + * @returns true if not empty, false if empty (NULL or ""). + */ + bool isNotEmpty() const { return m_bstr != NULL && *m_bstr != 0; } + + size_t length() const { return isEmpty() ? 0 : ::RTUtf16Len((PRTUTF16)m_bstr); } + +#if defined(VBOX_WITH_XPCOM) + /** + * Returns a pointer to the raw member UTF-16 string. If the member string is empty, + * returns a pointer to a global variable containing an empty BSTR with a proper zero + * length prefix so that Windows is happy. + */ + CBSTR raw() const + { + if (m_bstr) + return m_bstr; + + return g_bstrEmpty; + } +#else + /** + * Windows-only hack, as the automatically generated headers use BSTR. + * So if we don't want to cast like crazy we have to be more loose than + * on XPCOM. + * + * Returns a pointer to the raw member UTF-16 string. If the member string is empty, + * returns a pointer to a global variable containing an empty BSTR with a proper zero + * length prefix so that Windows is happy. + */ + BSTR raw() const + { + if (m_bstr) + return m_bstr; + + return g_bstrEmpty; + } +#endif + + /** + * Returns a non-const raw pointer that allows to modify the string directly. + * As opposed to raw(), this DOES return NULL if the member string is empty + * because we cannot return a mutable pointer to the global variable with the + * empty string. + * + * @warning + * Be sure not to modify data beyond the allocated memory! The + * guaranteed size of the allocated memory is at least #length() + * bytes after creation and after every assignment operation. + */ + BSTR mutableRaw() { return m_bstr; } + + /** + * Intended to assign copies of instances to |BSTR| out parameters from + * within the interface method. Transfers the ownership of the duplicated + * string to the caller. + * + * If the member string is empty, this allocates an empty BSTR in *pstr + * (i.e. makes it point to a new buffer with a null byte). + * + * @deprecated Use cloneToEx instead to avoid throwing exceptions. + */ + void cloneTo(BSTR *pstr) const + { + if (pstr) + { + *pstr = ::SysAllocString((const OLECHAR *)raw()); // raw() returns a pointer to "" if empty +#ifdef RT_EXCEPTIONS_ENABLED + if (!*pstr) + throw std::bad_alloc(); +#endif + } + } + + /** + * A version of cloneTo that does not throw any out of memory exceptions, but + * returns E_OUTOFMEMORY intead. + * @returns S_OK or E_OUTOFMEMORY. + */ + HRESULT cloneToEx(BSTR *pstr) const + { + if (!pstr) + return S_OK; + *pstr = ::SysAllocString((const OLECHAR *)raw()); // raw() returns a pointer to "" if empty + return pstr ? S_OK : E_OUTOFMEMORY; + } + + /** + * Intended to assign instances to |BSTR| out parameters from within the + * interface method. Transfers the ownership of the original string to the + * caller and resets the instance to null. + * + * As opposed to cloneTo(), this method doesn't create a copy of the + * string. + * + * If the member string is empty, this allocates an empty BSTR in *pstr + * (i.e. makes it point to a new buffer with a null byte). + * + * @param pbstrDst The BSTR variable to detach the string to. + * + * @throws std::bad_alloc if we failed to allocate a new empty string. + */ + void detachTo(BSTR *pbstrDst) + { + if (m_bstr) + { + *pbstrDst = m_bstr; + m_bstr = NULL; + } + else + { + // allocate null BSTR + *pbstrDst = ::SysAllocString((const OLECHAR *)g_bstrEmpty); +#ifdef RT_EXCEPTIONS_ENABLED + if (!*pbstrDst) + throw std::bad_alloc(); +#endif + } + } + + /** + * A version of detachTo that does not throw exceptions on out-of-memory + * conditions, but instead returns E_OUTOFMEMORY. + * + * @param pbstrDst The BSTR variable to detach the string to. + * @returns S_OK or E_OUTOFMEMORY. + */ + HRESULT detachToEx(BSTR *pbstrDst) + { + if (m_bstr) + { + *pbstrDst = m_bstr; + m_bstr = NULL; + } + else + { + // allocate null BSTR + *pbstrDst = ::SysAllocString((const OLECHAR *)g_bstrEmpty); + if (!*pbstrDst) + return E_OUTOFMEMORY; + } + return S_OK; + } + + /** + * Intended to pass instances as |BSTR| out parameters to methods. + * Takes the ownership of the returned data. + */ + BSTR *asOutParam() + { + cleanup(); + return &m_bstr; + } + + /** + * Static immutable empty-string object. May be used for comparison purposes. + */ + static const Bstr Empty; + +protected: + + void cleanup() + { + if (m_bstr) + { + ::SysFreeString(m_bstr); + m_bstr = NULL; + } + } + + /** + * Protected internal helper to copy a string. This ignores the previous object + * state, so either call this from a constructor or call cleanup() first. + * + * This variant copies from a zero-terminated UTF-16 string (which need not + * be a BSTR, i.e. need not have a length prefix). + * + * If the source is empty, this sets the member string to NULL. + * + * @param a_bstrSrc The source string. The caller guarantees + * that this is valid UTF-16. + * + * @throws std::bad_alloc - the object is representing an empty string. + */ + void copyFrom(const OLECHAR *a_bstrSrc) + { + if (a_bstrSrc && *a_bstrSrc) + { + m_bstr = ::SysAllocString(a_bstrSrc); +#ifdef RT_EXCEPTIONS_ENABLED + if (!m_bstr) + throw std::bad_alloc(); +#endif + } + else + m_bstr = NULL; + } + + /** + * Protected internal helper to copy a string. This ignores the previous object + * state, so either call this from a constructor or call cleanup() first. + * + * This variant copies and converts from a zero-terminated UTF-8 string. + * + * If the source is empty, this sets the member string to NULL. + * + * @param a_pszSrc The source string. The caller guarantees + * that this is valid UTF-8. + * + * @throws std::bad_alloc - the object is representing an empty string. + */ + void copyFrom(const char *a_pszSrc) + { + copyFromN(a_pszSrc, RTSTR_MAX); + } + + /** + * Variant of copyFrom for sub-string constructors. + * + * @param a_pszSrc The source string. The caller guarantees + * that this is valid UTF-8. + * @param a_cchSrc The maximum number of chars (not codepoints) to + * copy. If you pass RTSTR_MAX it'll be exactly + * like copyFrom(). + * + * @throws std::bad_alloc - the object is representing an empty string. + */ + void copyFromN(const char *a_pszSrc, size_t a_cchSrc); + + BSTR m_bstr; + + friend class Utf8Str; /* to access our raw_copy() */ +}; + +/* symmetric compare operators */ +inline bool operator==(CBSTR l, const Bstr &r) { return r.operator==(l); } +inline bool operator!=(CBSTR l, const Bstr &r) { return r.operator!=(l); } +inline bool operator==(BSTR l, const Bstr &r) { return r.operator==(l); } +inline bool operator!=(BSTR l, const Bstr &r) { return r.operator!=(l); } + + + + +/** + * String class used universally in Main for UTF-8 strings. + * + * This is based on RTCString, to which some functionality has been + * moved. Here we keep things that are specific to Main, such as conversions + * with UTF-16 strings (Bstr). + * + * Like RTCString, Utf8Str does not differentiate between NULL strings + * and empty strings. In other words, Utf8Str("") and Utf8Str(NULL) behave the + * same. In both cases, RTCString allocates no memory, reports + * a zero length and zero allocated bytes for both, and returns an empty + * C string from c_str(). + * + * @note All Utf8Str methods ASSUMES valid UTF-8 or UTF-16 input strings. + * The VirtualBox policy in this regard is to validate strings coming + * from external sources before passing them to Utf8Str or Bstr. + */ +class Utf8Str : public RTCString +{ +public: + + Utf8Str() {} + + Utf8Str(const RTCString &that) + : RTCString(that) + {} + + Utf8Str(const char *that) + : RTCString(that) + {} + + Utf8Str(const Bstr &that) + { + copyFrom(that.raw()); + } + + Utf8Str(CBSTR that, size_t a_cwcSize = RTSTR_MAX) + { + copyFrom(that, a_cwcSize); + } + + Utf8Str(const char *a_pszSrc, size_t a_cchSrc) + : RTCString(a_pszSrc, a_cchSrc) + { + } + + /** + * Constructs a new string given the format string and the list of the + * arguments for the format string. + * + * @param a_pszFormat Pointer to the format string (UTF-8), + * @see pg_rt_str_format. + * @param a_va Argument vector containing the arguments + * specified by the format string. + * @sa RTCString::printfV + */ + Utf8Str(const char *a_pszFormat, va_list a_va) RT_IPRT_FORMAT_ATTR(1, 0) + : RTCString(a_pszFormat, a_va) + { + } + + Utf8Str& operator=(const RTCString &that) + { + RTCString::operator=(that); + return *this; + } + + Utf8Str& operator=(const char *that) + { + RTCString::operator=(that); + return *this; + } + + Utf8Str& operator=(const Bstr &that) + { + cleanup(); + copyFrom(that.raw()); + return *this; + } + + Utf8Str& operator=(CBSTR that) + { + cleanup(); + copyFrom(that); + return *this; + } + + /** + * Extended assignment method that returns a COM status code instead of an + * exception on failure. + * + * @returns S_OK or E_OUTOFMEMORY. + * @param a_rSrcStr The source string + */ + HRESULT assignEx(Utf8Str const &a_rSrcStr) + { + return copyFromExNComRC(a_rSrcStr.m_psz, 0, a_rSrcStr.m_cch); + } + + /** + * Extended assignment method that returns a COM status code instead of an + * exception on failure. + * + * @returns S_OK, E_OUTOFMEMORY or E_INVALIDARG. + * @param a_rSrcStr The source string + * @param a_offSrc The character (byte) offset of the substring. + * @param a_cchSrc The number of characters (bytes) to copy from the source + * string. + */ + HRESULT assignEx(Utf8Str const &a_rSrcStr, size_t a_offSrc, size_t a_cchSrc) + { + if ( a_offSrc + a_cchSrc > a_rSrcStr.m_cch + || a_offSrc > a_rSrcStr.m_cch) + return E_INVALIDARG; + return copyFromExNComRC(a_rSrcStr.m_psz, a_offSrc, a_cchSrc); + } + + /** + * Extended assignment method that returns a COM status code instead of an + * exception on failure. + * + * @returns S_OK or E_OUTOFMEMORY. + * @param a_pcszSrc The source string + */ + HRESULT assignEx(const char *a_pcszSrc) + { + return copyFromExNComRC(a_pcszSrc, 0, a_pcszSrc ? strlen(a_pcszSrc) : 0); + } + + /** + * Extended assignment method that returns a COM status code instead of an + * exception on failure. + * + * @returns S_OK or E_OUTOFMEMORY. + * @param a_pcszSrc The source string + * @param a_cchSrc The number of characters (bytes) to copy from the source + * string. + */ + HRESULT assignEx(const char *a_pcszSrc, size_t a_cchSrc) + { + return copyFromExNComRC(a_pcszSrc, 0, a_cchSrc); + } + + RTMEMEF_NEW_AND_DELETE_OPERATORS(); + +#if defined(VBOX_WITH_XPCOM) + /** + * Intended to assign instances to |char *| out parameters from within the + * interface method. Transfers the ownership of the duplicated string to the + * caller. + * + * This allocates a single 0 byte in the target if the member string is empty. + * + * This uses XPCOM memory allocation and thus only works on XPCOM. MSCOM doesn't + * like char* strings anyway. + */ + void cloneTo(char **pstr) const; + + /** + * A version of cloneTo that does not throw allocation errors but returns + * E_OUTOFMEMORY instead. + * @returns S_OK or E_OUTOFMEMORY (COM status codes). + */ + HRESULT cloneToEx(char **pstr) const; +#endif + + /** + * Intended to assign instances to |BSTR| out parameters from within the + * interface method. Transfers the ownership of the duplicated string to the + * caller. + */ + void cloneTo(BSTR *pstr) const + { + if (pstr) + { + Bstr bstr(*this); + bstr.cloneTo(pstr); + } + } + + /** + * A version of cloneTo that does not throw allocation errors but returns + * E_OUTOFMEMORY instead. + * + * @param pbstr Where to store a clone of the string. + * @returns S_OK or E_OUTOFMEMORY (COM status codes). + */ + HRESULT cloneToEx(BSTR *pbstr) const + { + if (!pbstr) + return S_OK; + Bstr bstr(*this); + return bstr.detachToEx(pbstr); + } + + /** + * Safe assignment from BSTR. + * + * @param pbstrSrc The source string. + * @returns S_OK or E_OUTOFMEMORY (COM status codes). + */ + HRESULT cloneEx(CBSTR pbstrSrc) + { + cleanup(); + return copyFromEx(pbstrSrc); + } + + /** + * Removes a trailing slash from the member string, if present. + * Calls RTPathStripTrailingSlash() without having to mess with mutableRaw(). + */ + Utf8Str& stripTrailingSlash(); + + /** + * Removes a trailing filename from the member string, if present. + * Calls RTPathStripFilename() without having to mess with mutableRaw(). + */ + Utf8Str& stripFilename(); + + /** + * Removes the path component from the member string, if present. + * Calls RTPathFilename() without having to mess with mutableRaw(). + */ + Utf8Str& stripPath(); + + /** + * Removes a trailing file name suffix from the member string, if present. + * Calls RTPathStripSuffix() without having to mess with mutableRaw(). + */ + Utf8Str& stripSuffix(); + + /** + * Parses key=value pairs. + * + * @returns offset of the @a a_rPairSeparator following the returned value. + * @retval npos is returned if there are no more key/value pairs. + * + * @param a_rKey Reference to variable that should receive + * the key substring. This is set to null if + * no key/value found. (It's also possible the + * key is an empty string, so be careful.) + * @param a_rValue Reference to variable that should receive + * the value substring. This is set to null if + * no key/value found. (It's also possible the + * value is an empty string, so be careful.) + * @param a_offStart The offset to start searching from. This is + * typically 0 for the first call, and the + * return value of the previous call for the + * subsequent ones. + * @param a_rPairSeparator The pair separator string. If this is an + * empty string, the whole string will be + * considered as a single key/value pair. + * @param a_rKeyValueSeparator The key/value separator string. + */ + size_t parseKeyValue(Utf8Str &a_rKey, Utf8Str &a_rValue, size_t a_offStart = 0, + const Utf8Str &a_rPairSeparator = ",", const Utf8Str &a_rKeyValueSeparator = "=") const; + + /** + * Static immutable empty-string object. May be used for comparison purposes. + */ + static const Utf8Str Empty; +protected: + + void copyFrom(CBSTR a_pbstr, size_t a_cwcMax = RTSTR_MAX); + HRESULT copyFromEx(CBSTR a_pbstr); + HRESULT copyFromExNComRC(const char *a_pcszSrc, size_t a_offSrc, size_t a_cchSrc); + + friend class Bstr; /* to access our raw_copy() */ +}; + +/** + * Class with RTCString::printf as constructor for your convenience. + * + * Constructing a Utf8Str string object from a format string and a variable + * number of arguments can easily be confused with the other Utf8Str + * constructures, thus this child class. + * + * The usage of this class is like the following: + * @code + Utf8StrFmt strName("program name = %s", argv[0]); + @endcode + */ +class Utf8StrFmt : public Utf8Str +{ +public: + + /** + * Constructs a new string given the format string and the list of the + * arguments for the format string. + * + * @param a_pszFormat Pointer to the format string (UTF-8), + * @see pg_rt_str_format. + * @param ... Ellipsis containing the arguments specified by + * the format string. + */ + explicit Utf8StrFmt(const char *a_pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2) + { + va_list va; + va_start(va, a_pszFormat); + printfV(a_pszFormat, va); + va_end(va); + } + + RTMEMEF_NEW_AND_DELETE_OPERATORS(); + +protected: + Utf8StrFmt() + { } + +private: +}; + +/** + * The BstrFmt class is a shortcut to <tt>Bstr(Utf8StrFmt(...))</tt>. + */ +class BstrFmt : public Bstr +{ +public: + + /** + * Constructs a new string given the format string and the list of the + * arguments for the format string. + * + * @param aFormat printf-like format string (in UTF-8 encoding). + * @param ... List of the arguments for the format string. + */ + explicit BstrFmt(const char *aFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2) + { + va_list args; + va_start(args, aFormat); + copyFrom(Utf8Str(aFormat, args).c_str()); + va_end(args); + } + + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +/** + * The BstrFmtVA class is a shortcut to <tt>Bstr(Utf8Str(format,va))</tt>. + */ +class BstrFmtVA : public Bstr +{ +public: + + /** + * Constructs a new string given the format string and the list of the + * arguments for the format string. + * + * @param aFormat printf-like format string (in UTF-8 encoding). + * @param aArgs List of arguments for the format string + */ + BstrFmtVA(const char *aFormat, va_list aArgs) RT_IPRT_FORMAT_ATTR(1, 0) + { + copyFrom(Utf8Str(aFormat, aArgs).c_str()); + } + + RTMEMEF_NEW_AND_DELETE_OPERATORS(); +}; + +} /* namespace com */ + +/** @} */ + +#endif /* !VBOX_INCLUDED_com_string_h */ + |