summaryrefslogtreecommitdiffstats
path: root/include/VBox/com
diff options
context:
space:
mode:
Diffstat (limited to 'include/VBox/com')
-rw-r--r--include/VBox/com/AutoLock.h704
-rw-r--r--include/VBox/com/ErrorInfo.h545
-rw-r--r--include/VBox/com/EventQueue.h151
-rw-r--r--include/VBox/com/Guid.h526
-rw-r--r--include/VBox/com/Makefile.kup0
-rw-r--r--include/VBox/com/MultiResult.h278
-rw-r--r--include/VBox/com/NativeEventQueue.h161
-rw-r--r--include/VBox/com/VirtualBox.h71
-rw-r--r--include/VBox/com/array.h1872
-rw-r--r--include/VBox/com/assert.h135
-rw-r--r--include/VBox/com/com.h102
-rw-r--r--include/VBox/com/defs.h606
-rw-r--r--include/VBox/com/errorprint.h401
-rw-r--r--include/VBox/com/list.h223
-rw-r--r--include/VBox/com/listeners.h194
-rw-r--r--include/VBox/com/microatl.h1408
-rw-r--r--include/VBox/com/mtlist.h220
-rw-r--r--include/VBox/com/ptr.h569
-rw-r--r--include/VBox/com/string.h1494
-rw-r--r--include/VBox/com/utils.h132
20 files changed, 9792 insertions, 0 deletions
diff --git a/include/VBox/com/AutoLock.h b/include/VBox/com/AutoLock.h
new file mode 100644
index 00000000..88c656e6
--- /dev/null
+++ b/include/VBox/com/AutoLock.h
@@ -0,0 +1,704 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Automatic locks, implementation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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)
+ LOCKCLASS_TRANSLATOR = 13 // translator internal lock
+};
+
+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:
+ virtual ~Lockable() { } /* To make VC++ 2019 happy. */
+
+ /**
+ * 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..10631a06
--- /dev/null
+++ b/include/VBox/com/ErrorInfo.h
@@ -0,0 +1,545 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - ErrorInfo class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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..02293285
--- /dev/null
+++ b/include/VBox/com/EventQueue.h
@@ -0,0 +1,151 @@
+/* $Id: EventQueue.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Event queue class declaration.
+ */
+
+/*
+ * Copyright (C) 2013-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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..caa30fff
--- /dev/null
+++ b/include/VBox/com/Guid.h
@@ -0,0 +1,526 @@
+/* $Id: Guid.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Guid class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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);
+ }
+
+ /**
+ * Convert the GUID to a C string.
+ *
+ * @returns See RTUuidToStr.
+ * @param pszUuid The output buffer
+ * @param cbUuid The size of the output buffer. Should be at least
+ * RTUUID_STR_LENGTH in length.
+ */
+ int toString(char *pszUuid, size_t cbUuid) const
+ {
+ return ::RTUuidToStr(mGuidState != GUID_INVALID ? &mUuid : &Empty.mUuid, pszUuid, cbUuid);
+ }
+
+ 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; }
+
+ /** Compare with a UUID string representation.
+ * @note Not an operator as that could lead to confusion. */
+ bool equalsString(const char *pszUuid2) const { return ::RTUuidCompareStr(&mUuid, pszUuid2) == 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..2658a34a
--- /dev/null
+++ b/include/VBox/com/MultiResult.h
@@ -0,0 +1,278 @@
+/* $Id: MultiResult.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - MultiResult class declarations.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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..0fde3b97
--- /dev/null
+++ b/include/VBox/com/NativeEventQueue.h
@@ -0,0 +1,161 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Event and EventQueue class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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 vrc);
+#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..d6bd4a90
--- /dev/null
+++ b/include/VBox/com/VirtualBox.h
@@ -0,0 +1,71 @@
+/** @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-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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 <iprt/win/windows.h> /* Included by VirtualBox.h via rpc.h, so include our wrapper with cleanups. */
+# 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..7cba02e7
--- /dev/null
+++ b/include/VBox/com/array.h
@@ -0,0 +1,1872 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Safe array helper class declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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"
+
+/** @def ComSafeArrayAsInParam
+ * 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.
+ */
+
+/** @def ComSafeArrayAsOutParam
+ * 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.
+ */
+
+/** @def ComSafeArrayNullInParam
+ * Helper for passing a NULL array parameter to a COM / XPCOM method.
+ */
+
+#ifdef VBOX_WITH_XPCOM
+
+# define ComSafeArrayAsInParam(aArray) \
+ (PRUint32)(aArray).size(), (aArray).__asInParam_Arr((aArray).raw())
+
+# define ComSafeArrayAsOutParam(aArray) \
+ (aArray).__asOutParam_Size(), (aArray).__asOutParam_Arr()
+
+# define ComSafeArrayNullInParam() 0, NULL
+
+#else /* !VBOX_WITH_XPCOM */
+
+# define ComSafeArrayAsInParam(aArray) (aArray).__asInParam()
+
+# define ComSafeArrayAsOutParam(aArray) (aArray).__asOutParam()
+
+# define ComSafeArrayNullInParam() (NULL)
+
+#endif /* !VBOX_WITH_XPCOM */
+
+/**
+ *
+ */
+namespace com
+{
+
+/** Used for dummy element access in com::SafeArray, avoiding crashes. */
+extern const char Zeroes[16];
+
+
+#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());
+
+ size_t 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
+ }
+
+ /**
+ * Prepends a copy of the given element at the beginning 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_front(string);</tt>. If you create a string
+ * just to put it in the array, you may find #appendedRaw() more useful.
+ *
+ * @param aElement Element to prepend.
+ *
+ * @return @c true on success and @c false if there is not enough
+ * memory for resizing.
+ */
+ bool push_front(const T &aElement)
+ {
+ if (!ensureCapacity(size() + 1))
+ return false;
+
+ for (size_t i = size(); i > 0; --i)
+ {
+#ifdef VBOX_WITH_XPCOM
+ SafeArray::Copy(m.arr[i - 1], m.arr[i]);
+#else
+ Copy(m.raw[i - 1], m.raw[i]);
+#endif
+ }
+
+#ifdef VBOX_WITH_XPCOM
+ SafeArray::Copy(aElement, m.arr[0]);
+ ++ m.size;
+#else
+ Copy(aElement, m.raw[0]);
+#endif
+ return true;
+ }
+
+ /**
+ * 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 a reference to a static zero
+ * element (const, i.e. writes will fail) 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)
+ {
+ /** @todo r=klaus should do this as a AssertCompile, but cannot find a way which works. */
+ Assert(sizeof(T) <= sizeof(Zeroes));
+ AssertReturn(m.arr != NULL, *(T *)&Zeroes[0]);
+ AssertReturn(aIdx < size(), *(T *)&Zeroes[0]);
+#ifdef VBOX_WITH_XPCOM
+ return m.arr[aIdx];
+#else
+ AssertReturn(m.raw != NULL, *(T *)&Zeroes[0]);
+ 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, *(const T *)&Zeroes[0]);
+ AssertReturn(aIdx < size(), *(const T *)&Zeroes[0]);
+#ifdef VBOX_WITH_XPCOM
+ return m.arr[aIdx];
+#else
+ AssertReturn(m.raw != NULL, *(const T *)&Zeroes[0]);
+ 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);
+ }
+
+ HRESULT cloneTo(SafeArray<T>& aOther) const
+ {
+ aOther.reset(size());
+ return 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 HRESULT initFrom(const com::SafeArray<T> & aRef);
+ inline HRESULT 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 HRESULT com::SafeArray<BYTE>::initFrom(const com::SafeArray<BYTE> & aRef)
+{
+ size_t sSize = aRef.size();
+ if (resize(sSize))
+ {
+ ::memcpy(raw(), aRef.raw(), sSize);
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+template<>
+inline HRESULT com::SafeArray<BYTE>::initFrom(const BYTE *aPtr, size_t aSize)
+{
+ if (resize(aSize))
+ {
+ ::memcpy(raw(), aPtr, aSize);
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+
+
+template<>
+inline HRESULT com::SafeArray<SHORT>::initFrom(const com::SafeArray<SHORT> & aRef)
+{
+ size_t sSize = aRef.size();
+ if (resize(sSize))
+ {
+ ::memcpy(raw(), aRef.raw(), sSize * sizeof(SHORT));
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+template<>
+inline HRESULT com::SafeArray<SHORT>::initFrom(const SHORT *aPtr, size_t aSize)
+{
+ if (resize(aSize))
+ {
+ ::memcpy(raw(), aPtr, aSize * sizeof(SHORT));
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+
+template<>
+inline HRESULT com::SafeArray<USHORT>::initFrom(const com::SafeArray<USHORT> & aRef)
+{
+ size_t sSize = aRef.size();
+ if (resize(sSize))
+ {
+ ::memcpy(raw(), aRef.raw(), sSize * sizeof(USHORT));
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+template<>
+inline HRESULT com::SafeArray<USHORT>::initFrom(const USHORT *aPtr, size_t aSize)
+{
+ if (resize(aSize))
+ {
+ ::memcpy(raw(), aPtr, aSize * sizeof(USHORT));
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+
+template<>
+inline HRESULT com::SafeArray<LONG>::initFrom(const com::SafeArray<LONG> & aRef)
+{
+ size_t sSize = aRef.size();
+ if (resize(sSize))
+ {
+ ::memcpy(raw(), aRef.raw(), sSize * sizeof(LONG));
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+template<>
+inline HRESULT com::SafeArray<LONG>::initFrom(const LONG *aPtr, size_t aSize)
+{
+ if (resize(aSize))
+ {
+ ::memcpy(raw(), aPtr, aSize * sizeof(LONG));
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+#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) { AssertCompile(sizeof(nsID) <= sizeof(Zeroes)); }
+
+ operator const nsID &() const { return mVal ? *mVal : *(const nsID *)&Zeroes[0]; }
+ operator nsID() const { return mVal ? *mVal : *(nsID *)&Zeroes[0]; }
+
+ const nsID *operator&() const { return mVal ? mVal : (const nsID *)&Zeroes[0]; }
+
+ nsIDRef &operator= (const nsID &aThat)
+ {
+ if (mVal == NULL)
+ Copy(&aThat, mVal);
+ else
+ *mVal = aThat;
+ return *this;
+ }
+
+ private:
+
+ nsID * &mVal;
+
+ 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] : *(const nsID *)&Zeroes[0];
+ }
+};
+
+/**
+ * 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() { AssertCompile(sizeof(nsID) <= sizeof(Zeroes)); }
+
+ /* 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 *)&Zeroes[0]);
+ AssertReturn(aIdx < size(), *(const nsID *)&Zeroes[0]);
+ 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());
+
+ size_t 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());
+
+ size_t 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());
+
+ size_t 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());
+
+ size_t 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..0e741bf1
--- /dev/null
+++ b/include/VBox/com/assert.h
@@ -0,0 +1,135 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Assertion macros for COM/XPCOM.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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..b29b38f9
--- /dev/null
+++ b/include/VBox/com/com.h
@@ -0,0 +1,102 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - COM initialization / shutdown.
+ */
+
+/*
+ * Copyright (C) 2005-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_com_h
+#define VBOX_INCLUDED_com_com_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBox/com/defs.h"
+#include "VBox/com/utils.h"
+
+/** @defgroup grp_com MS COM / XPCOM Abstraction Layer
+ * @{
+ */
+
+namespace com
+{
+
+/** @name VBOX_COM_INIT_F_XXX - flags for com::Initialize().
+ * @{ */
+/** Windows: Caller is the GUI and needs a STA rather than MTA apartment. */
+#define VBOX_COM_INIT_F_GUI RT_BIT_32(0)
+/** Windows: Auto registration updating, if privileged enough. */
+#define VBOX_COM_INIT_F_AUTO_REG_UPDATE RT_BIT_32(1)
+/** Windows: Opt-out of COM patching (client code should do this). */
+#define VBOX_COM_INIT_F_NO_COM_PATCHING RT_BIT_32(2)
+/** The default flags. */
+#define VBOX_COM_INIT_F_DEFAULT (VBOX_COM_INIT_F_AUTO_REG_UPDATE)
+/** @} */
+
+/**
+ * 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.
+ *
+ * @return COM result code
+ */
+HRESULT Initialize(uint32_t fInitFlags = VBOX_COM_INIT_F_DEFAULT);
+
+/**
+ * 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);
+
+#ifdef RT_OS_WINDOWS
+void PatchComBugs(void);
+#endif
+
+} /* 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..7db5fcf5
--- /dev/null
+++ b/include/VBox/com/defs.h
@@ -0,0 +1,606 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Common Definitions.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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>
+ * &lt;param name="arg" ... dir="in" safearray="yes"/&gt;
+ * ...
+ * &lt;attribute name="arg" ... safearray="yes"/&gt;
+ * </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>
+ * &lt;param name="arg" ... dir="out" safearray="yes"/&gt;
+ * &lt;param name="arg" ... dir="return" safearray="yes"/&gt;
+ * ...
+ * &lt;attribute name="arg" ... safearray="yes"/&gt;
+ * </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 S_FALSE ((nsresult)1)
+#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 const *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..7c019a2c
--- /dev/null
+++ b/include/VBox/com/errorprint.h
@@ -0,0 +1,401 @@
+/** @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-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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, bool fWarning = false);
+void GluePrintRCMessage(HRESULT hrc);
+void GlueHandleComError(ComPtr<IUnknown> iface, const char *pcszContext, HRESULT hrc, const char *pcszSourceFile, uint32_t uLine);
+void GlueHandleComErrorNoCtx(ComPtr<IUnknown> iface, HRESULT hrc);
+void GlueHandleComErrorProgress(ComPtr<IProgress> progress, const char *pcszContext, HRESULT hrc,
+ 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) && !SUCCEEDED_WARNING(hrc)) \
+ { /*likely*/ } \
+ else \
+ { \
+ com::GlueHandleComError(iface, #method, (hrc), __FILE__, __LINE__); \
+ if (!SUCCEEDED_WARNING(hrc)) \
+ { \
+ 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 { \
+ hrc = iface->method; \
+ if (FAILED(hrc) || SUCCEEDED_WARNING(hrc)) \
+ com::GlueHandleComError(iface, #method, hrc, __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 { \
+ hrc = iface->method; \
+ if (FAILED(hrc) || SUCCEEDED_WARNING(hrc)) \
+ { \
+ com::GlueHandleComError(iface, #method, hrc, __FILE__, __LINE__); \
+ if (!SUCCEEDED_WARNING(hrc) \
+ { \
+ 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__ \
+ ({ \
+ hrc = iface->method; \
+ if (FAILED(hrc) || SUCCEEDED_WARNING(hrc)) \
+ { \
+ com::GlueHandleComError(iface, #method, hrc, __FILE__, __LINE__); \
+ if (!SUCCEEDED_WARNING(hrc)) \
+ break; \
+ } \
+ })
+#else
+# define CHECK_ERROR_BREAK(iface, method) \
+ if (1) \
+ { \
+ hrc = iface->method; \
+ if (FAILED(hrc)) \
+ { \
+ com::GlueHandleComError(iface, #method, hrc, __FILE__, __LINE__); \
+ if (!SUCCEEDED_WARNING(hrc)) \
+ 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 { \
+ hrc = iface->method; \
+ if (FAILED(hrc) || SUCCEEDED_WARNING(hrc)) \
+ { \
+ com::GlueHandleComError(iface, #method, hrc, __FILE__, __LINE__); \
+ if (!SUCCEEDED_WARNING(hrc)) \
+ 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; \
+ hrc = progress->COMGETTER(ResultCode)(&iRc); \
+ if (FAILED(hrc) || FAILED(iRc)) \
+ { \
+ if (SUCCEEDED(hrc)) hrc = iRc; else iRc = hrc; \
+ 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; \
+ hrc = progress->COMGETTER(ResultCode)(&iRc); \
+ if (FAILED(hrc) || FAILED(iRc)) \
+ { \
+ if (SUCCEEDED(hrc)) hrc = iRc; else iRc = hrc; \
+ RTMsgError msg; \
+ com::GlueHandleComErrorProgress(progress, __PRETTY_FUNCTION__, iRc, __FILE__, __LINE__); \
+ break; \
+ } \
+ })
+#else
+# define CHECK_PROGRESS_ERROR_BREAK(progress, msg) \
+ if (1) \
+ { \
+ LONG iRc; \
+ hrc = progress->COMGETTER(ResultCode)(&iRc); \
+ if (FAILED(hrc) || FAILED(iRc)) \
+ { \
+ if (SUCCEEDED(hrc)) hrc = iRc; else iRc = hrc; \
+ 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..14f1f6b5
--- /dev/null
+++ b/include/VBox/com/list.h
@@ -0,0 +1,223 @@
+/* $Id: list.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - List classes declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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..09ed2a9c
--- /dev/null
+++ b/include/VBox/com/listeners.h
@@ -0,0 +1,194 @@
+/* $Id: listeners.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Listener helpers.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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;
+ HRESULT hrc = aEvent->COMGETTER(Type)(&aType);
+ AssertMsg(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc)); RT_NOREF(hrc);
+ 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..4ce31c16
--- /dev/null
+++ b/include/VBox/com/microatl.h
@@ -0,0 +1,1408 @@
+/** @file
+ * ATL lookalike, just the tiny subset we actually need.
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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 CComCritSectLockManual
+{
+public:
+ CComCritSectLockManual(CComCriticalSection &cs)
+ : m_cs(cs)
+ , m_fLocked(false)
+ {
+ }
+
+ ~CComCritSectLockManual() 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;
+
+ CComCritSectLockManual(const CComCritSectLockManual&) throw(); // Do not call.
+ CComCritSectLockManual &operator=(const CComCritSectLockManual &) throw(); // Do not call.
+};
+
+
+#ifdef RT_EXCEPTIONS_ENABLED
+/** This is called CComCritSecLock in real ATL... */
+template<class TLock>
+class CComCritSectLock : public CComCritSectLockManual<TLock>
+{
+public:
+ CComCritSectLock(CComCriticalSection &cs, bool fInitialLock = true)
+ : CComCritSectLockManual(cs)
+ {
+ if (fInitialLock)
+ {
+ HRESULT hrc = Lock();
+ if (FAILED(hrc))
+ throw hrc;
+ }
+ }
+
+private:
+ CComCritSectLock(const CComCritSectLock&) throw(); // Do not call.
+ CComCritSectLock &operator=(const CComCritSectLock &) throw(); // Do not call.
+};
+#endif
+
+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;
+ CComCritSectLockManual<CComCriticalSection> lock(m_csStaticDataInitAndTypeInfo);
+ 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)
+ {
+ CComCritSectLockManual<CComCriticalSection> lock(_AtlComModule.m_csObjMap);
+ 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;
+
+ CComCritSectLockManual<CComCriticalSection> lock(_pAtlModule->m_csStaticDataInitAndTypeInfo);
+ HRESULT hrc = lock.Lock();
+ if (SUCCEEDED(hrc))
+ {
+ 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 = NULL;
+ try
+ {
+ p = new CComObject<Base>();
+ }
+ catch (std::bad_alloc &)
+ {
+ p = NULL;
+ }
+ if (p)
+ {
+ p->InternalFinalConstructAddRef();
+ try
+ {
+ hrc = p->_AtlInitialConstruct();
+ if (SUCCEEDED(hrc))
+ hrc = p->FinalConstruct();
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ 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..a3598530
--- /dev/null
+++ b/include/VBox/com/mtlist.h
@@ -0,0 +1,220 @@
+/* $Id: mtlist.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Thread-safe list classes declaration.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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..563f18c5
--- /dev/null
+++ b/include/VBox/com/ptr.h
@@ -0,0 +1,569 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Smart COM pointer classes declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_ptr_h
+#define VBOX_INCLUDED_com_ptr_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/* Make sure all the stdint.h macros are included - must come first! */
+#ifndef __STDC_LIMIT_MACROS
+# define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
+# define __STDC_CONSTANT_MACROS
+#endif
+
+#ifdef VBOX_WITH_XPCOM
+# include <nsISupportsUtils.h>
+#endif /* VBOX_WITH_XPCOM */
+
+#include <VBox/com/defs.h>
+#include <new> /* For bad_alloc. */
+
+
+/** @defgroup grp_com_ptr Smart COM Pointer Classes
+ * @ingroup grp_com
+ * @{
+ */
+
+#ifdef VBOX_WITH_XPCOM
+
+namespace com
+{
+// declare a couple of XPCOM helper methods (defined in glue/com.cpp)
+// so we don't have to include a ton of XPCOM implementation headers here
+HRESULT GlueCreateObjectOnServer(const CLSID &clsid,
+ const char *serverName,
+ const nsIID &id,
+ void **ppobj);
+HRESULT GlueCreateInstance(const CLSID &clsid,
+ const nsIID &id,
+ void **ppobj);
+}
+
+#endif // VBOX_WITH_XPCOM
+
+/**
+ * COM autopointer class which takes care of all required reference counting.
+ *
+ * This automatically calls the required basic COM methods on COM pointers
+ * given to it:
+ *
+ * -- AddRef() gets called automatically whenever a new COM pointer is assigned
+ * to the ComPtr instance (either in the copy constructor or by assignment);
+ *
+ * -- Release() gets called automatically by the destructor and when an existing
+ * object gets released in assignment;
+ *
+ * -- QueryInterface() gets called automatically when COM pointers get converted
+ * from one interface to another.
+ *
+ * Example usage:
+ *
+ * @code
+ *
+ * {
+ * ComPtr<IMachine> pMachine = findMachine("blah"); // calls AddRef()
+ * ComPtr<IUnknown> pUnknown = pMachine; // calls QueryInterface()
+ * } # ComPtr destructor of both instances calls Release()
+ *
+ * @endcode
+ */
+template <class T>
+class ComPtr
+{
+public:
+
+ /**
+ * Default constructor, sets up a NULL pointer.
+ */
+ ComPtr()
+ : m_p(NULL)
+ { }
+
+ /**
+ * Destructor. Calls Release() on the contained COM object.
+ */
+ ~ComPtr()
+ {
+ cleanup();
+ }
+
+ /**
+ * Copy constructor from another ComPtr of any interface.
+ *
+ * This calls QueryInterface(T) and can result in a NULL pointer if the input
+ * pointer p does not support the ComPtr interface T.
+ *
+ * Does not call AddRef explicitly because if QueryInterface succeeded, then
+ * the refcount will have been increased by one already.
+ */
+ template <class T2>
+ ComPtr(const ComPtr<T2> &that)
+ {
+ m_p = NULL;
+ if (!that.isNull())
+ that->QueryInterface(COM_IIDOF(T), (void **)&m_p);
+ }
+
+ /**
+ * Specialization: copy constructor from another ComPtr<T>. Calls AddRef().
+ */
+ ComPtr(const ComPtr &that)
+ {
+ copyFrom(that.m_p);
+ }
+
+ /**
+ * Copy constructor from another interface pointer of any interface.
+ *
+ * This calls QueryInterface(T) and can result in a NULL pointer if the input
+ * pointer p does not support the ComPtr interface T.
+ *
+ * Does not call AddRef explicitly because if QueryInterface succeeded, then
+ * the refcount will have been increased by one already.
+ */
+ template <class T2>
+ ComPtr(T2 *p)
+ {
+ m_p = NULL;
+ if (p)
+ p->QueryInterface(COM_IIDOF(T), (void **)&m_p);
+ }
+
+ /**
+ * Specialization: copy constructor from a plain T * pointer. Calls AddRef().
+ */
+ ComPtr(T *that_p)
+ {
+ copyFrom(that_p);
+ }
+
+ /**
+ * Assignment from another ComPtr of any interface.
+ *
+ * This calls QueryInterface(T) and can result in a NULL pointer if the input
+ * pointer p does not support the ComPtr interface T.
+ *
+ * Does not call AddRef explicitly because if QueryInterface succeeded, then
+ * the refcount will have been increased by one already.
+ */
+ template <class T2>
+ ComPtr& operator=(const ComPtr<T2> &that)
+ {
+ return operator=((T2 *)that);
+ }
+
+ /**
+ * Specialization of the previous: assignment from another ComPtr<T>.
+ * Calls Release() on the previous member pointer, if any, and AddRef() on the new one.
+ */
+ ComPtr& operator=(const ComPtr &that)
+ {
+ return operator=((T *)that);
+ }
+
+ /**
+ * Assignment from another interface pointer of any interface.
+ *
+ * This calls QueryInterface(T) and can result in a NULL pointer if the input
+ * pointer p does not support the ComPtr interface T.
+ *
+ * Does not call AddRef explicitly because if QueryInterface succeeded, then
+ * the refcount will have been increased by one already.
+ */
+ template <class T2>
+ ComPtr& operator=(T2 *p)
+ {
+ cleanup();
+ if (p)
+ p->QueryInterface(COM_IIDOF(T), (void **)&m_p);
+ return *this;
+ }
+
+ /**
+ * Specialization of the previous: assignment from a plain T * pointer.
+ * Calls Release() on the previous member pointer, if any, and AddRef() on the new one.
+ */
+ ComPtr& operator=(T *p)
+ {
+ cleanup();
+ copyFrom(p);
+ return *this;
+ }
+
+ /**
+ * Resets the ComPtr to NULL. Works like a NULL assignment except it avoids the templates.
+ */
+ void setNull()
+ {
+ cleanup();
+ }
+
+ /**
+ * Returns true if the pointer is NULL.
+ */
+ bool isNull() const
+ {
+ return (m_p == NULL);
+ }
+
+ /**
+ * Returns true if the pointer is not NULL.
+ */
+ bool isNotNull() const
+ {
+ return (m_p != NULL);
+ }
+
+ bool operator<(T *p) const
+ {
+ return m_p < p;
+ }
+
+ /**
+ * Conversion operator, most often used to pass ComPtr instances as
+ * parameters to COM method calls.
+ */
+ operator T *() const
+ {
+ return m_p;
+ }
+
+ /**
+ * Dereferences the instance (redirects the -> operator to the managed
+ * pointer).
+ */
+ T *operator->() const
+ {
+ return m_p;
+ }
+
+ /**
+ * Special method which allows using a ComPtr as an output argument of a COM method.
+ * The ComPtr will then accept the method's interface pointer without calling AddRef()
+ * itself, since by COM convention this must has been done by the method which created
+ * the object that is being accepted.
+ *
+ * The ComPtr destructor will then still invoke Release() so that the returned object
+ * can get cleaned up properly.
+ */
+ T **asOutParam()
+ {
+ cleanup();
+ return &m_p;
+ }
+
+ /**
+ * Converts the contained pointer to a different interface
+ * by calling QueryInterface() on it.
+ * @param pp
+ * @return
+ */
+ template <class T2>
+ HRESULT queryInterfaceTo(T2 **pp) const
+ {
+ if (pp)
+ {
+ if (m_p)
+ return m_p->QueryInterface(COM_IIDOF(T2), (void **)pp);
+ *pp = NULL;
+ return S_OK;
+ }
+ return E_INVALIDARG;
+ }
+
+ /**
+ * Equality test operator. By COM definition, two COM objects are considered
+ * equal if their IUnknown interface pointers are equal.
+ */
+ template <class T2>
+ bool operator==(T2 *p)
+ {
+ IUnknown *p1 = NULL;
+ bool fNeedsRelease1 = false;
+ if (m_p)
+ fNeedsRelease1 = (SUCCEEDED(m_p->QueryInterface(COM_IIDOF(IUnknown), (void **)&p1)));
+
+ IUnknown *p2 = NULL;
+ bool fNeedsRelease2 = false;
+ if (p)
+ fNeedsRelease2 = (SUCCEEDED(p->QueryInterface(COM_IIDOF(IUnknown), (void **)&p2)));
+
+ bool f = p1 == p2;
+ if (fNeedsRelease1)
+ p1->Release();
+ if (fNeedsRelease2)
+ p2->Release();
+ return f;
+ }
+
+ /**
+ * Creates an in-process object of the given class ID and starts to
+ * manage a reference to the created object in case of success.
+ */
+ HRESULT createInprocObject(const CLSID &clsid)
+ {
+ HRESULT rc;
+ T *obj = NULL;
+#ifndef VBOX_WITH_XPCOM
+ rc = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, COM_IIDOF(T),
+ (void **)&obj);
+#else /* VBOX_WITH_XPCOM */
+ using namespace com;
+ rc = GlueCreateInstance(clsid, NS_GET_IID(T), (void **)&obj);
+#endif /* VBOX_WITH_XPCOM */
+ *this = obj;
+ if (SUCCEEDED(rc))
+ obj->Release();
+ return rc;
+ }
+
+ /**
+ * Creates a local (out-of-process) object of the given class ID and starts
+ * to manage a reference to the created object in case of success.
+ *
+ * Note: In XPCOM, the out-of-process functionality is currently emulated
+ * through in-process wrapper objects (that start a dedicated process and
+ * redirect all object requests to that process). For this reason, this
+ * method is fully equivalent to #createInprocObject() for now.
+ */
+ HRESULT createLocalObject(const CLSID &clsid)
+ {
+#ifndef VBOX_WITH_XPCOM
+ HRESULT rc;
+ T *obj = NULL;
+ rc = CoCreateInstance(clsid, NULL, CLSCTX_LOCAL_SERVER, COM_IIDOF(T),
+ (void **)&obj);
+ *this = obj;
+ if (SUCCEEDED(rc))
+ obj->Release();
+ return rc;
+#else /* VBOX_WITH_XPCOM */
+ return createInprocObject(clsid);
+#endif /* VBOX_WITH_XPCOM */
+ }
+
+#ifdef VBOX_WITH_XPCOM
+ /**
+ * Creates an object of the given class ID on the specified server and
+ * starts to manage a reference to the created object in case of success.
+ *
+ * @param serverName Name of the server to create an object within.
+ */
+ HRESULT createObjectOnServer(const CLSID &clsid, const char *serverName)
+ {
+ T *obj = NULL;
+ HRESULT rc = GlueCreateObjectOnServer(clsid, serverName, NS_GET_IID(T), (void **)&obj);
+ *this = obj;
+ if (SUCCEEDED(rc))
+ obj->Release();
+ return rc;
+ }
+#endif
+
+protected:
+ void copyFrom(T *p)
+ {
+ m_p = p;
+ if (m_p)
+ m_p->AddRef();
+ }
+
+ void cleanup()
+ {
+ if (m_p)
+ {
+ m_p->Release();
+ m_p = NULL;
+ }
+ }
+
+public:
+ // Do NOT access this member unless you really know what you're doing!
+ T *m_p;
+};
+
+/**
+ * ComObjPtr is a more specialized variant of ComPtr designed to be used for implementation
+ * objects. For example, use ComPtr<IMachine> for a client pointer that calls the interface
+ * but ComObjPtr<Machine> for a pointer to an implementation object.
+ *
+ * The methods behave the same except that ComObjPtr has the additional createObject()
+ * method which allows for instantiating a new implementation object.
+ *
+ * Note: To convert a ComObjPtr<InterfaceImpl> to a ComObj<IInterface> you have
+ * to query the interface. See the following example code for the IProgress
+ * interface:
+ *
+ * @code
+ *
+ * {
+ * ComObjPtr<Progress> pProgress; // create the server side object
+ * pProgress.createObject(); // ...
+ * pProgress->init(...); // ...
+ * ComPtr<IProgress> pProgress2; // create an interface pointer
+ * pProgress.queryInterfaceTo(pProgress2.asOutParam()); // transfer the interface
+ * }
+ *
+ * @endcode
+ */
+template <class T>
+class ComObjPtr : public ComPtr<T>
+{
+public:
+
+ ComObjPtr()
+ : ComPtr<T>()
+ {}
+
+ ComObjPtr(const ComObjPtr &that)
+ : ComPtr<T>(that)
+ {}
+
+ ComObjPtr(T *that_p)
+ : ComPtr<T>(that_p)
+ {}
+
+ ComObjPtr& operator=(const ComObjPtr &that)
+ {
+ ComPtr<T>::operator=(that);
+ return *this;
+ }
+
+ ComObjPtr& operator=(T *that_p)
+ {
+ ComPtr<T>::operator=(that_p);
+ return *this;
+ }
+
+ /**
+ * Creates a new server-side object of the given component class and
+ * immediately starts to manage a pointer to the created object (the
+ * previous pointer, if any, is of course released when appropriate).
+ *
+ * @note Win32: when VBOX_COM_OUTOFPROC_MODULE is defined, the created
+ * object doesn't increase the lock count of the server module, as it
+ * does otherwise.
+ *
+ * @note In order to make it easier to use, this method does _not_ throw
+ * bad_alloc, but instead returns E_OUTOFMEMORY.
+ */
+ HRESULT createObject()
+ {
+ HRESULT hrc;
+#ifndef VBOX_WITH_XPCOM
+# ifdef VBOX_COM_OUTOFPROC_MODULE
+ ATL::CComObjectNoLock<T> *obj = NULL;
+ try
+ {
+ obj = new ATL::CComObjectNoLock<T>();
+ }
+ catch (std::bad_alloc &)
+ {
+ obj = NULL;
+ }
+ if (obj)
+ {
+ obj->InternalFinalConstructAddRef();
+ try
+ {
+ hrc = obj->FinalConstruct();
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+ obj->InternalFinalConstructRelease();
+ if (FAILED(hrc))
+ {
+ delete obj;
+ obj = NULL;
+ }
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+# else
+ ATL::CComObject<T> *obj = NULL;
+ hrc = ATL::CComObject<T>::CreateInstance(&obj);
+# endif
+#else /* VBOX_WITH_XPCOM */
+ ATL::CComObject<T> *obj;
+# ifndef RT_EXCEPTIONS_ENABLED
+ obj = new ATL::CComObject<T>();
+# else
+ try
+ {
+ obj = new ATL::CComObject<T>();
+ }
+ catch (std::bad_alloc &)
+ {
+ obj = NULL;
+ }
+# endif
+ if (obj)
+ {
+# ifndef RT_EXCEPTIONS_ENABLED
+ hrc = obj->FinalConstruct();
+# else
+ try
+ {
+ hrc = obj->FinalConstruct();
+ }
+ catch (std::bad_alloc &)
+ {
+ hrc = E_OUTOFMEMORY;
+ }
+# endif
+ if (FAILED(hrc))
+ {
+ delete obj;
+ obj = NULL;
+ }
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+#endif /* VBOX_WITH_XPCOM */
+ *this = obj;
+ return hrc;
+ }
+};
+
+/** @} */
+
+#endif /* !VBOX_INCLUDED_com_ptr_h */
+
diff --git a/include/VBox/com/string.h b/include/VBox/com/string.h
new file mode 100644
index 00000000..6bf19ae6
--- /dev/null
+++ b/include/VBox/com/string.h
@@ -0,0 +1,1494 @@
+/* $Id: string.h $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Smart string classes declaration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_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)
+ {
+ cleanupAndCopyFrom((const OLECHAR *)that.m_bstr);
+ return *this;
+ }
+
+ Bstr &operator=(CBSTR that)
+ {
+ cleanupAndCopyFrom((const OLECHAR *)that);
+ return *this;
+ }
+
+#if defined(VBOX_WITH_XPCOM)
+ Bstr &operator=(const wchar_t *that)
+ {
+ cleanupAndCopyFrom((const OLECHAR *)that);
+ return *this;
+ }
+#endif
+
+ Bstr &setNull()
+ {
+ cleanup();
+ 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(const Bstr &a_rSrcStr) RT_NOEXCEPT
+ {
+ return cleanupAndCopyFromEx((const OLECHAR *)a_rSrcStr.m_bstr);
+ }
+
+ /**
+ * Extended assignment method that returns a COM status code instead of an
+ * exception on failure.
+ *
+ * @returns S_OK or E_OUTOFMEMORY.
+ * @param a_pSrcStr The source string
+ */
+ HRESULT assignEx(CBSTR a_pSrcStr) RT_NOEXCEPT
+ {
+ return cleanupAndCopyFromEx((const OLECHAR *)a_pSrcStr);
+ }
+
+ /**
+ * Assign the value of a RTCString/Utf8Str string, no exceptions.
+ *
+ * @returns S_OK or E_OUTOFMEMORY.
+ * @param a_rSrcStr The source string
+ */
+ HRESULT assignEx(RTCString const &a_rSrcStr) RT_NOEXCEPT
+ {
+ return cleanupAndCopyFromNoThrow(a_rSrcStr.c_str(), a_rSrcStr.length());
+ }
+
+ /**
+ * Assign the value of a RTCString/Utf8Str substring, no exceptions.
+ *
+ * @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(RTCString const &a_rSrcStr, size_t a_offSrc, size_t a_cchSrc) RT_NOEXCEPT
+ {
+ size_t const cchTmp = a_rSrcStr.length();
+ if ( a_offSrc + a_cchSrc < cchTmp
+ && a_offSrc < cchTmp)
+ return cleanupAndCopyFromNoThrow(a_rSrcStr.c_str() + a_offSrc, a_cchSrc);
+ return E_INVALIDARG;
+ }
+
+ /**
+ * Assign the value of a zero terminated UTF-8 string, no exceptions.
+ *
+ * @returns S_OK or E_OUTOFMEMORY.
+ * @param a_pszSrcStr The source string.
+ */
+ HRESULT assignEx(const char *a_pszSrcStr) RT_NOEXCEPT
+ {
+ return cleanupAndCopyFromNoThrow(a_pszSrcStr, RTSTR_MAX);
+ }
+
+ /**
+ * Assign the value of a UTF-8 substring, no exceptions.
+ *
+ * @returns S_OK or E_OUTOFMEMORY.
+ * @param a_pszSrcStr The source string.
+ * @param a_cchSrc The number of characters (bytes) to copy from the source
+ * string.
+ */
+ HRESULT assignEx(const char *a_pszSrcStr, size_t a_cchSrc) RT_NOEXCEPT
+ {
+ return cleanupAndCopyFromNoThrow(a_pszSrcStr, a_cchSrc);
+ }
+
+#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; }
+
+ /**
+ * Checks if the string starts with @a a_rStart.
+ */
+ bool startsWith(Bstr const &a_rStart) const;
+ /**
+ * Checks if the string starts with @a a_rStart.
+ */
+ bool startsWith(RTCString const &a_rStart) const;
+ /**
+ * Checks if the string starts with @a a_pszStart.
+ */
+ bool startsWith(const char *a_pszStart) const;
+
+ /**
+ * 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); }
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ Bstr &printf(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ Bstr &printfV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Assigns the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /** @name Append methods and operators
+ * @{ */
+
+ /**
+ * Appends the string @a that to @a rThat.
+ *
+ * @param rThat The string to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(const Bstr &rThat);
+
+ /**
+ * Appends the string @a that to @a rThat.
+ *
+ * @param rThat The string to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(const Bstr &rThat) RT_NOEXCEPT;
+
+ /**
+ * Appends the UTF-8 string @a that to @a rThat.
+ *
+ * @param rThat The string to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(const RTCString &rThat);
+
+ /**
+ * Appends the UTF-8 string @a that to @a rThat.
+ *
+ * @param rThat The string to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(const RTCString &rThat) RT_NOEXCEPT;
+
+ /**
+ * Appends the UTF-16 string @a pszSrc to @a this.
+ *
+ * @param pwszSrc The C-style UTF-16 string to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(CBSTR pwszSrc);
+
+ /**
+ * Appends the UTF-16 string @a pszSrc to @a this.
+ *
+ * @param pwszSrc The C-style UTF-16 string to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(CBSTR pwszSrc) RT_NOEXCEPT;
+
+ /**
+ * Appends the UTF-8 string @a pszSrc to @a this.
+ *
+ * @param pszSrc The C-style string to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(const char *pszSrc);
+
+ /**
+ * Appends the UTF-8 string @a pszSrc to @a this.
+ *
+ * @param pszSrc The C-style string to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(const char *pszSrc) RT_NOEXCEPT;
+
+ /**
+ * Appends the a substring from @a rThat to @a this.
+ *
+ * @param rThat The string to append a substring from.
+ * @param offStart The start of the substring to append (UTF-16
+ * offset, not codepoint).
+ * @param cwcMax The maximum number of UTF-16 units to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(const Bstr &rThat, size_t offStart, size_t cwcMax = RTSTR_MAX);
+
+ /**
+ * Appends the a substring from @a rThat to @a this.
+ *
+ * @param rThat The string to append a substring from.
+ * @param offStart The start of the substring to append (UTF-16
+ * offset, not codepoint).
+ * @param cwcMax The maximum number of UTF-16 units to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(const Bstr &rThat, size_t offStart, size_t cwcMax = RTSTR_MAX) RT_NOEXCEPT;
+
+ /**
+ * Appends the a substring from UTF-8 @a rThat to @a this.
+ *
+ * @param rThat The string to append a substring from.
+ * @param offStart The start of the substring to append (byte offset,
+ * not codepoint).
+ * @param cchMax The maximum number of bytes to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(const RTCString &rThat, size_t offStart, size_t cchMax = RTSTR_MAX);
+
+ /**
+ * Appends the a substring from UTF-8 @a rThat to @a this.
+ *
+ * @param rThat The string to append a substring from.
+ * @param offStart The start of the substring to append (byte offset,
+ * not codepoint).
+ * @param cchMax The maximum number of bytes to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(const RTCString &rThat, size_t offStart, size_t cchMax = RTSTR_MAX) RT_NOEXCEPT;
+
+ /**
+ * Appends the first @a cchMax chars from UTF-16 string @a pszThat to @a this.
+ *
+ * @param pwszThat The C-style UTF-16 string to append.
+ * @param cchMax The maximum number of bytes to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(CBSTR pwszThat, size_t cchMax);
+
+ /**
+ * Appends the first @a cchMax chars from UTF-16 string @a pszThat to @a this.
+ *
+ * @param pwszThat The C-style UTF-16 string to append.
+ * @param cchMax The maximum number of bytes to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(CBSTR pwszThat, size_t cchMax) RT_NOEXCEPT;
+
+ /**
+ * Appends the first @a cchMax chars from string @a pszThat to @a this.
+ *
+ * @param pszThat The C-style string to append.
+ * @param cchMax The maximum number of bytes to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(const char *pszThat, size_t cchMax);
+
+ /**
+ * Appends the first @a cchMax chars from string @a pszThat to @a this.
+ *
+ * @param pszThat The C-style string to append.
+ * @param cchMax The maximum number of bytes to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(const char *pszThat, size_t cchMax) RT_NOEXCEPT;
+
+ /**
+ * Appends the given character to @a this.
+ *
+ * @param ch The character to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &append(char ch);
+
+ /**
+ * Appends the given character to @a this.
+ *
+ * @param ch The character to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendNoThrow(char ch) RT_NOEXCEPT;
+
+ /**
+ * Appends the given unicode code point to @a this.
+ *
+ * @param uc The unicode code point to append.
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ * @returns Reference to the object.
+ */
+ Bstr &appendCodePoint(RTUNICP uc);
+
+ /**
+ * Appends the given unicode code point to @a this.
+ *
+ * @param uc The unicode code point to append.
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendCodePointNoThrow(RTUNICP uc) RT_NOEXCEPT;
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ Bstr &appendPrintf(const char *pszFormat, ...) RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintf).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param ... Ellipsis containing the arguments specified by
+ * the format string.
+ *
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendPrintfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 2);
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @throws std::bad_alloc On allocation error. Object state is undefined.
+ *
+ * @returns Reference to the object.
+ */
+ Bstr &appendPrintfV(const char *pszFormat, va_list va) RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Appends the output of the string format operation (RTStrPrintfV).
+ *
+ * @param pszFormat Pointer to the format string,
+ * @see pg_rt_str_format.
+ * @param va Argument vector containing the arguments
+ * specified by the format string.
+ *
+ * @returns S_OK, E_OUTOFMEMORY or E_INVAL (bad encoding).
+ */
+ HRESULT appendPrintfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT RT_IPRT_FORMAT_ATTR(1, 0);
+
+ /**
+ * Shortcut to append(), Bstr variant.
+ *
+ * @param rThat The string to append.
+ * @returns Reference to the object.
+ */
+ Bstr &operator+=(const Bstr &rThat)
+ {
+ return append(rThat);
+ }
+
+ /**
+ * Shortcut to append(), RTCString variant.
+ *
+ * @param rThat The string to append.
+ * @returns Reference to the object.
+ */
+ Bstr &operator+=(const RTCString &rThat)
+ {
+ return append(rThat);
+ }
+
+ /**
+ * Shortcut to append(), CBSTR variant.
+ *
+ * @param pwszThat The C-style string to append.
+ * @returns Reference to the object.
+ */
+ Bstr &operator+=(CBSTR pwszThat)
+ {
+ return append(pwszThat);
+ }
+
+ /**
+ * Shortcut to append(), const char * variant.
+ *
+ * @param pszThat The C-style string to append.
+ * @returns Reference to the object.
+ */
+ Bstr &operator+=(const char *pszThat)
+ {
+ return append(pszThat);
+ }
+
+ /**
+ * Shortcut to append(), char variant.
+ *
+ * @param ch The character to append.
+ *
+ * @returns Reference to the object.
+ */
+ Bstr &operator+=(char ch)
+ {
+ return append(ch);
+ }
+
+ /** @} */
+
+ /**
+ * Erases a sequence from the string.
+ *
+ * @returns Reference to the object.
+ * @param offStart Where in @a this string to start erasing (UTF-16
+ * units, not codepoints).
+ * @param cwcLength How much following @a offStart to erase (UTF-16
+ * units, not codepoints).
+ */
+ Bstr &erase(size_t offStart = 0, size_t cwcLength = RTSTR_MAX) RT_NOEXCEPT;
+
+
+ /** @name BASE64 related methods
+ * @{ */
+ /**
+ * Encodes the given data as BASE64.
+ *
+ * @returns S_OK or E_OUTOFMEMORY.
+ * @param pvData Pointer to the data to encode.
+ * @param cbData Number of bytes to encode.
+ * @param fLineBreaks Whether to add line breaks (true) or just encode it
+ * as a continuous string.
+ * @sa RTBase64EncodeUtf16
+ */
+ HRESULT base64Encode(const void *pvData, size_t cbData, bool fLineBreaks = false);
+
+ /**
+ * Decodes the string as BASE64.
+ *
+ * @returns IPRT status code, see RTBase64DecodeUtf16Ex.
+ * @param pvData Where to return the decoded bytes.
+ * @param cbData Size of the @a pvData return buffer.
+ * @param pcbActual Where to return number of bytes actually decoded.
+ * This is optional and if not specified, the request
+ * will fail unless @a cbData matches the data size
+ * exactly.
+ * @param ppwszEnd Where to return pointer to the first non-base64
+ * character following the encoded data. This is
+ * optional and if NULL, the request will fail if there
+ * are anything trailing the encoded bytes in the
+ * string.
+ * @sa base64DecodedSize, RTBase64DecodeUtf16
+ */
+ int base64Decode(void *pvData, size_t cbData, size_t *pcbActual = NULL, PRTUTF16 *ppwszEnd = NULL);
+
+ /**
+ * Determins the size of the BASE64 encoded data in the string.
+ *
+ * @returns The length in bytes. -1 if the encoding is bad.
+ *
+ * @param ppwszEnd If not NULL, this will point to the first char
+ * following the Base64 encoded text block. If
+ * NULL the entire string is assumed to be Base64.
+ * @sa base64Decode, RTBase64DecodedUtf16Size
+ */
+ ssize_t base64DecodedSize(PRTUTF16 *ppwszEnd = NULL);
+ /** @} */
+
+#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 modifying the string directly.
+ *
+ * @note 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.
+ *
+ * @note If modifying the string size (only shrinking it is allows), #jolt() or
+ * #joltNoThrow() must be called!
+ *
+ * @note Do not modify memory beyond the #length() of the string!
+ *
+ * @sa joltNoThrow(), mutalbleRaw(), reserve(), reserveNoThrow()
+ */
+ BSTR mutableRaw() { return m_bstr; }
+
+ /**
+ * Correct the embedded length after using mutableRaw().
+ *
+ * This is needed on COM (Windows) to update the embedded string length. It is
+ * a stub on hosts using XPCOM.
+ *
+ * @param cwcNew The new string length, if handy, otherwise a negative
+ * number.
+ * @sa joltNoThrow(), mutalbleRaw(), reserve(), reserveNoThrow()
+ */
+#ifndef VBOX_WITH_XPCOM
+ void jolt(ssize_t cwcNew = -1);
+#else
+ void jolt(ssize_t cwcNew = -1)
+ {
+ Assert(cwcNew < 0 || (cwcNew == 0 && !m_bstr) || m_bstr[cwcNew] == '\0'); RT_NOREF(cwcNew);
+ }
+#endif
+
+ /**
+ * Correct the embedded length after using mutableRaw().
+ *
+ * This is needed on COM (Windows) to update the embedded string length. It is
+ * a stub on hosts using XPCOM.
+ *
+ * @returns S_OK on success, E_OUTOFMEMORY if shrinking the string failed.
+ * @param cwcNew The new string length, if handy, otherwise a negative
+ * number.
+ * @sa jolt(), mutalbleRaw(), reserve(), reserveNoThrow()
+ */
+#ifndef VBOX_WITH_XPCOM
+ HRESULT joltNoThrow(ssize_t cwcNew = -1) RT_NOEXCEPT;
+#else
+ HRESULT joltNoThrow(ssize_t cwcNew = -1) RT_NOEXCEPT
+ {
+ Assert(cwcNew < 0 || (cwcNew == 0 && !m_bstr) || m_bstr[cwcNew] == '\0'); RT_NOREF(cwcNew);
+ return S_OK;
+ }
+#endif
+
+ /**
+ * Make sure at that least @a cwc of buffer space is reserved.
+ *
+ * Requests that the contained memory buffer have at least cb bytes allocated.
+ * This may expand or shrink the string's storage, but will never truncate the
+ * contained string. In other words, cb will be ignored if it's smaller than
+ * length() + 1.
+ *
+ * @param cwcMin The new minimum string length that the can be stored. This
+ * does not include the terminator.
+ * @param fForce Force this size.
+ *
+ * @throws std::bad_alloc On allocation error. The object is left unchanged.
+ */
+ void reserve(size_t cwcMin, bool fForce = false);
+
+ /**
+ * A C like version of the #reserve() method, i.e. return code instead of throw.
+ *
+ * @returns S_OK or E_OUTOFMEMORY.
+ * @param cwcMin The new minimum string length that the can be stored. This
+ * does not include the terminator.
+ * @param fForce Force this size.
+ */
+ HRESULT reserveNoThrow(size_t cwcMin, bool fForce = false) RT_NOEXCEPT;
+
+ /**
+ * 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();
+
+ /**
+ * 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);
+
+ /** cleanup() + copyFrom() - for assignment operators. */
+ void cleanupAndCopyFrom(const OLECHAR *a_bstrSrc);
+
+ /**
+ * Protected internal helper to copy a string, implying cleanup().
+ *
+ * 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.
+ * @returns S_OK or E_OUTOFMEMORY
+ */
+ HRESULT cleanupAndCopyFromEx(const OLECHAR *a_bstrSrc) RT_NOEXCEPT;
+
+ /**
+ * 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);
+
+ /** cleanup() + non-throwing copyFromN(). */
+ HRESULT cleanupAndCopyFromNoThrow(const char *a_pszSrc, size_t a_cchMax) RT_NOEXCEPT;
+
+ Bstr &appendWorkerUtf16(PCRTUTF16 pwszSrc, size_t cwcSrc);
+ Bstr &appendWorkerUtf8(const char *pszSrc, size_t cchSrc);
+ HRESULT appendWorkerUtf16NoThrow(PCRTUTF16 pwszSrc, size_t cwcSrc) RT_NOEXCEPT;
+ HRESULT appendWorkerUtf8NoThrow(const char *pszSrc, size_t cchSrc) RT_NOEXCEPT;
+
+ static DECLCALLBACK(size_t) printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT;
+
+ 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 RT_NOEXCEPT;
+
+ /**
+ * 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
+ *
+ * @note Do not use in assignments to Utf8Str variables. Instead use
+ * RTCString::printf directly on the variable! This avoid an extra
+ * temporary Utf8Str instance and assignment operation.
+ */
+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:
+};
+
+/**
+ * Class with Bstr::printf as constructor for your convenience.
+ */
+class BstrFmt : public Bstr
+{
+public:
+
+ /**
+ * Constructs a new string given the format string and the list of the
+ * arguments for the format string.
+ *
+ * @param a_pszFormat printf-like format string (in UTF-8 encoding), see
+ * iprt/string.h for details.
+ * @param ... List of the arguments for the format string.
+ */
+ explicit BstrFmt(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:
+ BstrFmt()
+ { }
+};
+
+/**
+ * Class with Bstr::printfV as constructor for your convenience.
+ */
+class BstrFmtVA : public Bstr
+{
+public:
+
+ /**
+ * Constructs a new string given the format string and the list of the
+ * arguments for the format string.
+ *
+ * @param a_pszFormat printf-like format string (in UTF-8 encoding), see
+ * iprt/string.h for details.
+ * @param a_va List of arguments for the format string
+ */
+ BstrFmtVA(const char *a_pszFormat, va_list a_va) RT_IPRT_FORMAT_ATTR(1, 0)
+ {
+ printfV(a_pszFormat, a_va);
+ }
+
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+
+protected:
+ BstrFmtVA()
+ { }
+};
+
+} /* namespace com */
+
+/** @} */
+
+#endif /* !VBOX_INCLUDED_com_string_h */
+
diff --git a/include/VBox/com/utils.h b/include/VBox/com/utils.h
new file mode 100644
index 00000000..1fd37e72
--- /dev/null
+++ b/include/VBox/com/utils.h
@@ -0,0 +1,132 @@
+/** @file
+ * MS COM / XPCOM Abstraction Layer - COM initialization / shutdown.
+ */
+
+/*
+ * Copyright (C) 2005-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef VBOX_INCLUDED_com_utils_h
+#define VBOX_INCLUDED_com_utils_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "iprt/types.h"
+
+/** @addtogroup grp_com
+ * @{
+ */
+
+namespace com
+{
+
+/**
+ * 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.
+ * @returns 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 pszEntity Human readable name of the program.
+ * @param pszLogFile Name of the release log file.
+ * @param fFlags Logger instance flags.
+ * @param pszGroupSettings Group logging settings.
+ * @param pszEnvVarBase 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.
+ *
+ * @returns VBox status code.
+ */
+int VBoxLogRelCreate(const char *pszEntity, const char *pszLogFile,
+ uint32_t fFlags, const char *pszGroupSettings,
+ const char *pszEnvVarBase, uint32_t fDestFlags,
+ uint32_t cMaxEntriesPerGroup, uint32_t cHistory,
+ uint32_t uHistoryFileTime, uint64_t uHistoryFileSize,
+ PRTERRINFO pErrInfo);
+
+/**
+ * Creates a release log file, used both in VBoxSVC and in API clients.
+ *
+ * @param pszEntity Human readable name of the program.
+ * @param pszLogFile Name of the release log file.
+ * @param fFlags Logger instance flags.
+ * @param pszGroupSettings Group logging settings.
+ * @param pszEnvVarBase 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 pOutputIf The optional file output interface, can be NULL which will
+ * make use of the default one.
+ * @param pvOutputIfUser The opaque user data to pass to the callbacks in the output interface.
+ * @param pErrInfo Where to return extended error information.
+ * Optional.
+ *
+ * @returns VBox status code.
+ *
+ * @note Can't include log.h here because of precompiled header fun, hence pOutputIf is void *...
+ */
+int VBoxLogRelCreateEx(const char *pszEntity, const char *pszLogFile,
+ uint32_t fFlags, const char *pszGroupSettings,
+ const char *pszEnvVarBase, uint32_t fDestFlags,
+ uint32_t cMaxEntriesPerGroup, uint32_t cHistory,
+ uint32_t uHistoryFileTime, uint64_t uHistoryFileSize,
+ const void *pOutputIf, void *pvOutputIfUser,
+ PRTERRINFO pErrInfo);
+
+} /* namespace com */
+
+/** @} */
+#endif /* !VBOX_INCLUDED_com_utils_h */
+