summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/glue
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Main/glue
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Main/glue')
-rw-r--r--src/VBox/Main/glue/AutoLock.cpp795
-rw-r--r--src/VBox/Main/glue/ErrorInfo.cpp374
-rw-r--r--src/VBox/Main/glue/EventQueue.cpp257
-rw-r--r--src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp142
-rw-r--r--src/VBox/Main/glue/Makefile.kup0
-rw-r--r--src/VBox/Main/glue/NativeEventQueue.cpp675
-rw-r--r--src/VBox/Main/glue/VBoxLogRelCreate.cpp214
-rw-r--r--src/VBox/Main/glue/com.cpp185
-rwxr-xr-xsrc/VBox/Main/glue/constants-python.xsl198
-rw-r--r--src/VBox/Main/glue/errorprint.cpp222
-rw-r--r--src/VBox/Main/glue/glue-java.xsl5259
-rw-r--r--src/VBox/Main/glue/initterm.cpp861
-rw-r--r--src/VBox/Main/glue/string-base64.cpp61
-rw-r--r--src/VBox/Main/glue/string.cpp1037
-rw-r--r--src/VBox/Main/glue/tests/Makefile81
-rw-r--r--src/VBox/Main/glue/tests/TestVBox.java310
-rw-r--r--src/VBox/Main/glue/tests/TestVBoxNATEngine.java206
-rw-r--r--src/VBox/Main/glue/vbox-err-consts.sed59
-rwxr-xr-xsrc/VBox/Main/glue/vboxapi.py1294
-rw-r--r--src/VBox/Main/glue/xpcom/Makefile.kup0
-rw-r--r--src/VBox/Main/glue/xpcom/helpers.cpp204
21 files changed, 12434 insertions, 0 deletions
diff --git a/src/VBox/Main/glue/AutoLock.cpp b/src/VBox/Main/glue/AutoLock.cpp
new file mode 100644
index 00000000..bc7b6eff
--- /dev/null
+++ b/src/VBox/Main/glue/AutoLock.cpp
@@ -0,0 +1,795 @@
+/* $Id: AutoLock.cpp $ */
+/** @file
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define GLUE_USE_CRITSECTRW
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/critsect.h>
+#include <iprt/thread.h>
+#include <iprt/semaphore.h>
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+
+#if defined(RT_LOCK_STRICT)
+# include <iprt/asm.h> // for ASMReturnAddress
+#endif
+
+#include <iprt/string.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+
+#include "VBox/com/AutoLock.h"
+#include <VBox/com/string.h>
+
+#include <vector>
+#include <list>
+#include <map>
+
+
+namespace util
+{
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// RuntimeLockClass
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+typedef std::map<VBoxLockingClass, RTLOCKVALCLASS> LockValidationClassesMap;
+LockValidationClassesMap g_mapLockValidationClasses;
+#endif
+
+/**
+ * Called from initterm.cpp on process initialization (on the main thread)
+ * to give us a chance to initialize lock validation runtime data.
+ */
+void InitAutoLockSystem()
+{
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ struct
+ {
+ VBoxLockingClass cls;
+ const char *pcszDescription;
+ } aClasses[] =
+ {
+ { LOCKCLASS_VIRTUALBOXOBJECT, "2-VIRTUALBOXOBJECT" },
+ { LOCKCLASS_HOSTOBJECT, "3-HOSTOBJECT" },
+ { LOCKCLASS_LISTOFMACHINES, "4-LISTOFMACHINES" },
+ { LOCKCLASS_MACHINEOBJECT, "5-MACHINEOBJECT" },
+ { LOCKCLASS_SNAPSHOTOBJECT, "6-SNAPSHOTOBJECT" },
+ { LOCKCLASS_MEDIUMQUERY, "7-MEDIUMQUERY" },
+ { LOCKCLASS_LISTOFMEDIA, "8-LISTOFMEDIA" },
+ { LOCKCLASS_LISTOFOTHEROBJECTS, "9-LISTOFOTHEROBJECTS" },
+ { LOCKCLASS_OTHEROBJECT, "10-OTHEROBJECT" },
+ { LOCKCLASS_PROGRESSLIST, "11-PROGRESSLIST" },
+ { LOCKCLASS_OBJECTSTATE, "12-OBJECTSTATE" },
+ { LOCKCLASS_TRANSLATOR, "13-TRANSLATOR" }
+ };
+
+ RTLOCKVALCLASS hClass;
+ int vrc;
+ for (unsigned i = 0; i < RT_ELEMENTS(aClasses); ++i)
+ {
+ vrc = RTLockValidatorClassCreate(&hClass,
+ true, /*fAutodidact*/
+ RT_SRC_POS,
+ aClasses[i].pcszDescription);
+ AssertRC(vrc);
+
+ // teach the new class that the classes created previously can be held
+ // while the new class is being acquired
+ for (LockValidationClassesMap::iterator it = g_mapLockValidationClasses.begin();
+ it != g_mapLockValidationClasses.end();
+ ++it)
+ {
+ RTLOCKVALCLASS &canBeHeld = it->second;
+ vrc = RTLockValidatorClassAddPriorClass(hClass,
+ canBeHeld);
+ AssertRC(vrc);
+ }
+
+ // and store the new class
+ g_mapLockValidationClasses[aClasses[i].cls] = hClass;
+ }
+
+/* WriteLockHandle critsect1(LOCKCLASS_VIRTUALBOXOBJECT);
+ WriteLockHandle critsect2(LOCKCLASS_VIRTUALBOXLIST);
+
+ AutoWriteLock lock1(critsect1 COMMA_LOCKVAL_SRC_POS);
+ AutoWriteLock lock2(critsect2 COMMA_LOCKVAL_SRC_POS);*/
+#endif
+}
+
+bool AutoLockHoldsLocksInClass(VBoxLockingClass lockClass)
+{
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ return RTLockValidatorHoldsLocksInClass(NIL_RTTHREAD, g_mapLockValidationClasses[lockClass]);
+#else
+ RT_NOREF(lockClass);
+ return false;
+#endif
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// RWLockHandle
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct RWLockHandle::Data
+{
+ Data()
+ { }
+
+#ifdef GLUE_USE_CRITSECTRW
+ mutable RTCRITSECTRW CritSect;
+#else
+ RTSEMRW sem;
+#endif
+ VBoxLockingClass lockClass;
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ com::Utf8Str strDescription;
+#endif
+};
+
+RWLockHandle::RWLockHandle(VBoxLockingClass lockClass)
+{
+ m = new Data();
+
+ m->lockClass = lockClass;
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ m->strDescription.printf("r/w %RCv", this);
+#endif
+
+#ifdef GLUE_USE_CRITSECTRW
+# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ int vrc = RTCritSectRwInitEx(&m->CritSect, 0 /*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL);
+# else
+ int vrc = RTCritSectRwInitEx(&m->CritSect, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL);
+# endif
+#else
+# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ int vrc = RTSemRWCreateEx(&m->sem, 0 /*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL);
+# else
+ int vrc = RTSemRWCreateEx(&m->sem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL);
+# endif
+#endif
+ AssertRC(vrc);
+}
+
+/*virtual*/ RWLockHandle::~RWLockHandle()
+{
+#ifdef GLUE_USE_CRITSECTRW
+ RTCritSectRwDelete(&m->CritSect);
+#else
+ RTSemRWDestroy(m->sem);
+#endif
+ delete m;
+}
+
+/*virtual*/ bool RWLockHandle::isWriteLockOnCurrentThread() const
+{
+#ifdef GLUE_USE_CRITSECTRW
+ return RTCritSectRwIsWriteOwner(&m->CritSect);
+#else
+ return RTSemRWIsWriteOwner(m->sem);
+#endif
+}
+
+/*virtual*/ void RWLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL)
+{
+#ifdef GLUE_USE_CRITSECTRW
+# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ int vrc = RTCritSectRwEnterExclDebug(&m->CritSect, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
+# else
+ int vrc = RTCritSectRwEnterExcl(&m->CritSect);
+# endif
+#else
+# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ int vrc = RTSemRWRequestWriteDebug(m->sem, RT_INDEFINITE_WAIT, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
+# else
+ int vrc = RTSemRWRequestWrite(m->sem, RT_INDEFINITE_WAIT);
+# endif
+#endif
+ AssertRC(vrc);
+}
+
+/*virtual*/ void RWLockHandle::unlockWrite()
+{
+#ifdef GLUE_USE_CRITSECTRW
+ int vrc = RTCritSectRwLeaveExcl(&m->CritSect);
+#else
+ int vrc = RTSemRWReleaseWrite(m->sem);
+#endif
+ AssertRC(vrc);
+
+}
+
+/*virtual*/ bool RWLockHandle::isReadLockedOnCurrentThread(bool fWannaHear) const
+{
+#ifdef GLUE_USE_CRITSECTRW
+ return RTCritSectRwIsReadOwner(&m->CritSect, fWannaHear);
+#else
+ return RTSemRWIsReadOwner(m->sem, fWannaHear);
+#endif
+}
+
+/*virtual*/ void RWLockHandle::lockRead(LOCKVAL_SRC_POS_DECL)
+{
+#ifdef GLUE_USE_CRITSECTRW
+# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ int vrc = RTCritSectRwEnterSharedDebug(&m->CritSect, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
+# else
+ int vrc = RTCritSectRwEnterShared(&m->CritSect);
+# endif
+#else
+# ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ int vrc = RTSemRWRequestReadDebug(m->sem, RT_INDEFINITE_WAIT, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
+# else
+ int vrc = RTSemRWRequestRead(m->sem, RT_INDEFINITE_WAIT);
+# endif
+#endif
+ AssertRC(vrc);
+}
+
+/*virtual*/ void RWLockHandle::unlockRead()
+{
+#ifdef GLUE_USE_CRITSECTRW
+ int vrc = RTCritSectRwLeaveShared(&m->CritSect);
+#else
+ int vrc = RTSemRWReleaseRead(m->sem);
+#endif
+ AssertRC(vrc);
+}
+
+/*virtual*/ uint32_t RWLockHandle::writeLockLevel() const
+{
+ /* Note! This does not include read recursions done by the writer! */
+#ifdef GLUE_USE_CRITSECTRW
+ return RTCritSectRwGetWriteRecursion(&m->CritSect);
+#else
+ return RTSemRWGetWriteRecursion(m->sem);
+#endif
+}
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+/*virtual*/ const char* RWLockHandle::describe() const
+{
+ return m->strDescription.c_str();
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// WriteLockHandle
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct WriteLockHandle::Data
+{
+ Data()
+ { }
+
+ mutable RTCRITSECT sem;
+ VBoxLockingClass lockClass;
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ com::Utf8Str strDescription;
+#endif
+};
+
+WriteLockHandle::WriteLockHandle(VBoxLockingClass lockClass)
+{
+ m = new Data;
+
+ m->lockClass = lockClass;
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ m->strDescription = com::Utf8StrFmt("crit %RCv", this);
+ int vrc = RTCritSectInitEx(&m->sem, 0/*fFlags*/, g_mapLockValidationClasses[lockClass], RTLOCKVAL_SUB_CLASS_ANY, NULL);
+#else
+ int vrc = RTCritSectInitEx(&m->sem, 0/*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_ANY, NULL);
+#endif
+ AssertRC(vrc);
+}
+
+WriteLockHandle::~WriteLockHandle()
+{
+ RTCritSectDelete(&m->sem);
+ delete m;
+}
+
+/*virtual*/ bool WriteLockHandle::isWriteLockOnCurrentThread() const
+{
+ return RTCritSectIsOwner(&m->sem);
+}
+
+/*virtual*/ void WriteLockHandle::lockWrite(LOCKVAL_SRC_POS_DECL)
+{
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ RTCritSectEnterDebug(&m->sem, (uintptr_t)ASMReturnAddress(), RT_SRC_POS_ARGS);
+#else
+ RTCritSectEnter(&m->sem);
+#endif
+}
+
+/*virtual*/ bool WriteLockHandle::isReadLockedOnCurrentThread(bool fWannaHear) const
+{
+ RT_NOREF(fWannaHear);
+ return RTCritSectIsOwner(&m->sem);
+}
+
+/*virtual*/ void WriteLockHandle::unlockWrite()
+{
+ RTCritSectLeave(&m->sem);
+}
+
+/*virtual*/ void WriteLockHandle::lockRead(LOCKVAL_SRC_POS_DECL)
+{
+ lockWrite(LOCKVAL_SRC_POS_ARGS);
+}
+
+/*virtual*/ void WriteLockHandle::unlockRead()
+{
+ unlockWrite();
+}
+
+/*virtual*/ uint32_t WriteLockHandle::writeLockLevel() const
+{
+ return RTCritSectGetRecursion(&m->sem);
+}
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+/*virtual*/ const char* WriteLockHandle::describe() const
+{
+ return m->strDescription.c_str();
+}
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AutoLockBase
+//
+////////////////////////////////////////////////////////////////////////////////
+
+typedef std::vector<LockHandle*> HandlesVector;
+
+struct AutoLockBase::Data
+{
+ Data(size_t cHandles
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ , const char *pcszFile_,
+ unsigned uLine_,
+ const char *pcszFunction_
+#endif
+ )
+ : fIsLocked(false),
+ aHandles(cHandles) // size of array
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ , pcszFile(pcszFile_),
+ uLine(uLine_),
+ pcszFunction(pcszFunction_)
+#endif
+ {
+ for (uint32_t i = 0; i < cHandles; ++i)
+ aHandles[i] = NULL;
+ }
+
+ bool fIsLocked; // if true, then all items in aHandles are locked by this AutoLock and
+ // need to be unlocked in the destructor
+ HandlesVector aHandles; // array (vector) of LockHandle instances; in the case of AutoWriteLock
+ // and AutoReadLock, there will only be one item on the list; with the
+ // AutoMulti* derivatives, there will be multiple
+
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ // information about where the lock occurred (passed down from the AutoLock classes)
+ const char *pcszFile;
+ unsigned uLine;
+ const char *pcszFunction;
+#endif
+};
+
+AutoLockBase::AutoLockBase(uint32_t cHandles
+ COMMA_LOCKVAL_SRC_POS_DECL)
+{
+ m = new Data(cHandles COMMA_LOCKVAL_SRC_POS_ARGS);
+}
+
+AutoLockBase::AutoLockBase(uint32_t cHandles,
+ LockHandle *pHandle
+ COMMA_LOCKVAL_SRC_POS_DECL)
+{
+ Assert(cHandles == 1); NOREF(cHandles);
+ m = new Data(1 COMMA_LOCKVAL_SRC_POS_ARGS);
+ m->aHandles[0] = pHandle;
+}
+
+AutoLockBase::~AutoLockBase()
+{
+ delete m;
+}
+
+/**
+ * Requests ownership of all contained lock handles by calling
+ * the pure virtual callLockImpl() function on each of them,
+ * which must be implemented by the descendant class; in the
+ * implementation, AutoWriteLock will request a write lock
+ * whereas AutoReadLock will request a read lock.
+ *
+ * Does *not* modify the lock counts in the member variables.
+ */
+void AutoLockBase::callLockOnAllHandles()
+{
+ for (HandlesVector::iterator it = m->aHandles.begin();
+ it != m->aHandles.end();
+ ++it)
+ {
+ LockHandle *pHandle = *it;
+ if (pHandle)
+ // call virtual function implemented in AutoWriteLock or AutoReadLock
+ this->callLockImpl(*pHandle);
+ }
+}
+
+/**
+ * Releases ownership of all contained lock handles by calling
+ * the pure virtual callUnlockImpl() function on each of them,
+ * which must be implemented by the descendant class; in the
+ * implementation, AutoWriteLock will release a write lock
+ * whereas AutoReadLock will release a read lock.
+ *
+ * Does *not* modify the lock counts in the member variables.
+ */
+void AutoLockBase::callUnlockOnAllHandles()
+{
+ // unlock in reverse order!
+ for (HandlesVector::reverse_iterator it = m->aHandles.rbegin();
+ it != m->aHandles.rend();
+ ++it)
+ {
+ LockHandle *pHandle = *it;
+ if (pHandle)
+ // call virtual function implemented in AutoWriteLock or AutoReadLock
+ this->callUnlockImpl(*pHandle);
+ }
+}
+
+/**
+ * Destructor implementation that can also be called explicitly, if required.
+ * Restores the exact state before the AutoLock was created; that is, unlocks
+ * all contained semaphores.
+ */
+void AutoLockBase::cleanup()
+{
+ if (m->fIsLocked)
+ callUnlockOnAllHandles();
+}
+
+/**
+ * Requests ownership of all contained semaphores. Public method that can
+ * only be called once and that also gets called by the AutoLock constructors.
+ */
+void AutoLockBase::acquire()
+{
+ AssertMsgReturnVoid(!m->fIsLocked, ("m->fIsLocked is true, attempting to lock twice!"));
+ callLockOnAllHandles();
+ m->fIsLocked = true;
+}
+
+/**
+ * Releases ownership of all contained semaphores. Public method.
+ */
+void AutoLockBase::release()
+{
+ AssertMsgReturnVoid(m->fIsLocked, ("m->fIsLocked is false, cannot release!"));
+ callUnlockOnAllHandles();
+ m->fIsLocked = false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AutoReadLock
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Release all read locks acquired by this instance through the #lock()
+ * call and destroys the instance.
+ *
+ * Note that if there there are nested #lock() calls without the
+ * corresponding number of #unlock() 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*/ AutoReadLock::~AutoReadLock()
+{
+ LockHandle *pHandle = m->aHandles[0];
+
+ if (pHandle)
+ {
+ if (m->fIsLocked)
+ callUnlockImpl(*pHandle);
+ }
+}
+
+/**
+ * Implementation of the pure virtual declared in AutoLockBase.
+ * This gets called by AutoLockBase.acquire() to actually request
+ * the semaphore; in the AutoReadLock implementation, we request
+ * the semaphore in read mode.
+ */
+/*virtual*/ void AutoReadLock::callLockImpl(LockHandle &l)
+{
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ l.lockRead(m->pcszFile, m->uLine, m->pcszFunction);
+#else
+ l.lockRead();
+#endif
+}
+
+/**
+ * Implementation of the pure virtual declared in AutoLockBase.
+ * This gets called by AutoLockBase.release() to actually release
+ * the semaphore; in the AutoReadLock implementation, we release
+ * the semaphore in read mode.
+ */
+/*virtual*/ void AutoReadLock::callUnlockImpl(LockHandle &l)
+{
+ l.unlockRead();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AutoWriteLockBase
+//
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Implementation of the pure virtual declared in AutoLockBase.
+ * This gets called by AutoLockBase.acquire() to actually request
+ * the semaphore; in the AutoWriteLock implementation, we request
+ * the semaphore in write mode.
+ */
+/*virtual*/ void AutoWriteLockBase::callLockImpl(LockHandle &l)
+{
+#ifdef VBOX_WITH_MAIN_LOCK_VALIDATION
+ l.lockWrite(m->pcszFile, m->uLine, m->pcszFunction);
+#else
+ l.lockWrite();
+#endif
+}
+
+/**
+ * Implementation of the pure virtual declared in AutoLockBase.
+ * This gets called by AutoLockBase.release() to actually release
+ * the semaphore; in the AutoWriteLock implementation, we release
+ * the semaphore in write mode.
+ */
+/*virtual*/ void AutoWriteLockBase::callUnlockImpl(LockHandle &l)
+{
+ l.unlockWrite();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AutoWriteLock
+//
+////////////////////////////////////////////////////////////////////////////////
+
+AutoWriteLock::AutoWriteLock(uint32_t cHandles,
+ LockHandle** pHandles
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(cHandles
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ Assert(cHandles);
+ Assert(pHandles);
+
+ for (uint32_t i = 0; i < cHandles; ++i)
+ m->aHandles[i] = pHandles[i];
+
+ acquire();
+}
+
+
+
+/**
+ * Attaches another handle to this auto lock instance.
+ *
+ * The previous object's lock is completely released before the new one is
+ * acquired. The lock level of the new handle will be the same. This
+ * also means that if the lock was not acquired at all before #attach(), it
+ * will not be acquired on the new handle too.
+ *
+ * @param aHandle New handle to attach.
+ */
+void AutoWriteLock::attach(LockHandle *aHandle)
+{
+ LockHandle *pHandle = m->aHandles[0];
+
+ /* detect simple self-reattachment */
+ if (pHandle != aHandle)
+ {
+ bool fWasLocked = m->fIsLocked;
+
+ cleanup();
+
+ m->aHandles[0] = aHandle;
+ m->fIsLocked = fWasLocked;
+
+ if (aHandle)
+ if (fWasLocked)
+ callLockImpl(*aHandle);
+ }
+}
+
+/**
+ * Returns @c true if the current thread holds a write lock on the managed
+ * read/write semaphore. Returns @c false if the managed semaphore is @c
+ * NULL.
+ *
+ * @note Intended for debugging only.
+ */
+bool AutoWriteLock::isWriteLockOnCurrentThread() const
+{
+ return m->aHandles[0] ? m->aHandles[0]->isWriteLockOnCurrentThread() : false;
+}
+
+ /**
+ * Returns the current write lock level of the managed semaphore. The lock
+ * level determines the number of nested #lock() calls on the given
+ * semaphore handle. Returns @c 0 if the managed semaphore is @c
+ * NULL.
+ *
+ * Note that this call is valid only when the current thread owns a write
+ * lock on the given semaphore handle and will assert otherwise.
+ *
+ * @note Intended for debugging only.
+ */
+uint32_t AutoWriteLock::writeLockLevel() const
+{
+ return m->aHandles[0] ? m->aHandles[0]->writeLockLevel() : 0;
+}
+
+/**
+ * Returns @c true if the current thread holds a write lock on the managed
+ * read/write semaphore. Returns @c false if the managed semaphore is @c
+ * NULL.
+ *
+ * @note Intended for debugging only (esp. considering fWannaHear).
+ */
+bool AutoWriteLock::isReadLockedOnCurrentThread(bool fWannaHear) const
+{
+ return m->aHandles[0] ? m->aHandles[0]->isReadLockedOnCurrentThread(fWannaHear) : false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AutoMultiWriteLock*
+//
+////////////////////////////////////////////////////////////////////////////////
+
+AutoMultiWriteLock2::AutoMultiWriteLock2(Lockable *pl1,
+ Lockable *pl2
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(2
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ if (pl1)
+ m->aHandles[0] = pl1->lockHandle();
+ if (pl2)
+ m->aHandles[1] = pl2->lockHandle();
+ acquire();
+}
+
+AutoMultiWriteLock2::AutoMultiWriteLock2(LockHandle *pl1,
+ LockHandle *pl2
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(2
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ m->aHandles[0] = pl1;
+ m->aHandles[1] = pl2;
+ acquire();
+}
+
+AutoMultiWriteLock3::AutoMultiWriteLock3(Lockable *pl1,
+ Lockable *pl2,
+ Lockable *pl3
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(3
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ if (pl1)
+ m->aHandles[0] = pl1->lockHandle();
+ if (pl2)
+ m->aHandles[1] = pl2->lockHandle();
+ if (pl3)
+ m->aHandles[2] = pl3->lockHandle();
+ acquire();
+}
+
+AutoMultiWriteLock3::AutoMultiWriteLock3(LockHandle *pl1,
+ LockHandle *pl2,
+ LockHandle *pl3
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(3
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ m->aHandles[0] = pl1;
+ m->aHandles[1] = pl2;
+ m->aHandles[2] = pl3;
+ acquire();
+}
+
+AutoMultiWriteLock4::AutoMultiWriteLock4(Lockable *pl1,
+ Lockable *pl2,
+ Lockable *pl3,
+ Lockable *pl4
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(4
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ if (pl1)
+ m->aHandles[0] = pl1->lockHandle();
+ if (pl2)
+ m->aHandles[1] = pl2->lockHandle();
+ if (pl3)
+ m->aHandles[2] = pl3->lockHandle();
+ if (pl4)
+ m->aHandles[3] = pl4->lockHandle();
+ acquire();
+}
+
+AutoMultiWriteLock4::AutoMultiWriteLock4(LockHandle *pl1,
+ LockHandle *pl2,
+ LockHandle *pl3,
+ LockHandle *pl4
+ COMMA_LOCKVAL_SRC_POS_DECL)
+ : AutoWriteLockBase(4
+ COMMA_LOCKVAL_SRC_POS_ARGS)
+{
+ m->aHandles[0] = pl1;
+ m->aHandles[1] = pl2;
+ m->aHandles[2] = pl3;
+ m->aHandles[3] = pl4;
+ acquire();
+}
+
+} /* namespace util */
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
diff --git a/src/VBox/Main/glue/ErrorInfo.cpp b/src/VBox/Main/glue/ErrorInfo.cpp
new file mode 100644
index 00000000..1fffa14a
--- /dev/null
+++ b/src/VBox/Main/glue/ErrorInfo.cpp
@@ -0,0 +1,374 @@
+/* $Id: ErrorInfo.cpp $ */
+
+/** @file
+ *
+ * ErrorInfo class definition
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#if defined(VBOX_WITH_XPCOM)
+# include <nsIServiceManager.h>
+# include <nsIExceptionService.h>
+# include <nsCOMPtr.h>
+#endif
+
+#include "VBox/com/VirtualBox.h"
+#include "VBox/com/ErrorInfo.h"
+#include "VBox/com/assert.h"
+#include "VBox/com/com.h"
+#include "VBox/com/MultiResult.h"
+
+#include <iprt/stream.h>
+#include <iprt/string.h>
+
+#include <iprt/errcore.h>
+
+namespace com
+{
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ErrorInfo class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+HRESULT ErrorInfo::getVirtualBoxErrorInfo(ComPtr<IVirtualBoxErrorInfo> &pVirtualBoxErrorInfo)
+{
+ HRESULT hrc = S_OK;
+ if (mErrorInfo)
+ hrc = mErrorInfo.queryInterfaceTo(pVirtualBoxErrorInfo.asOutParam());
+ else
+ pVirtualBoxErrorInfo.setNull();
+ return hrc;
+}
+
+void ErrorInfo::copyFrom(const ErrorInfo &x)
+{
+ mIsBasicAvailable = x.mIsBasicAvailable;
+ mIsFullAvailable = x.mIsFullAvailable;
+
+ mResultCode = x.mResultCode;
+ mResultDetail = x.mResultDetail;
+ mInterfaceID = x.mInterfaceID;
+ mComponent = x.mComponent;
+ mText = x.mText;
+
+ if (x.m_pNext != NULL)
+ m_pNext = new ErrorInfo(*x.m_pNext);
+ else
+ m_pNext = NULL;
+
+ mInterfaceName = x.mInterfaceName;
+ mCalleeIID = x.mCalleeIID;
+ mCalleeName = x.mCalleeName;
+
+ mErrorInfo = x.mErrorInfo;
+}
+
+void ErrorInfo::cleanup()
+{
+ mIsBasicAvailable = false;
+ mIsFullAvailable = false;
+
+ if (m_pNext)
+ {
+ delete m_pNext;
+ m_pNext = NULL;
+ }
+
+ mResultCode = S_OK;
+ mResultDetail = 0;
+ mInterfaceID.clear();
+ mComponent.setNull();
+ mText.setNull();
+ mInterfaceName.setNull();
+ mCalleeIID.clear();
+ mCalleeName.setNull();
+ mErrorInfo.setNull();
+}
+
+void ErrorInfo::init(bool aKeepObj /* = false */)
+{
+ HRESULT hrc = E_FAIL;
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ ComPtr<IErrorInfo> err;
+ hrc = ::GetErrorInfo(0, err.asOutParam());
+ if (hrc == S_OK && err)
+ {
+ if (aKeepObj)
+ mErrorInfo = err;
+
+ ComPtr<IVirtualBoxErrorInfo> info;
+ hrc = err.queryInterfaceTo(info.asOutParam());
+ if (SUCCEEDED(hrc) && info)
+ init(info);
+
+ if (!mIsFullAvailable)
+ {
+ bool gotSomething = false;
+
+ hrc = err->GetGUID(mInterfaceID.asOutParam());
+ gotSomething |= SUCCEEDED(hrc);
+ if (SUCCEEDED(hrc))
+ GetInterfaceNameByIID(mInterfaceID.ref(), mInterfaceName.asOutParam());
+
+ hrc = err->GetSource(mComponent.asOutParam());
+ gotSomething |= SUCCEEDED(hrc);
+
+ hrc = err->GetDescription(mText.asOutParam());
+ gotSomething |= SUCCEEDED(hrc);
+
+ if (gotSomething)
+ mIsBasicAvailable = true;
+
+ AssertMsg(gotSomething, ("Nothing to fetch!\n"));
+ }
+ }
+
+#else // defined(VBOX_WITH_XPCOM)
+
+ nsCOMPtr<nsIExceptionService> es;
+ es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &hrc);
+ if (NS_SUCCEEDED(hrc))
+ {
+ nsCOMPtr<nsIExceptionManager> em;
+ hrc = es->GetCurrentExceptionManager(getter_AddRefs(em));
+ if (NS_SUCCEEDED(hrc))
+ {
+ ComPtr<nsIException> ex;
+ hrc = em->GetCurrentException(ex.asOutParam());
+ if (NS_SUCCEEDED(hrc) && ex)
+ {
+ if (aKeepObj)
+ mErrorInfo = ex;
+
+ ComPtr<IVirtualBoxErrorInfo> info;
+ hrc = ex.queryInterfaceTo(info.asOutParam());
+ if (NS_SUCCEEDED(hrc) && info)
+ init(info);
+
+ if (!mIsFullAvailable)
+ {
+ bool gotSomething = false;
+
+ hrc = ex->GetResult(&mResultCode);
+ gotSomething |= NS_SUCCEEDED(hrc);
+
+ char *pszMsg;
+ hrc = ex->GetMessage(&pszMsg);
+ gotSomething |= NS_SUCCEEDED(hrc);
+ if (NS_SUCCEEDED(hrc))
+ {
+ mText = Bstr(pszMsg);
+ nsMemory::Free(pszMsg);
+ }
+
+ if (gotSomething)
+ mIsBasicAvailable = true;
+
+ AssertMsg(gotSomething, ("Nothing to fetch!\n"));
+ }
+
+ // set the exception to NULL (to emulate Win32 behavior)
+ em->SetCurrentException(NULL);
+
+ hrc = NS_OK;
+ }
+ }
+ }
+ /* Ignore failure when called after nsComponentManagerImpl::Shutdown(). */
+ else if (hrc == NS_ERROR_UNEXPECTED)
+ hrc = NS_OK;
+
+ AssertComRC(hrc);
+
+#endif // defined(VBOX_WITH_XPCOM)
+}
+
+void ErrorInfo::init(IUnknown *aI,
+ const GUID &aIID,
+ bool aKeepObj /* = false */)
+{
+ AssertReturnVoid(aI);
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ ComPtr<IUnknown> iface = aI;
+ ComPtr<ISupportErrorInfo> serr;
+ HRESULT hrc = iface.queryInterfaceTo(serr.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ hrc = serr->InterfaceSupportsErrorInfo(aIID);
+ if (SUCCEEDED(hrc))
+ init(aKeepObj);
+ }
+
+#else
+
+ init(aKeepObj);
+
+#endif
+
+ if (mIsBasicAvailable)
+ {
+ mCalleeIID = aIID;
+ GetInterfaceNameByIID(aIID, mCalleeName.asOutParam());
+ }
+}
+
+void ErrorInfo::init(IVirtualBoxErrorInfo *info)
+{
+ AssertReturnVoid(info);
+
+ HRESULT hrc = E_FAIL;
+ bool gotSomething = false;
+ bool gotAll = true;
+ LONG lrc, lrd;
+
+ hrc = info->COMGETTER(ResultCode)(&lrc); mResultCode = lrc;
+ gotSomething |= SUCCEEDED(hrc);
+ gotAll &= SUCCEEDED(hrc);
+
+ hrc = info->COMGETTER(ResultDetail)(&lrd); mResultDetail = lrd;
+ gotSomething |= SUCCEEDED(hrc);
+ gotAll &= SUCCEEDED(hrc);
+
+ Bstr iid;
+ hrc = info->COMGETTER(InterfaceID)(iid.asOutParam());
+ gotSomething |= SUCCEEDED(hrc);
+ gotAll &= SUCCEEDED(hrc);
+ if (SUCCEEDED(hrc))
+ {
+ mInterfaceID = iid;
+ GetInterfaceNameByIID(mInterfaceID.ref(), mInterfaceName.asOutParam());
+ }
+
+ hrc = info->COMGETTER(Component)(mComponent.asOutParam());
+ gotSomething |= SUCCEEDED(hrc);
+ gotAll &= SUCCEEDED(hrc);
+
+ hrc = info->COMGETTER(Text)(mText.asOutParam());
+ gotSomething |= SUCCEEDED(hrc);
+ gotAll &= SUCCEEDED(hrc);
+
+ m_pNext = NULL;
+
+ ComPtr<IVirtualBoxErrorInfo> next;
+ hrc = info->COMGETTER(Next)(next.asOutParam());
+ if (SUCCEEDED(hrc) && !next.isNull())
+ {
+ m_pNext = new ErrorInfo(next);
+ Assert(m_pNext != NULL);
+ if (!m_pNext)
+ hrc = E_OUTOFMEMORY;
+ }
+
+ gotSomething |= SUCCEEDED(hrc);
+ gotAll &= SUCCEEDED(hrc);
+
+ mIsBasicAvailable = gotSomething;
+ mIsFullAvailable = gotAll;
+
+ mErrorInfo = info;
+
+ AssertMsg(gotSomething, ("Nothing to fetch!\n"));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ProgressErrorInfo class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+ProgressErrorInfo::ProgressErrorInfo(IProgress *progress) :
+ ErrorInfo(false /* aDummy */)
+{
+ Assert(progress);
+ if (!progress)
+ return;
+
+ ComPtr<IVirtualBoxErrorInfo> info;
+ HRESULT hrc = progress->COMGETTER(ErrorInfo)(info.asOutParam());
+ if (SUCCEEDED(hrc) && info)
+ init(info);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ErrorInfoKeeper class
+//
+////////////////////////////////////////////////////////////////////////////////
+
+HRESULT ErrorInfoKeeper::restore()
+{
+ if (mForgot)
+ return S_OK;
+
+ HRESULT hrc = S_OK;
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ ComPtr<IErrorInfo> err;
+ if (!mErrorInfo.isNull())
+ {
+ hrc = mErrorInfo.queryInterfaceTo(err.asOutParam());
+ AssertComRC(hrc);
+ }
+ hrc = ::SetErrorInfo(0, err);
+
+#else // defined(VBOX_WITH_XPCOM)
+
+ nsCOMPtr <nsIExceptionService> es;
+ es = do_GetService(NS_EXCEPTIONSERVICE_CONTRACTID, &hrc);
+ if (NS_SUCCEEDED(hrc))
+ {
+ nsCOMPtr <nsIExceptionManager> em;
+ hrc = es->GetCurrentExceptionManager(getter_AddRefs(em));
+ if (NS_SUCCEEDED(hrc))
+ {
+ ComPtr<nsIException> ex;
+ if (!mErrorInfo.isNull())
+ {
+ hrc = mErrorInfo.queryInterfaceTo(ex.asOutParam());
+ AssertComRC(hrc);
+ }
+ hrc = em->SetCurrentException(ex);
+ }
+ }
+
+#endif // defined(VBOX_WITH_XPCOM)
+
+ if (SUCCEEDED(hrc))
+ {
+ mErrorInfo.setNull();
+ mForgot = true;
+ }
+
+ return hrc;
+}
+
+} /* namespace com */
+
diff --git a/src/VBox/Main/glue/EventQueue.cpp b/src/VBox/Main/glue/EventQueue.cpp
new file mode 100644
index 00000000..a8a6fad1
--- /dev/null
+++ b/src/VBox/Main/glue/EventQueue.cpp
@@ -0,0 +1,257 @@
+/* $Id: EventQueue.cpp $ */
+/** @file
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/** @todo Adapt / update documentation! */
+
+#include "VBox/com/EventQueue.h"
+
+#include <iprt/asm.h>
+#include <new> /* For bad_alloc. */
+
+#include <iprt/err.h>
+#include <iprt/semaphore.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+
+namespace com
+{
+
+// EventQueue class
+////////////////////////////////////////////////////////////////////////////////
+
+EventQueue::EventQueue(void)
+ : mUserCnt(0),
+ mShutdown(false)
+{
+ int vrc = RTCritSectInit(&mCritSect);
+ AssertRC(vrc);
+
+ vrc = RTSemEventCreate(&mSemEvent);
+ AssertRC(vrc);
+}
+
+EventQueue::~EventQueue(void)
+{
+ int vrc = RTCritSectDelete(&mCritSect);
+ AssertRC(vrc);
+
+ vrc = RTSemEventDestroy(mSemEvent);
+ AssertRC(vrc);
+
+ EventQueueListIterator it = mEvents.begin();
+ while (it != mEvents.end())
+ {
+ (*it)->Release();
+ it = mEvents.erase(it);
+ }
+}
+
+/**
+ * Process events pending on this event queue, and wait up to given timeout, if
+ * nothing is available.
+ *
+ * Must be called on same thread this event queue was created on.
+ *
+ * @param cMsTimeout The timeout specified as milliseconds. Use
+ * RT_INDEFINITE_WAIT to wait till an event is posted on the
+ * queue.
+ *
+ * @returns VBox status code
+ * @retval VINF_SUCCESS if one or more messages was processed.
+ * @retval VERR_TIMEOUT if cMsTimeout expired.
+ * @retval VERR_INVALID_CONTEXT if called on the wrong thread.
+ * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called.
+ * On Windows will also be returned when WM_QUIT is encountered.
+ * On Darwin this may also be returned when the native queue is
+ * stopped or destroyed/finished.
+ * @retval VINF_INTERRUPTED if the native system call was interrupted by a
+ * an asynchronous event delivery (signal) or just felt like returning
+ * out of bounds. On darwin it will also be returned if the queue is
+ * stopped.
+ */
+int EventQueue::processEventQueue(RTMSINTERVAL cMsTimeout)
+{
+ size_t cNumEvents;
+ int vrc = RTCritSectEnter(&mCritSect);
+ if (RT_SUCCESS(vrc))
+ {
+ if (mUserCnt == 0) /* No concurrent access allowed. */
+ {
+ mUserCnt++;
+
+ cNumEvents = mEvents.size();
+ if (!cNumEvents)
+ {
+ int vrc2 = RTCritSectLeave(&mCritSect);
+ AssertRC(vrc2);
+
+ vrc = RTSemEventWaitNoResume(mSemEvent, cMsTimeout);
+
+ vrc2 = RTCritSectEnter(&mCritSect);
+ AssertRC(vrc2);
+
+ if (RT_SUCCESS(vrc))
+ {
+ if (mShutdown)
+ vrc = VERR_INTERRUPTED;
+ cNumEvents = mEvents.size();
+ }
+ }
+
+ if (RT_SUCCESS(vrc))
+ vrc = processPendingEvents(cNumEvents);
+
+ Assert(mUserCnt);
+ mUserCnt--;
+ }
+ else
+ vrc = VERR_WRONG_ORDER;
+
+ int vrc2 = RTCritSectLeave(&mCritSect);
+ if (RT_SUCCESS(vrc))
+ vrc = vrc2;
+ }
+
+ Assert(vrc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT);
+ return vrc;
+}
+
+/**
+ * Processes all pending events in the queue at the time of
+ * calling. Note: Does no initial locking, must be done by the
+ * caller!
+ *
+ * @return IPRT status code.
+ */
+int EventQueue::processPendingEvents(size_t cNumEvents)
+{
+ if (!cNumEvents) /* Nothing to process? Bail out early. */
+ return VINF_SUCCESS;
+
+ int vrc = VINF_SUCCESS;
+
+ EventQueueListIterator it = mEvents.begin();
+ for (size_t i = 0;
+ i < cNumEvents
+ && it != mEvents.end(); i++)
+ {
+ Event *pEvent = *it;
+ AssertPtr(pEvent);
+
+ mEvents.erase(it);
+
+ int vrc2 = RTCritSectLeave(&mCritSect);
+ AssertRC(vrc2);
+
+ pEvent->handler();
+ pEvent->Release();
+
+ vrc2 = RTCritSectEnter(&mCritSect);
+ AssertRC(vrc2);
+
+ it = mEvents.begin();
+ if (mShutdown)
+ {
+ vrc = VERR_INTERRUPTED;
+ break;
+ }
+ }
+
+ return vrc;
+}
+
+/**
+ * Interrupt thread waiting on event queue processing.
+ *
+ * Can be called on any thread.
+ *
+ * @returns VBox status code.
+ */
+int EventQueue::interruptEventQueueProcessing(void)
+{
+ ASMAtomicWriteBool(&mShutdown, true);
+
+ return RTSemEventSignal(mSemEvent);
+}
+
+/**
+ * Posts an event to this event loop asynchronously.
+ *
+ * @param pEvent the event to post, must be allocated using |new|
+ * @return TRUE if successful and false otherwise
+ */
+BOOL EventQueue::postEvent(Event *pEvent)
+{
+ int vrc = RTCritSectEnter(&mCritSect);
+ if (RT_SUCCESS(vrc))
+ {
+ try
+ {
+ if (pEvent)
+ {
+ pEvent->AddRef();
+ mEvents.push_back(pEvent);
+ }
+ else /* No locking, since we're already in our crit sect. */
+ mShutdown = true;
+
+ size_t cEvents = mEvents.size();
+ if (cEvents > _1K) /** @todo Make value configurable? */
+ {
+ static int s_cBitchedAboutLotEvents = 0;
+ if (s_cBitchedAboutLotEvents < 10)
+ LogRel(("Warning: Event queue received lots of events (%zu), expect delayed event handling (%d/10)\n",
+ cEvents, ++s_cBitchedAboutLotEvents));
+ }
+
+ /* Leave critical section before signalling event. */
+ vrc = RTCritSectLeave(&mCritSect);
+ if (RT_SUCCESS(vrc))
+ {
+ int vrc2 = RTSemEventSignal(mSemEvent);
+ AssertRC(vrc2);
+ }
+ }
+ catch (std::bad_alloc &ba)
+ {
+ NOREF(ba);
+ vrc = VERR_NO_MEMORY;
+ }
+
+ if (RT_FAILURE(vrc))
+ {
+ int vrc2 = RTCritSectLeave(&mCritSect);
+ AssertRC(vrc2);
+ }
+ }
+
+ return RT_SUCCESS(vrc) ? TRUE : FALSE;
+}
+
+}
+/* namespace com */
diff --git a/src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp b/src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp
new file mode 100644
index 00000000..cdfa6a01
--- /dev/null
+++ b/src/VBox/Main/glue/GetVBoxUserHomeDirectory.cpp
@@ -0,0 +1,142 @@
+/* $Id: GetVBoxUserHomeDirectory.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - GetVBoxUserHomeDirectory.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN
+#include <VBox/com/utils.h>
+
+#include <iprt/env.h>
+#include <iprt/dir.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+
+#include <VBox/err.h>
+#include <VBox/log.h>
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#if !defined(RT_OS_DARWIN) && !defined(RT_OS_WINDOWS)
+char g_szXdgConfigHome[RTPATH_MAX] = "";
+#endif
+
+/**
+ * Possible locations for the VirtualBox user configuration folder,
+ * listed from oldest (as in legacy) to newest. These can be either
+ * absolute or relative to the home directory. We use the first entry
+ * of the list which corresponds to a real folder on storage, or
+ * create a folder corresponding to the last in the list (the least
+ * legacy) if none do.
+ */
+const char * const g_apcszUserHome[] =
+{
+#ifdef RT_OS_DARWIN
+ "Library/VirtualBox",
+#elif defined RT_OS_WINDOWS
+ ".VirtualBox",
+#else
+ ".VirtualBox", g_szXdgConfigHome,
+#endif
+};
+
+
+namespace com
+{
+
+static int composeHomePath(char *aDir, size_t aDirLen, const char *pcszBase)
+{
+ int vrc;
+ if (RTPathStartsWithRoot(pcszBase))
+ vrc = RTStrCopy(aDir, aDirLen, pcszBase);
+ else
+ {
+ /* compose the config directory (full path) */
+ /** @todo r=bird: RTPathUserHome doesn't necessarily return a
+ * full (abs) path like the comment above seems to indicate. */
+ vrc = RTPathUserHome(aDir, aDirLen);
+ if (RT_SUCCESS(vrc))
+ vrc = RTPathAppend(aDir, aDirLen, pcszBase);
+ }
+ return vrc;
+}
+
+int GetVBoxUserHomeDirectory(char *aDir, size_t aDirLen, bool fCreateDir)
+{
+ AssertReturn(aDir, VERR_INVALID_POINTER);
+ AssertReturn(aDirLen > 0, VERR_BUFFER_OVERFLOW);
+
+ /* start with null */
+ *aDir = 0;
+
+ char szTmp[RTPATH_MAX];
+ int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_HOME", szTmp, sizeof(szTmp), NULL);
+ if (RT_SUCCESS(vrc) || vrc == VERR_ENV_VAR_NOT_FOUND)
+ {
+ bool fFound = false;
+ if (RT_SUCCESS(vrc))
+ {
+ /* get the full path name */
+ vrc = RTPathAbs(szTmp, aDir, aDirLen);
+ }
+ else
+ {
+#if !defined(RT_OS_WINDOWS) && !defined(RT_OS_DARWIN)
+ vrc = RTEnvGetEx(RTENV_DEFAULT, "XDG_CONFIG_HOME", g_szXdgConfigHome, sizeof(g_szXdgConfigHome), NULL);
+ if (RT_SUCCESS(vrc))
+ vrc = RTPathAppend(g_szXdgConfigHome, sizeof(g_szXdgConfigHome), "VirtualBox");
+ AssertMsg(vrc == VINF_SUCCESS || vrc == VERR_ENV_VAR_NOT_FOUND, ("%Rrc\n", vrc));
+ if (RT_FAILURE_NP(vrc))
+ vrc = RTStrCopy(g_szXdgConfigHome, sizeof(g_szXdgConfigHome), ".config/VirtualBox");
+#endif
+ for (unsigned i = 0; i < RT_ELEMENTS(g_apcszUserHome); ++i)
+ {
+ vrc = composeHomePath(aDir, aDirLen, g_apcszUserHome[i]);
+ if ( RT_SUCCESS(vrc)
+ && RTDirExists(aDir))
+ {
+ fFound = true;
+ break;
+ }
+ }
+ }
+
+ /* ensure the home directory exists */
+ if (RT_SUCCESS(vrc))
+ if (!fFound && fCreateDir)
+ vrc = RTDirCreateFullPath(aDir, 0700);
+ }
+
+ return vrc;
+}
+
+} /* namespace com */
diff --git a/src/VBox/Main/glue/Makefile.kup b/src/VBox/Main/glue/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Main/glue/Makefile.kup
diff --git a/src/VBox/Main/glue/NativeEventQueue.cpp b/src/VBox/Main/glue/NativeEventQueue.cpp
new file mode 100644
index 00000000..3f8b1e98
--- /dev/null
+++ b/src/VBox/Main/glue/NativeEventQueue.cpp
@@ -0,0 +1,675 @@
+/* $Id: NativeEventQueue.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer:
+ * Main event queue 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBox/com/NativeEventQueue.h"
+
+#include <new> /* For bad_alloc. */
+
+#ifdef RT_OS_DARWIN
+# include <CoreFoundation/CFRunLoop.h>
+#endif
+
+#if defined(VBOX_WITH_XPCOM) && !defined(RT_OS_DARWIN) && !defined(RT_OS_OS2)
+# define USE_XPCOM_QUEUE
+#endif
+
+#include <iprt/err.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#ifdef USE_XPCOM_QUEUE
+# include <errno.h>
+#endif
+
+namespace com
+{
+
+// NativeEventQueue class
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef VBOX_WITH_XPCOM
+
+# define CHECK_THREAD_RET(ret) \
+ do { \
+ AssertMsg(GetCurrentThreadId() == mThreadId, ("Must be on event queue thread!")); \
+ if (GetCurrentThreadId() != mThreadId) \
+ return ret; \
+ } while (0)
+
+/** Magic LPARAM value for the WM_USER messages that we're posting.
+ * @remarks This magic value is duplicated in
+ * vboxapi/PlatformMSCOM::interruptWaitEvents(). */
+#define EVENTQUEUE_WIN_LPARAM_MAGIC UINT32_C(0xf241b819)
+
+
+#else // VBOX_WITH_XPCOM
+
+# define CHECK_THREAD_RET(ret) \
+ do { \
+ if (!mEventQ) \
+ return ret; \
+ BOOL isOnCurrentThread = FALSE; \
+ mEventQ->IsOnCurrentThread(&isOnCurrentThread); \
+ AssertMsg(isOnCurrentThread, ("Must be on event queue thread!")); \
+ if (!isOnCurrentThread) \
+ return ret; \
+ } while (0)
+
+#endif // VBOX_WITH_XPCOM
+
+/** Pointer to the main event queue. */
+NativeEventQueue *NativeEventQueue::sMainQueue = NULL;
+
+
+#ifdef VBOX_WITH_XPCOM
+
+struct MyPLEvent : public PLEvent
+{
+ MyPLEvent(NativeEvent *e) : event(e) {}
+ NativeEvent *event;
+};
+
+/* static */
+void *PR_CALLBACK com::NativeEventQueue::plEventHandler(PLEvent *self)
+{
+ NativeEvent *ev = ((MyPLEvent *)self)->event;
+ if (ev)
+ ev->handler();
+ else
+ {
+ NativeEventQueue *eq = (NativeEventQueue *)self->owner;
+ Assert(eq);
+ eq->mInterrupted = true;
+ }
+ return NULL;
+}
+
+/* static */
+void PR_CALLBACK com::NativeEventQueue::plEventDestructor(PLEvent *self)
+{
+ NativeEvent *ev = ((MyPLEvent *)self)->event;
+ if (ev)
+ delete ev;
+ delete self;
+}
+
+#endif // VBOX_WITH_XPCOM
+
+/**
+ * Constructs an event queue for the current thread.
+ *
+ * Currently, there can be only one event queue per thread, so if an event
+ * queue for the current thread already exists, this object is simply attached
+ * to the existing event queue.
+ */
+NativeEventQueue::NativeEventQueue()
+{
+#ifndef VBOX_WITH_XPCOM
+
+ mThreadId = GetCurrentThreadId();
+ // force the system to create the message queue for the current thread
+ MSG msg;
+ PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
+
+ if (!DuplicateHandle(GetCurrentProcess(),
+ GetCurrentThread(),
+ GetCurrentProcess(),
+ &mhThread,
+ 0 /*dwDesiredAccess*/,
+ FALSE /*bInheritHandle*/,
+ DUPLICATE_SAME_ACCESS))
+ mhThread = INVALID_HANDLE_VALUE;
+
+#else // VBOX_WITH_XPCOM
+
+ mEQCreated = false;
+ mInterrupted = false;
+
+ // Here we reference the global nsIEventQueueService instance and hold it
+ // until we're destroyed. This is necessary to keep NS_ShutdownXPCOM() away
+ // from calling StopAcceptingEvents() on all event queues upon destruction of
+ // nsIEventQueueService, and makes sense when, for some reason, this happens
+ // *before* we're able to send a NULL event to stop our event handler thread
+ // when doing unexpected cleanup caused indirectly by NS_ShutdownXPCOM()
+ // that is performing a global cleanup of everything. A good example of such
+ // situation is when NS_ShutdownXPCOM() is called while the VirtualBox component
+ // is still alive (because it is still referenced): eventually, it results in
+ // a VirtualBox::uninit() call from where it is already not possible to post
+ // NULL to the event thread (because it stopped accepting events).
+
+ nsresult hrc = NS_GetEventQueueService(getter_AddRefs(mEventQService));
+
+ if (NS_SUCCEEDED(hrc))
+ {
+ hrc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQ));
+ if (hrc == NS_ERROR_NOT_AVAILABLE)
+ {
+ hrc = mEventQService->CreateThreadEventQueue();
+ if (NS_SUCCEEDED(hrc))
+ {
+ mEQCreated = true;
+ hrc = mEventQService->GetThreadEventQueue(NS_CURRENT_THREAD, getter_AddRefs(mEventQ));
+ }
+ }
+ }
+ AssertComRC(hrc);
+
+#endif // VBOX_WITH_XPCOM
+}
+
+NativeEventQueue::~NativeEventQueue()
+{
+#ifndef VBOX_WITH_XPCOM
+ if (mhThread != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle(mhThread);
+ mhThread = INVALID_HANDLE_VALUE;
+ }
+#else // VBOX_WITH_XPCOM
+ // process all pending events before destruction
+ if (mEventQ)
+ {
+ if (mEQCreated)
+ {
+ mEventQ->StopAcceptingEvents();
+ mEventQ->ProcessPendingEvents();
+ mEventQService->DestroyThreadEventQueue();
+ }
+ mEventQ = nsnull;
+ mEventQService = nsnull;
+ }
+#endif // VBOX_WITH_XPCOM
+}
+
+/**
+ * Initializes the main event queue instance.
+ * @returns VBox status code.
+ *
+ * @remarks If you're using the rest of the COM/XPCOM glue library,
+ * com::Initialize() will take care of initializing and uninitializing
+ * the NativeEventQueue class. If you don't call com::Initialize, you must
+ * make sure to call this method on the same thread that did the
+ * XPCOM initialization or we'll end up using the wrong main queue.
+ */
+/* static */
+int NativeEventQueue::init()
+{
+ Assert(sMainQueue == NULL);
+ Assert(RTThreadIsMain(RTThreadSelf()));
+
+ try
+ {
+ sMainQueue = new NativeEventQueue();
+ AssertPtr(sMainQueue);
+#ifdef VBOX_WITH_XPCOM
+ /* Check that it actually is the main event queue, i.e. that
+ we're called on the right thread. */
+ nsCOMPtr<nsIEventQueue> q;
+ nsresult rv = NS_GetMainEventQ(getter_AddRefs(q));
+ AssertComRCReturn(rv, VERR_INVALID_POINTER);
+ Assert(q == sMainQueue->mEventQ);
+
+ /* Check that it's a native queue. */
+ PRBool fIsNative = PR_FALSE;
+ rv = sMainQueue->mEventQ->IsQueueNative(&fIsNative);
+ Assert(NS_SUCCEEDED(rv) && fIsNative);
+#endif // VBOX_WITH_XPCOM
+ }
+ catch (std::bad_alloc &ba)
+ {
+ NOREF(ba);
+ return VERR_NO_MEMORY;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Uninitialize the global resources (i.e. the main event queue instance).
+ * @returns VINF_SUCCESS
+ */
+/* static */
+int NativeEventQueue::uninit()
+{
+ if (sMainQueue)
+ {
+ /* Must process all events to make sure that no NULL event is left
+ * after this point. It would need to modify the state of sMainQueue. */
+#ifdef RT_OS_DARWIN /* Do not process the native runloop, the toolkit may not be ready for it. */
+ sMainQueue->mEventQ->ProcessPendingEvents();
+#else
+ sMainQueue->processEventQueue(0);
+#endif
+ delete sMainQueue;
+ sMainQueue = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * Get main event queue instance.
+ *
+ * Depends on init() being called first.
+ */
+/* static */
+NativeEventQueue* NativeEventQueue::getMainEventQueue()
+{
+ return sMainQueue;
+}
+
+#ifdef VBOX_WITH_XPCOM
+# ifdef RT_OS_DARWIN
+/**
+ * Wait for events and process them (Darwin).
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VERR_INTERRUPTED
+ *
+ * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT.
+ */
+static int waitForEventsOnDarwin(RTMSINTERVAL cMsTimeout)
+{
+ /*
+ * Wait for the requested time, if we get a hit we do a poll to process
+ * any other pending messages.
+ *
+ * Note! About 1.0e10: According to the sources anything above 3.1556952e+9
+ * means indefinite wait and 1.0e10 is what CFRunLoopRun() uses.
+ */
+ CFTimeInterval rdTimeout = cMsTimeout == RT_INDEFINITE_WAIT ? 1e10 : (double)cMsTimeout / 1000;
+ OSStatus orc = CFRunLoopRunInMode(kCFRunLoopDefaultMode, rdTimeout, true /*returnAfterSourceHandled*/);
+ if (orc == kCFRunLoopRunHandledSource)
+ {
+ OSStatus orc2 = CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.0, false /*returnAfterSourceHandled*/);
+ if ( orc2 == kCFRunLoopRunStopped
+ || orc2 == kCFRunLoopRunFinished)
+ orc = orc2;
+ }
+ if ( orc == 0 /*???*/
+ || orc == kCFRunLoopRunHandledSource)
+ return VINF_SUCCESS;
+ if ( orc == kCFRunLoopRunStopped
+ || orc == kCFRunLoopRunFinished)
+ return VERR_INTERRUPTED;
+ AssertMsg(orc == kCFRunLoopRunTimedOut, ("Unexpected status code from CFRunLoopRunInMode: %#x", orc));
+ return VERR_TIMEOUT;
+}
+# else // !RT_OS_DARWIN
+
+/**
+ * Wait for events (generic XPCOM).
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VINF_INTERRUPTED
+ * @retval VERR_INTERNAL_ERROR_4
+ *
+ * @param pQueue The queue to wait on.
+ * @param cMsTimeout How long to wait, or RT_INDEFINITE_WAIT.
+ */
+static int waitForEventsOnXPCOM(nsIEventQueue *pQueue, RTMSINTERVAL cMsTimeout)
+{
+ int fd = pQueue->GetEventQueueSelectFD();
+ fd_set fdsetR;
+ FD_ZERO(&fdsetR);
+ FD_SET(fd, &fdsetR);
+
+ fd_set fdsetE = fdsetR;
+
+ struct timeval tv = {0,0};
+ struct timeval *ptv;
+ if (cMsTimeout == RT_INDEFINITE_WAIT)
+ ptv = NULL;
+ else
+ {
+ tv.tv_sec = cMsTimeout / 1000;
+ tv.tv_usec = (cMsTimeout % 1000) * 1000;
+ ptv = &tv;
+ }
+
+ int iRc = select(fd + 1, &fdsetR, NULL, &fdsetE, ptv);
+ int vrc;
+ if (iRc > 0)
+ vrc = VINF_SUCCESS;
+ else if (iRc == 0)
+ vrc = VERR_TIMEOUT;
+ else if (errno == EINTR)
+ vrc = VINF_INTERRUPTED;
+ else
+ {
+ static uint32_t s_ErrorCount = 0;
+ if (s_ErrorCount < 500)
+ {
+ LogRel(("waitForEventsOnXPCOM iRc=%d errno=%d\n", iRc, errno));
+ ++s_ErrorCount;
+ }
+
+ AssertMsgFailed(("iRc=%d errno=%d\n", iRc, errno));
+ vrc = VERR_INTERNAL_ERROR_4;
+ }
+ return vrc;
+}
+
+# endif // !RT_OS_DARWIN
+#endif // VBOX_WITH_XPCOM
+
+#ifndef VBOX_WITH_XPCOM
+
+/**
+ * Dispatch a message on Windows.
+ *
+ * This will pick out our events and handle them specially.
+ *
+ * @returns @a vrc or VERR_INTERRUPTED (WM_QUIT or NULL msg).
+ * @param pMsg The message to dispatch.
+ * @param vrc The current status code.
+ */
+/*static*/
+int NativeEventQueue::dispatchMessageOnWindows(MSG const *pMsg, int vrc)
+{
+ /*
+ * Check for and dispatch our events.
+ */
+ if ( pMsg->hwnd == NULL
+ && pMsg->message == WM_USER)
+ {
+ if (pMsg->lParam == EVENTQUEUE_WIN_LPARAM_MAGIC)
+ {
+ NativeEvent *pEvent = (NativeEvent *)pMsg->wParam;
+ if (pEvent)
+ {
+ pEvent->handler();
+ delete pEvent;
+ }
+ else
+ vrc = VERR_INTERRUPTED;
+ return vrc;
+ }
+ AssertMsgFailed(("lParam=%p wParam=%p\n", pMsg->lParam, pMsg->wParam));
+ }
+
+ /*
+ * Check for the quit message and dispatch the message the normal way.
+ */
+ if (pMsg->message == WM_QUIT)
+ vrc = VERR_INTERRUPTED;
+ TranslateMessage(pMsg);
+ DispatchMessage(pMsg);
+
+ return vrc;
+}
+
+
+/**
+ * Process pending events (Windows).
+ *
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VERR_INTERRUPTED.
+ */
+static int processPendingEvents(void)
+{
+ int vrc = VERR_TIMEOUT;
+ MSG Msg;
+ if (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE))
+ {
+ vrc = VINF_SUCCESS;
+ do
+ vrc = NativeEventQueue::dispatchMessageOnWindows(&Msg, vrc);
+ while (PeekMessage(&Msg, NULL /*hWnd*/, 0 /*wMsgFilterMin*/, 0 /*wMsgFilterMax*/, PM_REMOVE));
+ }
+ return vrc;
+}
+
+#else // VBOX_WITH_XPCOM
+
+/**
+ * Process pending XPCOM events.
+ * @param pQueue The queue to process events on.
+ * @retval VINF_SUCCESS
+ * @retval VERR_TIMEOUT
+ * @retval VERR_INTERRUPTED (darwin only)
+ * @retval VERR_INTERNAL_ERROR_2
+ */
+static int processPendingEvents(nsIEventQueue *pQueue)
+{
+ /* ProcessPendingEvents doesn't report back what it did, so check here. */
+ PRBool fHasEvents = PR_FALSE;
+ nsresult hrc = pQueue->PendingEvents(&fHasEvents);
+ if (NS_FAILED(hrc))
+ return VERR_INTERNAL_ERROR_2;
+
+ /* Process pending events. */
+ int vrc = VINF_SUCCESS;
+ if (fHasEvents)
+ pQueue->ProcessPendingEvents();
+ else
+ vrc = VERR_TIMEOUT;
+
+# ifdef RT_OS_DARWIN
+ /* Process pending native events. */
+ int vrc2 = waitForEventsOnDarwin(0);
+ if (vrc == VERR_TIMEOUT || vrc2 == VERR_INTERRUPTED)
+ vrc = vrc2;
+# endif
+
+ return vrc;
+}
+
+#endif // VBOX_WITH_XPCOM
+
+/**
+ * Process events pending on this event queue, and wait up to given timeout, if
+ * nothing is available.
+ *
+ * Must be called on same thread this event queue was created on.
+ *
+ * @param cMsTimeout The timeout specified as milliseconds. Use
+ * RT_INDEFINITE_WAIT to wait till an event is posted on the
+ * queue.
+ *
+ * @returns VBox status code
+ * @retval VINF_SUCCESS if one or more messages was processed.
+ * @retval VERR_TIMEOUT if cMsTimeout expired.
+ * @retval VERR_INVALID_CONTEXT if called on the wrong thread.
+ * @retval VERR_INTERRUPTED if interruptEventQueueProcessing was called.
+ * On Windows will also be returned when WM_QUIT is encountered.
+ * On Darwin this may also be returned when the native queue is
+ * stopped or destroyed/finished.
+ * @retval VINF_INTERRUPTED if the native system call was interrupted by a
+ * an asynchronous event delivery (signal) or just felt like returning
+ * out of bounds. On darwin it will also be returned if the queue is
+ * stopped.
+ *
+ * @note On darwin this function will not return when the thread receives a
+ * signal, it will just resume the wait.
+ */
+int NativeEventQueue::processEventQueue(RTMSINTERVAL cMsTimeout)
+{
+ int vrc;
+ CHECK_THREAD_RET(VERR_INVALID_CONTEXT);
+
+#ifdef VBOX_WITH_XPCOM
+ /*
+ * Process pending events, if none are available and we're not in a
+ * poll call, wait for some to appear. (We have to be a little bit
+ * careful after waiting for the events since Darwin will process
+ * them as part of the wait, while the XPCOM case will not.)
+ *
+ * Note! Unfortunately, WaitForEvent isn't interruptible with Ctrl-C,
+ * while select() is. So we cannot use it for indefinite waits.
+ */
+ vrc = processPendingEvents(mEventQ);
+ if ( vrc == VERR_TIMEOUT
+ && cMsTimeout > 0)
+ {
+# ifdef RT_OS_DARWIN
+ /** @todo check how Ctrl-C works on Darwin.
+ * Update: It doesn't work. MACH_RCV_INTERRUPT could perhaps be returned
+ * to __CFRunLoopServiceMachPort, but neither it nor __CFRunLoopRun
+ * has any way of expressing it via their return values. So, if
+ * Ctrl-C handling is important, signal needs to be handled on
+ * a different thread or something. */
+ vrc = waitForEventsOnDarwin(cMsTimeout);
+# else // !RT_OS_DARWIN
+ vrc = waitForEventsOnXPCOM(mEventQ, cMsTimeout);
+# endif // !RT_OS_DARWIN
+ if ( RT_SUCCESS(vrc)
+ || vrc == VERR_TIMEOUT)
+ {
+ int vrc2 = processPendingEvents(mEventQ);
+ /* If the wait was successful don't fail the whole operation. */
+ if (RT_FAILURE(vrc) && RT_FAILURE(vrc2))
+ vrc = vrc2;
+ }
+ }
+
+ if ( ( RT_SUCCESS(vrc)
+ || vrc == VERR_INTERRUPTED
+ || vrc == VERR_TIMEOUT)
+ && mInterrupted)
+ {
+ mInterrupted = false;
+ vrc = VERR_INTERRUPTED;
+ }
+
+#else // !VBOX_WITH_XPCOM
+ if (cMsTimeout == RT_INDEFINITE_WAIT)
+ {
+ BOOL fRet = 0; /* Shut up MSC */
+ MSG Msg;
+ vrc = VINF_SUCCESS;
+ while ( vrc != VERR_INTERRUPTED
+ && (fRet = GetMessage(&Msg, NULL /*hWnd*/, WM_USER, WM_USER))
+ && fRet != -1)
+ vrc = NativeEventQueue::dispatchMessageOnWindows(&Msg, vrc);
+ if (fRet == 0)
+ vrc = VERR_INTERRUPTED;
+ else if (fRet == -1)
+ vrc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ vrc = processPendingEvents();
+ if ( vrc == VERR_TIMEOUT
+ && cMsTimeout != 0)
+ {
+ DWORD rcW = MsgWaitForMultipleObjects(1,
+ &mhThread,
+ TRUE /*fWaitAll*/,
+ cMsTimeout,
+ QS_ALLINPUT);
+ AssertMsgReturn(rcW == WAIT_TIMEOUT || rcW == WAIT_OBJECT_0,
+ ("%d\n", rcW),
+ VERR_INTERNAL_ERROR_4);
+ vrc = processPendingEvents();
+ }
+ }
+#endif // !VBOX_WITH_XPCOM
+
+ Assert(vrc != VERR_TIMEOUT || cMsTimeout != RT_INDEFINITE_WAIT);
+ return vrc;
+}
+
+/**
+ * Interrupt thread waiting on event queue processing.
+ *
+ * Can be called on any thread.
+ *
+ * @returns VBox status code.
+ */
+int NativeEventQueue::interruptEventQueueProcessing()
+{
+ /* Send a NULL event. This event will be picked up and handled specially
+ * both for XPCOM and Windows. It is the responsibility of the caller to
+ * take care of not running the loop again in a way which will hang. */
+ postEvent(NULL);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Posts an event to this event loop asynchronously.
+ *
+ * @param pEvent the event to post, must be allocated using |new|
+ * @return @c TRUE if successful and false otherwise
+ */
+BOOL NativeEventQueue::postEvent(NativeEvent *pEvent)
+{
+#ifndef VBOX_WITH_XPCOM
+ /* Note! The event == NULL case is duplicated in vboxapi/PlatformMSCOM::interruptWaitEvents(). */
+ BOOL fRc = PostThreadMessage(mThreadId, WM_USER, (WPARAM)pEvent, EVENTQUEUE_WIN_LPARAM_MAGIC);
+ if (!fRc)
+ {
+ static int s_cBitchedAboutFullNativeEventQueue = 0;
+ if ( GetLastError() == ERROR_NOT_ENOUGH_QUOTA
+ && s_cBitchedAboutFullNativeEventQueue < 10)
+ LogRel(("Warning: Asynchronous event queue (%p, thread %RI32) full, event (%p) not delivered (%d/10)\n",
+ this, mThreadId, pEvent, ++s_cBitchedAboutFullNativeEventQueue));
+ else
+ AssertFailed();
+ }
+ return fRc;
+#else // VBOX_WITH_XPCOM
+ if (!mEventQ)
+ return FALSE;
+
+ try
+ {
+ MyPLEvent *pMyEvent = new MyPLEvent(pEvent);
+ mEventQ->InitEvent(pMyEvent, this, com::NativeEventQueue::plEventHandler,
+ com::NativeEventQueue::plEventDestructor);
+ HRESULT hrc = mEventQ->PostEvent(pMyEvent);
+ return NS_SUCCEEDED(hrc);
+ }
+ catch (std::bad_alloc &ba)
+ {
+ AssertMsgFailed(("Out of memory while allocating memory for event=%p: %s\n",
+ pEvent, ba.what()));
+ }
+
+ return FALSE;
+#endif // VBOX_WITH_XPCOM
+}
+
+/**
+ * Get select()'able selector for this event queue.
+ * This will return -1 on platforms and queue variants not supporting such
+ * functionality.
+ */
+int NativeEventQueue::getSelectFD()
+{
+#ifdef VBOX_WITH_XPCOM
+ return mEventQ->GetEventQueueSelectFD();
+#else
+ return -1;
+#endif
+}
+
+}
+/* namespace com */
diff --git a/src/VBox/Main/glue/VBoxLogRelCreate.cpp b/src/VBox/Main/glue/VBoxLogRelCreate.cpp
new file mode 100644
index 00000000..98b72427
--- /dev/null
+++ b/src/VBox/Main/glue/VBoxLogRelCreate.cpp
@@ -0,0 +1,214 @@
+/* $Id: VBoxLogRelCreate.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - VBoxLogRelCreate.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/com/utils.h>
+
+#include <iprt/buildconfig.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/process.h>
+#include <iprt/time.h>
+
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+#include <VBox/version.h>
+#include "package-generated.h"
+
+
+
+namespace com
+{
+
+static const char *g_pszLogEntity = NULL;
+
+static DECLCALLBACK(void) vboxHeaderFooter(PRTLOGGER pReleaseLogger, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
+{
+ /* some introductory information */
+ static RTTIMESPEC s_TimeSpec;
+ char szTmp[256];
+ if (enmPhase == RTLOGPHASE_BEGIN)
+ RTTimeNow(&s_TimeSpec);
+ RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
+
+ switch (enmPhase)
+ {
+ case RTLOGPHASE_BEGIN:
+ {
+ bool fOldBuffered = RTLogSetBuffering(pReleaseLogger, true /*fBuffered*/);
+ pfnLog(pReleaseLogger,
+ "VirtualBox %s %s r%u %s (%s %s) release log\n"
+#ifdef VBOX_BLEEDING_EDGE
+ "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n"
+#endif
+ "Log opened %s\n",
+ g_pszLogEntity, VBOX_VERSION_STRING, RTBldCfgRevision(),
+ RTBldCfgTargetDotArch(), __DATE__, __TIME__, szTmp);
+
+ pfnLog(pReleaseLogger, "Build Type: %s\n", KBUILD_TYPE);
+ int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pReleaseLogger, "OS Product: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pReleaseLogger, "OS Release: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pReleaseLogger, "OS Version: %s\n", szTmp);
+ vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pReleaseLogger, "OS Service Pack: %s\n", szTmp);
+
+ vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pReleaseLogger, "DMI Product Name: %s\n", szTmp);
+ vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp));
+ if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+ pfnLog(pReleaseLogger, "DMI Product Version: %s\n", szTmp);
+
+ RTSYSFWTYPE enmType;
+ vrc = RTSystemQueryFirmwareType(&enmType);
+ if (RT_SUCCESS(vrc))
+ {
+ pfnLog(pReleaseLogger, "Firmware type: %s\n", RTSystemFirmwareTypeName(enmType));
+ if (enmType == RTSYSFWTYPE_UEFI)
+ {
+ bool fValue;
+ vrc = RTSystemQueryFirmwareBoolean(RTSYSFWBOOL_SECURE_BOOT, &fValue);
+ if (RT_SUCCESS(vrc))
+ pfnLog(pReleaseLogger, "Secure Boot: %s\n", fValue ? "Enabled" : "Disabled");
+ else
+ pfnLog(pReleaseLogger, "Secure Boot: %Rrc\n", vrc);
+ }
+ }
+ else
+ pfnLog(pReleaseLogger, "Firmware type: failed - %Rrc\n", vrc);
+
+ uint64_t cbHostRam = 0, cbHostRamAvail = 0;
+ vrc = RTSystemQueryTotalRam(&cbHostRam);
+ if (RT_SUCCESS(vrc))
+ vrc = RTSystemQueryAvailableRam(&cbHostRamAvail);
+ if (RT_SUCCESS(vrc))
+ {
+ pfnLog(pReleaseLogger, "Host RAM: %lluMB", cbHostRam / _1M);
+ if (cbHostRam > _2G)
+ pfnLog(pReleaseLogger, " (%lld.%lldGB)",
+ cbHostRam / _1G, (cbHostRam % _1G) / (_1G / 10));
+ pfnLog(pReleaseLogger, " total, %lluMB", cbHostRamAvail / _1M);
+ if (cbHostRamAvail > _2G)
+ pfnLog(pReleaseLogger, " (%lld.%lldGB)",
+ cbHostRamAvail / _1G, (cbHostRamAvail % _1G) / (_1G / 10));
+ pfnLog(pReleaseLogger, " available\n");
+ }
+
+ /* the package type is interesting for Linux distributions */
+ char szExecName[RTPATH_MAX];
+ char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
+ pfnLog(pReleaseLogger,
+ "Executable: %s\n"
+ "Process ID: %u\n"
+ "Package type: %s"
+#ifdef VBOX_OSE
+ " (OSE)"
+#endif
+ "\n",
+ pszExecName ? pszExecName : "unknown",
+ RTProcSelf(),
+ VBOX_PACKAGE_STRING);
+ RTLogSetBuffering(pReleaseLogger, fOldBuffered);
+ break;
+ }
+
+ case RTLOGPHASE_PREROTATE:
+ pfnLog(pReleaseLogger, "Log rotated - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_POSTROTATE:
+ pfnLog(pReleaseLogger, "Log continuation - Log started %s\n", szTmp);
+ break;
+
+ case RTLOGPHASE_END:
+ pfnLog(pReleaseLogger, "End of log file - Log started %s\n", szTmp);
+ break;
+
+ default:
+ /* nothing */;
+ }
+}
+
+int VBoxLogRelCreate(const char *pcszEntity, const char *pcszLogFile,
+ uint32_t fFlags, const char *pcszGroupSettings,
+ const char *pcszEnvVarBase, uint32_t fDestFlags,
+ uint32_t cMaxEntriesPerGroup, uint32_t cHistory,
+ uint32_t uHistoryFileTime, uint64_t uHistoryFileSize,
+ PRTERRINFO pErrInfo)
+{
+ return VBoxLogRelCreateEx(pcszEntity, pcszLogFile,
+ fFlags, pcszGroupSettings,
+ pcszEnvVarBase, fDestFlags,
+ cMaxEntriesPerGroup, cHistory,
+ uHistoryFileTime, uHistoryFileSize,
+ NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
+ pErrInfo);
+}
+
+int VBoxLogRelCreateEx(const char *pcszEntity, const char *pcszLogFile,
+ uint32_t fFlags, const char *pcszGroupSettings,
+ const char *pcszEnvVarBase, uint32_t fDestFlags,
+ uint32_t cMaxEntriesPerGroup, uint32_t cHistory,
+ uint32_t uHistoryFileTime, uint64_t uHistoryFileSize,
+ const void *pOutputIf, void *pvOutputIfUser,
+ PRTERRINFO pErrInfo)
+{
+ /* create release logger */
+ PRTLOGGER pReleaseLogger;
+ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+ fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+ g_pszLogEntity = pcszEntity;
+ int vrc = RTLogCreateEx(&pReleaseLogger, pcszEnvVarBase, fFlags, pcszGroupSettings, RT_ELEMENTS(s_apszGroups), s_apszGroups,
+ cMaxEntriesPerGroup, 0 /*cBufDescs*/, NULL /*paBufDescs*/, fDestFlags,
+ vboxHeaderFooter, cHistory, uHistoryFileSize, uHistoryFileTime,
+ (PCRTLOGOUTPUTIF)pOutputIf, pvOutputIfUser,
+ pErrInfo, pcszLogFile ? "%s" : NULL, pcszLogFile);
+ if (RT_SUCCESS(vrc))
+ {
+ /* explicitly flush the log, to have some info when buffering */
+ RTLogFlush(pReleaseLogger);
+
+ /* register this logger as the release logger */
+ RTLogRelSetDefaultInstance(pReleaseLogger);
+ }
+ return vrc;
+}
+
+} /* namespace com */
diff --git a/src/VBox/Main/glue/com.cpp b/src/VBox/Main/glue/com.cpp
new file mode 100644
index 00000000..25f0a49f
--- /dev/null
+++ b/src/VBox/Main/glue/com.cpp
@@ -0,0 +1,185 @@
+/* $Id: com.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN
+#if !defined(VBOX_WITH_XPCOM)
+
+# include <iprt/win/objbase.h>
+
+#else /* !defined (VBOX_WITH_XPCOM) */
+# include <stdlib.h>
+# include <nsCOMPtr.h>
+# include <nsIServiceManagerUtils.h>
+# include <nsIComponentManager.h>
+# include <ipcIService.h>
+# include <ipcCID.h>
+# include <ipcIDConnectService.h>
+# include <nsIInterfaceInfo.h>
+# include <nsIInterfaceInfoManager.h>
+# define IPC_DCONNECTSERVICE_CONTRACTID "@mozilla.org/ipc/dconnect-service;1" // official XPCOM headers don't define it yet
+#endif /* !defined (VBOX_WITH_XPCOM) */
+
+#include "VBox/com/com.h"
+#include "VBox/com/assert.h"
+
+#include "VBox/com/Guid.h"
+#include "VBox/com/array.h"
+
+#include <iprt/string.h>
+
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+namespace com
+{
+/* static */
+const Guid Guid::Empty; /* default ctor is OK */
+
+const char Zeroes[16] = {0, };
+
+
+void GetInterfaceNameByIID(const GUID &aIID, BSTR *aName)
+{
+ AssertPtrReturnVoid(aName);
+ *aName = NULL;
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ LPOLESTR iidStr = NULL;
+ if (StringFromIID(aIID, &iidStr) == S_OK)
+ {
+ HKEY ifaceKey;
+ LSTATUS lrc = RegOpenKeyExW(HKEY_CLASSES_ROOT, L"Interface", 0, KEY_QUERY_VALUE, &ifaceKey);
+ if (lrc == ERROR_SUCCESS)
+ {
+ HKEY iidKey;
+ lrc = RegOpenKeyExW(ifaceKey, iidStr, 0, KEY_QUERY_VALUE, &iidKey);
+ if (lrc == ERROR_SUCCESS)
+ {
+ /* determine the size and type */
+ DWORD sz, type;
+ lrc = RegQueryValueExW(iidKey, NULL, NULL, &type, NULL, &sz);
+ if (lrc == ERROR_SUCCESS && type == REG_SZ)
+ {
+ /* query the value to BSTR */
+ *aName = SysAllocStringLen(NULL, (sz + 1) / sizeof(TCHAR) + 1);
+ lrc = RegQueryValueExW(iidKey, NULL, NULL, NULL, (LPBYTE) *aName, &sz);
+ if (lrc != ERROR_SUCCESS)
+ {
+ SysFreeString(*aName);
+ *aName = NULL;
+ }
+ }
+ RegCloseKey(iidKey);
+ }
+ RegCloseKey(ifaceKey);
+ }
+ CoTaskMemFree(iidStr);
+ }
+
+#else /* !defined (VBOX_WITH_XPCOM) */
+
+ nsresult rv;
+ nsCOMPtr<nsIInterfaceInfoManager> iim =
+ do_GetService(NS_INTERFACEINFOMANAGER_SERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIInterfaceInfo> iinfo;
+ rv = iim->GetInfoForIID(&aIID, getter_AddRefs(iinfo));
+ if (NS_SUCCEEDED(rv))
+ {
+ const char *iname = NULL;
+ iinfo->GetNameShared(&iname);
+ char *utf8IName = NULL;
+ if (RT_SUCCESS(RTStrCurrentCPToUtf8(&utf8IName, iname)))
+ {
+ PRTUTF16 utf16IName = NULL;
+ if (RT_SUCCESS(RTStrToUtf16(utf8IName, &utf16IName)))
+ {
+ *aName = SysAllocString((OLECHAR *) utf16IName);
+ RTUtf16Free(utf16IName);
+ }
+ RTStrFree(utf8IName);
+ }
+ }
+ }
+
+#endif /* !defined (VBOX_WITH_XPCOM) */
+}
+
+#ifdef VBOX_WITH_XPCOM
+
+HRESULT GlueCreateObjectOnServer(const CLSID &clsid,
+ const char *serverName,
+ const nsIID &id,
+ void** ppobj)
+{
+ HRESULT hrc = E_UNEXPECTED;
+ nsCOMPtr<ipcIService> ipcServ = do_GetService(IPC_SERVICE_CONTRACTID, &hrc);
+ if (SUCCEEDED(hrc))
+ {
+ PRUint32 serverID = 0;
+ hrc = ipcServ->ResolveClientName(serverName, &serverID);
+ if (SUCCEEDED (hrc))
+ {
+ nsCOMPtr<ipcIDConnectService> dconServ = do_GetService(IPC_DCONNECTSERVICE_CONTRACTID, &hrc);
+ if (SUCCEEDED(hrc))
+ hrc = dconServ->CreateInstance(serverID,
+ clsid,
+ id,
+ ppobj);
+ }
+ }
+ return hrc;
+}
+
+HRESULT GlueCreateInstance(const CLSID &clsid,
+ const nsIID &id,
+ void** ppobj)
+{
+ nsCOMPtr<nsIComponentManager> manager;
+ HRESULT hrc = NS_GetComponentManager(getter_AddRefs(manager));
+ if (SUCCEEDED(hrc))
+ hrc = manager->CreateInstance(clsid,
+ nsnull,
+ id,
+ ppobj);
+ return hrc;
+}
+
+#endif // VBOX_WITH_XPCOM
+
+} /* namespace com */
+
diff --git a/src/VBox/Main/glue/constants-python.xsl b/src/VBox/Main/glue/constants-python.xsl
new file mode 100755
index 00000000..196377f8
--- /dev/null
+++ b/src/VBox/Main/glue/constants-python.xsl
@@ -0,0 +1,198 @@
+<xsl:stylesheet version = '1.0'
+ xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
+ xmlns:vbox="http://www.virtualbox.org/">
+
+<!--
+ constants-python.xsl:
+ XSLT stylesheet that generates VirtualBox_constants.py from
+ VirtualBox.xidl.
+-->
+<!--
+ 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>.
+
+ SPDX-License-Identifier: GPL-3.0-only
+-->
+
+<xsl:output
+ method="text"
+ version="1.0"
+ encoding="utf-8"
+ indent="no"/>
+
+<xsl:param name="g_sErrHFile"/>
+
+<xsl:template match="/">
+<xsl:text># -*- coding: utf-8 -*-
+
+"""
+VirtualBox COM/XPCOM constants.
+
+This file is autogenerated from VirtualBox.xidl, DO NOT EDIT!
+"""
+
+__copyright__ = \
+"""
+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 &lt;https://www.gnu.org/licenses&gt;.
+
+SPDX-License-Identifier: GPL-3.0-only
+"""
+
+__version__ = "$Revision: 155255 $";
+
+
+
+class VirtualBoxReflectionInfo:
+ """
+ Enum constants for the various python styles.
+ """
+
+ def __init__(self, fIsSym):
+ self.__fIsSym = fIsSym
+
+ # iprt/err.h + VBox/err.h constants:
+ __dVBoxStatuses = {</xsl:text>
+ <xsl:value-of select="document($g_sErrHFile)"/>
+
+ <xsl:text disable-output-escaping="yes"><![CDATA[
+ }
+
+ __dValues = {]]></xsl:text>
+ <xsl:for-each select="//enum">
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="@name"/><xsl:text>': {</xsl:text>
+ <xsl:for-each select="const">
+ <xsl:text>
+ '</xsl:text>
+ <xsl:value-of select="@name"/><xsl:text>': </xsl:text>
+ <xsl:value-of select="@value"/><xsl:text>,</xsl:text>
+ </xsl:for-each>
+ <xsl:text>
+ },</xsl:text>
+ </xsl:for-each>
+ <!-- VBox status codes: -->
+ <xsl:text disable-output-escaping="yes"><![CDATA[
+ # iprt/err.h + VBox/err.h constants:
+ 'VBoxStatus': __dVBoxStatuses,
+ }
+
+ __dValuesSym = {]]></xsl:text>
+ <xsl:for-each select="//enum">
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="@name"/> <xsl:text>': {</xsl:text>
+ <xsl:for-each select="const">
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="@name"/> <xsl:text>': '</xsl:text>
+ <xsl:value-of select="@name"/>
+ <xsl:text>',</xsl:text>
+ </xsl:for-each>
+ <xsl:text>
+ },</xsl:text>
+ </xsl:for-each>
+ <!-- hack alert: force new output element to avoid large reallocations. -->
+ <xsl:text disable-output-escaping="yes"><![CDATA[
+ }
+
+ __dValuesFlat = dict({]]></xsl:text>
+ <xsl:for-each select="//enum">
+ <xsl:variable name="ename">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="const">
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="$ename"/> <xsl:text>_</xsl:text>
+ <xsl:value-of select="@name"/> <xsl:text>': </xsl:text>
+ <xsl:value-of select="@value"/><xsl:text>,</xsl:text>
+ </xsl:for-each>
+ </xsl:for-each>
+ <!-- hack alert: force new output element to avoid large reallocations. -->
+ <xsl:text disable-output-escaping="yes"><![CDATA[
+ # Result constants:]]></xsl:text>
+ <xsl:for-each select="//result[@value]">
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="@name"/> <xsl:text>': </xsl:text>
+ <xsl:value-of select="@value"/><xsl:text>,</xsl:text>
+ </xsl:for-each>
+
+ <!-- hack alert: force new output element to avoid large reallocations. -->
+ <xsl:text>
+ }, **__dVBoxStatuses)
+
+ __dValuesFlatSym = {</xsl:text>
+ <xsl:for-each select="//enum">
+ <xsl:variable name="ename">
+ <xsl:value-of select="@name"/>
+ </xsl:variable>
+ <xsl:for-each select="const">
+ <xsl:variable name="eval">
+ <xsl:value-of select="concat($ename, '_', @name)"/>
+ </xsl:variable>
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="$eval"/> <xsl:text>': </xsl:text>
+ <xsl:text>'</xsl:text> <xsl:value-of select="@name"/> <xsl:text>',</xsl:text>
+ </xsl:for-each>
+ </xsl:for-each>
+ <xsl:text>
+ # Result constants:</xsl:text>
+ <xsl:for-each select="//result[@value]">
+ <xsl:text>
+ '</xsl:text> <xsl:value-of select="@name"/> <xsl:text>': </xsl:text>
+ <xsl:text>'</xsl:text><xsl:value-of select="@name"/><xsl:text>',</xsl:text>
+ </xsl:for-each>
+ <xsl:text>
+ }
+
+ def __getattr__(self, sAttrName):
+ if self.__fIsSym:
+ oValue = self.__dValuesFlatSym.get(sAttrName)
+ else:
+ oValue = self.__dValuesFlat.get(sAttrName)
+ if oValue is None:
+ raise AttributeError
+ return oValue
+
+ def all_values(self, sEnumName):
+ """ Returns a dictionary with all the value names for a given enum type. """
+ if self.__fIsSym:
+ dValues = self.__dValuesSym.get(sEnumName)
+ else:
+ dValues = self.__dValues.get(sEnumName)
+ if dValues is None:
+ dValues = {}
+ return dValues
+
+</xsl:text>
+</xsl:template>
+</xsl:stylesheet>
+
diff --git a/src/VBox/Main/glue/errorprint.cpp b/src/VBox/Main/glue/errorprint.cpp
new file mode 100644
index 00000000..756823ca
--- /dev/null
+++ b/src/VBox/Main/glue/errorprint.cpp
@@ -0,0 +1,222 @@
+/* $Id: errorprint.cpp $ */
+
+/** @file
+ * MS COM / XPCOM Abstraction Layer:
+ * Error info print helpers. This implements the shared code from the macros from errorprint.h.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+#include <VBox/com/ErrorInfo.h>
+#include <VBox/com/errorprint.h>
+#include <VBox/log.h>
+
+#include <ProgressImpl.h>
+
+#include <iprt/stream.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+
+namespace com
+{
+
+void GluePrintErrorInfo(const com::ErrorInfo &info)
+{
+ bool haveResultCode = false;
+#if defined (RT_OS_WIN)
+ haveResultCode = info.isFullAvailable();
+ bool haveComponent = true;
+ bool haveInterfaceID = true;
+#else /* defined (RT_OS_WIN) */
+ haveResultCode = true;
+ bool haveComponent = info.isFullAvailable();
+ bool haveInterfaceID = info.isFullAvailable();
+#endif
+
+ try
+ {
+ HRESULT hrc = S_OK;
+ Utf8Str str;
+ RTCList<Utf8Str> comp;
+
+ Bstr bstrDetailsText = info.getText();
+ if (!bstrDetailsText.isEmpty())
+ str = Utf8StrFmt("%ls\n",
+ bstrDetailsText.raw());
+ if (haveResultCode)
+ {
+ hrc = info.getResultCode();
+ comp.append(Utf8StrFmt("code %Rhrc (0x%RX32)", hrc, hrc));
+ }
+ if (haveComponent)
+ comp.append(Utf8StrFmt("component %ls",
+ info.getComponent().raw()));
+ if (haveInterfaceID)
+ comp.append(Utf8StrFmt("interface %ls",
+ info.getInterfaceName().raw()));
+ if (!info.getCalleeName().isEmpty())
+ comp.append(Utf8StrFmt("callee %ls",
+ info.getCalleeName().raw()));
+
+ if (comp.size() > 0)
+ {
+ str += "Details: ";
+ for (size_t i = 0; i < comp.size() - 1; ++i)
+ str += comp.at(i) + ", ";
+ str += comp.last();
+ str += "\n";
+ }
+
+ // print and log
+ if (FAILED(hrc))
+ {
+ RTMsgError("%s", str.c_str());
+ Log(("ERROR: %s", str.c_str()));
+ }
+ else
+ {
+ RTMsgWarning("%s", str.c_str());
+ Log(("WARNING: %s", str.c_str()));
+ }
+ }
+ catch (std::bad_alloc &)
+ {
+ RTMsgError("std::bad_alloc in GluePrintErrorInfo!");
+ Log(("ERROR: std::bad_alloc in GluePrintErrorInfo!\n"));
+ }
+}
+
+void GluePrintErrorContext(const char *pcszContext, const char *pcszSourceFile, uint32_t ulLine, bool fWarning /* = false */)
+{
+ // pcszSourceFile comes from __FILE__ macro, which always contains the full path,
+ // which we don't want to see printed:
+ // print and log
+ const char *pszFilenameOnly = RTPathFilename(pcszSourceFile);
+ if (!fWarning)
+ {
+ RTMsgError("Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly);
+ Log(("ERROR: Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly));
+ }
+ else
+ {
+ RTMsgWarning("Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly);
+ Log(("WARNING: Context: \"%s\" at line %d of file %s\n", pcszContext, ulLine, pszFilenameOnly));
+ }
+}
+
+void GluePrintRCMessage(HRESULT hrc)
+{
+ // print and log
+ if (FAILED(hrc))
+ {
+ RTMsgError("Code %Rhra (extended info not available)\n", hrc);
+ Log(("ERROR: Code %Rhra (extended info not available)\n", hrc));
+ }
+ else
+ {
+ RTMsgWarning("Code %Rhra (extended info not available)\n", hrc);
+ Log(("WARNING: Code %Rhra (extended info not available)\n", hrc));
+ }
+}
+
+static void glueHandleComErrorInternal(com::ErrorInfo &info,
+ const char *pcszContext,
+ HRESULT hrc,
+ const char *pcszSourceFile,
+ uint32_t ulLine)
+{
+ if (info.isFullAvailable() || info.isBasicAvailable())
+ {
+ const com::ErrorInfo *pInfo = &info;
+ do
+ {
+ GluePrintErrorInfo(*pInfo);
+
+ /* Use hrc for figuring out if there were just warnings. */
+ HRESULT hrc2 = pInfo->getResultCode();
+ if ( (SUCCEEDED_WARNING(hrc) && FAILED(hrc2))
+ || (SUCCEEDED(hrc) && (FAILED(hrc2) || SUCCEEDED_WARNING(hrc2))))
+ hrc = hrc2;
+
+ pInfo = pInfo->getNext();
+ /* If there is more than one error, separate them visually. */
+ if (pInfo)
+ {
+ /* If there are several errors then at least basic error
+ * information must be available, otherwise something went
+ * horribly wrong. */
+ Assert(pInfo->isFullAvailable() || pInfo->isBasicAvailable());
+
+ RTMsgError("--------\n");
+ }
+ } while (pInfo);
+ }
+ else
+ GluePrintRCMessage(hrc);
+
+ if (pcszContext != NULL || pcszSourceFile != NULL)
+ GluePrintErrorContext(pcszContext, pcszSourceFile, ulLine, SUCCEEDED_WARNING(hrc));
+}
+
+void GlueHandleComError(ComPtr<IUnknown> iface,
+ const char *pcszContext,
+ HRESULT hrc,
+ const char *pcszSourceFile,
+ uint32_t ulLine)
+{
+ /* If we have full error info, print something nice, and start with the
+ * actual error message. */
+ com::ErrorInfo info(iface, COM_IIDOF(IUnknown));
+
+ glueHandleComErrorInternal(info,
+ pcszContext,
+ hrc,
+ pcszSourceFile,
+ ulLine);
+
+}
+
+void GlueHandleComErrorNoCtx(ComPtr<IUnknown> iface, HRESULT hrc)
+{
+ GlueHandleComError(iface, NULL, hrc, NULL, 0);
+}
+
+void GlueHandleComErrorProgress(ComPtr<IProgress> progress,
+ const char *pcszContext,
+ HRESULT hrc,
+ const char *pcszSourceFile,
+ uint32_t ulLine)
+{
+ /* Get the error info out of the progress object. */
+ ProgressErrorInfo ei(progress);
+
+ glueHandleComErrorInternal(ei,
+ pcszContext,
+ hrc,
+ pcszSourceFile,
+ ulLine);
+}
+
+} /* namespace com */
+
diff --git a/src/VBox/Main/glue/glue-java.xsl b/src/VBox/Main/glue/glue-java.xsl
new file mode 100644
index 00000000..d042312d
--- /dev/null
+++ b/src/VBox/Main/glue/glue-java.xsl
@@ -0,0 +1,5259 @@
+<xsl:stylesheet version = '1.0'
+ xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
+ xmlns:vbox="http://www.virtualbox.org/"
+ xmlns:exsl="http://exslt.org/common"
+ extension-element-prefixes="exsl">
+
+<!--
+ glue-java.xsl:
+ XSLT stylesheet that generates Java glue code for XPCOM, MSCOM and JAX-WS from
+ VirtualBox.xidl.
+-->
+<!--
+ 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>.
+
+ SPDX-License-Identifier: GPL-3.0-only
+-->
+
+<xsl:output
+ method="text"
+ version="1.0"
+ encoding="utf-8"
+ indent="no"/>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - -
+ global XSLT variables
+ - - - - - - - - - - - - - - - - - - - - - - -->
+
+<xsl:variable name="G_xsltFilename" select="'glue-java.xsl'" />
+<xsl:variable name="G_virtualBoxPackage" select="concat('org.virtualbox', $G_vboxApiSuffix)" />
+<xsl:variable name="G_virtualBoxPackageCom" select="concat('org.virtualbox', $G_vboxApiSuffix, '.', $G_vboxGlueStyle)" />
+<xsl:variable name="G_virtualBoxWsdl" select="concat('&quot;vboxwebService', $G_vboxApiSuffix, '.wsdl&quot;')" />
+<!-- collect all interfaces with "wsmap='suppress'" in a global variable for quick lookup -->
+<xsl:variable name="G_setSuppressedInterfaces"
+ select="//interface[@wsmap='suppress']" />
+
+<xsl:include href="../idl/typemap-shared.inc.xsl" />
+
+<xsl:strip-space elements="*"/>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - -
+ Keys for more efficiently looking up of types.
+ - - - - - - - - - - - - - - - - - - - - - - -->
+
+<xsl:key name="G_keyEnumsByName" match="//enum[@name]" use="@name"/>
+<xsl:key name="G_keyInterfacesByName" match="//interface[@name]" use="@name"/>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - -
+ - - - - - - - - - - - - - - - - - - - - - - -->
+
+<xsl:template name="fileheader">
+ <xsl:param name="name" />
+ <xsl:text>/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of a free software library; you can redistribute
+ * it and/or modify it under the terms of the GNU Lesser General
+ * Public License version 2.1 as published by the Free Software
+ * Foundation and shipped in the \"COPYING.LIB\" file with this library.
+ * The library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY of any kind.
+ *
+ * Oracle LGPL Disclaimer: For the avoidance of doubt, except that if
+ * any license choice other than GPL or LGPL is available it will
+ * apply instead, Oracle elects to use only the Lesser General Public
+ * License version 2.1 (LGPLv2) at this time for any software where
+ * a choice of LGPL license versions is made available with the
+ * language indicating that LGPLv2 or any later version may be used,
+ * or where a choice of which version of the LGPL is applied is
+ * otherwise unspecified.
+
+ * http://www.virtualbox.org. This library is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License as published by the Free Software Foundation, in version 2.1
+ * as it comes in the "COPYING.LIB" file of the VirtualBox SDK distribution.
+ * This library 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 Lesser General Public
+ * License for more details.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ */
+
+/*
+</xsl:text>
+ <xsl:value-of select="concat(' * ', $name)"/>
+<xsl:text>
+ *
+ * DO NOT EDIT! This is a generated file.
+ * Generated from: src/VBox/Main/idl/VirtualBox.xidl (VirtualBox's interface definitions in XML)
+ * Generator: src/VBox/Main/glue/glue-java.xsl
+ */
+
+</xsl:text>
+</xsl:template>
+
+<xsl:template name="startFile">
+ <xsl:param name="file" />
+ <xsl:param name="package" />
+
+ <xsl:choose>
+ <xsl:when test="$filelistonly=''">
+ <xsl:value-of select="concat('&#10;// ##### BEGINFILE &quot;', $G_vboxDirPrefix, $file, '&quot;&#10;&#10;')" />
+ <xsl:call-template name="fileheader">
+ <xsl:with-param name="name" select="$file" />
+ </xsl:call-template>
+
+ <xsl:value-of select="concat('package ', $package, ';&#10;&#10;')" />
+ <xsl:value-of select="concat('import ', $G_virtualBoxPackageCom, '.*;&#10;')" />
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:text>import org.mozilla.interfaces.*;&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:text>import com.jacob.com.*;&#10;</xsl:text>
+ <xsl:text>import com.jacob.activeX.ActiveXComponent;&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:text>import javax.xml.ws.*;&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'no header rule (startFile)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(' \', $G_sNewLine, '&#9;', $G_vboxDirPrefix, $file)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="endFile">
+ <xsl:param name="file" />
+ <xsl:if test="$filelistonly=''">
+ <xsl:value-of select="concat('&#10;// ##### ENDFILE &quot;', $file, '&quot;&#10;')" />
+ <xsl:call-template name="xsltprocNewlineOutputHack"/>
+ </xsl:if>
+</xsl:template>
+
+<!-- strip-and-normalize-desc
+ Removes leading and trailing white space on each line in the given text.
+ -->
+<xsl:template name="strip-and-normalize-desc">
+ <xsl:param name="text"/>
+
+ <!-- Strip the whole string first so we won't leave trailing new line chars behind. -->
+ <xsl:variable name="sStrippedText">
+ <xsl:call-template name="strip-string">
+ <xsl:with-param name="text" select="$text"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <!-- If there are multiple lines, strip them one by one on a recursive fasion. -->
+ <xsl:when test="contains($sStrippedText, '&#10;')">
+ <xsl:call-template name="strip-string-right">
+ <xsl:with-param name="text" select="substring-before($sStrippedText, '&#10;')"/>
+ </xsl:call-template>
+ <xsl:value-of select="'&#10;'"/>
+ <xsl:call-template name="strip-and-normalize-desc-recursive">
+ <xsl:with-param name="text" select="substring-after($sStrippedText, '&#10;')"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- Single line, we're done. -->
+ <xsl:otherwise>
+ <xsl:value-of select="$sStrippedText"/>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<!-- Internal worker for strip-and-normalize-desc, don't use. -->
+<xsl:template name="strip-and-normalize-desc-recursive">
+ <xsl:param name="text"/>
+
+ <xsl:choose>
+ <!-- If there are multiple lines, strip them one by one on a recursive fasion. -->
+ <xsl:when test="contains($text, '&#10;')">
+ <xsl:call-template name="strip-string">
+ <xsl:with-param name="text" select="substring-before($text, '&#10;')"/>
+ </xsl:call-template>
+ <xsl:value-of select="'&#10;'"/>
+ <xsl:call-template name="strip-and-normalize-desc-recursive">
+ <xsl:with-param name="text" select="substring-after($text, '&#10;')"/>
+ </xsl:call-template>
+ </xsl:when>
+
+ <!-- Single line: Left strip it. -->
+ <xsl:otherwise>
+ <xsl:call-template name="strip-string-left">
+ <xsl:with-param name="text" select="$text"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<!-- descriptions -->
+
+<xsl:template match="*/text()">
+ <!-- TODO: strip out @c/@a for now. long term solution is changing that to a
+ tag in the xidl file, and translate it when generating doxygen etc. -->
+ <xsl:variable name="rep1">
+ <xsl:call-template name="string-replace">
+ <xsl:with-param name="haystack" select="."/>
+ <xsl:with-param name="needle" select="'@c'"/>
+ <xsl:with-param name="replacement" select="''"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="rep2">
+ <xsl:call-template name="string-replace">
+ <xsl:with-param name="haystack" select="$rep1"/>
+ <xsl:with-param name="needle" select="'@a'"/>
+ <xsl:with-param name="replacement" select="''"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="rep3">
+ <xsl:call-template name="string-replace">
+ <xsl:with-param name="haystack" select="$rep2"/>
+ <xsl:with-param name="needle" select="'@todo'"/>
+ <xsl:with-param name="replacement" select="'TODO'"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- &amp;, &lt; and &gt; must remain as they are or javadoc 8 throws a fit. -->
+ <xsl:variable name="rep4">
+ <xsl:call-template name="string-replace">
+ <xsl:with-param name="haystack" select="$rep3"/>
+ <xsl:with-param name="needle" select="'&amp;'"/>
+ <xsl:with-param name="replacement" select="'&amp;amp;'"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="rep5">
+ <xsl:call-template name="string-replace">
+ <xsl:with-param name="haystack" select="$rep4"/>
+ <xsl:with-param name="needle" select="'&lt;'"/>
+ <xsl:with-param name="replacement" select="'&amp;lt;'"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="rep6">
+ <xsl:call-template name="string-replace">
+ <xsl:with-param name="haystack" select="$rep5"/>
+ <xsl:with-param name="needle" select="'&gt;'"/>
+ <xsl:with-param name="replacement" select="'&amp;gt;'"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="rep7">
+ <xsl:call-template name="strip-and-normalize-desc">
+ <xsl:with-param name="text" select="$rep6"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:value-of select="$rep7"/>
+</xsl:template>
+
+<!--
+ * all sub-elements that are not explicitly matched are considered to be
+ * html tags and copied w/o modifications
+-->
+<xsl:template match="desc//*">
+ <xsl:variable name="tagname" select="local-name()"/>
+ <xsl:choose>
+ <xsl:when test="$tagname = 'tt'">
+ <xsl:text>&lt;code&gt;</xsl:text>
+ </xsl:when>
+ <xsl:when test="$tagname = 'h3'">
+ <xsl:text>&lt;h2&gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('&lt;', $tagname, '&gt;')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:apply-templates/>
+ <xsl:choose>
+ <xsl:when test="$tagname = 'tt'">
+ <xsl:text>&lt;/code&gt;</xsl:text>
+ </xsl:when>
+ <xsl:when test="$tagname = 'h3'">
+ <xsl:text>&lt;/h2&gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('&lt;/', $tagname, '&gt;')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="emit_refsig">
+ <xsl:param name="context"/>
+ <xsl:param name="identifier"/>
+
+ <xsl:choose>
+ <xsl:when test="key('G_keyEnumsByName', $context)/const[@name=$identifier]">
+ <xsl:value-of select="$identifier"/>
+ </xsl:when>
+ <xsl:when test="key('G_keyInterfacesByName', $context)/method[@name=$identifier]">
+ <xsl:value-of select="$identifier"/>
+ <xsl:text>(</xsl:text>
+ <xsl:for-each select="key('G_keyInterfacesByName', $context)/method[@name=$identifier]/param">
+ <xsl:if test="@dir!='return'">
+ <xsl:if test="position() > 1">
+ <xsl:text>,</xsl:text>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="@dir='out'">
+ <xsl:text>Holder</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="@type"/>
+ <xsl:with-param name="safearray" select="@safearray"/>
+ <xsl:with-param name="skiplisttype" select="'yes'"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>)</xsl:text>
+ </xsl:when>
+ <xsl:when test="key('G_keyInterfacesByName', $context)/attribute[@name=$identifier]">
+ <xsl:call-template name="makeGetterName">
+ <xsl:with-param name="attrname" select="$identifier" />
+ </xsl:call-template>
+ <xsl:text>()</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('unknown reference destination in @see/@link: context=', $context, ' identifier=', $identifier)" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!--
+ * link
+-->
+<xsl:template match="desc//link">
+ <xsl:text>{@link </xsl:text>
+ <xsl:apply-templates select="." mode="middle"/>
+ <xsl:text>}</xsl:text>
+</xsl:template>
+
+<xsl:template match="link" mode="middle">
+ <xsl:variable name="linktext">
+ <xsl:call-template name="string-replace-first">
+ <xsl:with-param name="haystack" select="@to"/>
+ <xsl:with-param name="needle" select="'_'"/>
+ <xsl:with-param name="replacement" select="'#'"/>
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="substring($linktext, 1, 1)='#'">
+ <xsl:variable name="context">
+ <xsl:choose>
+ <xsl:when test="local-name(../..)='interface' or local-name(../..)='enum'">
+ <xsl:value-of select="../../@name"/>
+ </xsl:when>
+ <xsl:when test="local-name(../../..)='interface' or local-name(../../..)='enum'">
+ <xsl:value-of select="../../../@name"/>
+ </xsl:when>
+ <xsl:when test="local-name(../../../..)='interface' or local-name(../../../..)='enum'">
+ <xsl:value-of select="../../../../@name"/>
+ </xsl:when>
+ <xsl:when test="local-name(../../../../..)='interface' or local-name(../../../../..)='enum'">
+ <xsl:value-of select="../../../../../@name"/>
+ </xsl:when>
+ <xsl:when test="local-name(../../../../../..)='interface' or local-name(../../../../../..)='enum'">
+ <xsl:value-of select="../../../../../../@name"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('cannot determine context for identifier ', $linktext)" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="linkname">
+ <xsl:value-of select="substring($linktext, 2)"/>
+ </xsl:variable>
+ <xsl:text>#</xsl:text>
+ <xsl:call-template name="emit_refsig">
+ <xsl:with-param name="context" select="$context"/>
+ <xsl:with-param name="identifier" select="$linkname"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="contains($linktext, '::')">
+ <xsl:variable name="context">
+ <xsl:value-of select="substring-before($linktext, '::')"/>
+ </xsl:variable>
+ <xsl:variable name="linkname">
+ <xsl:value-of select="substring-after($linktext, '::')"/>
+ </xsl:variable>
+ <xsl:value-of select="concat($G_virtualBoxPackage, '.', $context, '#')"/>
+ <xsl:call-template name="emit_refsig">
+ <xsl:with-param name="context" select="$context"/>
+ <xsl:with-param name="identifier" select="$linkname"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($G_virtualBoxPackage, '.', $linktext)"/>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+<!--
+ * note
+-->
+<xsl:template match="desc/note">
+ <xsl:if test="not(@internal='yes')">
+ <xsl:text>&#10;NOTE: </xsl:text>
+ <xsl:apply-templates/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ * see
+-->
+<xsl:template match="desc/see">
+ <!-- TODO: quirk in our xidl file: only one <see> tag with <link> nested
+ into it, translate this to multiple @see lines and strip the rest.
+ Should be replaced in the xidl by multiple <see> without nested tag -->
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates match="link"/>
+</xsl:template>
+
+<xsl:template match="desc/see/text()"/>
+
+<xsl:template match="desc/see/link">
+ <xsl:text>@see </xsl:text>
+ <xsl:apply-templates select="." mode="middle"/>
+ <xsl:text>&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * common comment prologue (handles group IDs)
+-->
+<xsl:template match="desc" mode="begin">
+ <!-- TODO,XXX: This is a hot spot. The whole $id crap isn't working though,
+ so it's been disabled to save precious time. -->
+<!--
+ <xsl:param name="id" select="@group | preceding::descGroup[1]/@id"/>
+ <xsl:text>&#10;/**&#10;</xsl:text>
+ <xsl:if test="$id">
+ <xsl:value-of select="concat(' @ingroup ', $id, '&#10;')"/>
+ </xsl:if>
+-->
+ <xsl:value-of select="concat($G_sNewLine, '/**', $G_sNewLine)"/>
+</xsl:template>
+
+<!--
+ * common middle part of the comment block
+-->
+<xsl:template match="desc" mode="middle">
+ <xsl:apply-templates select="text() | *[not(self::note or self::see)]"/>
+ <xsl:apply-templates select="note"/>
+ <xsl:apply-templates select="see"/>
+</xsl:template>
+
+<!--
+ * result part of the comment block
+-->
+<xsl:template match="desc" mode="results">
+ <xsl:if test="result">
+ <xsl:text>&#10;&lt;p&gt;&lt;/p&gt;&lt;dl&gt;&lt;dt&gt;&lt;b&gt;Expected result codes:&lt;/b&gt;&lt;/dt&gt;&#10;</xsl:text>
+ <xsl:for-each select="result">
+ <xsl:text>&lt;dd&gt;&lt;code&gt;</xsl:text>
+ <xsl:choose>
+ <xsl:when test="ancestor::library/result[@name=current()/@name]">
+ <xsl:value-of select="concat('@link ::', @name, ' ', @name)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="@name"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>&lt;/code&gt; - </xsl:text>
+ <xsl:apply-templates select="text() | *[not(self::note or self::see or
+ self::result)]"/>
+ <xsl:text>&lt;/dd&gt;&#10;</xsl:text>
+ </xsl:for-each>
+ <xsl:text>&lt;/dl&gt;&#10;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ * comment for interfaces
+-->
+<xsl:template match="desc" mode="interface">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates select="." mode="middle"/>
+ <xsl:text>&#10;&#10;Interface ID: &lt;code&gt;{</xsl:text>
+ <xsl:call-template name="string-to-upper">
+ <xsl:with-param name="str" select="../@uuid"/>
+ </xsl:call-template>
+ <xsl:text>}&lt;/code&gt;&#10;*/&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * comment for attribute getters
+-->
+<xsl:template match="desc" mode="attribute_get">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates select="text() | *[not(self::note or self::see or self::result)]"/>
+ <xsl:apply-templates select="." mode="results"/>
+ <xsl:apply-templates select="note"/>
+ <xsl:text>&#10;@return </xsl:text>
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="../@type"/>
+ <xsl:with-param name="safearray" select="../@safearray"/>
+ <xsl:with-param name="doubleescape">yes</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates select="see"/>
+ <xsl:text>*/&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * comment for attribute setters
+-->
+<xsl:template match="desc" mode="attribute_set">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates select="text() | *[not(self::note or self::see or self::result)]"/>
+ <xsl:apply-templates select="." mode="results"/>
+ <xsl:apply-templates select="note"/>
+ <xsl:text>&#10;@param value </xsl:text>
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="../@type"/>
+ <xsl:with-param name="safearray" select="../@safearray"/>
+ <xsl:with-param name="doubleescape">yes</xsl:with-param>
+ </xsl:call-template>
+ <xsl:text>&#10;</xsl:text>
+ <xsl:apply-templates select="see"/>
+ <xsl:text>&#10;*/&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * comment for methods
+-->
+<xsl:template match="desc" mode="method">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates select="text() | *[not(self::note or self::see or self::result)]"/>
+ <xsl:apply-templates select="." mode="results"/>
+ <xsl:for-each select="../param">
+ <xsl:apply-templates select="desc"/>
+ </xsl:for-each>
+ <xsl:apply-templates select="note"/>
+ <xsl:apply-templates select="../param/desc/note"/>
+ <xsl:apply-templates select="see"/>
+ <xsl:text>&#10;*/&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * comment for method parameters
+-->
+<xsl:template match="method/param/desc">
+ <xsl:if test="text() | *[not(self::note or self::see)]">
+ <xsl:choose>
+ <xsl:when test="../@dir='return'">
+ <xsl:text>&#10;@return </xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&#10;@param </xsl:text>
+ <xsl:value-of select="../@name"/>
+ <xsl:text> </xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:apply-templates select="text() | *[not(self::note or self::see)]"/>
+ <xsl:text>&#10;</xsl:text>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ * comment for enums
+-->
+<xsl:template match="desc" mode="enum">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates select="." mode="middle"/>
+ <xsl:text>&#10;Interface ID: &lt;code&gt;{</xsl:text>
+ <xsl:call-template name="string-to-upper">
+ <xsl:with-param name="str" select="../@uuid"/>
+ </xsl:call-template>
+ <xsl:text>}&lt;/code&gt;&#10;*/&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * comment for enum values
+-->
+<xsl:template match="desc" mode="enum_const">
+ <xsl:apply-templates select="." mode="begin"/>
+ <xsl:apply-templates select="." mode="middle"/>
+ <xsl:text>&#10;*/&#10;</xsl:text>
+</xsl:template>
+
+<!--
+ * ignore descGroups by default (processed in /idl)
+-->
+<xsl:template match="descGroup"/>
+
+
+
+<!-- actual code generation -->
+
+<xsl:template name="genEnum">
+ <xsl:param name="enumname" />
+ <xsl:param name="filename" />
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="$filename" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:apply-templates select="desc" mode="enum"/>
+ <xsl:value-of select="concat('public enum ', $enumname, '&#10;')" />
+ <xsl:text>{&#10;</xsl:text>
+ <xsl:for-each select="const">
+ <xsl:apply-templates select="desc" mode="enum_const"/>
+ <xsl:variable name="enumconst" select="@name" />
+ <xsl:value-of select="concat(' ', $enumconst, '(', @value, ')')" />
+ <xsl:choose>
+ <xsl:when test="not(position()=last())">
+ <xsl:text>,&#10;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>;&#10;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <xsl:text>&#10;</xsl:text>
+ <xsl:text> private final int value;&#10;&#10;</xsl:text>
+
+ <xsl:value-of select="concat(' ', $enumname, '(int v)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> value = v;&#10;</xsl:text>
+ <xsl:text> }&#10;&#10;</xsl:text>
+
+ <xsl:text> public int value()&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> return value;&#10;</xsl:text>
+ <xsl:text> }&#10;&#10;</xsl:text>
+
+ <xsl:value-of select="concat(' public static ', $enumname, ' fromValue(long v)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:value-of select="concat(' for (', $enumname, ' c: ', $enumname, '.values())&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> if (c.value == (int)v)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> return c;&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text> throw new IllegalArgumentException(Long.toString(v));&#10;</xsl:text>
+ <xsl:text> }&#10;&#10;</xsl:text>
+
+ <xsl:value-of select="concat(' public static ', $enumname, ' fromValue(String v)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:value-of select="concat(' return valueOf(', $enumname, '.class, v);&#10;')" />
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text>}&#10;&#10;</xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="$filename" />
+ </xsl:call-template>
+
+</xsl:template>
+
+<xsl:template name="startExcWrapper">
+ <xsl:param name="preventObjRelease" />
+
+ <xsl:if test="$G_vboxGlueStyle='jaxws' and $preventObjRelease">
+ <xsl:text> this.getObjMgr().preventObjRelease();&#10;</xsl:text>
+ </xsl:if>
+ <xsl:text> try&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+</xsl:template>
+
+<xsl:template name="endExcWrapper">
+ <xsl:param name="allowObjRelease" />
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text> catch (org.mozilla.xpcom.XPCOMException e)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> throw new VBoxException(e.getMessage(), e);&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text> catch (com.jacob.com.ComException e)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> throw new VBoxException(e.getMessage(), e);&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text> catch (InvalidObjectFaultMsg e)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> throw new VBoxException(e.getMessage(), e, this.getObjMgr(), this.port);&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:text> catch (RuntimeFaultMsg e)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> throw new VBoxException(e.getMessage(), e, this.getObjMgr(), this.port);&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:if test="$allowObjRelease">
+ <xsl:text> finally&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> getObjMgr().allowObjRelease();&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'no header rule (startFile)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="wrappedName">
+ <xsl:param name="ifname" />
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat('org.mozilla.interfaces.', $ifname)" />
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:text>com.jacob.com.Dispatch</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'no wrapper naming rule defined (wrappedName)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="fullClassName">
+ <xsl:param name="name" />
+ <xsl:param name="origname" />
+ <xsl:param name="collPrefix" />
+
+ <xsl:choose>
+ <xsl:when test="(count(key('G_keyEnumsByName', $name)) > 0) or (count(key('G_keyEnumsByName', $origname)) > 0)">
+ <xsl:value-of select="concat($G_virtualBoxPackage, concat('.', $name))" />
+ </xsl:when>
+ <xsl:when test="count(key('G_keyInterfacesByName', $name)) > 0">
+ <xsl:value-of select="concat($G_virtualBoxPackage, concat('.', $name))" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('fullClassName: Type &quot;', $name, '&quot; is not supported.')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="typeIdl2Glue">
+ <xsl:param name="type" />
+ <xsl:param name="safearray" />
+ <xsl:param name="forceelem" />
+ <xsl:param name="skiplisttype" />
+ <xsl:param name="doubleescape" />
+
+ <xsl:variable name="needarray" select="($safearray='yes') and not($forceelem='yes')" />
+ <xsl:variable name="needlist" select="($needarray) and not($type='octet')" />
+
+ <xsl:if test="($needlist)">
+ <xsl:text>List</xsl:text>
+ <xsl:if test="not($skiplisttype='yes')">
+ <xsl:choose>
+ <xsl:when test="$doubleescape='yes'">
+ <xsl:text>&amp;lt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&lt;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:if>
+
+ <xsl:if test="not($needlist) or not($skiplisttype='yes')">
+ <!-- look up Java type from IDL type from table array in typemap-shared.inc.xsl -->
+ <xsl:variable name="javatypefield" select="exsl:node-set($G_aSharedTypes)/type[@idlname=$type]/@javaname" />
+
+ <xsl:choose>
+ <xsl:when test="string-length($javatypefield)">
+ <xsl:value-of select="$javatypefield" />
+ </xsl:when>
+ <!-- not a standard type: then it better be one of the types defined in the XIDL -->
+ <xsl:when test="$type='$unknown'">IUnknown</xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fullClassName">
+ <xsl:with-param name="name" select="$type" />
+ <xsl:with-param name="collPrefix" select="''"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <xsl:choose>
+ <xsl:when test="($needlist)">
+ <xsl:if test="not($skiplisttype='yes')">
+ <xsl:choose>
+ <xsl:when test="$doubleescape='yes'">
+ <xsl:text>&amp;gt;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>&gt;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="($needarray)">
+ <xsl:text>[]</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+</xsl:template>
+
+<!--
+ typeIdl2Back: converts $type into a type as used by the backend.
+ -->
+<xsl:template name="typeIdl2Back">
+ <xsl:param name="type" />
+ <xsl:param name="safearray" />
+ <xsl:param name="forceelem" />
+
+ <xsl:choose>
+ <xsl:when test="($G_vboxGlueStyle='xpcom')">
+ <xsl:variable name="needarray" select="($safearray='yes') and not($forceelem='yes')" />
+
+ <xsl:choose>
+ <xsl:when test="$type='long long'">
+ <xsl:text>long</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='unsigned long'">
+ <xsl:text>long</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='long'">
+ <xsl:text>int</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='unsigned short'">
+ <xsl:text>int</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='short'">
+ <xsl:text>short</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='octet'">
+ <xsl:text>byte</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='boolean'">
+ <xsl:text>boolean</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='$unknown'">
+ <xsl:text>nsISupports</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='wstring'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='uuid'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="key('G_keyInterfacesByName', $type)/@wsmap='struct'">
+ <xsl:call-template name="wrappedName">
+ <xsl:with-param name="ifname" select="$type" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyInterfacesByName', $type)) > 0">
+ <xsl:call-template name="wrappedName">
+ <xsl:with-param name="ifname" select="$type" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $type)) > 0">
+ <xsl:text>long</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fullClassName">
+ <xsl:with-param name="name" select="$type" />
+ </xsl:call-template>
+ </xsl:otherwise>
+
+ </xsl:choose>
+ <xsl:if test="$needarray">
+ <xsl:text>[]</xsl:text>
+ </xsl:if>
+ </xsl:when>
+
+ <xsl:when test="($G_vboxGlueStyle='mscom')">
+ <xsl:text>Variant</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="($G_vboxGlueStyle='jaxws')">
+ <xsl:variable name="needarray" select="($safearray='yes' and not($type='octet')) and not($forceelem='yes')" />
+
+ <xsl:if test="$needarray">
+ <xsl:text>List&lt;</xsl:text>
+ </xsl:if>
+ <xsl:choose>
+ <xsl:when test="$type='$unknown'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="key('G_keyInterfacesByName', $type)/@wsmap='managed'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="key('G_keyInterfacesByName', $type)/@wsmap='struct'">
+ <xsl:value-of select="concat($G_virtualBoxPackageCom, '.', $type)" />
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $type)) > 0">
+ <xsl:value-of select="concat($G_virtualBoxPackageCom, '.', $type)" />
+ </xsl:when>
+
+ <!-- we encode byte arrays as Base64 strings. -->
+ <xsl:when test="$type='octet'">
+ <xsl:text>/*base64*/String</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='long long'">
+ <xsl:text>Long</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='unsigned long'">
+ <xsl:text>Long</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='long'">
+ <xsl:text>Integer</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='unsigned short'">
+ <xsl:text>Integer</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='short'">
+ <xsl:text>Short</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='boolean'">
+ <xsl:text>Boolean</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='wstring'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$type='uuid'">
+ <xsl:text>String</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Unhandled type ', $type, ' (typeIdl2Back)')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+
+ </xsl:choose>
+
+ <xsl:if test="$needarray">
+ <xsl:text>&gt;</xsl:text>
+ </xsl:if>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Write typeIdl2Back for this style (typeIdl2Back)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="cookOutParamXpcom">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+ <xsl:variable name="isstruct"
+ select="key('G_keyInterfacesByName', $idltype)/@wsmap='struct'" />
+
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="elemgluetype">
+ <xsl:if test="$safearray='yes'">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$idltype = '$unknown' or (count(key('G_keyInterfacesByName', $idltype)) > 0)">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elembacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat('Helper.wrap2(', $elemgluetype, '.class, ', $elembacktype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('(', $value, ' != null) ? new ', $gluetype, '(', $value, ') : null')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elembacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat('Helper.wrapEnum(', $elemgluetype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($gluetype, '.fromValue(', $value, ')')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="($safearray='yes') and ($idltype='octet')">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.wrap(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$value"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="cookOutParamMscom">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elemgluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="($idltype='octet')">
+ <xsl:value-of select="concat('Helper.wrapBytes(', $value, '.toSafeArray())')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('Helper.wrap(', $elemgluetype, '.class, ', $value, '.toSafeArray())')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$idltype = '$unknown' or (count(key('G_keyInterfacesByName', $idltype)) > 0)">
+ <xsl:value-of select="concat('Helper.wrapDispatch(', $gluetype, '.class, ', $value, '.getDispatch())')"/>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:value-of select="concat($gluetype, '.fromValue(', $value, '.getInt())')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='wstring'">
+ <xsl:value-of select="concat($value, '.getString()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='uuid'">
+ <xsl:value-of select="concat($value, '.getString()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='boolean'">
+ <xsl:value-of select="concat($value, '.toBoolean()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='unsigned short'">
+ <xsl:value-of select="concat('(int)', $value, '.getShort()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='short'">
+ <xsl:value-of select="concat($value, '.getShort()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='long'">
+ <xsl:value-of select="concat($value, '.getInt()')"/>
+ </xsl:when>
+
+
+ <xsl:when test="$idltype='unsigned long'">
+ <xsl:value-of select="concat('(long)', $value, '.getInt()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='long'">
+ <xsl:value-of select="concat($value, '.getInt()')"/>
+ </xsl:when>
+
+ <xsl:when test="$idltype='long long'">
+ <xsl:value-of select="concat($value, '.getLong()')"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Unhandled type' , $idltype, ' (cookOutParamMscom)')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="cookOutParamJaxws">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+
+ <xsl:variable name="isstruct"
+ select="key('G_keyInterfacesByName', $idltype)/@wsmap='struct'" />
+
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elemgluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="''" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="elembacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="''" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$isstruct">
+ <xsl:value-of select="concat('Helper.wrap2(', $elemgluetype, '.class, ', $elembacktype, '.class, objMgr, port, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:value-of select="concat('Helper.convertEnums(', $elembacktype, '.class, ', $elemgluetype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="$idltype = '$unknown' or (count(key('G_keyInterfacesByName', $idltype)) > 0)">
+ <xsl:value-of select="concat('Helper.wrap(', $elemgluetype, '.class, getObjMgr(), port, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="$idltype='octet'">
+ <xsl:value-of select="concat('Helper.decodeBase64(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$value" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:value-of select="concat($gluetype, '.fromValue(', $value, '.value())')"/>
+ </xsl:when>
+ <xsl:when test="$idltype='boolean'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='long long'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='unsigned long long'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='long'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='unsigned long'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='short'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='unsigned short'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='wstring'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$idltype='uuid'">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+ <xsl:when test="$isstruct">
+ <xsl:value-of select="concat('(', $value, ' != null) ? new ', $gluetype, '(', $value, ', getObjMgr(), port) : null')" />
+ </xsl:when>
+ <xsl:when test="$idltype = '$unknown' or (count(key('G_keyInterfacesByName', $idltype)) > 0)">
+ <!-- if the MOR string is empty, that means NULL, so return NULL instead of an object then -->
+ <xsl:value-of select="concat('(', $value, '.length() > 0) ? new ', $gluetype, '(', $value, ', getObjMgr(), port) : null')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Unhandled type ', $idltype, ' (cookOutParamJaxws)')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="cookOutParam">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+ <xsl:choose>
+ <xsl:when test="($G_vboxGlueStyle='xpcom')">
+ <xsl:call-template name="cookOutParamXpcom">
+ <xsl:with-param name="value" select="$value" />
+ <xsl:with-param name="idltype" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="($G_vboxGlueStyle='mscom')">
+ <xsl:call-template name="cookOutParamMscom">
+ <xsl:with-param name="value" select="$value" />
+ <xsl:with-param name="idltype" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="($G_vboxGlueStyle='jaxws')">
+ <xsl:call-template name="cookOutParamJaxws">
+ <xsl:with-param name="value" select="$value" />
+ <xsl:with-param name="idltype" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Unhandled style(cookOutParam)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="cookInParamXpcom">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+ <xsl:variable name="isstruct"
+ select="key('G_keyInterfacesByName', $idltype)/@wsmap='struct'" />
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="backtype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="elemgluetype">
+ <xsl:if test="$safearray='yes'">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="count(key('G_keyInterfacesByName', $idltype)) > 0">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elembacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat('Helper.unwrap2(', $elemgluetype, '.class, ', $elembacktype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('(', $value, ' != null) ? ', $value, '.getTypedWrapped() : null')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$idltype='$unknown'">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrap2(', $elemgluetype, '.class, nsISupports.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('(', $value, ' != null) ? (nsISupports)', $value, '.getWrapped() : null')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapEnum(', $elemgluetype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($value, '.value()')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="($idltype='octet') and ($safearray='yes')">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:choose>
+ <xsl:when test="$idltype='boolean'">
+ <xsl:value-of select="concat('Helper.unwrapBoolean(', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="($idltype='long') or ($idltype='unsigned long') or ($idltype='integer')">
+ <xsl:value-of select="concat('Helper.unwrapInteger(', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="($idltype='short') or ($idltype='unsigned short')">
+ <xsl:value-of select="concat('Helper.unwrapUShort(', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="($idltype='unsigned long long') or ($idltype='long long')">
+ <xsl:value-of select="concat('Helper.unwrapULong(', $value, ')')"/>
+ </xsl:when>
+ <xsl:when test="($idltype='wstring') or ($idltype='uuid')">
+ <xsl:value-of select="concat('Helper.unwrapStr(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$value"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$value"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="cookInParamMscom">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="backtype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="elemgluetype">
+ <xsl:if test="$safearray='yes'">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="count(key('G_keyInterfacesByName', $idltype)) > 0">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elembacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <!-- Sometimes javac needs a boost of self-confidence regarding
+ varargs calls, and this (Object) cast makes sure that it calls
+ the varargs method - as if there is any other. -->
+ <xsl:value-of select="concat('(Object)Helper.unwrap2(', $elemgluetype, '.class, ', $elembacktype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('(', $value, ' != null) ? ', $value, '.getTypedWrapped() : null')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$idltype='$unknown'">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrap2(', $elemgluetype, '.class, Dispatch.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('(', $value, ' != null) ? (Dispatch)', $value, '.getWrapped() : null')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapEnum(', $elemgluetype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($value, '.value()')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="$idltype='boolean'">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapBool(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('new Variant(', $value, ')')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="($idltype='short') or ($idltype='unsigned short')">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapShort(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('new Variant(', $value, ')')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+
+ <xsl:when test="($idltype='long') or ($idltype='unsigned long')">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapInt(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('new Variant(', $value, ')')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="($idltype='wstring') or ($idltype='uuid')">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapString(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('new Variant(', $value, ')')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="($idltype='unsigned long long') or ($idltype='long long')">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrapLong(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('new Variant(', $value, '.longValue())')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="($idltype='octet') and ($safearray='yes')">
+ <xsl:value-of select="$value"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Unhandled type: ', $idltype)" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="cookInParamJaxws">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+ <xsl:variable name="isstruct"
+ select="key('G_keyInterfacesByName', $idltype)/@wsmap='struct'" />
+
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="elemgluetype">
+ <xsl:if test="$safearray='yes'">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="$idltype = '$unknown' or (count(key('G_keyInterfacesByName', $idltype)) > 0)">
+ <xsl:choose>
+ <xsl:when test="@safearray='yes'">
+ <xsl:value-of select="concat('Helper.unwrap(', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat('((', $value, ' == null) ? null :', $value, '.getWrapped())')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="count(key('G_keyEnumsByName', $idltype)) > 0">
+ <xsl:choose>
+ <xsl:when test="$safearray='yes'">
+ <xsl:variable name="elembacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat('Helper.convertEnums(', $elemgluetype, '.class, ', $elembacktype, '.class, ', $value, ')')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="backtype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$idltype" />
+ <xsl:with-param name="safearray" select="'no'" />
+ <xsl:with-param name="forceelem" select="'yes'" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat($backtype, '.fromValue(', $value, '.name())')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+
+ <xsl:when test="($idltype='octet') and ($safearray='yes')">
+ <xsl:value-of select="concat('Helper.encodeBase64(', $value, ')')"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:value-of select="$value"/>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<xsl:template name="cookInParam">
+ <xsl:param name="value"/>
+ <xsl:param name="idltype"/>
+ <xsl:param name="safearray"/>
+ <xsl:choose>
+ <xsl:when test="($G_vboxGlueStyle='xpcom')">
+ <xsl:call-template name="cookInParamXpcom">
+ <xsl:with-param name="value" select="$value" />
+ <xsl:with-param name="idltype" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="($G_vboxGlueStyle='mscom')">
+ <xsl:call-template name="cookInParamMscom">
+ <xsl:with-param name="value" select="$value" />
+ <xsl:with-param name="idltype" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:when test="($G_vboxGlueStyle='jaxws')">
+ <xsl:call-template name="cookInParamJaxws">
+ <xsl:with-param name="value" select="$value" />
+ <xsl:with-param name="idltype" select="$idltype" />
+ <xsl:with-param name="safearray" select="$safearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Unhandled style (cookInParam)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Invoke backend method, including parameter conversion -->
+<xsl:template name="genBackMethodCall">
+ <xsl:param name="ifname"/>
+ <xsl:param name="methodname"/>
+ <xsl:param name="retval"/>
+
+ <xsl:choose>
+ <xsl:when test="($G_vboxGlueStyle='xpcom')">
+ <xsl:text> </xsl:text>
+ <xsl:if test="param[@dir='return']">
+ <xsl:value-of select="concat($retval, ' = ')" />
+ </xsl:if>
+ <xsl:value-of select="concat('getTypedWrapped().', $methodname, '(')"/>
+ <xsl:for-each select="param">
+ <xsl:choose>
+ <xsl:when test="@dir='return'">
+ <xsl:if test="@safearray='yes'">
+ <xsl:text>null</xsl:text>
+ </xsl:if>
+ </xsl:when>
+ <xsl:when test="@dir='out'">
+ <xsl:if test="@safearray='yes'">
+ <xsl:text>null, </xsl:text>
+ </xsl:if>
+ <xsl:value-of select="concat('tmp_', @name)" />
+ </xsl:when>
+ <xsl:when test="@dir='in'">
+ <xsl:if test="(@safearray='yes') and not(@type = 'octet')">
+ <xsl:value-of select="concat(@name, ' != null ? ', @name, '.size() : 0, ')" />
+ </xsl:if>
+ <xsl:variable name="unwrapped">
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="value" select="@name" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$unwrapped"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Unsupported param dir: ', @dir, '&quot;.')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last()) and not(following-sibling::param[1]/@dir='return' and not(following-sibling::param[1]/@safearray='yes'))">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>);&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="($G_vboxGlueStyle='mscom')">
+ <xsl:text> </xsl:text>
+ <xsl:if test="param[@dir='return']">
+ <xsl:value-of select="concat($retval, ' = ')" />
+ </xsl:if>
+ <xsl:value-of select="concat('Helper.invoke(getTypedWrapped(), &quot;', $methodname, '&quot; ')"/>
+ <xsl:for-each select="param[not(@dir='return')]">
+ <xsl:text>, </xsl:text>
+ <xsl:choose>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat('tmp_', @name)" />
+ </xsl:when>
+ <xsl:when test="@dir='in'">
+ <xsl:variable name="unwrapped">
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="value" select="@name" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$unwrapped"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:for-each>
+ <xsl:text>);&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="($G_vboxGlueStyle='jaxws')">
+ <xsl:variable name="jaxwsmethod">
+ <xsl:call-template name="makeJaxwsMethod">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="$methodname" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="portArg">
+ <xsl:if test="not(key('G_keyInterfacesByName', $ifname)/@wsmap='global')">
+ <xsl:text>obj</xsl:text>
+ </xsl:if>
+ </xsl:variable>
+ <xsl:variable name="paramsinout" select="param[@dir='in' or @dir='out']" />
+
+ <xsl:text> </xsl:text>
+ <xsl:if test="param[@dir='return'] and not(param[@dir='out'])">
+ <xsl:value-of select="concat($retval, ' = ')" />
+ </xsl:if>
+ <xsl:value-of select="concat('port.', $jaxwsmethod, '(', $portArg)" />
+ <xsl:if test="$paramsinout and not($portArg='')">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+
+ <!-- jax-ws has an oddity: if both out params and a return value exist,
+ then the return value is moved to the function's argument list... -->
+ <xsl:choose>
+ <xsl:when test="param[@dir='out'] and param[@dir='return']">
+ <xsl:for-each select="param">
+ <xsl:choose>
+ <xsl:when test="@dir='return'">
+ <xsl:value-of select="$retval"/>
+ </xsl:when>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat('tmp_', @name)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="value" select="@name" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last())">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:for-each select="$paramsinout">
+ <xsl:choose>
+ <xsl:when test="@dir='return'">
+ <xsl:value-of select="$retval"/>
+ </xsl:when>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat('tmp_', @name)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="value" select="@name" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last())">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text>);&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (genBackMethodCall)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="genGetterCall">
+ <xsl:param name="ifname"/>
+ <xsl:param name="gettername"/>
+ <xsl:param name="backtype"/>
+ <xsl:param name="retval"/>
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat(' ', $backtype, ' ', $retval, ' = getTypedWrapped().', $gettername, '(')" />
+ <xsl:if test="@safearray='yes'">
+ <xsl:text>null</xsl:text>
+ </xsl:if>
+ <xsl:text>);&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat(' ', $backtype, ' ', $retval, ' = Dispatch.get(getTypedWrapped(), &quot;', @name, '&quot;);&#10;')" />
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:variable name="jaxwsGetter">
+ <xsl:call-template name="makeJaxwsMethod">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="$gettername" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' ', $backtype, ' ', $retval, ' = port.', $jaxwsGetter, '(obj);&#10;')" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (genGetterCall)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="genSetterCall">
+ <xsl:param name="ifname"/>
+ <xsl:param name="settername"/>
+ <xsl:param name="value"/>
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat(' getTypedWrapped().', $settername, '(', $value, ');&#10;')" />
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat(' Dispatch.put(getTypedWrapped(), &quot;', @name, '&quot;, ', $value, ');&#10;')" />
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:variable name="jaxwsSetter">
+ <xsl:call-template name="makeJaxwsMethod">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="$settername" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' port.', $jaxwsSetter, '(obj, ', $value, ');&#10;')" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (genSetterCall)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<xsl:template name="genStructWrapperJaxws">
+ <xsl:param name="ifname"/>
+
+ <xsl:value-of select="concat(' private ', $G_virtualBoxPackageCom, '.', $ifname, ' real;&#10;')"/>
+ <xsl:text> private VboxPortType port;&#10;</xsl:text>
+ <xsl:text> private ObjectRefManager objMgr;&#10;</xsl:text>
+
+ <!-- For structs which contain references to other objects we have to create the stub object during construction time
+ or it is possible that the reference was released by the garbage collector because the reference is not
+ accounted for. -->
+ <!-- Create the private members for containing objects here. -->
+ <xsl:for-each select="attribute">
+ <xsl:variable name="attrname"><xsl:value-of select="@name" /></xsl:variable>
+ <xsl:variable name="attrtype"><xsl:value-of select="@type" /></xsl:variable>
+ <xsl:variable name="attrsafearray"><xsl:value-of select="@safearray" /></xsl:variable>
+
+ <xsl:if test="(not(@wsmap = 'suppress')) and ($attrtype = '$unknown' or (count(key('G_keyInterfacesByName', $attrtype)) > 0))">
+ <xsl:variable name="gluegettertype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' private ', $gluegettertype, ' ', $attrname, ';&#10;')" />
+ </xsl:if>
+ </xsl:for-each>
+
+ <xsl:value-of select="concat('&#10; public ', $ifname, '(', $G_virtualBoxPackageCom, '.', $ifname, ' real, ObjectRefManager objMgr, VboxPortType port)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> this.real = real;&#10;</xsl:text>
+ <xsl:text> this.port = port;&#10;</xsl:text>
+ <xsl:text> this.objMgr = objMgr;&#10;</xsl:text>
+ <!-- Construct stub objects for every attribute containing a reference to a webserver side object -->
+ <xsl:for-each select="attribute">
+ <xsl:variable name="attrname"><xsl:value-of select="@name" /></xsl:variable>
+ <xsl:variable name="attrtype"><xsl:value-of select="@type" /></xsl:variable>
+ <xsl:variable name="attrsafearray"><xsl:value-of select="@safearray" /></xsl:variable>
+
+ <xsl:if test="(not(@wsmap = 'suppress')) and ($attrtype = '$unknown' or (count(key('G_keyInterfacesByName', $attrtype)) > 0))">
+ <xsl:variable name="backgettername">
+ <xsl:choose>
+ <!-- Stupid, but backend boolean getters called isFoo(), not getFoo() -->
+ <xsl:when test="$attrtype = 'boolean'">
+ <xsl:variable name="capsname">
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="$attrname" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat('is', $capsname)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="makeGetterName">
+ <xsl:with-param name="attrname" select="$attrname" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="wrapped">
+ <xsl:call-template name="cookOutParam">
+ <xsl:with-param name="value" select="concat('real.', $backgettername, '()')" />
+ <xsl:with-param name="idltype" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' ', $attrname, ' = ', $wrapped, ';&#10;')" />
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text> }&#10;&#10;</xsl:text>
+
+ <xsl:text><![CDATA[ private ObjectRefManager getObjMgr()
+ {
+ return this.objMgr;
+ }
+]]></xsl:text>
+
+ <xsl:for-each select="attribute">
+ <xsl:variable name="attrname"><xsl:value-of select="@name" /></xsl:variable>
+ <xsl:variable name="attrtype"><xsl:value-of select="@type" /></xsl:variable>
+ <xsl:variable name="attrsafearray"><xsl:value-of select="@safearray" /></xsl:variable>
+
+ <xsl:if test="not(@wsmap = 'suppress')">
+
+ <xsl:if test="not(@readonly = 'yes')">
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Non read-only struct (genStructWrapperJaxws) in interface ', $ifname, ', attribute ', $attrname)" />
+ </xsl:call-template>
+ </xsl:if>
+
+ <!-- Emit getter -->
+ <xsl:variable name="backgettername">
+ <xsl:choose>
+ <!-- Stupid, but backend boolean getters called isFoo(), not getFoo() -->
+ <xsl:when test="$attrtype = 'boolean'">
+ <xsl:variable name="capsname">
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="$attrname" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat('is', $capsname)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="makeGetterName">
+ <xsl:with-param name="attrname" select="$attrname" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <xsl:variable name="gluegettername">
+ <xsl:call-template name="makeGetterName">
+ <xsl:with-param name="attrname" select="$attrname" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="gluegettertype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <xsl:variable name="backgettertype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- For attributes containing a reference to another object just return the already cerated stub -->
+ <xsl:apply-templates select="desc" mode="attribute_get"/>
+ <xsl:value-of select="concat(' public ', $gluegettertype, ' ', $gluegettername, '()&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$attrtype = '$unknown' or (count(key('G_keyInterfacesByName', $attrtype)) > 0)">
+ <xsl:value-of select="concat(' return ', $attrname, ';&#10;')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(' ', $backgettertype, ' retVal = real.', $backgettername, '();&#10;')" />
+ <xsl:variable name="wrapped">
+ <xsl:call-template name="cookOutParam">
+ <xsl:with-param name="value" select="'retVal'" />
+ <xsl:with-param name="idltype" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' return ', $wrapped, ';&#10;')" />
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:if>
+
+ </xsl:for-each>
+
+</xsl:template>
+
+<!-- Interface method wrapper -->
+<xsl:template name="genMethod">
+ <xsl:param name="ifname"/>
+ <xsl:param name="methodname"/>
+
+ <xsl:choose>
+ <xsl:when test="(param[@mod='ptr']) or (($G_vboxGlueStyle='jaxws') and (param[@type=($G_setSuppressedInterfaces/@name)]))" >
+ <xsl:value-of select="concat(' // Skipping method ', $methodname, ' for it has parameters with suppressed types&#10;')" />
+ </xsl:when>
+ <xsl:when test="($G_vboxGlueStyle='jaxws') and (@wsmap = 'suppress')" >
+ <xsl:value-of select="concat(' // Skipping method ', $methodname, ' for it is suppressed&#10;')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="hasReturnParms" select="param[@dir='return']" />
+ <xsl:variable name="hasOutParms" select="count(param[@dir='out']) > 0" />
+ <xsl:variable name="returnidltype" select="param[@dir='return']/@type" />
+ <xsl:variable name="returnidlsafearray" select="param[@dir='return']/@safearray" />
+ <xsl:if test="$hasOutParms and not($hasReturnParms) and (string-length(@wsmap) = 0) and (count(param[@dir='out']) = 1)">
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('genMethod: ', $ifname, $hasOutParms, not($hasReturnParms), 'a', string-length(@wsmap) = 0, 'b', @wsmap, (count(param[@dir='out']) = 1), '::', $methodname, ' has exactly one out parameter and no return parameter, this causes trouble with JAX-WS and the out parameter needs to be converted to return')" />
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:variable name="returngluetype">
+ <xsl:choose>
+ <xsl:when test="$returnidltype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>void</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="retValValue">
+ <xsl:choose>
+ <xsl:when test="(param[@dir='out']) and ($G_vboxGlueStyle='jaxws')">
+ <xsl:text>retVal.value</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>retVal</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:apply-templates select="desc" mode="method"/>
+ <xsl:value-of select="concat(' public ', $returngluetype, ' ', $methodname, '(')" />
+ <xsl:variable name="paramsinout" select="param[@dir='in' or @dir='out']" />
+ <xsl:for-each select="exsl:node-set($paramsinout)">
+ <xsl:variable name="paramgluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat('Holder&lt;', $paramgluetype, '&gt; ', @name)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($paramgluetype, ' ', @name)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last())">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+
+ <xsl:call-template name="startExcWrapper">
+ <xsl:with-param name="preventObjRelease" select="$hasReturnParms and ($returnidltype = '$unknown' or (count(key('G_keyInterfacesByName', $returnidltype)) > 0))" />
+ </xsl:call-template>
+
+ <!-- declare temp out params -->
+ <xsl:for-each select="param[@dir='out']">
+ <xsl:variable name="backouttype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat(' ', $backouttype, '[] tmp_', @name, ' = (', $backouttype, '[])java.lang.reflect.Array.newInstance(', $backouttype, '.class, 1);&#10;')"/>
+ </xsl:when>
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat(' Variant tmp_', @name, ' = new Variant();&#10;')"/>
+ </xsl:when>
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:value-of select="concat(' javax.xml.ws.Holder&lt;', $backouttype, '&gt; tmp_', @name, ' = new javax.xml.ws.Holder&lt;', $backouttype, '&gt;();&#10;')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Handle out param (genMethod)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <!-- declare return param, if any -->
+ <xsl:if test="$hasReturnParms">
+ <xsl:variable name="backrettype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="(param[@dir='out']) and ($G_vboxGlueStyle='jaxws')">
+ <xsl:value-of select="concat(' javax.xml.ws.Holder&lt;', $backrettype, '&gt;',
+ ' retVal = new javax.xml.ws.Holder&lt;', $backrettype,
+ '&gt;();&#10;')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(' ', $backrettype, ' retVal;&#10;')"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:if>
+
+ <!-- Method call -->
+ <xsl:call-template name="genBackMethodCall">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="$methodname" />
+ <xsl:with-param name="retval" select="'retVal'" />
+ </xsl:call-template>
+
+ <!-- return out params -->
+ <xsl:for-each select="param[@dir='out']">
+ <xsl:variable name="varval">
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat('tmp_', @name, '[0]')" />
+ </xsl:when>
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat('tmp_', @name)" />
+ </xsl:when>
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:value-of select="concat('tmp_', @name, '.value')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (genMethod, outparam)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="wrapped">
+ <xsl:call-template name="cookOutParam">
+ <xsl:with-param name="value" select="$varval" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' ', @name, '.value = ', $wrapped, ';&#10;')"/>
+ </xsl:for-each>
+
+ <xsl:if test="$hasReturnParms">
+ <!-- actual 'return' statement -->
+ <xsl:variable name="wrapped">
+ <xsl:call-template name="cookOutParam">
+ <xsl:with-param name="value" select="$retValValue" />
+ <xsl:with-param name="idltype" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' return ', $wrapped, ';&#10;')" />
+ </xsl:if>
+ <xsl:call-template name="endExcWrapper">
+ <xsl:with-param name="allowObjRelease" select="$hasReturnParms and ($returnidltype = '$unknown' or (count(key('G_keyInterfacesByName', $returnidltype)) > 0))" />
+ </xsl:call-template>
+
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+
+</xsl:template>
+
+<!-- Callback interface method -->
+<xsl:template name="genCbMethodDecl">
+ <xsl:param name="ifname"/>
+ <xsl:param name="methodname"/>
+
+ <xsl:choose>
+ <xsl:when test="(param[@mod='ptr'])" >
+ <xsl:value-of select="concat(' // Skipping method ', $methodname, ' for it has parameters with suppressed types&#10;')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="returnidltype" select="param[@dir='return']/@type" />
+ <xsl:variable name="returnidlsafearray" select="param[@dir='return']/@safearray" />
+ <xsl:variable name="returngluetype">
+ <xsl:choose>
+ <xsl:when test="$returnidltype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>void</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:value-of select="concat(' public ', $returngluetype, ' ', $methodname, '(')" />
+ <xsl:variable name="paramsinout" select="param[@dir='in' or @dir='out']" />
+ <xsl:for-each select="exsl:node-set($paramsinout)">
+ <xsl:variable name="paramgluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat('Holder&lt;', $paramgluetype, '&gt; ', @name)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat($paramgluetype, ' ', @name)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last())">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>);&#10;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- queryInterface wrapper -->
+<xsl:template name="genQI">
+ <xsl:param name="ifname"/>
+ <xsl:param name="uuid" />
+
+ <xsl:value-of select="concat(' public static ', $ifname, ' queryInterface(IUnknown obj)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:variable name="backtype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$ifname" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:text> nsISupports nsobj = obj != null ? (nsISupports)obj.getWrapped() : null;&#10;</xsl:text>
+ <xsl:text> if (nsobj == null) return null;&#10;</xsl:text>
+ <xsl:value-of select="concat(' ', $backtype, ' qiobj = Helper.queryInterface(nsobj, &quot;{', $uuid, '}&quot;, ', $backtype, '.class);&#10;')" />
+ <xsl:value-of select="concat(' return qiobj == null ? null : new ', $ifname, '(qiobj);&#10;')" />
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat(' return', ' obj == null ? null : new ', $ifname, '((com.jacob.com.Dispatch)obj.getWrapped());&#10;')" />
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <!-- bad, need to check that we really can be casted to this type -->
+ <xsl:value-of select="concat(' return obj == null ? null : new ', $ifname, '(obj.getWrapped(), obj.getObjMgr(), obj.getRemoteWSPort());&#10;')" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (genQI)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+
+ </xsl:choose>
+ <xsl:text> }&#10;</xsl:text>
+</xsl:template>
+
+
+<xsl:template name="genCbMethodImpl">
+ <xsl:param name="ifname"/>
+ <xsl:param name="methodname"/>
+
+ <xsl:choose>
+ <xsl:when test="(param[@mod='ptr'])" >
+ <xsl:value-of select="concat(' // Skipping method ', $methodname, ' for it has parameters with suppressed types&#10;')" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="hasReturnParms" select="param[@dir='return']" />
+ <xsl:variable name="returnidltype" select="param[@dir='return']/@type" />
+ <xsl:variable name="returnidlsafearray" select="param[@dir='return']/@safearray" />
+ <xsl:variable name="returnbacktype">
+ <xsl:choose>
+ <xsl:when test="$returnidltype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:text>void</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="paramsinout" select="param[@dir='in' or @dir='out']" />
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat(' public ', $returnbacktype, ' ', $methodname, '(')" />
+ <xsl:for-each select="exsl:node-set($paramsinout)">
+ <xsl:variable name="parambacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat($parambacktype, '[] ', @name)" />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:if test="@safearray='yes'">
+ <xsl:value-of select="concat('long len_', @name, ', ')" />
+ </xsl:if>
+ <xsl:value-of select="concat($parambacktype, ' ', @name)" />
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last())">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>)&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:variable name="capsname">
+ <xsl:call-template name="capitalize">
+ <xsl:with-param name="str" select="$methodname" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' public ', $returnbacktype, ' ', $capsname, '(')" />
+ <xsl:text>Variant _args[])&#10;</xsl:text>
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:for-each select="exsl:node-set($paramsinout)">
+ <xsl:variable name="parambacktype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' ', $parambacktype, ' ', @name, '=_args[', count(preceding-sibling::param), '];&#10;')" />
+ </xsl:for-each>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (genSetterCall)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- declare temp out params -->
+ <xsl:for-each select="param[@dir='out']">
+ <xsl:variable name="glueouttype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' Holder&lt;', $glueouttype, '&gt; tmp_', @name, ' = new Holder&lt;', $glueouttype, '&gt;();&#10;')"/>
+ </xsl:for-each>
+
+ <!-- declare return param, if any -->
+ <xsl:if test="$hasReturnParms">
+ <xsl:variable name="gluerettype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' ', $gluerettype, ' retVal = &#10;')"/>
+ </xsl:if>
+
+ <!-- Method call -->
+ <xsl:value-of select="concat(' sink.', $methodname, '(')"/>
+ <xsl:for-each select="param[not(@dir='return')]">
+ <xsl:choose>
+ <xsl:when test="@dir='out'">
+ <xsl:value-of select="concat('tmp_', @name)" />
+ </xsl:when>
+ <xsl:when test="@dir='in'">
+ <xsl:variable name="wrapped">
+ <xsl:call-template name="cookOutParam">
+ <xsl:with-param name="value" select="@name" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="$wrapped"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Unsupported param dir: ', @dir, '&quot;.')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test="not(position()=last())">
+ <xsl:text>, </xsl:text>
+ </xsl:if>
+ </xsl:for-each>
+ <xsl:text>);&#10;</xsl:text>
+
+ <!-- return out params -->
+ <xsl:for-each select="param[@dir='out']">
+
+ <xsl:variable name="unwrapped">
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="value" select="concat('tmp_', @name, '.value')" />
+ <xsl:with-param name="idltype" select="@type" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat(' ', @name, '[0] = ', $unwrapped, ';&#10;')"/>
+ </xsl:when>
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat(' _args[', count(preceding-sibling::param), '] = ', $unwrapped, ';&#10;')"/>
+ </xsl:when>
+ </xsl:choose>
+ </xsl:for-each>
+
+ <xsl:if test="$hasReturnParms">
+ <!-- actual 'return' statement -->
+ <xsl:variable name="unwrapped">
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="value" select="'retVal'" />
+ <xsl:with-param name="idltype" select="$returnidltype" />
+ <xsl:with-param name="safearray" select="$returnidlsafearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' return ', $unwrapped, ';&#10;')" />
+ </xsl:if>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!-- Interface method -->
+<xsl:template name="genIfaceWrapper">
+ <xsl:param name="ifname"/>
+
+ <xsl:variable name="wrappedType">
+ <xsl:call-template name="wrappedName">
+ <xsl:with-param name="ifname" select="$ifname" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Constructor -->
+ <xsl:choose>
+ <xsl:when test="($G_vboxGlueStyle='jaxws')">
+ <xsl:value-of select="concat(' public ', $ifname, '(String wrapped, ObjectRefManager objMgr, VboxPortType port)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> super(wrapped, objMgr, port);&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="($G_vboxGlueStyle='xpcom') or ($G_vboxGlueStyle='mscom')">
+ <xsl:value-of select="concat(' public ', $ifname, '(', $wrappedType, ' wrapped)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> super(wrapped);&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+
+ <!-- Typed wrapped object accessor -->
+ <xsl:value-of select="concat(' public ', $wrappedType, ' getTypedWrapped()&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:value-of select="concat(' return (', $wrappedType, ') getWrapped();&#10;')" />
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (root, ctr)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <!-- Attributes -->
+ <xsl:for-each select="attribute[not(@mod='ptr')]">
+ <xsl:variable name="attrname"><xsl:value-of select="@name" /></xsl:variable>
+ <xsl:variable name="attrtype"><xsl:value-of select="@type" /></xsl:variable>
+ <xsl:variable name="attrsafearray"><xsl:value-of select="@safearray" /></xsl:variable>
+
+ <xsl:choose>
+ <xsl:when test="($G_vboxGlueStyle='jaxws') and ($attrtype=($G_setSuppressedInterfaces/@name))">
+ <xsl:value-of select="concat(' // Skipping attribute ', $attrname, ' of suppressed type ', $attrtype, '&#10;&#10;')" />
+ </xsl:when>
+ <xsl:when test="($G_vboxGlueStyle='jaxws') and (@wsmap = 'suppress')" >
+ <xsl:value-of select="concat(' // Skipping attribute ', $attrname, ' for it is suppressed&#10;')" />
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- emit getter method -->
+ <xsl:apply-templates select="desc" mode="attribute_get"/>
+ <xsl:variable name="gettername">
+ <xsl:call-template name="makeGetterName">
+ <xsl:with-param name="attrname" select="$attrname" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="gluetype">
+ <xsl:call-template name="typeIdl2Glue">
+ <xsl:with-param name="type" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="backtype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:variable name="wrapped">
+ <xsl:call-template name="cookOutParam">
+ <xsl:with-param name="value" select="'retVal'" />
+ <xsl:with-param name="idltype" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' public ', $gluetype, ' ', $gettername, '()&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+
+
+ <xsl:call-template name="startExcWrapper">
+ <xsl:with-param name="preventObjRelease" select="$attrtype = '$unknown' or (count(key('G_keyInterfacesByName', $attrtype)) > 0)" />
+ </xsl:call-template>
+
+ <!-- Actual getter implementation -->
+ <xsl:call-template name="genGetterCall">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="gettername" select="$gettername" />
+ <xsl:with-param name="backtype" select="$backtype" />
+ <xsl:with-param name="retval" select="'retVal'" />
+ </xsl:call-template>
+
+ <xsl:value-of select="concat(' return ', $wrapped, ';&#10;')" />
+
+ <xsl:call-template name="endExcWrapper">
+ <xsl:with-param name="allowObjRelease" select="$attrtype = '$unknown' or (count(key('G_keyInterfacesByName', $attrtype)) > 0)" />
+ </xsl:call-template>
+
+ <xsl:text> }&#10;</xsl:text>
+ <xsl:if test="not(@readonly = 'yes')">
+ <!-- emit setter method -->
+ <xsl:apply-templates select="desc" mode="attribute_set"/>
+ <xsl:variable name="settername"><xsl:call-template name="makeSetterName"><xsl:with-param name="attrname" select="$attrname" /></xsl:call-template></xsl:variable>
+ <xsl:variable name="unwrapped">
+ <xsl:call-template name="cookInParam">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="value" select="'value'" />
+ <xsl:with-param name="idltype" select="$attrtype" />
+ <xsl:with-param name="safearray" select="@safearray" />
+ </xsl:call-template>
+ </xsl:variable>
+ <xsl:value-of select="concat(' public void ', $settername, '(', $gluetype, ' value)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:call-template name="startExcWrapper"/>
+ <!-- Actual setter implementation -->
+ <xsl:call-template name="genSetterCall">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="settername" select="$settername" />
+ <xsl:with-param name="value" select="$unwrapped" />
+ </xsl:call-template>
+ <xsl:call-template name="endExcWrapper"/>
+ <xsl:text> }&#10;</xsl:text>
+ </xsl:if>
+
+ </xsl:otherwise>
+ </xsl:choose>
+
+ </xsl:for-each>
+
+ <!-- emit queryInterface() *to* this class -->
+ <xsl:call-template name="genQI">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="uuid" select="@uuid" />
+ </xsl:call-template>
+
+ <!-- emit methods -->
+ <xsl:for-each select="method">
+ <xsl:call-template name="genMethod">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="@name" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+</xsl:template>
+
+<xsl:template name="genIface">
+ <xsl:param name="ifname" />
+ <xsl:param name="filename" />
+
+ <xsl:variable name="wsmap" select="@wsmap" />
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="$filename" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text>import java.util.List;&#10;&#10;</xsl:text>
+
+ <xsl:apply-templates select="desc" mode="interface"/>
+
+ <xsl:choose>
+ <xsl:when test="($wsmap='struct') and ($G_vboxGlueStyle='jaxws')">
+ <xsl:value-of select="concat('public class ', $ifname, '&#10;')" />
+ <xsl:text>{&#10;&#10;</xsl:text>
+ <xsl:call-template name="genStructWrapperJaxws">
+ <xsl:with-param name="ifname" select="$ifname" />
+ </xsl:call-template>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:variable name="extends" select="key('G_keyInterfacesByName', $ifname)/@extends" />
+ <xsl:choose>
+ <xsl:when test="($extends = '$unknown') or ($extends = '$errorinfo')">
+ <xsl:value-of select="concat('public class ', $ifname, ' extends IUnknown&#10;')" />
+ <xsl:text>{&#10;&#10;</xsl:text>
+ </xsl:when>
+ <xsl:when test="count(key('G_keyInterfacesByName', $extends)) > 0">
+ <xsl:value-of select="concat('public class ', $ifname, ' extends ', $extends, '&#10;')" />
+ <xsl:text>{&#10;&#10;</xsl:text>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="concat('Interface generation: interface &quot;', $ifname, '&quot; has invalid &quot;extends&quot; value ', $extends, '.')" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ <xsl:call-template name="genIfaceWrapper">
+ <xsl:with-param name="ifname" select="$ifname" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- end of class -->
+ <xsl:text>}&#10;</xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="$filename" />
+ </xsl:call-template>
+
+</xsl:template>
+
+<xsl:template name="genCb">
+ <xsl:param name="ifname" />
+ <xsl:param name="filename" />
+ <xsl:param name="filenameimpl" />
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="$filename" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:text>import java.util.List;&#10;</xsl:text>
+
+ <xsl:value-of select="concat('public interface ', $ifname, '&#10;')" />
+ <xsl:text>{&#10;</xsl:text>
+
+ <!-- emit methods declarations-->
+ <xsl:for-each select="method">
+ <xsl:call-template name="genCbMethodDecl">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="@name" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <xsl:text>}&#10;&#10;</xsl:text>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="$filename" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="$filenameimpl" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:text>import java.util.List;&#10;</xsl:text>
+
+ <xsl:variable name="backtype">
+ <xsl:call-template name="typeIdl2Back">
+ <xsl:with-param name="type" select="$ifname" />
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- emit glue methods body -->
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:value-of select="concat('class ', $ifname, 'Impl extends nsISupportsBase implements ', $backtype, '&#10;')" />
+ <xsl:text>{&#10;</xsl:text>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:value-of select="concat('public class ', $ifname, 'Impl&#10;')" />
+ <xsl:text>{&#10;</xsl:text>
+ </xsl:when>
+ </xsl:choose>
+
+ <xsl:value-of select="concat(' ', $ifname, ' sink;&#10;')" />
+
+ <xsl:value-of select="concat(' ', $ifname, 'Impl(', $ifname, ' sink)&#10;')" />
+ <xsl:text> {&#10;</xsl:text>
+ <xsl:text> this.sink = sink;&#10;</xsl:text>
+ <xsl:text> }&#10;</xsl:text>
+
+ <!-- emit methods implementations -->
+ <xsl:for-each select="method">
+ <xsl:call-template name="genCbMethodImpl">
+ <xsl:with-param name="ifname" select="$ifname" />
+ <xsl:with-param name="methodname" select="@name" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <xsl:text>}&#10;&#10;</xsl:text>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="$filenameimpl" />
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emitHandwritten">
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'Holder.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+public class Holder<T>
+{
+ public T value;
+
+ public Holder()
+ {
+ }
+ public Holder(T value)
+ {
+ this.value = value;
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'Holder.java'" />
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emitHandwrittenXpcom">
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'IUnknown.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackageCom" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+public class IUnknown
+{
+ private Object obj;
+ public IUnknown(Object obj)
+ {
+ this.obj = obj;
+ }
+
+ public Object getWrapped()
+ {
+ return this.obj;
+ }
+
+ public void setWrapped(Object obj)
+ {
+ this.obj = obj;
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'IUnknown.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'Helper.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackageCom" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+public class Helper
+{
+ public static List<Short> wrap(byte[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Short> ret = new ArrayList<Short>(values.length);
+ for (short v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<Short> wrap(short[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Short> ret = new ArrayList<Short>(values.length);
+ for (short v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<Integer> wrap(int[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Integer> ret = new ArrayList<Integer>(values.length);
+ for (int v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<Long> wrap(long[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Long> ret = new ArrayList<Long>(values.length);
+ for (long v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<Boolean> wrap(boolean[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Boolean> ret = new ArrayList<Boolean>(values.length);
+ for (boolean v: values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<String> wrap(String[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<String> ret = new ArrayList<String>(values.length);
+ for (String v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static <T> List<T> wrap(Class<T> wrapperClass, T[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<T> ret = new ArrayList<T>(values.length);
+ for (T v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ @SuppressWarnings( "unchecked")
+ public static <T> List<T> wrapEnum(Class<T> wrapperClass, long values[])
+ {
+ try
+ {
+ if (values == null)
+ return null;
+ //// This code is questionable, as it invokes a private constructor
+ //// (all enums only have default constructors), and we don't really
+ //// know what to pass as the name, and the ordinal may or may not
+ //// be sensible, especially if the long was abused as a bitset.
+ //Constructor<T> c = wrapperClass.getDeclaredConstructor(String.class, int.class, int.class);
+ //c.setAccessible(true); // make it callable
+ //List<T> ret = new ArrayList<T>(values.length);
+ //for (long v : values)
+ //{
+ // T convEnum = c.newInstance("unknown", (int)v, (int)v);
+ // ret.add(convEnum);
+ //}
+
+ // Alternative implementation: use the fromValue method, which is
+ // what the code handling single enums will do. I see no reason to
+ // use the above very ugly hack if there are better alternatives,
+ // which as a bonus complain about unknown values. This variant is
+ // slower, but also orders of magnitude safer.
+ java.lang.reflect.Method fromValue = wrapperClass.getMethod("fromValue", long.class);
+ List<T> ret = new ArrayList<T>(values.length);
+ for (long v : values)
+ {
+ T convEnum = (T)fromValue.invoke(null, v);
+ ret.add(convEnum);
+ }
+ return ret;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ //catch (InstantiationException e)
+ //{
+ // throw new AssertionError(e);
+ //}
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+ public static short[] unwrapUShort(List<Short> values)
+ {
+ if (values == null)
+ return null;
+
+ short[] ret = new short[values.size()];
+ int i = 0;
+ for (short l : values)
+ {
+ ret[i++] = l;
+ }
+ return ret;
+ }
+
+ public static int[] unwrapInteger(List<Integer> values)
+ {
+ if (values == null)
+ return null;
+
+ int[] ret = new int[values.size()];
+ int i = 0;
+ for (int l : values)
+ {
+ ret[i++] = l;
+ }
+ return ret;
+ }
+
+ public static long[] unwrapULong(List<Long> values)
+ {
+ if (values == null)
+ return null;
+
+ long[] ret = new long[values.size()];
+ int i = 0;
+ for (long l : values)
+ {
+ ret[i++] = l;
+ }
+ return ret;
+ }
+
+ public static boolean[] unwrapBoolean(List<Boolean> values)
+ {
+ if (values == null)
+ return null;
+
+ boolean[] ret = new boolean[values.size()];
+ int i = 0;
+ for (boolean l : values)
+ {
+ ret[i++] = l;
+ }
+ return ret;
+ }
+
+ public static String[] unwrapStr(List<String> values)
+ {
+ if (values == null)
+ return null;
+
+ String[] ret = new String[values.size()];
+ int i = 0;
+ for (String l : values)
+ {
+ ret[i++] = l;
+ }
+ return ret;
+ }
+
+ public static <T extends Enum <T>> long[] unwrapEnum(Class<T> enumClass, List<T> values)
+ {
+ if (values == null)
+ return null;
+
+ long result[] = new long[values.size()];
+ try
+ {
+ java.lang.reflect.Method valueM = enumClass.getMethod("value");
+ int i = 0;
+ for (T v : values)
+ {
+ result[i++] = (Integer)valueM.invoke(v);
+ }
+ return result;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch(SecurityException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static <T1, T2> List<T1> wrap2(Class<T1> wrapperClass1, Class<T2> wrapperClass2, T2[] values)
+ {
+ try
+ {
+ if (values == null)
+ return null;
+
+ Constructor<T1> c = wrapperClass1.getConstructor(wrapperClass2);
+ List<T1> ret = new ArrayList<T1>(values.length);
+ for (T2 v : values)
+ {
+ ret.add(c.newInstance(v));
+ }
+ return ret;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ @SuppressWarnings( "unchecked")
+ public static <T> T[] unwrap(Class<T> wrapperClass, List<T> values)
+ {
+ if (values == null)
+ return null;
+ if (values.size() == 0)
+ return null;
+ return (T[])values.toArray((T[])Array.newInstance(wrapperClass, values.size()));
+ }
+
+ @SuppressWarnings( "unchecked" )
+ public static <T> T queryInterface(Object obj, String uuid, Class<T> iface)
+ {
+ return (T)queryInterface(obj, uuid);
+ }
+
+ public static Object queryInterface(Object obj, String uuid)
+ {
+ try
+ {
+ /* Kind of ugly, but does the job of casting */
+ org.mozilla.xpcom.Mozilla moz = org.mozilla.xpcom.Mozilla.getInstance();
+ long xpobj = moz.wrapJavaObject(obj, uuid);
+ return moz.wrapXPCOMObject(xpobj, uuid);
+ }
+ catch (Exception e)
+ {
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T1 extends IUnknown, T2> T2[] unwrap2(Class<T1> wrapperClass1, Class<T2> wrapperClass2, List<T1> values)
+ {
+ if (values == null)
+ return null;
+
+ T2 ret[] = (T2[])Array.newInstance(wrapperClass2, values.size());
+ int i = 0;
+ for (T1 obj : values)
+ {
+ ret[i++] = (T2)obj.getWrapped();
+ }
+ return ret;
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'Helper.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VBoxException.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text>
+import org.mozilla.xpcom.*;
+
+public class VBoxException extends RuntimeException
+{
+ private int resultCode;
+ private IVirtualBoxErrorInfo errorInfo;
+
+ public VBoxException(String message)
+ {
+ super(message);
+ resultCode = -1;
+ errorInfo = null;
+ }
+
+ public VBoxException(String message, Throwable cause)
+ {
+ super(message, cause);
+ if (cause instanceof org.mozilla.xpcom.XPCOMException)
+ {
+ resultCode = (int)((org.mozilla.xpcom.XPCOMException)cause).errorcode;
+ try
+ {
+ Mozilla mozilla = Mozilla.getInstance();
+ nsIServiceManager sm = mozilla.getServiceManager();
+ nsIExceptionService es = (nsIExceptionService)sm.getServiceByContractID("@mozilla.org/exceptionservice;1", nsIExceptionService.NS_IEXCEPTIONSERVICE_IID);
+ nsIExceptionManager em = es.getCurrentExceptionManager();
+ nsIException ex = em.getCurrentException();
+ errorInfo = new IVirtualBoxErrorInfo((org.mozilla.interfaces.IVirtualBoxErrorInfo)ex.queryInterface(org.mozilla.interfaces.IVirtualBoxErrorInfo.IVIRTUALBOXERRORINFO_IID));
+ }
+ catch (NullPointerException e)
+ {
+ e.printStackTrace();
+ // nothing we can do
+ errorInfo = null;
+ }
+ }
+ else
+ resultCode = -1;
+ }
+
+ public int getResultCode()
+ {
+ return resultCode;
+ }
+
+ public IVirtualBoxErrorInfo getVirtualBoxErrorInfo()
+ {
+ return errorInfo;
+ }
+}
+</xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VBoxException.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VirtualBoxManager.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+
+import java.io.File;
+
+import org.mozilla.xpcom.*;
+import org.mozilla.interfaces.*;
+
+public class VirtualBoxManager
+{
+ private Mozilla mozilla;
+ private IVirtualBox vbox;
+ private nsIComponentManager componentManager;
+
+ private VirtualBoxManager(Mozilla mozilla)
+ {
+ this.mozilla = mozilla;
+ this.componentManager = mozilla.getComponentManager();
+ this.vbox = new IVirtualBox((org.mozilla.interfaces.IVirtualBox) this.componentManager
+ .createInstanceByContractID("@virtualbox.org/VirtualBox;1",
+ null,
+ org.mozilla.interfaces.IVirtualBox.IVIRTUALBOX_IID));
+ }
+
+ public void connect(String url, String username, String passwd)
+ {
+ throw new VBoxException("Connect doesn't make sense for local bindings");
+ }
+
+ public void disconnect()
+ {
+ throw new VBoxException("Disconnect doesn't make sense for local bindings");
+ }
+
+ public static void initPerThread()
+ {
+ }
+
+ public static void deinitPerThread()
+ {
+ }
+
+ public String getClientAPIVersion()
+ {
+ return "]]></xsl:text>
+ <xsl:value-of select="substring($G_vboxApiSuffix, 2)" />
+ <xsl:text><![CDATA[";
+ }
+
+ public IVirtualBox getVBox()
+ {
+ return this.vbox;
+ }
+
+ public ISession getSessionObject()
+ {
+ return new ISession((org.mozilla.interfaces.ISession) componentManager
+ .createInstanceByContractID("@virtualbox.org/Session;1", null,
+ org.mozilla.interfaces.ISession.ISESSION_IID));
+ }
+
+ public ISession openMachineSession(IMachine m) throws Exception
+ {
+ ISession s = getSessionObject();
+ m.lockMachine(s, LockType.Shared);
+ return s;
+ }
+
+ public void closeMachineSession(ISession s)
+ {
+ if (s != null)
+ s.unlockMachine();
+ }
+
+ private static boolean hasInstance = false;
+ private static boolean isMozillaInited = false;
+
+ public static synchronized VirtualBoxManager createInstance(String home)
+ {
+ if (hasInstance)
+ throw new VBoxException("only one instance of VirtualBoxManager at a time allowed");
+ if (home == null || home.equals(""))
+ home = System.getProperty("vbox.home");
+
+ if (home == null)
+ throw new VBoxException("vbox.home Java property must be defined to use XPCOM bridge");
+
+ File grePath = new File(home);
+
+ Mozilla mozilla = Mozilla.getInstance();
+ if (!isMozillaInited)
+ {
+ mozilla.initialize(grePath);
+ try
+ {
+ mozilla.initXPCOM(grePath, null);
+ isMozillaInited = true;
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ hasInstance = true;
+
+ return new VirtualBoxManager(mozilla);
+ }
+
+ public IEventListener createListener(Object sink)
+ {
+ return new IEventListener(new EventListenerImpl(sink));
+ }
+
+ public void cleanup()
+ {
+ deinitPerThread();
+ // cleanup, we don't do that, as XPCOM bridge doesn't cleanly
+ // shuts down, so we prefer to avoid native shutdown
+ // mozilla.shutdownXPCOM(null);
+ mozilla = null;
+ hasInstance = false;
+ }
+
+ public void waitForEvents(long tmo)
+ {
+ mozilla.waitForEvents(tmo);
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VirtualBoxManager.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'EventListenerImpl.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+import org.mozilla.interfaces.*;
+
+public class EventListenerImpl extends nsISupportsBase implements org.mozilla.interfaces.IEventListener
+{
+ private Object obj;
+ private java.lang.reflect.Method handleEvent;
+ EventListenerImpl(Object obj)
+ {
+ this.obj = obj;
+ try
+ {
+ this.handleEvent = obj.getClass().getMethod("handleEvent", IEvent.class);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ public void handleEvent(org.mozilla.interfaces.IEvent ev)
+ {
+ try
+ {
+ if (obj != null && handleEvent != null)
+ handleEvent.invoke(obj, ev != null ? new IEvent(ev) : null);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+}]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'EventListenerImpl.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VBoxObjectBase.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+abstract class nsISupportsBase implements org.mozilla.interfaces.nsISupports
+{
+ public org.mozilla.interfaces.nsISupports queryInterface(String iid)
+ {
+ return org.mozilla.xpcom.Mozilla.queryInterface(this, iid);
+ }
+}
+
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VBoxObjectBase.java'" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template name="emitHandwrittenMscom">
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'IUnknown.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackageCom" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+public class IUnknown
+{
+ private Object obj;
+ public IUnknown(Object obj)
+ {
+ this.obj = obj;
+ }
+
+ public Object getWrapped()
+ {
+ return this.obj;
+ }
+
+ public void setWrapped(Object obj)
+ {
+ this.obj = obj;
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'IUnknown.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'Helper.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackageCom" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import com.jacob.com.*;
+
+public class Helper
+{
+ public static List<Short> wrap(short[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Short> ret = new ArrayList<Short>(values.length);
+ for (short v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<Integer> wrap(int[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Integer> ret = new ArrayList<Integer>(values.length);
+ for (int v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<Long> wrap(long[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<Long> ret = new ArrayList<Long>(values.length);
+ for (long v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static List<String> wrap(String[] values)
+ {
+ if (values == null)
+ return null;
+
+ List<String> ret = new ArrayList<String>(values.length);
+ for (String v : values)
+ {
+ ret.add(v);
+ }
+ return ret;
+ }
+
+ public static <T> T wrapDispatch(Class<T> wrapperClass, Dispatch d)
+ {
+ try
+ {
+ if (d == null || d.m_pDispatch == 0)
+ return null;
+ Constructor<T> c = wrapperClass.getConstructor(Dispatch.class);
+ return (T)c.newInstance(d);
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> Object wrapVariant(Class<T> wrapperClass, Variant v)
+ {
+ if (v == null)
+ return null;
+
+ short vt = v.getvt();
+ switch (vt)
+ {
+ case Variant.VariantNull:
+ return null;
+ case Variant.VariantBoolean:
+ return v.getBoolean();
+ case Variant.VariantByte:
+ return v.getByte();
+ case Variant.VariantShort:
+ return v.getShort();
+ case Variant.VariantInt:
+ return v.getInt();
+ case Variant.VariantLongInt:
+ return v.getLong();
+ case Variant.VariantString:
+ return v.getString();
+ case Variant.VariantDispatch:
+ return wrapDispatch(wrapperClass, v.getDispatch());
+ default:
+ throw new IllegalArgumentException("unhandled variant type " + vt);
+ }
+ }
+
+ public static byte[] wrapBytes(SafeArray sa)
+ {
+ if (sa == null)
+ return null;
+
+ int saLen = sa.getUBound() - sa.getLBound() + 1;
+
+ byte[] ret = new byte[saLen];
+ int j = 0;
+ for (int i = sa.getLBound(); i <= sa.getUBound(); i++)
+ {
+ Variant v = sa.getVariant(i);
+ // come up with more effective approach!!!
+ ret[j++] = v.getByte();
+ }
+ return ret;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> List<T> wrap(Class<T> wrapperClass, SafeArray sa)
+ {
+ if (sa == null)
+ return null;
+
+ int saLen = sa.getUBound() - sa.getLBound() + 1;
+ if (saLen == 0)
+ return Collections.emptyList();
+
+ List<T> ret = new ArrayList<T>(saLen);
+ for (int i = sa.getLBound(); i <= sa.getUBound(); i++)
+ {
+ Variant v = sa.getVariant(i);
+ ret.add((T)wrapVariant(wrapperClass, v));
+ }
+ return ret;
+ }
+
+ public static <T> List<T> wrapEnum(Class<T> wrapperClass, SafeArray sa)
+ {
+ try
+ {
+ if (sa == null)
+ return null;
+
+ int saLen = sa.getUBound() - sa.getLBound() + 1;
+ if (saLen == 0)
+ return Collections.emptyList();
+ List<T> ret = new ArrayList<T>(saLen);
+ Constructor<T> c = wrapperClass.getConstructor(int.class);
+ for (int i = sa.getLBound(); i <= sa.getUBound(); i++)
+ {
+ Variant v = sa.getVariant(i);
+ ret.add(c.newInstance(v.getInt()));
+ }
+ return ret;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static SafeArray unwrapInt(List<Integer> values)
+ {
+ if (values == null)
+ return null;
+ SafeArray ret = new SafeArray(Variant.VariantInt, values.size());
+ int i = 0;
+ for (int l : values)
+ {
+ ret.setInt(i++, l);
+ }
+ return ret;
+ }
+
+ public static SafeArray unwrapLong(List<Long> values)
+ {
+ if (values == null)
+ return null;
+ SafeArray ret = new SafeArray(Variant.VariantLongInt, values.size());
+ int i = 0;
+ for (long l : values)
+ {
+ ret.setLong(i++, l);
+ }
+ return ret;
+ }
+
+ public static SafeArray unwrapBool(List<Boolean> values)
+ {
+ if (values == null)
+ return null;
+
+ SafeArray result = new SafeArray(Variant.VariantBoolean, values.size());
+ int i = 0;
+ for (boolean l : values)
+ {
+ result.setBoolean(i++, l);
+ }
+ return result;
+ }
+
+
+ public static SafeArray unwrapBytes(byte[] values)
+ {
+ if (values == null)
+ return null;
+
+ SafeArray result = new SafeArray(Variant.VariantByte, values.length);
+ int i = 0;
+ for (byte l : values)
+ {
+ result.setByte(i++, l);
+ }
+ return result;
+ }
+
+
+ public static <T extends Enum <T>> SafeArray unwrapEnum(Class<T> enumClass, List<T> values)
+ {
+ if (values == null)
+ return null;
+
+ SafeArray result = new SafeArray(Variant.VariantInt, values.size());
+ try
+ {
+ java.lang.reflect.Method valueM = enumClass.getMethod("value");
+ int i = 0;
+ for (T v : values)
+ {
+ result.setInt(i++, (Integer)valueM.invoke(v));
+ }
+ return result;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch(SecurityException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalArgumentException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+ public static SafeArray unwrapString(List<String> values)
+ {
+ if (values == null)
+ return null;
+ SafeArray result = new SafeArray(Variant.VariantString, values.size());
+ int i = 0;
+ for (String l : values)
+ {
+ result.setString(i++, l);
+ }
+ return result;
+ }
+
+ public static <T1, T2> List<T1> wrap2(Class<T1> wrapperClass1, Class<T2> wrapperClass2, T2[] values)
+ {
+ try
+ {
+ if (values == null)
+ return null;
+ if (values.length == 0)
+ return Collections.emptyList();
+
+ Constructor<T1> c = wrapperClass1.getConstructor(wrapperClass2);
+ List<T1> ret = new ArrayList<T1>(values.length);
+ for (T2 v : values)
+ {
+ ret.add(c.newInstance(v));
+ }
+ return ret;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T> T[] unwrap(Class<T> wrapperClass, List<T> values)
+ {
+ if (values == null)
+ return null;
+ return (T[])values.toArray((T[])Array.newInstance(wrapperClass, values.size()));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <T1 extends IUnknown, T2> T2[] unwrap2(Class<T1> wrapperClass1, Class<T2> wrapperClass2, List<T1> values)
+ {
+ if (values == null)
+ return null;
+
+ T2 ret[] = (T2[])Array.newInstance(wrapperClass2, values.size());
+ int i = 0;
+ for (T1 obj : values)
+ {
+ ret[i++] = (T2)obj.getWrapped();
+ }
+ return ret;
+ }
+
+ /* We have very long invoke lists sometimes */
+ public static Variant invoke(Dispatch d, String method, Object ... args)
+ {
+ return Dispatch.callN(d, method, args);
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'Helper.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VBoxException.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text>
+
+public class VBoxException extends RuntimeException
+{
+ private int resultCode;
+ private IVirtualBoxErrorInfo errorInfo;
+
+ public VBoxException(String message)
+ {
+ super(message);
+ resultCode = -1;
+ errorInfo = null;
+ }
+
+ public VBoxException(String message, Throwable cause)
+ {
+ super(message, cause);
+ if (cause instanceof com.jacob.com.ComException)
+ {
+ resultCode = ((com.jacob.com.ComException)cause).getHResult();
+ // JACOB doesn't support calling GetErrorInfo, which
+ // means there is no way of getting an IErrorInfo reference,
+ // and that means no way of getting to IVirtualBoxErrorInfo.
+ errorInfo = null;
+ }
+ else
+ resultCode = -1;
+ }
+
+ public int getResultCode()
+ {
+ return resultCode;
+ }
+
+ public IVirtualBoxErrorInfo getVirtualBoxErrorInfo()
+ {
+ return errorInfo;
+ }
+}
+</xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VBoxException.java'" />
+ </xsl:call-template>
+
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VirtualBoxManager.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+
+import com.jacob.activeX.ActiveXComponent;
+import com.jacob.com.ComThread;
+import com.jacob.com.Dispatch;
+import com.jacob.com.Variant;
+import com.jacob.com.SafeArray;
+import com.jacob.com.DispatchEvents;
+
+public class VirtualBoxManager
+{
+ private IVirtualBox vbox;
+
+ private VirtualBoxManager()
+ {
+ initPerThread();
+ vbox = new IVirtualBox(new ActiveXComponent("VirtualBox.VirtualBox"));
+ }
+
+ public static void initPerThread()
+ {
+ ComThread.InitMTA();
+ }
+
+ public static void deinitPerThread()
+ {
+ ComThread.Release();
+ }
+
+ public void connect(String url, String username, String passwd)
+ {
+ throw new VBoxException("Connect doesn't make sense for local bindings");
+ }
+
+ public void disconnect()
+ {
+ throw new VBoxException("Disconnect doesn't make sense for local bindings");
+ }
+
+ public String getClientAPIVersion()
+ {
+ return "]]></xsl:text>
+ <xsl:value-of select="substring($G_vboxApiSuffix, 2)" />
+ <xsl:text><![CDATA[";
+ }
+
+ public IVirtualBox getVBox()
+ {
+ return this.vbox;
+ }
+
+ public ISession getSessionObject()
+ {
+ return new ISession(new ActiveXComponent("VirtualBox.Session"));
+ }
+
+ public ISession openMachineSession(IMachine m)
+ {
+ ISession s = getSessionObject();
+ m.lockMachine(s, LockType.Shared);
+ return s;
+ }
+
+ public void closeMachineSession(ISession s)
+ {
+ if (s != null)
+ s.unlockMachine();
+ }
+
+ private static boolean hasInstance = false;
+
+ public static synchronized VirtualBoxManager createInstance(String home)
+ {
+ if (hasInstance)
+ throw new VBoxException("only one instance of VirtualBoxManager at a time allowed");
+
+ hasInstance = true;
+ return new VirtualBoxManager();
+ }
+
+ public void cleanup()
+ {
+ deinitPerThread();
+ hasInstance = false;
+ }
+
+ public void waitForEvents(long tmo)
+ {
+ try
+ {
+ Thread.sleep(tmo);
+ }
+ catch (InterruptedException ie)
+ {
+ }
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VirtualBoxManager.java'" />
+ </xsl:call-template>
+</xsl:template>
+
+<xsl:template name="emitHandwrittenJaxws">
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'IUnknown.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+public class IUnknown
+{
+ protected String obj;
+ protected ObjectRefManager objMgr;
+ protected final VboxPortType port;
+
+ public IUnknown(String obj, ObjectRefManager objMgr, VboxPortType port)
+ {
+ this.obj = obj;
+ this.objMgr = objMgr;
+ this.port = port;
+ objMgr.registerObj(this);
+ }
+
+ public final String getWrapped()
+ {
+ return this.obj;
+ }
+
+ public final VboxPortType getRemoteWSPort()
+ {
+ return this.port;
+ }
+
+ public final ObjectRefManager getObjMgr()
+ {
+ return this.objMgr;
+ }
+
+ public synchronized void releaseRemote() throws WebServiceException
+ {
+ if (obj == null)
+ return;
+
+ try
+ {
+ this.port.iManagedObjectRefRelease(obj);
+ this.obj = null;
+ }
+ catch (InvalidObjectFaultMsg e)
+ {
+ throw new WebServiceException(e);
+ }
+ catch (RuntimeFaultMsg e)
+ {
+ throw new WebServiceException(e);
+ }
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'IUnknown.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'Helper.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text><![CDATA[
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+
+public class Helper
+{
+ public static <T> List<T> wrap(Class<T> wrapperClass, ObjectRefManager objMgr, VboxPortType pt, List<String> values)
+ {
+ try
+ {
+ if (values == null)
+ return null;
+
+ Constructor<T> c = wrapperClass.getConstructor(String.class, ObjectRefManager.class, VboxPortType.class);
+ List<T> ret = new ArrayList<T>(values.size());
+ for (String v : values)
+ {
+ ret.add(c.newInstance(v, objMgr, pt));
+ }
+ return ret;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static <T1, T2> List<T1> wrap2(Class<T1> wrapperClass1, Class<T2> wrapperClass2, ObjectRefManager objMgr, VboxPortType pt, List<T2> values)
+ {
+ try
+ {
+ if (values == null)
+ return null;
+
+ Constructor<T1> c = wrapperClass1.getConstructor(wrapperClass2, ObjectRefManager.class, VboxPortType.class);
+ List<T1> ret = new ArrayList<T1>(values.size());
+ for (T2 v : values)
+ {
+ ret.add(c.newInstance(v, objMgr, pt));
+ }
+ return ret;
+ }
+ catch (NoSuchMethodException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InstantiationException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AssertionError(e);
+ }
+ catch (InvocationTargetException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ public static <T extends IUnknown> List<String> unwrap(List<T> values)
+ {
+ if (values == null)
+ return null;
+
+ List<String> ret = new ArrayList<String>(values.size());
+ for (T obj : values)
+ {
+ ret.add(obj.getWrapped());
+ }
+ return ret;
+ }
+
+ @SuppressWarnings("unchecked" )
+ public static <T1 extends Enum <T1>, T2 extends Enum <T2>> List<T2> convertEnums(Class<T1> fromClass,
+ Class<T2> toClass,
+ List<T1> values)
+ {
+ try
+ {
+ if (values == null)
+ return null;
+ List<T2> ret = new ArrayList<T2>(values.size());
+ for (T1 v : values)
+ {
+ // Ordinal based enum conversion, as JAX-WS "invents" its own
+ // enum names and has string values with the expected content.
+ int enumOrdinal = v.ordinal();
+ T2 convEnum = toClass.getEnumConstants()[enumOrdinal];
+ ret.add(convEnum);
+ }
+ return ret;
+ }
+ catch (ArrayIndexOutOfBoundsException e)
+ {
+ throw new AssertionError(e);
+ }
+ }
+
+ /* Pretty naive Base64 encoder/decoder. */
+ private static final char[] valToChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
+ private static final int[] charToVal = new int[256];
+
+ /* Initialize recoding alphabet. */
+ static
+ {
+ for (int i = 0; i < charToVal.length; i++)
+ charToVal[i] = -1;
+
+ for (int i = 0; i < valToChar.length; i++)
+ charToVal[valToChar[i]] = i;
+
+ charToVal['='] = 0;
+ }
+
+ public static String encodeBase64(byte[] data)
+ {
+ if (data == null)
+ return null;
+
+ if (data.length == 0)
+ return "";
+
+ int fullTriplets = data.length / 3;
+ int resultLen = ((data.length - 1) / 3 + 1) * 4;
+ char[] result = new char[resultLen];
+ int dataIndex = 0, stringIndex = 0;
+
+ for (int i = 0; i < fullTriplets; i++)
+ {
+ int ch1 = data[dataIndex++] & 0xff;
+ result[stringIndex++] = valToChar[ch1 >> 2];
+ int ch2 = data[dataIndex++] & 0xff;
+ result[stringIndex++] = valToChar[((ch1 << 4) & 0x3f) | (ch2 >> 4)];
+ int ch3 = data[dataIndex++] & 0xff;
+ result[stringIndex++] = valToChar[((ch2 << 2) & 0x3f) | (ch3 >> 6)];
+ result[stringIndex++] = valToChar[ch3 & 0x3f];
+ }
+
+ switch (data.length - dataIndex)
+ {
+ case 0:
+ // do nothing
+ break;
+ case 1:
+ {
+ int ch1 = data[dataIndex++] & 0xff;
+ result[stringIndex++] = valToChar[ch1 >> 2];
+ result[stringIndex++] = valToChar[(ch1 << 4) & 0x3f];
+ result[stringIndex++] = '=';
+ result[stringIndex++] = '=';
+ break;
+ }
+ case 2:
+ {
+ int ch1 = data[dataIndex++] & 0xff;
+ result[stringIndex++] = valToChar[ch1 >> 2];
+ int ch2 = data[dataIndex++] & 0xff;
+ result[stringIndex++] = valToChar[((ch1 << 4) & 0x3f) | (ch2 >> 4)];
+ result[stringIndex++] = valToChar[(ch2 << 2) & 0x3f];
+ result[stringIndex++] = '=';
+ break;
+ }
+ default:
+ throw new VBoxException("bug!");
+ }
+
+ return new String(result);
+ }
+
+ private static int skipInvalid(String str, int stringIndex)
+ {
+ while (charToVal[str.charAt(stringIndex)] < 0)
+ stringIndex++;
+
+ return stringIndex;
+ }
+
+ public static byte[] decodeBase64(String str)
+ {
+ if (str == null)
+ return null;
+
+ int stringLength = str.length();
+ if (stringLength == 0)
+ return new byte[0];
+
+ int validChars = 0, padChars = 0;
+ for (int i = 0; i < str.length(); i++)
+ {
+ char ch = str.charAt(i);
+
+ if (charToVal[ch] >= 0)
+ validChars++;
+
+ if (ch == '=')
+ padChars++;
+ }
+
+ if ((validChars * 3 % 4) != 0)
+ throw new VBoxException("invalid base64 encoded string " + str);
+
+ int resultLength = validChars * 3 / 4 - padChars;
+ byte[] result = new byte[resultLength];
+
+ int dataIndex = 0, stringIndex = 0;
+ int quadraplets = validChars / 4;
+
+ for (int i = 0; i < quadraplets; i++)
+ {
+ stringIndex = skipInvalid(str, stringIndex);
+ int ch1 = str.charAt(stringIndex++);
+ stringIndex = skipInvalid(str, stringIndex);
+ int ch2 = str.charAt(stringIndex++);
+ stringIndex = skipInvalid(str, stringIndex);
+ int ch3 = str.charAt(stringIndex++);
+ stringIndex = skipInvalid(str, stringIndex);
+ int ch4 = str.charAt(stringIndex++);
+
+ result[dataIndex++] = (byte)(((charToVal[ch1] << 2) | charToVal[ch2] >> 4) & 0xff);
+ /* we check this to ensure that we don't override data with '=' padding. */
+ if (dataIndex < result.length)
+ result[dataIndex++] = (byte)(((charToVal[ch2] << 4) | charToVal[ch3] >> 2) & 0xff);
+ if (dataIndex < result.length)
+ result[dataIndex++] = (byte)(((charToVal[ch3] << 6) | charToVal[ch4]) & 0xff);
+ }
+
+ return result;
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'Helper.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VBoxException.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text>
+public class VBoxException extends RuntimeException
+{
+ private int resultCode;
+ private IVirtualBoxErrorInfo errorInfo;
+
+ public VBoxException(String message)
+ {
+ super(message);
+ resultCode = -1;
+ errorInfo = null;
+ }
+
+ public VBoxException(String message, Throwable cause)
+ {
+ super(message, cause);
+ resultCode = -1;
+ errorInfo = null;
+ }
+
+ public VBoxException(String message, Throwable cause, ObjectRefManager objMgr, VboxPortType port)
+ {
+ super(message, cause);
+ if (cause instanceof RuntimeFaultMsg)
+ {
+ RuntimeFaultMsg m = (RuntimeFaultMsg)cause;
+ RuntimeFault f = m.getFaultInfo();
+ resultCode = f.getResultCode();
+ String retVal = f.getReturnval();
+ errorInfo = (retVal.length() > 0) ? new IVirtualBoxErrorInfo(retVal, objMgr, port) : null;
+ }
+ else
+ resultCode = -1;
+ }
+
+ public int getResultCode()
+ {
+ return resultCode;
+ }
+
+ public IVirtualBoxErrorInfo getVirtualBoxErrorInfo()
+ {
+ return errorInfo;
+ }
+}
+</xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VBoxException.java'" />
+ </xsl:call-template>
+
+ <xsl:call-template name="startFile">
+ <xsl:with-param name="file" select="'VirtualBoxManager.java'" />
+ <xsl:with-param name="package" select="$G_virtualBoxPackage" />
+ </xsl:call-template>
+
+ <xsl:if test="$filelistonly=''">
+ <xsl:text>import java.net.URL;
+import java.math.BigInteger;
+import java.util.Iterator;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.lang.Integer;
+import java.lang.ref.WeakReference;
+import java.lang.ref.ReferenceQueue;
+import javax.xml.namespace.QName;
+import javax.xml.ws.BindingProvider;
+import javax.xml.ws.Holder;
+import javax.xml.ws.WebServiceException;
+import java.io.IOException;
+import java.net.UnknownHostException;
+import java.net.Socket;
+import java.net.InetAddress;
+import javax.net.SocketFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocketFactory;
+import javax.net.ssl.SSLSocket;
+
+class PortPool
+{
+ private final static String wsdlFile = </xsl:text>
+ <xsl:value-of select="$G_virtualBoxWsdl" />
+ <xsl:text><![CDATA[;
+ private Map<VboxPortType, Integer> known;
+ private boolean initStarted;
+ private VboxService svc;
+
+ PortPool(boolean usePreinit)
+ {
+ known = new HashMap<VboxPortType, Integer>();
+
+ if (usePreinit)
+ {
+ new Thread(new Runnable()
+ {
+ public void run()
+ {
+ // need to sync on something else but 'this'
+ synchronized (known)
+ {
+ initStarted = true;
+ known.notify();
+ }
+
+ preinit();
+ }
+ }).start();
+
+ synchronized (known)
+ {
+ while (!initStarted)
+ {
+ try
+ {
+ known.wait();
+ }
+ catch (InterruptedException e)
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ private synchronized void preinit()
+ {
+ VboxPortType port = getPort();
+ releasePort(port);
+ }
+
+ synchronized VboxPortType getPort()
+ {
+ VboxPortType port = null;
+ int ttl = 0;
+
+ for (VboxPortType cur: known.keySet())
+ {
+ int value = known.get(cur);
+ if ((value & 0x10000) == 0)
+ {
+ port = cur;
+ ttl = value & 0xffff;
+ break;
+ }
+ }
+
+ if (port == null)
+ {
+ if (svc == null)
+ {
+ URL wsdl = PortPool.class.getClassLoader().getResource(wsdlFile);
+ if (wsdl == null)
+ throw new LinkageError(wsdlFile + " not found, but it should have been in the jar");
+ svc = new VboxService(wsdl,
+ new QName("http://www.virtualbox.org/Service",
+ "vboxService"));
+ }
+ port = svc.getVboxServicePort();
+ // reuse this object 0x10 times
+ ttl = 0x10;
+ }
+ // mark as used
+ known.put(port, new Integer(0x10000 | ttl));
+ return port;
+ }
+
+ synchronized void releasePort(VboxPortType port)
+ {
+ Integer val = known.get(port);
+ if (val == null || val == 0)
+ {
+ // know you not
+ return;
+ }
+
+ int v = val;
+ int ttl = v & 0xffff;
+ // decrement TTL, and throw away port if used too much times
+ if (--ttl <= 0)
+ {
+ known.remove(port);
+ }
+ else
+ {
+ v = ttl; // set new TTL and clear busy bit
+ known.put(port, v);
+ }
+ }
+}
+
+
+/**
+ * This class manages the object references between us and the webservice server.
+ * It makes sure that the object on the server side is destroyed when all
+ */
+class ObjectRefManager
+{
+ private final static ReferenceQueue<IUnknown> refQ = new ReferenceQueue<IUnknown>();
+
+ private final ConcurrentMap<String, ManagedObj> map = new ConcurrentHashMap<String, ManagedObj>();
+ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ private final ObjRefMgrCleanupThread objRefMgrCleanup;
+
+ public ObjectRefManager()
+ {
+ this.objRefMgrCleanup = new ObjRefMgrCleanupThread(this, 100);
+ this.objRefMgrCleanup.start();
+ }
+
+ /**
+ * Prevents the object reference manager cleanup thread from releasing any
+ * server side objects to avoid a fundamental race in the multi threaded
+ * java environment where it is possible that a wrapper got the object ID
+ * from the server but couldn't create the local stub protecting the object
+ * before the cleanup thread released it.
+ */
+ public void preventObjRelease()
+ {
+ lock.readLock().lock();
+ }
+
+ /**
+ * Allows releasing server side objects from the cleanup thread again.
+ */
+ public void allowObjRelease()
+ {
+ lock.readLock().unlock();
+ }
+
+ /**
+ * Marks the start of a run to release server side objects which don't hold
+ * a reference locally anymore.
+ */
+ public void startObjRelease()
+ {
+ lock.writeLock().lock();
+ }
+
+ /**
+ * Marks the end of a cleanup run.
+ */
+ public void endObjRelease()
+ {
+ lock.writeLock().unlock();
+ }
+
+ /**
+ * Registers a new stub object for automatic reference managing.
+ */
+ public void registerObj(IUnknown obj)
+ {
+ assert lock.getReadLockCount() > 0;
+ ManagedObjRef ref = new ManagedObjRef(obj);
+
+ ManagedObj mgrobj = map.get(obj.getWrapped());
+ if (mgrobj != null)
+ {
+ mgrobj.addObject(ref);
+ }
+ else
+ {
+ /* Create new. */
+ mgrobj = new ManagedObj(obj.getWrapped(), obj.getRemoteWSPort());
+ mgrobj.addObject(ref);
+ map.put(obj.getWrapped(), mgrobj);
+ }
+ }
+
+ /**
+ * Removes a garbage collected object reference from our reference manager.
+ *
+ * Returns the server side object wrapper if there is no stub referencing it
+ * anymore otherwise null is returned.
+ */
+ public ManagedObj unregisterObj(ManagedObjRef objRef)
+ {
+ ManagedObj obj = this.map.get(objRef.objId);
+
+ assert obj != null;
+ obj.removeObject(objRef);
+ if (!obj.isReferenced())
+ return obj;
+
+ return null;
+ }
+
+ public void releaseRemoteObj(ManagedObj obj)
+ {
+ assert lock.isWriteLockedByCurrentThread();
+
+ if (!obj.isReferenced())
+ {
+ try
+ {
+ obj.port.iManagedObjectRefRelease(obj.objId);
+ }
+ catch (InvalidObjectFaultMsg e)
+ {
+ throw new WebServiceException(e);
+ }
+ catch (RuntimeFaultMsg e)
+ {
+ throw new WebServiceException(e);
+ }
+ finally
+ {
+ this.map.remove(obj.objId);
+ }
+ }
+ }
+
+ /**
+ * An object which is living on the server side. This can be referenced
+ * by multiple stub objects here.
+ */
+ static class ManagedObj
+ {
+ private final String objId;
+ private final VboxPortType port;
+ private final ConcurrentLinkedQueue<ManagedObjRef> refQ;
+
+ ManagedObj(String objId, VboxPortType port)
+ {
+ this.objId = objId;
+ this.port = port;
+ this.refQ = new ConcurrentLinkedQueue<ManagedObjRef>();
+ }
+
+ public void addObject(ManagedObjRef obj)
+ {
+ this.refQ.add(obj);
+ }
+
+ public void removeObject(ManagedObjRef obj)
+ {
+ this.refQ.remove(obj);
+ }
+
+ public boolean isReferenced()
+ {
+ return !this.refQ.isEmpty();
+ }
+ }
+
+ /**
+ * A private class extending WeakReference to get notified about garbage
+ * collected stub objects.
+ */
+ static class ManagedObjRef extends WeakReference<IUnknown>
+ {
+ final String objId;
+
+ ManagedObjRef(IUnknown obj)
+ {
+ super(obj, refQ);
+ this.objId = obj.getWrapped();
+ }
+ }
+
+ /**
+ * A private class implementing a thread getting notified
+ * about garbage collected objects so it can release the object on the
+ * server side if it is not used anymore.
+ */
+ static class ObjRefMgrCleanupThread extends Thread
+ {
+ ObjectRefManager objRefMgr;
+ int cStubsReleased;
+ int cStubsReleaseThreshold;
+ HashMap<String, ManagedObj> mapToRelease = new HashMap<String, ManagedObj>();
+
+ ObjRefMgrCleanupThread(ObjectRefManager objRefMgr)
+ {
+ init(objRefMgr, 500);
+ }
+
+ ObjRefMgrCleanupThread(ObjectRefManager objRefMgr, int cStubsReleaseThreshold)
+ {
+ init(objRefMgr, cStubsReleaseThreshold);
+ }
+
+ private void init(ObjectRefManager objRefMgr, int cStubsReleaseThreshold)
+ {
+ this.objRefMgr = objRefMgr;
+ this.cStubsReleased = 0;
+ this.cStubsReleaseThreshold = cStubsReleaseThreshold;
+ setName("ObjectRefManager-VBoxWSObjRefGcThrd");
+ /*
+ * setDaemon() makes sure the jvm exits and is not blocked
+ * if the thread is still running so we don't have to care about
+ * tearing it down.
+ */
+ setDaemon(true);
+ }
+
+ public void run()
+ {
+ while (true)
+ {
+ while (cStubsReleased < cStubsReleaseThreshold)
+ {
+ try
+ {
+ /* Accumulate a few objects before we start. */
+ while (cStubsReleased < cStubsReleaseThreshold)
+ {
+ ManagedObjRef ref = (ManagedObjRef)refQ.remove();
+ ManagedObj obj = this.objRefMgr.unregisterObj(ref);
+ /*
+ * If the server side object is not referenced anymore
+ * promote to map for releasing later.
+ */
+ if (obj != null && !mapToRelease.containsKey(ref.objId))
+ mapToRelease.put(ref.objId, obj);
+
+ cStubsReleased++;
+ }
+ }
+ catch (InterruptedException e)
+ { /* ignore */ }
+ catch (javax.xml.ws.WebServiceException e)
+ { /* ignore */ }
+ }
+
+ /*
+ * After we released enough stubs we go over all non referenced
+ * server side objects and release them if they were not
+ * referenced again in between.
+ */
+ cStubsReleased = 0;
+ if (!mapToRelease.isEmpty())
+ {
+ this.objRefMgr.startObjRelease();
+ try
+ {
+ Iterator<ManagedObj> it = mapToRelease.values().iterator();
+ while (it.hasNext())
+ {
+ ManagedObj obj = it.next();
+ this.objRefMgr.releaseRemoteObj(obj);
+ }
+
+ mapToRelease.clear();
+ }
+ catch (javax.xml.ws.WebServiceException e)
+ { /* ignore */ }
+ finally
+ {
+ this.objRefMgr.endObjRelease();
+ }
+ }
+ }
+ }
+ }
+}
+
+class VBoxTLSSocketFactory extends SSLSocketFactory
+{
+ private final SSLSocketFactory sf;
+
+ private void setupSocket(SSLSocket s)
+ {
+ String[] oldproto = s.getEnabledProtocols();
+ List<String> protolist = new ArrayList<String>();
+ for (int i = 0; i < oldproto.length; i++)
+ if (oldproto[i].toUpperCase().startsWith("TLS"))
+ protolist.add(oldproto[i]);
+ String[] newproto = protolist.toArray(new String[protolist.size()]);
+ s.setEnabledProtocols(newproto);
+ }
+
+ public VBoxTLSSocketFactory()
+ {
+ SSLSocketFactory tmp = null;
+ try
+ {
+ SSLContext sc = SSLContext.getInstance("TLS");
+ sc.init(null, null, null);
+ tmp = sc.getSocketFactory();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ sf = tmp;
+ }
+
+ public static SocketFactory getDefault()
+ {
+ return new VBoxTLSSocketFactory();
+ }
+
+ public Socket createSocket(Socket socket, String host, int port,
+ boolean autoClose) throws IOException, UnknownHostException
+ {
+ SSLSocket s = (SSLSocket)sf.createSocket(socket, host, port, autoClose);
+ setupSocket(s);
+ return s;
+ }
+
+ public Socket createSocket() throws IOException
+ {
+ SSLSocket s = (SSLSocket)sf.createSocket();
+ setupSocket(s);
+ return s;
+ }
+
+ public Socket createSocket(InetAddress host, int port) throws IOException
+ {
+ SSLSocket s = (SSLSocket)sf.createSocket(host, port);
+ setupSocket(s);
+ return s;
+ }
+
+ public Socket createSocket(InetAddress address, int port,
+ InetAddress localAddress, int localPort) throws IOException
+ {
+ SSLSocket s = (SSLSocket)sf.createSocket(address, port, localAddress, localPort);
+ setupSocket(s);
+ return s;
+ }
+
+ public Socket createSocket(String host, int port) throws IOException, UnknownHostException
+ {
+ SSLSocket s = (SSLSocket)sf.createSocket(host, port);
+ setupSocket(s);
+ return s;
+ }
+
+ public Socket createSocket(String host, int port,
+ InetAddress localHost, int localPort) throws IOException, UnknownHostException
+ {
+ SSLSocket s = (SSLSocket)sf.createSocket(host, port, localHost, localPort);
+ setupSocket(s);
+ return s;
+ }
+
+ public String[] getDefaultCipherSuites()
+ {
+ return sf.getDefaultCipherSuites();
+ }
+
+ public String[] getSupportedCipherSuites()
+ {
+ return sf.getSupportedCipherSuites();
+ }
+}
+
+
+public class VirtualBoxManager
+{
+ private static PortPool pool = new PortPool(true);
+ private static final ObjectRefManager objMgr = new ObjectRefManager();
+ protected VboxPortType port;
+
+ private IVirtualBox vbox;
+
+ private VirtualBoxManager()
+ {
+ }
+
+ public static void initPerThread()
+ {
+ }
+
+ public static void deinitPerThread()
+ {
+ }
+
+ public void connect(String url, String username, String passwd)
+ {
+ this.port = pool.getPort();
+ try
+ {
+ ((BindingProvider)port).getRequestContext().
+ put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
+
+ // Unfortunately there is no official way to make JAX-WS use
+ // TLS only, which means that a rather tedious approach is
+ // unavoidable (implementing a TLS only SSLSocketFactory,
+ // because the default one associated with a TLS SSLContext
+ // happily uses SSLv2/3 handshakes, which make TLS servers
+ // drop the connection), and additionally a not standardized,
+ // shotgun approach is needed to make the relevant JAX-WS
+ // implementations use this factory.
+ VBoxTLSSocketFactory sf = new VBoxTLSSocketFactory();
+ ((BindingProvider)port).getRequestContext().
+ put("com.sun.xml.internal.ws.transport.https.client.SSLSocketFactory", sf);
+ ((BindingProvider)port).getRequestContext().
+ put("com.sun.xml.ws.transport.https.client.SSLSocketFactory", sf);
+
+ String handle = port.iWebsessionManagerLogon(username, passwd);
+ this.objMgr.preventObjRelease();
+ try
+ {
+ this.vbox = new IVirtualBox(handle, this.objMgr, port);
+ }
+ finally
+ {
+ this.objMgr.allowObjRelease();
+ }
+ }
+ catch (Throwable t)
+ {
+ if (this.port != null && pool != null)
+ {
+ pool.releasePort(this.port);
+ this.port = null;
+ }
+ // we have to throw smth derived from RuntimeException
+ throw new VBoxException(t.getMessage(), t, this.objMgr, this.port);
+ }
+ }
+
+ public void connect(String url, String username, String passwd,
+ Map<String, Object> requestContext, Map<String, Object> responseContext)
+ {
+ this.port = pool.getPort();
+ try
+ {
+ ((BindingProvider)port).getRequestContext();
+ if (requestContext != null)
+ ((BindingProvider)port).getRequestContext().putAll(requestContext);
+
+ if (responseContext != null)
+ ((BindingProvider)port).getResponseContext().putAll(responseContext);
+
+ ((BindingProvider)port).getRequestContext().
+ put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, url);
+ String handle = port.iWebsessionManagerLogon(username, passwd);
+ this.objMgr.preventObjRelease();
+ try
+ {
+ this.vbox = new IVirtualBox(handle, this.objMgr, port);
+ }
+ finally
+ {
+ this.objMgr.allowObjRelease();
+ }
+ }
+ catch (Throwable t)
+ {
+ if (this.port != null && pool != null)
+ {
+ pool.releasePort(this.port);
+ this.port = null;
+ }
+ // we have to throw smth derived from RuntimeException
+ throw new VBoxException(t.getMessage(), t, this.objMgr, this.port);
+ }
+ }
+
+ public void disconnect()
+ {
+ if (this.port == null)
+ return;
+
+ try
+ {
+ if (this.vbox != null && port != null)
+ port.iWebsessionManagerLogoff(this.vbox.getWrapped());
+ }
+ catch (InvalidObjectFaultMsg e)
+ {
+ throw new VBoxException(e.getMessage(), e, this.objMgr, this.port);
+ }
+ catch (RuntimeFaultMsg e)
+ {
+ throw new VBoxException(e.getMessage(), e, this.objMgr, this.port);
+ }
+ finally
+ {
+ if (this.port != null)
+ {
+ pool.releasePort(this.port);
+ this.port = null;
+ }
+ }
+ }
+
+ public String getClientAPIVersion()
+ {
+ return "]]></xsl:text>
+ <xsl:value-of select="substring($G_vboxApiSuffix, 2)" />
+ <xsl:text><![CDATA[";
+ }
+
+ public IVirtualBox getVBox()
+ {
+ return this.vbox;
+ }
+
+ public ISession getSessionObject()
+ {
+ if (this.vbox == null)
+ throw new VBoxException("connect first");
+ try
+ {
+ String handle = port.iWebsessionManagerGetSessionObject(this.vbox.getWrapped());
+ this.objMgr.preventObjRelease();
+ try
+ {
+ return new ISession(handle, this.objMgr, port);
+ }
+ finally
+ {
+ this.objMgr.allowObjRelease();
+ }
+ }
+ catch (InvalidObjectFaultMsg e)
+ {
+ throw new VBoxException(e.getMessage(), e, this.objMgr, this.port);
+ }
+ catch (RuntimeFaultMsg e)
+ {
+ throw new VBoxException(e.getMessage(), e, this.objMgr, this.port);
+ }
+ }
+
+ public ISession openMachineSession(IMachine m) throws Exception
+ {
+ ISession s = getSessionObject();
+ m.lockMachine(s, LockType.Shared);
+ return s;
+ }
+
+ public void closeMachineSession(ISession s)
+ {
+ if (s != null)
+ s.unlockMachine();
+ }
+
+ public static synchronized VirtualBoxManager createInstance(String home)
+ {
+ return new VirtualBoxManager();
+ }
+
+ public IEventListener createListener(Object sink)
+ {
+ throw new VBoxException("no active listeners here");
+ }
+
+ public void cleanup()
+ {
+ disconnect();
+ deinitPerThread();
+ }
+
+ public void waitForEvents(long tmo)
+ {
+ try
+ {
+ Thread.sleep(tmo);
+ }
+ catch (InterruptedException ie)
+ {
+ }
+ }
+
+ protected void finalize() throws Throwable
+ {
+ try
+ {
+ cleanup();
+ }
+ catch(Exception e)
+ {
+ }
+ finally
+ {
+ super.finalize();
+ }
+ }
+}
+]]></xsl:text>
+ </xsl:if>
+
+ <xsl:call-template name="endFile">
+ <xsl:with-param name="file" select="'VirtualBoxManager.java'" />
+ </xsl:call-template>
+</xsl:template>
+
+
+<xsl:template match="/">
+
+ <xsl:if test="not($G_vboxApiSuffix)">
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'G_vboxApiSuffix must be given'" />
+ </xsl:call-template>
+ </xsl:if>
+
+ <xsl:if test="not($filelistonly='')">
+ <xsl:value-of select="concat($filelistonly, ' :=')"/>
+ </xsl:if>
+
+ <!-- Handwritten files -->
+ <xsl:call-template name="emitHandwritten"/>
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <xsl:call-template name="emitHandwrittenXpcom"/>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='mscom'">
+ <xsl:call-template name="emitHandwrittenMscom"/>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:call-template name="emitHandwrittenJaxws"/>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <xsl:call-template name="fatalError">
+ <xsl:with-param name="msg" select="'Style unknown (root)'" />
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+
+ <!-- Enums -->
+ <xsl:for-each select="//enum">
+ <xsl:call-template name="genEnum">
+ <xsl:with-param name="enumname" select="@name" />
+ <xsl:with-param name="filename" select="concat(@name, '.java')" />
+ </xsl:call-template>
+ </xsl:for-each>
+
+ <!-- Interfaces -->
+ <xsl:for-each select="//interface">
+ <xsl:variable name="self_target" select="current()/ancestor::if/@target"/>
+ <xsl:variable name="module" select="current()/ancestor::module/@name"/>
+
+ <xsl:choose>
+ <xsl:when test="$G_vboxGlueStyle='jaxws'">
+ <xsl:if test="not($module) and not(@wsmap='suppress')">
+ <xsl:call-template name="genIface">
+ <xsl:with-param name="ifname" select="@name" />
+ <xsl:with-param name="filename" select="concat(@name, '.java')" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:when>
+
+ <xsl:when test="$G_vboxGlueStyle='xpcom'">
+ <!-- We don't need WSDL-specific nor MIDL-specific interfaces here -->
+ <xsl:if test="not(@internal='yes') and not($self_target='wsdl') and not($module) and not($self_target='midl')">
+ <xsl:call-template name="genIface">
+ <xsl:with-param name="ifname" select="@name" />
+ <xsl:with-param name="filename" select="concat(@name, '.java')" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:when>
+
+ <xsl:otherwise>
+ <!-- We don't need WSDL-specific interfaces here -->
+ <xsl:if test="not(@internal='yes') and not($self_target='wsdl') and not($module)">
+ <xsl:call-template name="genIface">
+ <xsl:with-param name="ifname" select="@name" />
+ <xsl:with-param name="filename" select="concat(@name, '.java')" />
+ </xsl:call-template>
+ </xsl:if>
+ </xsl:otherwise>
+
+ </xsl:choose>
+ </xsl:for-each>
+
+ <xsl:if test="not($filelistonly='')">
+ <xsl:value-of select="concat($G_sNewLine, $G_sNewLine)"/>
+ </xsl:if>
+
+</xsl:template>
+</xsl:stylesheet>
diff --git a/src/VBox/Main/glue/initterm.cpp b/src/VBox/Main/glue/initterm.cpp
new file mode 100644
index 00000000..cdf8efe9
--- /dev/null
+++ b/src/VBox/Main/glue/initterm.cpp
@@ -0,0 +1,861 @@
+/* $Id: initterm.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - Initialization and Termination.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN
+#if !defined(VBOX_WITH_XPCOM)
+
+# if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x600
+# undef _WIN32_WINNT
+# define _WIN32_WINNT 0x600 /* GetModuleHandleExW */
+# endif
+# include <iprt/nt/nt-and-windows.h>
+# include <iprt/win/objbase.h>
+# include <iprt/win/rpcproxy.h>
+# include <rpcasync.h>
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+# include <stdlib.h>
+
+# include <nsIComponentRegistrar.h>
+# include <nsIServiceManager.h>
+# include <nsCOMPtr.h>
+# include <nsEventQueueUtils.h>
+# include <nsEmbedString.h>
+
+# include <nsILocalFile.h>
+# include <nsIDirectoryService.h>
+# include <nsDirectoryServiceDefs.h>
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+#include "VBox/com/com.h"
+#include "VBox/com/assert.h"
+#include "VBox/com/NativeEventQueue.h"
+#include "VBox/com/AutoLock.h"
+
+#include "../include/LoggingNew.h"
+
+#include <iprt/asm.h>
+#include <iprt/env.h>
+#include <iprt/ldr.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/system.h>
+#include <iprt/thread.h>
+
+#include <VBox/err.h>
+
+namespace com
+{
+
+#if defined(VBOX_WITH_XPCOM)
+
+class DirectoryServiceProvider : public nsIDirectoryServiceProvider
+{
+public:
+
+ NS_DECL_ISUPPORTS
+
+ DirectoryServiceProvider()
+ : mCompRegLocation(NULL), mXPTIDatLocation(NULL)
+ , mComponentDirLocation(NULL), mCurrProcDirLocation(NULL)
+ {}
+
+ virtual ~DirectoryServiceProvider();
+
+ HRESULT init(const char *aCompRegLocation,
+ const char *aXPTIDatLocation,
+ const char *aComponentDirLocation,
+ const char *aCurrProcDirLocation);
+
+ NS_DECL_NSIDIRECTORYSERVICEPROVIDER
+
+private:
+ /** @remarks This is not a UTF-8 string. */
+ char *mCompRegLocation;
+ /** @remarks This is not a UTF-8 string. */
+ char *mXPTIDatLocation;
+ /** @remarks This is not a UTF-8 string. */
+ char *mComponentDirLocation;
+ /** @remarks This is not a UTF-8 string. */
+ char *mCurrProcDirLocation;
+};
+
+NS_IMPL_ISUPPORTS1(DirectoryServiceProvider, nsIDirectoryServiceProvider)
+
+DirectoryServiceProvider::~DirectoryServiceProvider()
+{
+ if (mCompRegLocation)
+ {
+ RTStrFree(mCompRegLocation);
+ mCompRegLocation = NULL;
+ }
+ if (mXPTIDatLocation)
+ {
+ RTStrFree(mXPTIDatLocation);
+ mXPTIDatLocation = NULL;
+ }
+ if (mComponentDirLocation)
+ {
+ RTStrFree(mComponentDirLocation);
+ mComponentDirLocation = NULL;
+ }
+ if (mCurrProcDirLocation)
+ {
+ RTStrFree(mCurrProcDirLocation);
+ mCurrProcDirLocation = NULL;
+ }
+}
+
+/**
+ * @param aCompRegLocation Path to compreg.dat, in Utf8.
+ * @param aXPTIDatLocation Path to xpti.data, in Utf8.
+ */
+HRESULT
+DirectoryServiceProvider::init(const char *aCompRegLocation,
+ const char *aXPTIDatLocation,
+ const char *aComponentDirLocation,
+ const char *aCurrProcDirLocation)
+{
+ AssertReturn(aCompRegLocation, NS_ERROR_INVALID_ARG);
+ AssertReturn(aXPTIDatLocation, NS_ERROR_INVALID_ARG);
+
+/** @todo r=bird: Gotta check how this encoding stuff plays out on darwin!
+ * We get down to [VBoxNsxp]NS_NewNativeLocalFile and that file isn't
+ * nsLocalFileUnix.cpp on 32-bit darwin. On 64-bit darwin it's a question
+ * of what we're doing in IPRT and such... We should probably add a
+ * RTPathConvertToNative for use here. */
+ int vrc = RTStrUtf8ToCurrentCP(&mCompRegLocation, aCompRegLocation);
+ if (RT_SUCCESS(vrc))
+ vrc = RTStrUtf8ToCurrentCP(&mXPTIDatLocation, aXPTIDatLocation);
+ if (RT_SUCCESS(vrc) && aComponentDirLocation)
+ vrc = RTStrUtf8ToCurrentCP(&mComponentDirLocation, aComponentDirLocation);
+ if (RT_SUCCESS(vrc) && aCurrProcDirLocation)
+ vrc = RTStrUtf8ToCurrentCP(&mCurrProcDirLocation, aCurrProcDirLocation);
+
+ return RT_SUCCESS(vrc) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
+}
+
+NS_IMETHODIMP
+DirectoryServiceProvider::GetFile(const char *aProp,
+ PRBool *aPersistent,
+ nsIFile **aRetval)
+{
+ nsCOMPtr <nsILocalFile> localFile;
+ nsresult rv = NS_ERROR_FAILURE;
+
+ *aRetval = nsnull;
+ *aPersistent = PR_TRUE;
+
+ const char *fileLocation = NULL;
+
+ if (strcmp(aProp, NS_XPCOM_COMPONENT_REGISTRY_FILE) == 0)
+ fileLocation = mCompRegLocation;
+ else if (strcmp(aProp, NS_XPCOM_XPTI_REGISTRY_FILE) == 0)
+ fileLocation = mXPTIDatLocation;
+ else if (mComponentDirLocation && strcmp(aProp, NS_XPCOM_COMPONENT_DIR) == 0)
+ fileLocation = mComponentDirLocation;
+ else if (mCurrProcDirLocation && strcmp(aProp, NS_XPCOM_CURRENT_PROCESS_DIR) == 0)
+ fileLocation = mCurrProcDirLocation;
+ else
+ return NS_ERROR_FAILURE;
+
+ rv = NS_NewNativeLocalFile(nsEmbedCString(fileLocation),
+ PR_TRUE, getter_AddRefs(localFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ return localFile->QueryInterface(NS_GET_IID(nsIFile), (void **)aRetval);
+}
+
+/**
+ * Global XPCOM initialization flag (we maintain it ourselves since XPCOM
+ * doesn't provide such functionality)
+ */
+static bool volatile gIsXPCOMInitialized = false;
+
+/**
+ * Number of Initialize() calls on the main thread.
+ */
+static unsigned int gXPCOMInitCount = 0;
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+/**
+ * Replacement function for the InvokeStub method for the IRundown stub.
+ */
+static HRESULT STDMETHODCALLTYPE
+Rundown_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF
+{
+ /*
+ * Our mission here is to prevent remote calls to methods #8 and #9,
+ * as these contain raw pointers to callback functions.
+ *
+ * Note! APIs like I_RpcServerInqTransportType, I_RpcBindingInqLocalClientPID
+ * and RpcServerInqCallAttributesW are not usable in this context without
+ * a rpc binding handle (latter two).
+ *
+ * P.S. In more recent windows versions, the buffer implements a interface
+ * IID_IRpcChannelBufferMarshalingContext (undocumented) which has a
+ * GetIMarshallingContextAttribute() method that will return the client PID
+ * when asking for attribute #0x8000000e.
+ */
+ uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */
+ HRESULT hrc;
+ if ( ( iMethod != 8
+ && iMethod != 9)
+ || (pMsg->rpcFlags & RPCFLG_LOCAL_CALL) )
+ hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf);
+ else
+ {
+ LogRel(("Rundown_InvokeStub: Rejected call to CRundown::%s: rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n",
+ pMsg->iMethod == 8 ? "DoCallback" : "DoNonreentrantCallback", pMsg->rpcFlags, pMsg->cbBuffer,
+ pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer,
+ pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4]));
+ hrc = E_ACCESSDENIED;
+ }
+ return hrc;
+}
+
+/**
+ * Replacement function for the InvokeStub method for the IDLLHost stub.
+ */
+static HRESULT STDMETHODCALLTYPE
+DLLHost_InvokeStub(IRpcStubBuffer *pThis, RPCOLEMESSAGE *pMsg, IRpcChannelBuffer *pBuf) RT_NOTHROW_DEF
+{
+ /*
+ * Our mission here is to prevent remote calls to this interface as method #3
+ * contain a raw pointer an DllGetClassObject function. There are only that
+ * method in addition to the IUnknown stuff, and it's ASSUMED that it's
+ * process internal only (cross apartment stuff).
+ */
+ uint32_t const iMethod = pMsg->iMethod & 0xffff; /* Uncertain, but there are hints that the upper bits are flags. */
+ HRESULT hrc;
+ if (pMsg->rpcFlags & RPCFLG_LOCAL_CALL)
+ hrc = CStdStubBuffer_Invoke(pThis, pMsg, pBuf);
+ else
+ {
+ LogRel(("DLLHost_InvokeStub: Rejected call to CDLLHost::%s: rpcFlags=%#x cbBuffer=%#x dataRepresentation=%d buffer=%p:{%.*Rhxs} reserved1=%p reserved2={%p,%p,%p,%p,%p}\n",
+ pMsg->iMethod == 0 ? "QueryInterface" :
+ pMsg->iMethod == 1 ? "AddRef" :
+ pMsg->iMethod == 2 ? "ReleaseRef" :
+ pMsg->iMethod == 3 ? "DllGetClassObject" : "Unknown", pMsg->rpcFlags, pMsg->cbBuffer,
+ pMsg->dataRepresentation, pMsg->Buffer, RT_VALID_PTR(pMsg->Buffer) ? pMsg->cbBuffer : 0, pMsg->Buffer,
+ pMsg->reserved1, pMsg->reserved2[0], pMsg->reserved2[1], pMsg->reserved2[2], pMsg->reserved2[3], pMsg->reserved2[4]));
+ hrc = E_ACCESSDENIED;
+ }
+ return hrc;
+}
+
+/**
+ * Replaces the IRundown InvokeStub method with Rundown_InvokeStub so we can
+ * reject remote calls to a couple of misdesigned methods.
+ *
+ * Also replaces the IDLLHost for the same reasons.
+ */
+void PatchComBugs(void)
+{
+ static volatile bool s_fPatched = false;
+ if (s_fPatched)
+ return;
+
+ /*
+ * The combase.dll / ole32.dll is exporting a DllGetClassObject function
+ * that is implemented using NdrDllGetClassObject just like our own
+ * proxy/stub DLL. This means we can get at the stub interface lists,
+ * since what NdrDllGetClassObject has CStdPSFactoryBuffer as layout.
+ *
+ * Note! Tried using CoRegisterPSClsid instead of this mess, but no luck.
+ */
+ /* Locate the COM DLL, it must be loaded by now: */
+ HMODULE hmod = GetModuleHandleW(L"COMBASE.DLL");
+ if (!hmod)
+ hmod = GetModuleHandleW(L"OLE32.DLL"); /* w7 */
+ AssertReturnVoid(hmod != NULL);
+
+ /* Resolve the class getter: */
+ LPFNGETCLASSOBJECT pfnGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hmod, "DllGetClassObject");
+ AssertReturnVoid(pfnGetClassObject != NULL);
+
+ /* Get the factory instance: */
+ static const CLSID s_PSOlePrx32ClsId = {0x00000320,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
+ CStdPSFactoryBuffer *pFactoryBuffer = NULL;
+ HRESULT hrc = pfnGetClassObject(s_PSOlePrx32ClsId, IID_IPSFactoryBuffer, (void **)&pFactoryBuffer);
+ AssertMsgReturnVoid(SUCCEEDED(hrc), ("hrc=%Rhrc\n", hrc));
+ AssertReturnVoid(pFactoryBuffer != NULL);
+
+ /*
+ * Search thru the file list for the interface we want to patch.
+ */
+ static const IID s_IID_Rundown = {0x00000134,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
+ static const IID s_IID_DLLHost = {0x00000141,0x0000,0x0000,{0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
+ decltype(CStdStubBuffer_Invoke) *pfnInvoke = (decltype(pfnInvoke))GetProcAddress(hmod, "CStdStubBuffer_Invoke");
+ if (!pfnInvoke)
+ pfnInvoke = (decltype(pfnInvoke))GetProcAddress(GetModuleHandleW(L"RPCRT4.DLL"), "CStdStubBuffer_Invoke");
+
+ unsigned cPatched = 0;
+ unsigned cAlreadyPatched = 0;
+ Assert(pFactoryBuffer->pProxyFileList != NULL);
+ for (ProxyFileInfo const **ppCur = pFactoryBuffer->pProxyFileList; *ppCur != NULL; ppCur++)
+ {
+ ProxyFileInfo const *pCur = *ppCur;
+
+ if (pCur->pStubVtblList)
+ {
+ for (PCInterfaceStubVtblList const *ppCurStub = pCur->pStubVtblList; *ppCurStub != NULL; ppCurStub++)
+ {
+ PCInterfaceStubVtblList const pCurStub = *ppCurStub;
+ IID const *piid = pCurStub->header.piid;
+ if (piid)
+ {
+ if (IsEqualIID(*piid, s_IID_Rundown))
+ {
+ if (pCurStub->Vtbl.Invoke == pfnInvoke)
+ {
+ DWORD fOld = 0;
+ if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld))
+ {
+ pCurStub->Vtbl.Invoke = Rundown_InvokeStub;
+ VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld);
+ cPatched++;
+ }
+ else
+ AssertMsgFailed(("%d\n", GetLastError()));
+ }
+ else
+ cAlreadyPatched++;
+ }
+ else if (IsEqualIID(*piid, s_IID_DLLHost))
+ {
+ if (pCurStub->Vtbl.Invoke == pfnInvoke)
+ {
+ DWORD fOld = 0;
+ if (VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), PAGE_READWRITE, &fOld))
+ {
+ pCurStub->Vtbl.Invoke = DLLHost_InvokeStub;
+ VirtualProtect(&pCurStub->Vtbl.Invoke, sizeof(pCurStub->Vtbl.Invoke), fOld, &fOld);
+ cPatched++;
+ }
+ else
+ AssertMsgFailed(("%d\n", GetLastError()));
+ }
+ else
+ cAlreadyPatched++;
+ }
+ }
+ }
+ }
+ }
+
+ /* done */
+ pFactoryBuffer->lpVtbl->Release((IPSFactoryBuffer *)pFactoryBuffer);
+
+ /*
+ * If we patched anything we should try prevent being unloaded.
+ */
+ if (cPatched > 0)
+ {
+ s_fPatched = true;
+ HMODULE hmodSelf;
+ AssertLogRelMsg(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_PIN,
+ (LPCWSTR)(uintptr_t)Rundown_InvokeStub, &hmodSelf),
+ ("last error: %u; Rundown_InvokeStub=%p\n", GetLastError(), Rundown_InvokeStub));
+ }
+ AssertLogRelMsg(cAlreadyPatched + cPatched >= 2,
+ ("COM patching of IRundown/IDLLHost failed! (%d+%d)\n", cAlreadyPatched, cPatched));
+}
+
+
+/**
+ * The COM main thread handle. (The first caller of com::Initialize().)
+ */
+static RTTHREAD volatile gCOMMainThread = NIL_RTTHREAD;
+
+/**
+ * Number of Initialize() calls on the main thread.
+ */
+static uint32_t gCOMMainInitCount = 0;
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+
+/**
+ * Initializes the COM runtime.
+ *
+ * This method must be called on each thread of the client application that
+ * wants to access COM facilities. The initialization must be performed before
+ * calling any other COM method or attempting to instantiate COM objects.
+ *
+ * On platforms using XPCOM, this method uses the following scheme to search for
+ * XPCOM runtime:
+ *
+ * 1. If the VBOX_APP_HOME environment variable is set, the path it specifies
+ * is used to search XPCOM libraries and components. If this method fails to
+ * initialize XPCOM runtime using this path, it will immediately return a
+ * failure and will NOT check for other paths as described below.
+ *
+ * 2. If VBOX_APP_HOME is not set, this methods tries the following paths in the
+ * given order:
+ *
+ * a) Compiled-in application data directory (as returned by
+ * RTPathAppPrivateArch())
+ * b) "/usr/lib/virtualbox" (Linux only)
+ * c) "/opt/VirtualBox" (Linux only)
+ *
+ * The first path for which the initialization succeeds will be used.
+ *
+ * On MS COM platforms, the COM runtime is provided by the system and does not
+ * need to be searched for.
+ *
+ * Once the COM subsystem is no longer necessary on a given thread, Shutdown()
+ * must be called to free resources allocated for it. Note that a thread may
+ * call Initialize() several times but for each of tese calls there must be a
+ * corresponding Shutdown() call.
+ *
+ * @return S_OK on success and a COM result code in case of failure.
+ */
+HRESULT Initialize(uint32_t fInitFlags /*=VBOX_COM_INIT_F_DEFAULT*/)
+{
+ HRESULT hrc = E_FAIL;
+
+#if !defined(VBOX_WITH_XPCOM)
+
+# ifdef VBOX_WITH_AUTO_COM_REG_UPDATE
+ /*
+ * First time we're called in a process, we refresh the VBox COM
+ * registrations. Use a global mutex to prevent updating when there are
+ * API users already active, as that could lead to a bit of a mess.
+ */
+ if ( (fInitFlags & VBOX_COM_INIT_F_AUTO_REG_UPDATE)
+ && gCOMMainThread == NIL_RTTHREAD)
+ {
+ SetLastError(ERROR_SUCCESS);
+ HANDLE hLeakIt = CreateMutexW(NULL/*pSecAttr*/, FALSE, L"Global\\VirtualBoxComLazyRegistrationMutant");
+ DWORD dwErr = GetLastError();
+ AssertMsg(dwErr == ERROR_SUCCESS || dwErr == ERROR_ALREADY_EXISTS || dwErr == ERROR_ACCESS_DENIED, ("%u\n", dwErr));
+ if (dwErr == ERROR_SUCCESS)
+ {
+ char szPath[RTPATH_MAX];
+ int vrc = RTPathAppPrivateArch(szPath, sizeof(szPath));
+ if (RT_SUCCESS(vrc))
+# ifndef VBOX_IN_32_ON_64_MAIN_API
+ vrc = RTPathAppend(szPath, sizeof(szPath),
+ RT_MAKE_U64(((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMinorVersion,
+ ((PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA)->NtMajorVersion)
+ >= RT_MAKE_U64(1/*Lo*/,6/*Hi*/)
+ ? "VBoxProxyStub.dll" : "VBoxProxyStubLegacy.dll");
+# else
+ vrc = RTPathAppend(szPath, sizeof(szPath), "x86\\VBoxProxyStub-x86.dll");
+# endif
+ if (RT_SUCCESS(vrc))
+ {
+ RTLDRMOD hMod;
+ vrc = RTLdrLoad(szPath, &hMod);
+ if (RT_SUCCESS(vrc))
+ {
+ union
+ {
+ void *pv;
+ DECLCALLBACKMEMBER(uint32_t, pfnRegUpdate,(void));
+ } u;
+ vrc = RTLdrGetSymbol(hMod, "VbpsUpdateRegistrations", &u.pv);
+ if (RT_SUCCESS(vrc))
+ u.pfnRegUpdate();
+ /* Just keep it loaded. */
+ }
+ }
+ Assert(hLeakIt != NULL); NOREF(hLeakIt);
+ }
+ }
+# endif
+
+ /*
+ * We initialize COM in GUI thread in STA, to be compliant with QT and
+ * OLE requirments (for example to allow D&D), while other threads
+ * initialized in regular MTA. To allow fast proxyless access from
+ * GUI thread to COM objects, we explicitly provide our COM objects
+ * with free threaded marshaller.
+ * !!!!! Please think twice before touching this code !!!!!
+ */
+ DWORD flags = fInitFlags & VBOX_COM_INIT_F_GUI
+ ? COINIT_APARTMENTTHREADED
+ | COINIT_SPEED_OVER_MEMORY
+ : COINIT_MULTITHREADED
+ | COINIT_DISABLE_OLE1DDE
+ | COINIT_SPEED_OVER_MEMORY;
+
+ hrc = CoInitializeEx(NULL, flags);
+
+ /* the overall result must be either S_OK or S_FALSE (S_FALSE means
+ * "already initialized using the same apartment model") */
+ AssertMsg(hrc == S_OK || hrc == S_FALSE, ("hrc=%08X\n", hrc));
+
+#if defined(VBOX_WITH_SDS)
+ // Setup COM Security to enable impersonation
+ HRESULT hrcGUICoInitializeSecurity = CoInitializeSecurity(NULL,
+ -1,
+ NULL,
+ NULL,
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL,
+ EOAC_NONE,
+ NULL);
+ NOREF(hrcGUICoInitializeSecurity);
+ Assert(SUCCEEDED(hrcGUICoInitializeSecurity) || hrcGUICoInitializeSecurity == RPC_E_TOO_LATE);
+#endif
+
+ /*
+ * IRundown has unsafe two methods we need to patch to prevent remote access.
+ * Do that before we start using COM and open ourselves to possible attacks.
+ */
+ if (!(fInitFlags & VBOX_COM_INIT_F_NO_COM_PATCHING))
+ PatchComBugs();
+
+ /* To be flow compatible with the XPCOM case, we return here if this isn't
+ * the main thread or if it isn't its first initialization call.
+ * Note! CoInitializeEx and CoUninitialize does it's own reference
+ * counting, so this exercise is entirely for the EventQueue init. */
+ bool fRc;
+ RTTHREAD hSelf = RTThreadSelf();
+ if (hSelf != NIL_RTTHREAD)
+ ASMAtomicCmpXchgHandle(&gCOMMainThread, hSelf, NIL_RTTHREAD, fRc);
+ else
+ fRc = false;
+
+ if (fInitFlags & VBOX_COM_INIT_F_GUI)
+ Assert(RTThreadIsMain(hSelf));
+
+ if (!fRc)
+ {
+ if ( gCOMMainThread == hSelf
+ && SUCCEEDED(hrc))
+ gCOMMainInitCount++;
+
+ AssertComRC(hrc);
+ return hrc;
+ }
+ Assert(RTThreadIsMain(hSelf));
+
+ /* this is the first main thread initialization */
+ Assert(gCOMMainInitCount == 0);
+ if (SUCCEEDED(hrc))
+ gCOMMainInitCount = 1;
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+ /* Unused here */
+ RT_NOREF(fInitFlags);
+
+ if (ASMAtomicXchgBool(&gIsXPCOMInitialized, true) == true)
+ {
+ /* XPCOM is already initialized on the main thread, no special
+ * initialization is necessary on additional threads. Just increase
+ * the init counter if it's a main thread again (to correctly support
+ * nested calls to Initialize()/Shutdown() for compatibility with
+ * Win32). */
+
+ nsCOMPtr<nsIEventQueue> eventQ;
+ hrc = NS_GetMainEventQ(getter_AddRefs(eventQ));
+
+ if (NS_SUCCEEDED(hrc))
+ {
+ PRBool isOnMainThread = PR_FALSE;
+ hrc = eventQ->IsOnCurrentThread(&isOnMainThread);
+ if (NS_SUCCEEDED(hrc) && isOnMainThread)
+ ++gXPCOMInitCount;
+ }
+
+ AssertComRC(hrc);
+ return hrc;
+ }
+ Assert(RTThreadIsMain(RTThreadSelf()));
+
+ /* this is the first initialization */
+ gXPCOMInitCount = 1;
+
+ /* prepare paths for registry files */
+ char szCompReg[RTPATH_MAX];
+ char szXptiDat[RTPATH_MAX];
+
+ int vrc = GetVBoxUserHomeDirectory(szCompReg, sizeof(szCompReg));
+ if (vrc == VERR_ACCESS_DENIED)
+ return NS_ERROR_FILE_ACCESS_DENIED;
+ AssertRCReturn(vrc, NS_ERROR_FAILURE);
+ vrc = RTStrCopy(szXptiDat, sizeof(szXptiDat), szCompReg);
+ AssertRCReturn(vrc, NS_ERROR_FAILURE);
+# ifdef VBOX_IN_32_ON_64_MAIN_API
+ vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg-x86.dat");
+ AssertRCReturn(vrc, NS_ERROR_FAILURE);
+ vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti-x86.dat");
+ AssertRCReturn(vrc, NS_ERROR_FAILURE);
+# else
+ vrc = RTPathAppend(szCompReg, sizeof(szCompReg), "compreg.dat");
+ AssertRCReturn(vrc, NS_ERROR_FAILURE);
+ vrc = RTPathAppend(szXptiDat, sizeof(szXptiDat), "xpti.dat");
+ AssertRCReturn(vrc, NS_ERROR_FAILURE);
+# endif
+
+ LogFlowFunc(("component registry : \"%s\"\n", szCompReg));
+ LogFlowFunc(("XPTI data file : \"%s\"\n", szXptiDat));
+
+ static const char *kAppPathsToProbe[] =
+ {
+ NULL, /* 0: will use VBOX_APP_HOME */
+ NULL, /* 1: will try RTPathAppPrivateArch(), correctly installed release builds will never go further */
+ NULL, /* 2: will try parent directory of RTPathAppPrivateArch(), only for testcases in non-hardened builds */
+ /* There used to be hard coded paths, but they only caused trouble
+ * because they often led to mixing of builds or even versions.
+ * If you feel tempted to add anything here, think again. They would
+ * only be used if option 1 would not work, which is a sign of a big
+ * problem, as it returns a fixed location defined at compile time.
+ * It is better to fail than blindly trying to cover the problem. */
+ };
+
+ /* Find out the directory where VirtualBox binaries are located */
+ for (size_t i = 0; i < RT_ELEMENTS(kAppPathsToProbe); ++ i)
+ {
+ char szAppHomeDir[RTPATH_MAX];
+
+ if (i == 0)
+ {
+ /* Use VBOX_APP_HOME if present */
+ vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_APP_HOME", szAppHomeDir, sizeof(szAppHomeDir), NULL);
+ if (vrc == VERR_ENV_VAR_NOT_FOUND)
+ continue;
+ AssertRC(vrc);
+ }
+ else if (i == 1)
+ {
+ /* Use RTPathAppPrivateArch() first */
+ vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
+ AssertRC(vrc);
+ }
+ else if (i == 2)
+ {
+# ifdef VBOX_WITH_HARDENING
+ continue;
+# else /* !VBOX_WITH_HARDENING */
+ /* Use parent of RTPathAppPrivateArch() if ends with "testcase" */
+ vrc = RTPathAppPrivateArch(szAppHomeDir, sizeof(szAppHomeDir));
+ AssertRC(vrc);
+ vrc = RTPathStripTrailingSlash(szAppHomeDir);
+ AssertRC(vrc);
+ char *filename = RTPathFilename(szAppHomeDir);
+ if (!filename || strcmp(filename, "testcase"))
+ continue;
+ RTPathStripFilename(szAppHomeDir);
+# endif /* !VBOX_WITH_HARDENING */
+ }
+ else
+ {
+ /* Iterate over all other paths */
+ RTStrCopy(szAppHomeDir, sizeof(szAppHomeDir), kAppPathsToProbe[i]);
+ vrc = VINF_SUCCESS;
+ }
+ if (RT_FAILURE(vrc))
+ {
+ hrc = NS_ERROR_FAILURE;
+ continue;
+ }
+ char szCompDir[RTPATH_MAX];
+ vrc = RTStrCopy(szCompDir, sizeof(szCompDir), szAppHomeDir);
+ if (RT_FAILURE(vrc))
+ {
+ hrc = NS_ERROR_FAILURE;
+ continue;
+ }
+ vrc = RTPathAppend(szCompDir, sizeof(szCompDir), "components");
+ if (RT_FAILURE(vrc))
+ {
+ hrc = NS_ERROR_FAILURE;
+ continue;
+ }
+ LogFlowFunc(("component directory : \"%s\"\n", szCompDir));
+
+ nsCOMPtr<DirectoryServiceProvider> dsProv;
+ dsProv = new DirectoryServiceProvider();
+ if (dsProv)
+ hrc = dsProv->init(szCompReg, szXptiDat, szCompDir, szAppHomeDir);
+ else
+ hrc = NS_ERROR_OUT_OF_MEMORY;
+ if (NS_FAILED(hrc))
+ break;
+
+ /* Setup the application path for NS_InitXPCOM2. Note that we properly
+ * answer the NS_XPCOM_CURRENT_PROCESS_DIR query in our directory
+ * service provider but it seems to be activated after the directory
+ * service is used for the first time (see the source NS_InitXPCOM2). So
+ * use the same value here to be on the safe side. */
+ nsCOMPtr <nsIFile> appDir;
+ {
+ char *appDirCP = NULL;
+ vrc = RTStrUtf8ToCurrentCP(&appDirCP, szAppHomeDir);
+ if (RT_SUCCESS(vrc))
+ {
+ nsCOMPtr<nsILocalFile> file;
+ hrc = NS_NewNativeLocalFile(nsEmbedCString(appDirCP), PR_FALSE, getter_AddRefs(file));
+ if (NS_SUCCEEDED(hrc))
+ appDir = do_QueryInterface(file, &hrc);
+
+ RTStrFree(appDirCP);
+ }
+ else
+ hrc = NS_ERROR_FAILURE;
+ }
+ if (NS_FAILED(hrc))
+ break;
+
+ /* Set VBOX_XPCOM_HOME to the same app path to make XPCOM sources that
+ * still use it instead of the directory service happy */
+ vrc = RTEnvSetEx(RTENV_DEFAULT, "VBOX_XPCOM_HOME", szAppHomeDir);
+ AssertRC(vrc);
+
+ /* Finally, initialize XPCOM */
+ {
+ nsCOMPtr<nsIServiceManager> serviceManager;
+ hrc = NS_InitXPCOM2(getter_AddRefs(serviceManager), appDir, dsProv);
+ if (NS_SUCCEEDED(hrc))
+ {
+ nsCOMPtr<nsIComponentRegistrar> registrar = do_QueryInterface(serviceManager, &hrc);
+ if (NS_SUCCEEDED(hrc))
+ {
+ hrc = registrar->AutoRegister(nsnull);
+ if (NS_SUCCEEDED(hrc))
+ {
+ /* We succeeded, stop probing paths */
+ LogFlowFunc(("Succeeded.\n"));
+ break;
+ }
+ }
+ }
+ }
+
+ /* clean up before the new try */
+ HRESULT hrc2 = NS_ShutdownXPCOM(nsnull);
+ if (SUCCEEDED(hrc))
+ hrc = hrc2;
+
+ if (i == 0)
+ {
+ /* We failed with VBOX_APP_HOME, don't probe other paths */
+ break;
+ }
+ }
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+ AssertComRCReturnRC(hrc);
+
+ // for both COM and XPCOM, we only get here if this is the main thread;
+ // only then initialize the autolock system (AutoLock.cpp)
+ Assert(RTThreadIsMain(RTThreadSelf()));
+ util::InitAutoLockSystem();
+
+ /*
+ * Init the main event queue (ASSUMES it cannot fail).
+ */
+ if (SUCCEEDED(hrc))
+ NativeEventQueue::init();
+
+ return hrc;
+}
+
+HRESULT Shutdown()
+{
+ HRESULT hrc = S_OK;
+
+#if !defined(VBOX_WITH_XPCOM)
+
+ /* EventQueue::uninit reference counting fun. */
+ RTTHREAD hSelf = RTThreadSelf();
+ if ( hSelf == gCOMMainThread
+ && hSelf != NIL_RTTHREAD)
+ {
+ if (-- gCOMMainInitCount == 0)
+ {
+ NativeEventQueue::uninit();
+ ASMAtomicWriteHandle(&gCOMMainThread, NIL_RTTHREAD);
+ }
+ }
+
+ CoUninitialize();
+
+#else /* !defined(VBOX_WITH_XPCOM) */
+
+ nsCOMPtr<nsIEventQueue> eventQ;
+ hrc = NS_GetMainEventQ(getter_AddRefs(eventQ));
+
+ if (NS_SUCCEEDED(hrc) || hrc == NS_ERROR_NOT_AVAILABLE)
+ {
+ /* NS_ERROR_NOT_AVAILABLE seems to mean that
+ * nsIEventQueue::StopAcceptingEvents() has been called (see
+ * nsEventQueueService.cpp). We hope that this error code always means
+ * just that in this case and assume that we're on the main thread
+ * (it's a kind of unexpected behavior if a non-main thread ever calls
+ * StopAcceptingEvents() on the main event queue). */
+
+ PRBool isOnMainThread = PR_FALSE;
+ if (NS_SUCCEEDED(hrc))
+ {
+ hrc = eventQ->IsOnCurrentThread(&isOnMainThread);
+ eventQ = nsnull; /* early release before shutdown */
+ }
+ else
+ {
+ isOnMainThread = RTThreadIsMain(RTThreadSelf());
+ hrc = NS_OK;
+ }
+
+ if (NS_SUCCEEDED(hrc) && isOnMainThread)
+ {
+ /* only the main thread needs to uninitialize XPCOM and only if
+ * init counter drops to zero */
+ if (--gXPCOMInitCount == 0)
+ {
+ NativeEventQueue::uninit();
+ hrc = NS_ShutdownXPCOM(nsnull);
+
+ /* This is a thread initialized XPCOM and set gIsXPCOMInitialized to
+ * true. Reset it back to false. */
+ bool wasInited = ASMAtomicXchgBool(&gIsXPCOMInitialized, false);
+ Assert(wasInited == true);
+ NOREF(wasInited);
+ }
+ }
+ }
+
+#endif /* !defined(VBOX_WITH_XPCOM) */
+
+ AssertComRC(hrc);
+
+ return hrc;
+}
+
+} /* namespace com */
diff --git a/src/VBox/Main/glue/string-base64.cpp b/src/VBox/Main/glue/string-base64.cpp
new file mode 100644
index 00000000..7bad63ae
--- /dev/null
+++ b/src/VBox/Main/glue/string-base64.cpp
@@ -0,0 +1,61 @@
+/* $Id: string-base64.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - UTF-8 and UTF-16 string classes, BASE64 bits.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBox/com/string.h"
+
+#include <iprt/base64.h>
+#include <iprt/errcore.h>
+
+
+namespace com
+{
+
+HRESULT Bstr::base64Encode(const void *pvData, size_t cbData, bool fLineBreaks /*= false*/)
+{
+ uint32_t const fFlags = fLineBreaks ? RTBASE64_FLAGS_EOL_LF : RTBASE64_FLAGS_NO_LINE_BREAKS;
+ size_t cwcEncoded = RTBase64EncodedUtf16LengthEx(cbData, fFlags);
+ HRESULT hrc = reserveNoThrow(cwcEncoded + 1);
+ if (SUCCEEDED(hrc))
+ {
+ int vrc = RTBase64EncodeUtf16Ex(pvData, cbData, fFlags, mutableRaw(), cwcEncoded + 1, &cwcEncoded);
+ AssertRCReturnStmt(vrc, setNull(), E_FAIL);
+ hrc = joltNoThrow(cwcEncoded);
+ }
+ return hrc;
+}
+
+int Bstr::base64Decode(void *pvData, size_t cbData, size_t *pcbActual /*= NULL*/, PRTUTF16 *ppwszEnd /*= NULL*/)
+{
+ return RTBase64DecodeUtf16Ex(raw(), RTSTR_MAX, pvData, cbData, pcbActual, ppwszEnd);
+}
+
+ssize_t Bstr::base64DecodedSize(PRTUTF16 *ppwszEnd /*= NULL*/)
+{
+ return RTBase64DecodedUtf16SizeEx(raw(), RTSTR_MAX, ppwszEnd);
+}
+
+} /* namespace com */
diff --git a/src/VBox/Main/glue/string.cpp b/src/VBox/Main/glue/string.cpp
new file mode 100644
index 00000000..177ffb46
--- /dev/null
+++ b/src/VBox/Main/glue/string.cpp
@@ -0,0 +1,1037 @@
+/* $Id: string.cpp $ */
+/** @file
+ * MS COM / XPCOM Abstraction Layer - UTF-8 and UTF-16 string classes.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBox/com/string.h"
+
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/uni.h>
+
+namespace com
+{
+
+// BSTR representing a null wide char with 32 bits of length prefix (0);
+// this will work on Windows as well as other platforms where BSTR does
+// not use length prefixes
+const OLECHAR g_achEmptyBstr[3] = { 0, 0, 0 };
+const BSTR g_bstrEmpty = (BSTR)&g_achEmptyBstr[2];
+
+/* static */
+const Bstr Bstr::Empty; /* default ctor is OK */
+
+
+Bstr &Bstr::printf(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ HRESULT hrc = printfVNoThrow(pszFormat, va);
+ va_end(va);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (hrc == S_OK)
+ { /* likely */ }
+ else
+ throw std::bad_alloc();
+#else
+ Assert(hrc == S_OK); RT_NOREF(hrc);
+#endif
+ return *this;
+}
+
+HRESULT Bstr::printfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
+{
+ va_list va;
+ va_start(va, pszFormat);
+ HRESULT hrc = printfVNoThrow(pszFormat, va);
+ va_end(va);
+ return hrc;
+}
+
+
+Bstr &Bstr::printfV(const char *pszFormat, va_list va)
+{
+ HRESULT hrc = printfVNoThrow(pszFormat, va);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (hrc == S_OK)
+ { /* likely */ }
+ else
+ throw std::bad_alloc();
+#else
+ Assert(hrc == S_OK); RT_NOREF(hrc);
+#endif
+ return *this;
+}
+
+struct BSTRNOTHROW
+{
+ Bstr *pThis;
+ size_t cwcAlloc;
+ size_t offDst;
+ HRESULT hrc;
+};
+
+/**
+ * Callback used with RTStrFormatV by Bstr::printfVNoThrow.
+ *
+ * @returns The number of bytes added (not used).
+ *
+ * @param pvArg Pointer to a BSTRNOTHROW structure.
+ * @param pachChars The characters to append.
+ * @param cbChars The number of characters. 0 on the final callback.
+ */
+/*static*/ DECLCALLBACK(size_t)
+Bstr::printfOutputCallbackNoThrow(void *pvArg, const char *pachChars, size_t cbChars) RT_NOEXCEPT
+{
+ BSTRNOTHROW *pArgs = (BSTRNOTHROW *)pvArg;
+ if (cbChars)
+ {
+ size_t cwcAppend;
+ int vrc = ::RTStrCalcUtf16LenEx(pachChars, cbChars, &cwcAppend);
+ AssertRCReturnStmt(vrc, pArgs->hrc = E_UNEXPECTED, 0);
+
+ /*
+ * Ensure we've got sufficient memory.
+ */
+ Bstr *pThis = pArgs->pThis;
+ size_t const cwcBoth = pArgs->offDst + cwcAppend;
+ if (cwcBoth >= pArgs->cwcAlloc)
+ {
+ if (pArgs->hrc == S_OK)
+ {
+ /* Double the buffer size, if it's less that _1M. Align sizes like
+ for append. */
+ size_t cwcAlloc = RT_ALIGN_Z(pArgs->cwcAlloc, 128);
+ cwcAlloc += RT_MIN(cwcAlloc, _1M);
+ if (cwcAlloc <= cwcBoth)
+ cwcAlloc = RT_ALIGN_Z(cwcBoth + 1, 512);
+ pArgs->hrc = pThis->reserveNoThrow(cwcAlloc, true /*fForce*/);
+ AssertMsgReturn(pArgs->hrc == S_OK, ("cwcAlloc=%#zx\n", cwcAlloc), 0);
+ pArgs->cwcAlloc = cwcAlloc;
+ }
+ else
+ return 0;
+ }
+
+ /*
+ * Do the conversion.
+ */
+ PRTUTF16 pwszDst = pThis->m_bstr + pArgs->offDst;
+ Assert(pArgs->cwcAlloc > pArgs->offDst);
+ vrc = ::RTStrToUtf16Ex(pachChars, cbChars, &pwszDst, pArgs->cwcAlloc - pArgs->offDst, &cwcAppend);
+ AssertRCReturnStmt(vrc, pArgs->hrc = E_UNEXPECTED, 0);
+ pArgs->offDst += cwcAppend;
+ }
+ return cbChars;
+}
+
+HRESULT Bstr::printfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
+{
+ cleanup();
+
+ BSTRNOTHROW Args = { this, 0, 0, S_OK };
+ RTStrFormatV(printfOutputCallbackNoThrow, &Args, NULL, NULL, pszFormat, va);
+ if (Args.hrc == S_OK)
+ {
+ Args.hrc = joltNoThrow(Args.offDst);
+ if (Args.hrc == S_OK)
+ return S_OK;
+ }
+
+ cleanup();
+ return Args.hrc;
+}
+
+void Bstr::copyFromN(const char *a_pszSrc, size_t a_cchMax)
+{
+ /*
+ * Initialize m_bstr first in case of throws further down in the code, then
+ * check for empty input (m_bstr == NULL means empty, there are no NULL
+ * strings).
+ */
+ m_bstr = NULL;
+ if (!a_cchMax || !a_pszSrc || !*a_pszSrc)
+ return;
+
+ /*
+ * Calculate the length and allocate a BSTR string buffer of the right
+ * size, i.e. optimize heap usage.
+ */
+ size_t cwc;
+ int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc);
+ if (RT_SUCCESS(vrc))
+ {
+ m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR)));
+ if (RT_LIKELY(m_bstr))
+ {
+ PRTUTF16 pwsz = (PRTUTF16)m_bstr;
+ vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL);
+ if (RT_SUCCESS(vrc))
+ return;
+
+ /* This should not happen! */
+ AssertRC(vrc);
+ cleanup();
+ }
+ }
+ else /* ASSUME: input is valid Utf-8. Fake out of memory error. */
+ AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc));
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw std::bad_alloc();
+#endif
+}
+
+HRESULT Bstr::cleanupAndCopyFromNoThrow(const char *a_pszSrc, size_t a_cchMax) RT_NOEXCEPT
+{
+ /*
+ * Check for empty input (m_bstr == NULL means empty, there are no NULL strings).
+ */
+ cleanup();
+ if (!a_cchMax || !a_pszSrc || !*a_pszSrc)
+ return S_OK;
+
+ /*
+ * Calculate the length and allocate a BSTR string buffer of the right
+ * size, i.e. optimize heap usage.
+ */
+ HRESULT hrc;
+ size_t cwc;
+ int vrc = ::RTStrCalcUtf16LenEx(a_pszSrc, a_cchMax, &cwc);
+ if (RT_SUCCESS(vrc))
+ {
+ m_bstr = ::SysAllocStringByteLen(NULL, (unsigned)(cwc * sizeof(OLECHAR)));
+ if (RT_LIKELY(m_bstr))
+ {
+ PRTUTF16 pwsz = (PRTUTF16)m_bstr;
+ vrc = ::RTStrToUtf16Ex(a_pszSrc, a_cchMax, &pwsz, cwc + 1, NULL);
+ if (RT_SUCCESS(vrc))
+ return S_OK;
+
+ /* This should not happen! */
+ AssertRC(vrc);
+ cleanup();
+ hrc = E_UNEXPECTED;
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+ }
+ else
+ {
+ /* Unexpected: Invalid UTF-8 input. */
+ AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTStrNLen(a_pszSrc, a_cchMax), a_pszSrc));
+ hrc = E_UNEXPECTED;
+ }
+ return hrc;
+}
+
+
+int Bstr::compareUtf8(const char *a_pszRight, CaseSensitivity a_enmCase /*= CaseSensitive*/) const
+{
+ PCRTUTF16 pwszLeft = m_bstr;
+
+ /*
+ * Special case for null/empty strings. Unlike RTUtf16Cmp we
+ * treat null and empty equally.
+ */
+ if (!pwszLeft)
+ return !a_pszRight || *a_pszRight == '\0' ? 0 : -1;
+ if (!a_pszRight)
+ return *pwszLeft == '\0' ? 0 : 1;
+
+ /*
+ * Compare with a UTF-8 string by enumerating them char by char.
+ */
+ for (;;)
+ {
+ RTUNICP ucLeft;
+ int vrc = RTUtf16GetCpEx(&pwszLeft, &ucLeft);
+ AssertRCReturn(vrc, 1);
+
+ RTUNICP ucRight;
+ vrc = RTStrGetCpEx(&a_pszRight, &ucRight);
+ AssertRCReturn(vrc, -1);
+ if (ucLeft == ucRight)
+ {
+ if (ucLeft)
+ continue;
+ return 0;
+ }
+
+ if (a_enmCase == CaseInsensitive)
+ {
+ if (RTUniCpToUpper(ucLeft) == RTUniCpToUpper(ucRight))
+ continue;
+ if (RTUniCpToLower(ucLeft) == RTUniCpToLower(ucRight))
+ continue;
+ }
+
+ return ucLeft < ucRight ? -1 : 1;
+ }
+}
+
+
+bool Bstr::startsWith(Bstr const &a_rStart) const
+{
+ return RTUtf16NCmp(m_bstr, a_rStart.m_bstr, a_rStart.length()) == 0;
+}
+
+
+bool Bstr::startsWith(RTCString const &a_rStart) const
+{
+ return RTUtf16NCmpUtf8(m_bstr, a_rStart.c_str(), RTSTR_MAX, a_rStart.length()) == 0;
+}
+
+
+bool Bstr::startsWith(const char *a_pszStart) const
+{
+ return RTUtf16NCmpUtf8(m_bstr, a_pszStart, RTSTR_MAX, strlen(a_pszStart)) == 0;
+}
+
+
+#ifndef VBOX_WITH_XPCOM
+
+HRESULT Bstr::joltNoThrow(ssize_t cwcNew /* = -1*/) RT_NOEXCEPT
+{
+ if (m_bstr)
+ {
+ size_t const cwcAlloc = ::SysStringLen(m_bstr);
+ size_t const cwcActual = cwcNew < 0 ? ::RTUtf16Len(m_bstr) : (size_t)cwcNew;
+ Assert(cwcNew < 0 || cwcActual == ::RTUtf16Len(m_bstr));
+ if (cwcActual != cwcAlloc)
+ {
+ Assert(cwcActual <= cwcAlloc);
+ Assert((unsigned int)cwcActual == cwcActual);
+
+ /* Official way: Reallocate the string. We could of course just update the size-prefix if we dared... */
+ if (!::SysReAllocStringLen(&m_bstr, NULL, (unsigned int)cwcActual))
+ {
+ AssertFailed();
+ return E_OUTOFMEMORY;
+ }
+ }
+ }
+ else
+ Assert(cwcNew <= 0);
+ return S_OK;
+}
+
+
+void Bstr::jolt(ssize_t cwcNew /* = -1*/)
+{
+ HRESULT hrc = joltNoThrow(cwcNew);
+# ifdef RT_EXCEPTIONS_ENABLED
+ if (hrc != S_OK)
+ throw std::bad_alloc();
+# else
+ Assert(hrc == S_OK); RT_NOREF(hrc);
+# endif
+}
+
+#endif /* !VBOX_WITH_XPCOM */
+
+
+HRESULT Bstr::reserveNoThrow(size_t cwcMin, bool fForce /*= false*/) RT_NOEXCEPT
+{
+ /* If not forcing the string to the cwcMin length, check cwcMin against the
+ current string length: */
+ if (!fForce)
+ {
+ size_t cwcCur = m_bstr ? ::SysStringLen(m_bstr) : 0;
+ if (cwcCur >= cwcMin)
+ return S_OK;
+ }
+
+ /* The documentation for SysReAllocStringLen hints about it being allergic
+ to NULL in some way or another, so we call SysAllocStringLen directly
+ when appropriate: */
+ if (m_bstr)
+ AssertReturn(::SysReAllocStringLen(&m_bstr, NULL, (unsigned int)cwcMin) != FALSE, E_OUTOFMEMORY);
+ else if (cwcMin > 0)
+ {
+ m_bstr = ::SysAllocStringLen(NULL, (unsigned int)cwcMin);
+ AssertReturn(m_bstr, E_OUTOFMEMORY);
+ }
+
+ return S_OK;
+}
+
+
+void Bstr::reserve(size_t cwcMin, bool fForce /*= false*/)
+{
+ HRESULT hrc = reserveNoThrow(cwcMin, fForce);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (hrc != S_OK)
+ throw std::bad_alloc();
+#else
+ Assert(hrc == S_OK); RT_NOREF(hrc);
+#endif
+}
+
+
+Bstr &Bstr::append(const Bstr &rThat)
+{
+ if (rThat.isNotEmpty())
+ return appendWorkerUtf16(rThat.m_bstr, rThat.length());
+ return *this;
+}
+
+
+HRESULT Bstr::appendNoThrow(const Bstr &rThat) RT_NOEXCEPT
+{
+ if (rThat.isNotEmpty())
+ return appendWorkerUtf16NoThrow(rThat.m_bstr, rThat.length());
+ return S_OK;
+}
+
+
+Bstr &Bstr::append(const RTCString &rThat)
+{
+ if (rThat.isNotEmpty())
+ return appendWorkerUtf8(rThat.c_str(), rThat.length());
+ return *this;
+}
+
+
+HRESULT Bstr::appendNoThrow(const RTCString &rThat) RT_NOEXCEPT
+{
+ if (rThat.isNotEmpty())
+ return appendWorkerUtf8NoThrow(rThat.c_str(), rThat.length());
+ return S_OK;
+}
+
+
+Bstr &Bstr::append(CBSTR pwszSrc)
+{
+ if (pwszSrc && *pwszSrc)
+ return appendWorkerUtf16(pwszSrc, RTUtf16Len(pwszSrc));
+ return *this;
+}
+
+
+HRESULT Bstr::appendNoThrow(CBSTR pwszSrc) RT_NOEXCEPT
+{
+ if (pwszSrc && *pwszSrc)
+ return appendWorkerUtf16NoThrow(pwszSrc, RTUtf16Len(pwszSrc));
+ return S_OK;
+}
+
+
+Bstr &Bstr::append(const char *pszSrc)
+{
+ if (pszSrc && *pszSrc)
+ return appendWorkerUtf8(pszSrc, strlen(pszSrc));
+ return *this;
+}
+
+
+HRESULT Bstr::appendNoThrow(const char *pszSrc) RT_NOEXCEPT
+{
+ if (pszSrc && *pszSrc)
+ return appendWorkerUtf8NoThrow(pszSrc, strlen(pszSrc));
+ return S_OK;
+}
+
+
+Bstr &Bstr::append(const Bstr &rThat, size_t offStart, size_t cwcMax /*= RTSTR_MAX*/)
+{
+ size_t cwcSrc = rThat.length();
+ if (offStart < cwcSrc)
+ return appendWorkerUtf16(rThat.raw() + offStart, RT_MIN(cwcSrc - offStart, cwcMax));
+ return *this;
+}
+
+
+HRESULT Bstr::appendNoThrow(const Bstr &rThat, size_t offStart, size_t cwcMax /*= RTSTR_MAX*/) RT_NOEXCEPT
+{
+ size_t cwcSrc = rThat.length();
+ if (offStart < cwcSrc)
+ return appendWorkerUtf16NoThrow(rThat.raw() + offStart, RT_MIN(cwcSrc - offStart, cwcMax));
+ return S_OK;
+}
+
+
+Bstr &Bstr::append(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/)
+{
+ if (offStart < rThat.length())
+ return appendWorkerUtf8(rThat.c_str() + offStart, RT_MIN(rThat.length() - offStart, cchMax));
+ return *this;
+}
+
+
+HRESULT Bstr::appendNoThrow(const RTCString &rThat, size_t offStart, size_t cchMax /*= RTSTR_MAX*/) RT_NOEXCEPT
+{
+ if (offStart < rThat.length())
+ return appendWorkerUtf8NoThrow(rThat.c_str() + offStart, RT_MIN(rThat.length() - offStart, cchMax));
+ return S_OK;
+}
+
+
+Bstr &Bstr::append(CBSTR pwszThat, size_t cchMax)
+{
+ return appendWorkerUtf16(pwszThat, RTUtf16NLen(pwszThat, cchMax));
+}
+
+
+HRESULT Bstr::appendNoThrow(CBSTR pwszThat, size_t cchMax) RT_NOEXCEPT
+{
+ return appendWorkerUtf16NoThrow(pwszThat, RTUtf16NLen(pwszThat, cchMax));
+}
+
+
+Bstr &Bstr::append(const char *pszThat, size_t cchMax)
+{
+ return appendWorkerUtf8(pszThat, RTStrNLen(pszThat, cchMax));
+}
+
+
+HRESULT Bstr::appendNoThrow(const char *pszThat, size_t cchMax) RT_NOEXCEPT
+{
+ return appendWorkerUtf8NoThrow(pszThat, RTStrNLen(pszThat, cchMax));
+}
+
+
+Bstr &Bstr::append(char ch)
+{
+ AssertMsg(ch > 0 && ch < 127, ("%#x\n", ch));
+ return appendWorkerUtf8(&ch, 1);
+}
+
+
+HRESULT Bstr::appendNoThrow(char ch) RT_NOEXCEPT
+{
+ AssertMsg(ch > 0 && ch < 127, ("%#x\n", ch));
+ return appendWorkerUtf8NoThrow(&ch, 1);
+}
+
+
+Bstr &Bstr::appendCodePoint(RTUNICP uc)
+{
+ RTUTF16 wszTmp[3];
+ PRTUTF16 pwszEnd = RTUtf16PutCp(wszTmp, uc);
+ *pwszEnd = '\0';
+ return appendWorkerUtf16(&wszTmp[0], pwszEnd - &wszTmp[0]);
+}
+
+
+HRESULT Bstr::appendCodePointNoThrow(RTUNICP uc) RT_NOEXCEPT
+{
+ RTUTF16 wszTmp[3];
+ PRTUTF16 pwszEnd = RTUtf16PutCp(wszTmp, uc);
+ *pwszEnd = '\0';
+ return appendWorkerUtf16NoThrow(&wszTmp[0], pwszEnd - &wszTmp[0]);
+}
+
+
+Bstr &Bstr::appendWorkerUtf16(PCRTUTF16 pwszSrc, size_t cwcSrc)
+{
+ size_t cwcOld = length();
+ size_t cwcTotal = cwcOld + cwcSrc;
+ reserve(cwcTotal, true /*fForce*/);
+ if (cwcSrc)
+ memcpy(&m_bstr[cwcOld], pwszSrc, cwcSrc * sizeof(RTUTF16));
+ m_bstr[cwcTotal] = '\0';
+ return *this;
+}
+
+
+HRESULT Bstr::appendWorkerUtf16NoThrow(PCRTUTF16 pwszSrc, size_t cwcSrc) RT_NOEXCEPT
+{
+ size_t cwcOld = length();
+ size_t cwcTotal = cwcOld + cwcSrc;
+ HRESULT hrc = reserveNoThrow(cwcTotal, true /*fForce*/);
+ if (hrc == S_OK)
+ {
+ if (cwcSrc)
+ memcpy(&m_bstr[cwcOld], pwszSrc, cwcSrc * sizeof(RTUTF16));
+ m_bstr[cwcTotal] = '\0';
+ }
+ return hrc;
+}
+
+
+Bstr &Bstr::appendWorkerUtf8(const char *pszSrc, size_t cchSrc)
+{
+ size_t cwcSrc;
+ int vrc = RTStrCalcUtf16LenEx(pszSrc, cchSrc, &cwcSrc);
+#ifdef RT_EXCEPTIONS_ENABLED
+ AssertRCStmt(vrc, throw std::bad_alloc());
+#else
+ AssertRCReturn(vrc, *this);
+#endif
+
+ size_t cwcOld = length();
+ size_t cwcTotal = cwcOld + cwcSrc;
+ reserve(cwcTotal, true /*fForce*/);
+ if (cwcSrc)
+ {
+ PRTUTF16 pwszDst = &m_bstr[cwcOld];
+ vrc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszDst, cwcSrc + 1, NULL);
+#ifdef RT_EXCEPTIONS_ENABLED
+ AssertRCStmt(vrc, throw std::bad_alloc());
+#else
+ AssertRC(vrc);
+#endif
+ }
+ m_bstr[cwcTotal] = '\0';
+ return *this;
+}
+
+
+HRESULT Bstr::appendWorkerUtf8NoThrow(const char *pszSrc, size_t cchSrc) RT_NOEXCEPT
+{
+ size_t cwcSrc;
+ int vrc = RTStrCalcUtf16LenEx(pszSrc, cchSrc, &cwcSrc);
+ AssertRCStmt(vrc, E_INVALIDARG);
+
+ size_t cwcOld = length();
+ size_t cwcTotal = cwcOld + cwcSrc;
+ HRESULT hrc = reserveNoThrow(cwcTotal, true /*fForce*/);
+ AssertReturn(hrc == S_OK, hrc);
+ if (cwcSrc)
+ {
+ PRTUTF16 pwszDst = &m_bstr[cwcOld];
+ vrc = RTStrToUtf16Ex(pszSrc, cchSrc, &pwszDst, cwcSrc + 1, NULL);
+ AssertRCStmt(vrc, E_INVALIDARG);
+ }
+ m_bstr[cwcTotal] = '\0';
+ return S_OK;
+}
+
+
+Bstr &Bstr::appendPrintf(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ HRESULT hrc = appendPrintfVNoThrow(pszFormat, va);
+ va_end(va);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (hrc != S_OK)
+ throw std::bad_alloc();
+#else
+ Assert(hrc == S_OK); RT_NOREF(hrc);
+#endif
+ return *this;
+}
+
+
+HRESULT Bstr::appendPrintfNoThrow(const char *pszFormat, ...) RT_NOEXCEPT
+{
+ va_list va;
+ va_start(va, pszFormat);
+ HRESULT hrc = appendPrintfVNoThrow(pszFormat, va);
+ va_end(va);
+ return hrc;
+}
+
+
+Bstr &Bstr::appendPrintfV(const char *pszFormat, va_list va)
+{
+ HRESULT hrc = appendPrintfVNoThrow(pszFormat, va);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (hrc != S_OK)
+ throw std::bad_alloc();
+#else
+ Assert(hrc == S_OK); RT_NOREF(hrc);
+#endif
+ return *this;
+}
+
+
+HRESULT Bstr::appendPrintfVNoThrow(const char *pszFormat, va_list va) RT_NOEXCEPT
+{
+ size_t const cwcOld = length();
+ BSTRNOTHROW Args = { this, cwcOld, cwcOld, S_OK };
+
+ RTStrFormatV(printfOutputCallbackNoThrow, &Args, NULL, NULL, pszFormat, va);
+ if (Args.hrc == S_OK)
+ {
+ Args.hrc = joltNoThrow(Args.offDst);
+ if (Args.hrc == S_OK)
+ return S_OK;
+ }
+
+ if (m_bstr)
+ m_bstr[cwcOld] = '\0';
+ return Args.hrc;
+}
+
+
+Bstr &Bstr::erase(size_t offStart /*= 0*/, size_t cwcLength /*= RTSTR_MAX*/) RT_NOEXCEPT
+{
+ size_t cwc = length();
+ if (offStart < cwc)
+ {
+ if (cwcLength >= cwc - offStart)
+ {
+ if (!offStart)
+ cleanup();
+ else
+ {
+ /* Trail removal, nothing to move. */
+ m_bstr[offStart] = '\0';
+ joltNoThrow(offStart); /* not entirely optimal... */
+ }
+ }
+ else if (cwcLength > 0)
+ {
+ /* Pull up the tail to offStart. */
+ size_t cwcAfter = cwc - offStart - cwcLength;
+ memmove(&m_bstr[offStart], &m_bstr[offStart + cwcLength], cwcAfter * sizeof(*m_bstr));
+ cwc -= cwcLength;
+ m_bstr[cwc] = '\0';
+ joltNoThrow(cwc); /* not entirely optimal... */
+ }
+ }
+ return *this;
+}
+
+
+void Bstr::cleanup()
+{
+ if (m_bstr)
+ {
+ ::SysFreeString(m_bstr);
+ m_bstr = NULL;
+ }
+}
+
+
+void Bstr::copyFrom(const OLECHAR *a_bstrSrc)
+{
+ if (a_bstrSrc && *a_bstrSrc)
+ {
+ m_bstr = ::SysAllocString(a_bstrSrc);
+#ifdef RT_EXCEPTIONS_ENABLED
+ if (RT_LIKELY(m_bstr))
+ { /* likely */ }
+ else
+ throw std::bad_alloc();
+#else
+ Assert(m_bstr);
+#endif
+ }
+ else
+ m_bstr = NULL;
+}
+
+
+void Bstr::cleanupAndCopyFrom(const OLECHAR *a_bstrSrc)
+{
+ cleanup();
+ copyFrom(a_bstrSrc);
+}
+
+
+HRESULT Bstr::cleanupAndCopyFromEx(const OLECHAR *a_bstrSrc) RT_NOEXCEPT
+{
+ cleanup();
+
+ if (a_bstrSrc && *a_bstrSrc)
+ {
+ m_bstr = ::SysAllocString(a_bstrSrc);
+ if (RT_LIKELY(m_bstr))
+ { /* likely */ }
+ else
+ return E_OUTOFMEMORY;
+ }
+ else
+ m_bstr = NULL;
+ return S_OK;
+}
+
+
+
+/*********************************************************************************************************************************
+* Utf8Str Implementation *
+*********************************************************************************************************************************/
+
+/* static */
+const Utf8Str Utf8Str::Empty; /* default ctor is OK */
+
+#if defined(VBOX_WITH_XPCOM)
+void Utf8Str::cloneTo(char **pstr) const
+{
+ size_t cb = length() + 1;
+ *pstr = (char *)nsMemory::Alloc(cb);
+ if (RT_LIKELY(*pstr))
+ memcpy(*pstr, c_str(), cb);
+ else
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw std::bad_alloc();
+#else
+ AssertFailed();
+#endif
+}
+
+HRESULT Utf8Str::cloneToEx(char **pstr) const
+{
+ size_t cb = length() + 1;
+ *pstr = (char *)nsMemory::Alloc(cb);
+ if (RT_LIKELY(*pstr))
+ {
+ memcpy(*pstr, c_str(), cb);
+ return S_OK;
+ }
+ return E_OUTOFMEMORY;
+}
+#endif
+
+HRESULT Utf8Str::cloneToEx(BSTR *pbstr) const RT_NOEXCEPT
+{
+ if (!pbstr)
+ return S_OK;
+ Bstr bstr;
+ HRESULT hrc = bstr.assignEx(*this);
+ if (SUCCEEDED(hrc))
+ hrc = bstr.detachToEx(pbstr);
+ return hrc;
+}
+
+Utf8Str& Utf8Str::stripTrailingSlash()
+{
+ if (length())
+ {
+ ::RTPathStripTrailingSlash(m_psz);
+ jolt();
+ }
+ return *this;
+}
+
+Utf8Str& Utf8Str::stripFilename()
+{
+ if (length())
+ {
+ RTPathStripFilename(m_psz);
+ jolt();
+ }
+ return *this;
+}
+
+Utf8Str& Utf8Str::stripPath()
+{
+ if (length())
+ {
+ char *pszName = ::RTPathFilename(m_psz);
+ if (pszName)
+ {
+ size_t cchName = length() - (pszName - m_psz);
+ memmove(m_psz, pszName, cchName + 1);
+ jolt();
+ }
+ else
+ cleanup();
+ }
+ return *this;
+}
+
+Utf8Str& Utf8Str::stripSuffix()
+{
+ if (length())
+ {
+ RTPathStripSuffix(m_psz);
+ jolt();
+ }
+ return *this;
+}
+
+size_t Utf8Str::parseKeyValue(Utf8Str &a_rKey, Utf8Str &a_rValue, size_t a_offStart /* = 0*/,
+ const Utf8Str &a_rPairSeparator /*= ","*/, const Utf8Str &a_rKeyValueSeparator /*= "="*/) const
+{
+ /* Find the end of the next pair, skipping empty pairs.
+ Note! The skipping allows us to pass the return value of a parseKeyValue()
+ call as offStart to the next call. */
+ size_t offEnd;
+ while ( a_offStart == (offEnd = find(&a_rPairSeparator, a_offStart))
+ && offEnd != npos)
+ a_offStart++;
+
+ /* Look for a key/value separator before the end of the pair.
+ ASSUMES npos value returned by find when the substring is not found is
+ really high. */
+ size_t offKeyValueSep = find(&a_rKeyValueSeparator, a_offStart);
+ if (offKeyValueSep < offEnd)
+ {
+ a_rKey = substr(a_offStart, offKeyValueSep - a_offStart);
+ if (offEnd == npos)
+ offEnd = m_cch; /* No confusing npos when returning strings. */
+ a_rValue = substr(offKeyValueSep + 1, offEnd - offKeyValueSep - 1);
+ }
+ else
+ {
+ a_rKey.setNull();
+ a_rValue.setNull();
+ }
+
+ return offEnd;
+}
+
+/**
+ * Internal function used in Utf8Str copy constructors and assignment when
+ * copying from a UTF-16 string.
+ *
+ * As with the RTCString::copyFrom() variants, this unconditionally sets the
+ * members to a copy of the given other strings and makes no assumptions about
+ * previous contents. This can therefore be used both in copy constructors,
+ * when member variables have no defined value, and in assignments after having
+ * called cleanup().
+ *
+ * This variant converts from a UTF-16 string, most probably from
+ * a Bstr assignment.
+ *
+ * @param a_pbstr The source string. The caller guarantees that this
+ * is valid UTF-16.
+ * @param a_cwcMax The number of characters to be copied. If set to RTSTR_MAX,
+ * the entire string will be copied.
+ *
+ * @sa RTCString::copyFromN
+ */
+void Utf8Str::copyFrom(CBSTR a_pbstr, size_t a_cwcMax)
+{
+ if (a_pbstr && *a_pbstr)
+ {
+ int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr,
+ a_cwcMax, // size_t cwcString: translate entire string
+ &m_psz, // char **ppsz: output buffer
+ 0, // size_t cch: if 0, func allocates buffer in *ppsz
+ &m_cch); // size_t *pcch: receives the size of the output string, excluding the terminator.
+ if (RT_SUCCESS(vrc))
+ m_cbAllocated = m_cch + 1;
+ else
+ {
+ if ( vrc != VERR_NO_STR_MEMORY
+ && vrc != VERR_NO_MEMORY)
+ {
+ /* ASSUME: input is valid Utf-16. Fake out of memory error. */
+ AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len(a_pbstr) * sizeof(RTUTF16), a_pbstr));
+ }
+
+ m_cch = 0;
+ m_cbAllocated = 0;
+ m_psz = NULL;
+
+#ifdef RT_EXCEPTIONS_ENABLED
+ throw std::bad_alloc();
+#else
+ AssertFailed();
+#endif
+ }
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+ m_psz = NULL;
+ }
+}
+
+/**
+ * A variant of Utf8Str::copyFrom that does not throw any exceptions but returns
+ * E_OUTOFMEMORY instead.
+ *
+ * @param a_pbstr The source string.
+ * @returns S_OK or E_OUTOFMEMORY.
+ */
+HRESULT Utf8Str::copyFromEx(CBSTR a_pbstr)
+{
+ if (a_pbstr && *a_pbstr)
+ {
+ int vrc = RTUtf16ToUtf8Ex((PCRTUTF16)a_pbstr,
+ RTSTR_MAX, // size_t cwcString: translate entire string
+ &m_psz, // char **ppsz: output buffer
+ 0, // size_t cch: if 0, func allocates buffer in *ppsz
+ &m_cch); // size_t *pcch: receives the size of the output string, excluding the terminator.
+ if (RT_SUCCESS(vrc))
+ m_cbAllocated = m_cch + 1;
+ else
+ {
+ if ( vrc != VERR_NO_STR_MEMORY
+ && vrc != VERR_NO_MEMORY)
+ {
+ /* ASSUME: input is valid Utf-16. Fake out of memory error. */
+ AssertLogRelMsgFailed(("%Rrc %.*Rhxs\n", vrc, RTUtf16Len(a_pbstr) * sizeof(RTUTF16), a_pbstr));
+ }
+
+ m_cch = 0;
+ m_cbAllocated = 0;
+ m_psz = NULL;
+
+ return E_OUTOFMEMORY;
+ }
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+ m_psz = NULL;
+ }
+ return S_OK;
+}
+
+
+/**
+ * A variant of Utf8Str::copyFromN that does not throw any exceptions but
+ * returns E_OUTOFMEMORY instead.
+ *
+ * @param a_pcszSrc The source string.
+ * @param a_offSrc Start offset to copy from.
+ * @param a_cchSrc How much to copy
+ * @returns S_OK or E_OUTOFMEMORY.
+ *
+ * @remarks This calls cleanup() first, so the caller doesn't have to. (Saves
+ * code space.)
+ */
+HRESULT Utf8Str::copyFromExNComRC(const char *a_pcszSrc, size_t a_offSrc, size_t a_cchSrc)
+{
+ Assert(!a_cchSrc || !m_psz || (uintptr_t)&a_pcszSrc[a_offSrc] - (uintptr_t)m_psz >= (uintptr_t)m_cbAllocated);
+ cleanup();
+ if (a_cchSrc)
+ {
+ m_psz = RTStrAlloc(a_cchSrc + 1);
+ if (RT_LIKELY(m_psz))
+ {
+ m_cch = a_cchSrc;
+ m_cbAllocated = a_cchSrc + 1;
+ memcpy(m_psz, a_pcszSrc + a_offSrc, a_cchSrc);
+ m_psz[a_cchSrc] = '\0';
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+ return E_OUTOFMEMORY;
+ }
+ }
+ else
+ {
+ m_cch = 0;
+ m_cbAllocated = 0;
+ m_psz = NULL;
+ }
+ return S_OK;
+}
+
+} /* namespace com */
diff --git a/src/VBox/Main/glue/tests/Makefile b/src/VBox/Main/glue/tests/Makefile
new file mode 100644
index 00000000..84d670f1
--- /dev/null
+++ b/src/VBox/Main/glue/tests/Makefile
@@ -0,0 +1,81 @@
+## @file
+# Makefile for a sample/testcase using the 'glue' Java API bindings
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+#
+# User serviceable parts: adjust the following lines appropriately
+#
+
+# The host OS: linux, solaris, win, darwin, freebsd
+HOSTOS=linux
+# Absolute (!) path to the VirtualBox install directory
+VBOX_BIN = /opt/VirtualBox
+# Path to the sdk directory of the unpacked VirtualBox SDK
+VBOX_SDK = ../../..
+# On Windows, if you want to use the COM API: install directory of Jacob lib
+JACOB_DIR =
+# Extra classpath entries needed for compiling/running the sample
+CLASSPATH =
+# Java compiler to use
+JAVAC = javac
+# Java VM to use
+JAVA = java
+
+
+#
+# No changes should be necessary after this point.
+#
+
+ifeq ($(HOSTOS),win)
+ JACOB_JAR=$(JACOB_DIR)/jacob.jar
+ VBOX_JAR=$(VBOX_SDK)/bindings/mscom/java/vboxjmscom.jar
+ SEP=\;
+ JAVA_COM_ARGS += -Djava.library.path=$(JACOB_DIR)
+ CLASSPATH += $(JACOB_JAR)$(SEP)
+else
+ VBOX_JAR=$(VBOX_SDK)/bindings/xpcom/java/vboxjxpcom.jar
+ SEP=:
+ JAVA_COM_ARGS += -Dvbox.home=$(VBOX_BIN)
+endif
+
+VBOX_JAR_WS=$(VBOX_SDK)/bindings/webservice/java/jax-ws/vboxjws.jar
+
+
+all: ws/TestVBox.class com/TestVBox.class
+
+test: ws/test-TestVBox com/test-TestVBox
+
+com/TestVBox.class:
+ @mkdir com 2>/dev/null || true
+ $(JAVAC) -d com -cp $(VBOX_JAR)$(SEP)$(CLASSPATH) TestVBox.java
+
+com/test-TestVBox: com/TestVBox.class
+ $(JAVA) $(JAVA_COM_ARGS) -cp com$(SEP)$(VBOX_JAR)$(SEP)$(CLASSPATH) TestVBox
+
+ws/TestVBox.class:
+ @mkdir ws 2>/dev/null || true
+ $(JAVAC) -d ws -cp $(VBOX_JAR_WS)$(SEP)$(CLASSPATH) TestVBox.java
+
+ws/test-TestVBox: ws/TestVBox.class
+ $(JAVA) $(JAVA_WS_ARGS) -cp ws$(SEP)$(VBOX_JAR_WS)$(SEP)$(CLASSPATH) TestVBox -w -url http://localhost:18083/
diff --git a/src/VBox/Main/glue/tests/TestVBox.java b/src/VBox/Main/glue/tests/TestVBox.java
new file mode 100644
index 00000000..37bce704
--- /dev/null
+++ b/src/VBox/Main/glue/tests/TestVBox.java
@@ -0,0 +1,310 @@
+/* $Id: TestVBox.java $ */
+/*! file
+ * Small sample/testcase which demonstrates that the same source code can
+ * be used to connect to the webservice and (XP)COM APIs.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+import org.virtualbox_6_2.*;
+import java.util.List;
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.math.BigInteger;
+
+public class TestVBox
+{
+ static void processEvent(IEvent ev)
+ {
+ System.out.println("got event: " + ev);
+ VBoxEventType type = ev.getType();
+ System.out.println("type = " + type);
+ switch (type)
+ {
+ case OnMachineStateChanged:
+ {
+ IMachineStateChangedEvent mcse = IMachineStateChangedEvent.queryInterface(ev);
+ if (mcse == null)
+ System.out.println("Cannot query an interface");
+ else
+ System.out.println("mid=" + mcse.getMachineId());
+ break;
+ }
+ }
+ }
+
+ static class EventHandler
+ {
+ EventHandler() {}
+ public void handleEvent(IEvent ev)
+ {
+ try {
+ processEvent(ev);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ }
+ }
+
+ static void testEvents(VirtualBoxManager mgr, IEventSource es)
+ {
+ // active mode for Java doesn't fully work yet, and using passive
+ // is more portable (the only mode for MSCOM and WS) and thus generally
+ // recommended
+ IEventListener listener = es.createListener();
+
+ es.registerListener(listener, Arrays.asList(VBoxEventType.Any), false);
+
+ try {
+ for (int i = 0; i < 50; i++)
+ {
+ System.out.print(".");
+ IEvent ev = es.getEvent(listener, 500);
+ if (ev != null)
+ {
+ processEvent(ev);
+ es.eventProcessed(listener, ev);
+ }
+ // process system event queue
+ mgr.waitForEvents(0);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ es.unregisterListener(listener);
+ }
+
+ static void testEnumeration(VirtualBoxManager mgr, IVirtualBox vbox)
+ {
+ List<IMachine> machs = vbox.getMachines();
+ for (IMachine m : machs)
+ {
+ String name;
+ Long ram = 0L;
+ boolean hwvirtEnabled = false, hwvirtNestedPaging = false;
+ boolean paeEnabled = false;
+ boolean inaccessible = false;
+ try
+ {
+ name = m.getName();
+ ram = m.getMemorySize();
+ hwvirtEnabled = m.getHWVirtExProperty(HWVirtExPropertyType.Enabled);
+ hwvirtNestedPaging = m.getHWVirtExProperty(HWVirtExPropertyType.NestedPaging);
+ paeEnabled = m.getCPUProperty(CPUPropertyType.PAE);
+ String osType = m.getOSTypeId();
+ IGuestOSType foo = vbox.getGuestOSType(osType);
+ }
+ catch (VBoxException e)
+ {
+ name = "<inaccessible>";
+ inaccessible = true;
+ }
+ System.out.println("VM name: " + name);
+ if (!inaccessible)
+ {
+ System.out.println(" RAM size: " + ram + "MB"
+ + ", HWVirt: " + hwvirtEnabled
+ + ", Nested Paging: " + hwvirtNestedPaging
+ + ", PAE: " + paeEnabled);
+ }
+ }
+ // process system event queue
+ mgr.waitForEvents(0);
+ }
+
+ static boolean progressBar(VirtualBoxManager mgr, IProgress p, long waitMillis)
+ {
+ long end = System.currentTimeMillis() + waitMillis;
+ while (!p.getCompleted())
+ {
+ // process system event queue
+ mgr.waitForEvents(0);
+ // wait for completion of the task, but at most 200 msecs
+ p.waitForCompletion(200);
+ if (System.currentTimeMillis() >= end)
+ return false;
+ }
+ return true;
+ }
+
+ static void testStart(VirtualBoxManager mgr, IVirtualBox vbox)
+ {
+ IMachine m = vbox.getMachines().get(0);
+ String name = m.getName();
+ System.out.println("\nAttempting to start VM '" + name + "'");
+
+ ISession session = mgr.getSessionObject();
+ ArrayList<String> env = new ArrayList<String>();
+ IProgress p = m.launchVMProcess(session, "gui", env);
+ progressBar(mgr, p, 10000);
+ session.unlockMachine();
+ // process system event queue
+ mgr.waitForEvents(0);
+ }
+
+ static void testMultiServer()
+ {
+ VirtualBoxManager mgr1 = VirtualBoxManager.createInstance(null);
+ VirtualBoxManager mgr2 = VirtualBoxManager.createInstance(null);
+
+ try {
+ mgr1.connect("http://i7:18083", "", "");
+ mgr2.connect("http://main:18083", "", "");
+
+ IMachine m1 = mgr1.getVBox().getMachines().get(0);
+ IMachine m2 = mgr2.getVBox().getMachines().get(0);
+ String name1 = m1.getName();
+ String name2 = m2.getName();
+ ISession session1 = mgr1.getSessionObject();
+ ISession session2 = mgr2.getSessionObject();
+ ArrayList<String> env = new ArrayList<String>();
+ IProgress p1 = m1.launchVMProcess(session1, "gui", env);
+ IProgress p2 = m2.launchVMProcess(session2, "gui", env);
+ progressBar(mgr1, p1, 10000);
+ progressBar(mgr2, p2, 10000);
+ session1.unlockMachine();
+ session2.unlockMachine();
+ // process system event queue
+ mgr1.waitForEvents(0);
+ mgr2.waitForEvents(0);
+ } finally {
+ mgr1.cleanup();
+ mgr2.cleanup();
+ }
+ }
+
+ static void testReadLog(VirtualBoxManager mgr, IVirtualBox vbox)
+ {
+ IMachine m = vbox.getMachines().get(0);
+ long logNo = 0;
+ long off = 0;
+ long size = 16 * 1024;
+ while (true)
+ {
+ byte[] buf = m.readLog(logNo, off, size);
+ if (buf.length == 0)
+ break;
+ System.out.print(new String(buf));
+ off += buf.length;
+ }
+ // process system event queue
+ mgr.waitForEvents(0);
+ }
+
+ static void printErrorInfo(VBoxException e)
+ {
+ System.out.println("VBox error: " + e.getMessage());
+ System.out.println("Error cause message: " + e.getCause());
+ System.out.println("Overall result code: " + Integer.toHexString(e.getResultCode()));
+ int i = 1;
+ for (IVirtualBoxErrorInfo ei = e.getVirtualBoxErrorInfo(); ei != null; ei = ei.getNext(), i++)
+ {
+ System.out.println("Detail information #" + i);
+ System.out.println("Error mesage: " + ei.getText());
+ System.out.println("Result code: " + Integer.toHexString(ei.getResultCode()));
+ // optional, usually provides little additional information:
+ System.out.println("Component: " + ei.getComponent());
+ System.out.println("Interface ID: " + ei.getInterfaceID());
+ }
+ }
+
+
+ public static void main(String[] args)
+ {
+ VirtualBoxManager mgr = VirtualBoxManager.createInstance(null);
+
+ boolean ws = false;
+ String url = null;
+ String user = null;
+ String passwd = null;
+
+ for (int i = 0; i < args.length; i++)
+ {
+ if (args[i].equals("-w"))
+ ws = true;
+ else if (args[i].equals("-url"))
+ url = args[++i];
+ else if (args[i].equals("-user"))
+ user = args[++i];
+ else if (args[i].equals("-passwd"))
+ passwd = args[++i];
+ }
+
+ if (ws)
+ {
+ try {
+ mgr.connect(url, user, passwd);
+ } catch (VBoxException e) {
+ e.printStackTrace();
+ System.out.println("Cannot connect, start webserver first!");
+ }
+ }
+
+ try
+ {
+ IVirtualBox vbox = mgr.getVBox();
+ if (vbox != null)
+ {
+ System.out.println("VirtualBox version: " + vbox.getVersion() + "\n");
+ testEnumeration(mgr, vbox);
+ testReadLog(mgr, vbox);
+ testStart(mgr, vbox);
+ testEvents(mgr, vbox.getEventSource());
+
+ System.out.println("done, press Enter...");
+ int ch = System.in.read();
+ }
+ }
+ catch (VBoxException e)
+ {
+ printErrorInfo(e);
+ System.out.println("Java stack trace:");
+ e.printStackTrace();
+ }
+ catch (RuntimeException e)
+ {
+ System.out.println("Runtime error: " + e.getMessage());
+ e.printStackTrace();
+ }
+ catch (java.io.IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ // process system event queue
+ mgr.waitForEvents(0);
+ if (ws)
+ {
+ try {
+ mgr.disconnect();
+ } catch (VBoxException e) {
+ e.printStackTrace();
+ }
+ }
+
+ mgr.cleanup();
+
+ }
+
+}
diff --git a/src/VBox/Main/glue/tests/TestVBoxNATEngine.java b/src/VBox/Main/glue/tests/TestVBoxNATEngine.java
new file mode 100644
index 00000000..a54f163c
--- /dev/null
+++ b/src/VBox/Main/glue/tests/TestVBoxNATEngine.java
@@ -0,0 +1,206 @@
+/* $Id: TestVBoxNATEngine.java $ */
+/*!file
+ * Small sample/testcase which demonstrates that the same source code can
+ * be used to connect to the webservice and (XP)COM APIs.
+ */
+
+/*
+ * 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+import org.virtualbox_5_0.*;
+import java.util.List;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.math.BigInteger;
+
+public class TestVBoxNATEngine
+{
+ void testEnumeration(VirtualBoxManager mgr, IVirtualBox vbox, IMachine vm)
+ {
+ String name;
+ boolean inaccessible = false;
+ /* different chipsets might have different number of attachments */
+ ChipsetType chipsetType = vm.getChipsetType();
+ INetworkAdapter adapters[] =
+ new INetworkAdapter[
+ vbox.getSystemProperties().getMaxNetworkAdapters(chipsetType).intValue()];
+
+ try
+ {
+ name = vm.getName();
+ /*
+ * Dump adapters and if it's got NAT attachment
+ * dump it settings
+ */
+
+ for (int nidx = 0; nidx < adapters.length; ++nidx)
+ {
+ /* select available and NATs only. */
+ adapters[nidx] = vm.getNetworkAdapter(new Long(nidx));
+ INetworkAdapter n = adapters[nidx];
+
+ if (n == null)
+ continue;
+ NetworkAttachmentType attachmentType = n.getAttachmentType();
+ if (attachmentType.equals(NetworkAttachmentType.NAT))
+ {
+ INATEngine nat = n.getNATEngine();
+ List<String> portForward = nat.getRedirects();
+ String pf = null;
+ Iterator<String> itPortForward = portForward.iterator();
+ for (;itPortForward.hasNext();)
+ {
+ pf = itPortForward.next();
+ System.out.println(name + ":NIC" + n.getSlot() /* name:NIC<slot number>*/
+ + " pf: " + pf); /* port-forward rule */
+ }
+ if (pf != null)
+ {
+ String pfAttributes[] = pf.split(",");
+ /* name,proto,hostip,host,hostport,guestip,guestport */
+ nat.removeRedirect(pfAttributes[0]);
+ nat.addRedirect("",
+ NATProtocol.fromValue(new Integer(pfAttributes[1]).longValue()),
+ pfAttributes[2],
+ new Integer(
+ new Integer(pfAttributes[3]).intValue() + 1),
+ pfAttributes[4],
+ new Integer(pfAttributes[5]));
+ }
+
+ }
+ }
+
+ }
+ catch (VBoxException e)
+ {
+ name = "<inaccessible>";
+ inaccessible = true;
+ }
+
+ // process system event queue
+ mgr.waitForEvents(0);
+ }
+
+ static void testStart(VirtualBoxManager mgr, IVirtualBox vbox, IMachine vm)
+ {
+ System.out.println("\nAttempting to start VM '" + vm.getName() + "'");
+ mgr.startVm(vm.getName(), null, 7000);
+ // process system event queue
+ mgr.waitForEvents(0);
+ }
+
+ public TestVBoxNATEngine(String[] args)
+ {
+ VirtualBoxManager mgr = VirtualBoxManager.createInstance(null);
+
+ boolean ws = false;
+ String url = null;
+ String user = null;
+ String passwd = null;
+ String vmname = null;
+ IMachine vm = null;
+
+ for (int i = 0; i<args.length; i++)
+ {
+ if ("-w".equals(args[i]))
+ ws = true;
+ else if ("-url".equals(args[i]))
+ url = args[++i];
+ else if ("-user".equals(args[i]))
+ user = args[++i];
+ else if ("-passwd".equals(args[i]))
+ passwd = args[++i];
+ else if ("-vm".equals(args[i]))
+ vmname = args[++i];
+ }
+
+ if (ws)
+ {
+ try {
+ mgr.connect(url, user, passwd);
+ } catch (VBoxException e) {
+ e.printStackTrace();
+ System.out.println("Cannot connect, start webserver first!");
+ }
+ }
+
+ try
+ {
+ IVirtualBox vbox = mgr.getVBox();
+ if (vbox != null)
+ {
+ if (vmname != null)
+ {
+ for (IMachine m:vbox.getMachines())
+ {
+ if (m.getName().equals(vmname))
+ {
+ vm = m;
+ break;
+ }
+ }
+
+ }
+ else
+ vm = vbox.getMachines().get(0);
+ System.out.println("VirtualBox version: " + vbox.getVersion() + "\n");
+ if (vm != null)
+ {
+ testEnumeration(mgr, vbox, vm);
+ testStart(mgr, vbox, vm);
+ }
+ System.out.println("done, press Enter...");
+ int ch = System.in.read();
+ }
+ }
+ catch (VBoxException e)
+ {
+ System.out.println("VBox error: "+e.getMessage()+" original="+e.getWrapped());
+ e.printStackTrace();
+ }
+ catch (java.io.IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ // process system event queue
+ mgr.waitForEvents(0);
+
+ if (ws)
+ {
+ try {
+ mgr.disconnect();
+ } catch (VBoxException e) {
+ e.printStackTrace();
+ }
+ }
+ /* cleanup do the disconnect */
+ mgr.cleanup();
+
+ }
+ public static void main(String[] args)
+ {
+ new TestVBoxNATEngine(args);
+ }
+
+}
diff --git a/src/VBox/Main/glue/vbox-err-consts.sed b/src/VBox/Main/glue/vbox-err-consts.sed
new file mode 100644
index 00000000..2917d617
--- /dev/null
+++ b/src/VBox/Main/glue/vbox-err-consts.sed
@@ -0,0 +1,59 @@
+# $Id: vbox-err-consts.sed $
+## @file
+# IPRT - SED script for converting */err.h to a python dictionary.
+#
+
+#
+# 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>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+# Header and footer text:
+1i <text>
+$a </text>
+
+# Handle text inside the markers.
+/SED-START/,/SED-END/{
+
+# if (#define) goto defines
+/^[[:space:]]*#[[:space:]]*define/b defines
+
+}
+
+# Everything else is deleted!
+:delete
+d
+b end
+
+
+##
+# Convert the defines
+:defines
+
+/^[[:space:]]*#[[:space:]]*define[[:space:]]*\([[:alnum:]_]*\)[[:space:](]*\([-+]*[[:space:]]*[[:digit:]][[:digit:]]*\)[[:space:])]*$/b define_okay
+b delete
+:define_okay
+s/^[[:space:]]*#[[:space:]]*define[[:space:]]*\([[:alnum:]_]*\)[[:space:](]*\([-+]*[[:space:]]*[[:digit:]][[:digit:]]*\)[[:space:])]*$/ '\1': \2,/
+
+b end
+
+
+# next expression
+:end
diff --git a/src/VBox/Main/glue/vboxapi.py b/src/VBox/Main/glue/vboxapi.py
new file mode 100755
index 00000000..fe78ce62
--- /dev/null
+++ b/src/VBox/Main/glue/vboxapi.py
@@ -0,0 +1,1294 @@
+# -*- coding: utf-8 -*-
+# $Id: vboxapi.py $
+"""
+VirtualBox Python API Glue.
+"""
+
+__copyright__ = \
+"""
+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
+"""
+__version__ = "$Revision: 155244 $"
+
+
+# Note! To set Python bitness on OSX use 'export VERSIONER_PYTHON_PREFER_32_BIT=yes'
+
+
+# Standard Python imports.
+import os
+import sys
+import traceback
+
+
+if sys.version_info >= (3, 0):
+ xrange = range
+ long = int
+
+#
+# Globals, environment and sys.path changes.
+#
+import platform
+VBoxBinDir = os.environ.get("VBOX_PROGRAM_PATH", None)
+VBoxSdkDir = os.environ.get("VBOX_SDK_PATH", None)
+
+if VBoxBinDir is None:
+ if platform.system() == 'Darwin':
+ VBoxBinDir = '/Applications/VirtualBox.app/Contents/MacOS'
+ else: # Will be set by the installer
+ VBoxBinDir = "%VBOX_INSTALL_PATH%"
+else:
+ VBoxBinDir = os.path.abspath(VBoxBinDir)
+
+if VBoxSdkDir is None:
+ if platform.system() == 'Darwin':
+ VBoxSdkDir = '/Applications/VirtualBox.app/Contents/MacOS/sdk'
+ else: # Will be set by the installer
+ VBoxSdkDir = "%VBOX_SDK_PATH%"
+else:
+ VBoxSdkDir = os.path.abspath(VBoxSdkDir)
+
+os.environ["VBOX_PROGRAM_PATH"] = VBoxBinDir
+os.environ["VBOX_SDK_PATH"] = VBoxSdkDir
+sys.path.append(VBoxBinDir)
+
+
+#
+# Import the generated VirtualBox constants.
+#
+from .VirtualBox_constants import VirtualBoxReflectionInfo
+
+
+class PerfCollector(object):
+ """ This class provides a wrapper over IPerformanceCollector in order to
+ get more 'pythonic' interface.
+
+ To begin collection of metrics use setup() method.
+
+ To get collected data use query() method.
+
+ It is possible to disable metric collection without changing collection
+ parameters with disable() method. The enable() method resumes metric
+ collection.
+ """
+
+ def __init__(self, mgr, vbox):
+ """ Initializes the instance.
+
+ """
+ self.mgr = mgr
+ self.isMscom = (mgr.type == 'MSCOM')
+ self.collector = vbox.performanceCollector
+
+ def setup(self, names, objects, period, nsamples):
+ """ Discards all previously collected values for the specified
+ metrics, sets the period of collection and the number of retained
+ samples, enables collection.
+ """
+ self.collector.setupMetrics(names, objects, period, nsamples)
+
+ def enable(self, names, objects):
+ """ Resumes metric collection for the specified metrics.
+ """
+ self.collector.enableMetrics(names, objects)
+
+ def disable(self, names, objects):
+ """ Suspends metric collection for the specified metrics.
+ """
+ self.collector.disableMetrics(names, objects)
+
+ def query(self, names, objects):
+ """ Retrieves collected metric values as well as some auxiliary
+ information. Returns an array of dictionaries, one dictionary per
+ metric. Each dictionary contains the following entries:
+ 'name': metric name
+ 'object': managed object this metric associated with
+ 'unit': unit of measurement
+ 'scale': divide 'values' by this number to get float numbers
+ 'values': collected data
+ 'values_as_string': pre-processed values ready for 'print' statement
+ """
+ # Get around the problem with input arrays returned in output
+ # parameters (see #3953) for MSCOM.
+ if self.isMscom:
+ (values, names, objects, names_out, objects_out, units, scales, sequence_numbers,
+ indices, lengths) = self.collector.queryMetricsData(names, objects)
+ else:
+ (values, names_out, objects_out, units, scales, sequence_numbers,
+ indices, lengths) = self.collector.queryMetricsData(names, objects)
+ out = []
+ for i in xrange(0, len(names_out)):
+ scale = int(scales[i])
+ if scale != 1:
+ fmt = '%.2f%s'
+ else:
+ fmt = '%d %s'
+ out.append({
+ 'name': str(names_out[i]),
+ 'object': str(objects_out[i]),
+ 'unit': str(units[i]),
+ 'scale': scale,
+ 'values': [int(values[j]) for j in xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))],
+ 'values_as_string': '[' + ', '.join([fmt % (int(values[j]) / scale, units[i]) for j in
+ xrange(int(indices[i]), int(indices[i]) + int(lengths[i]))]) + ']'
+ })
+ return out
+
+
+#
+# Attribute hacks.
+#
+def ComifyName(name):
+ return name[0].capitalize() + name[1:]
+
+
+## This is for saving the original DispatchBaseClass __getattr__ and __setattr__
+# method references.
+_g_dCOMForward = {
+ 'getattr': None,
+ 'setattr': None,
+}
+
+
+def _CustomGetAttr(self, sAttr):
+ """ Our getattr replacement for DispatchBaseClass. """
+ # Fastpath.
+ oRet = self.__class__.__dict__.get(sAttr)
+ if oRet is not None:
+ return oRet
+
+ # Try case-insensitivity workaround for class attributes (COM methods).
+ sAttrLower = sAttr.lower()
+ for k in list(self.__class__.__dict__.keys()):
+ if k.lower() == sAttrLower:
+ setattr(self.__class__, sAttr, self.__class__.__dict__[k])
+ return getattr(self, k)
+
+ # Slow path.
+ try:
+ return _g_dCOMForward['getattr'](self, ComifyName(sAttr))
+ except AttributeError:
+ return _g_dCOMForward['getattr'](self, sAttr)
+
+
+def _CustomSetAttr(self, sAttr, oValue):
+ """ Our setattr replacement for DispatchBaseClass. """
+ try:
+ return _g_dCOMForward['setattr'](self, ComifyName(sAttr), oValue)
+ except AttributeError:
+ return _g_dCOMForward['setattr'](self, sAttr, oValue)
+
+
+class PlatformBase(object):
+ """
+ Base class for the platform specific code.
+ """
+
+ def __init__(self, aoParams):
+ _ = aoParams
+
+ def getVirtualBox(self):
+ """
+ Gets a the IVirtualBox singleton.
+ """
+ return None
+
+ def getSessionObject(self):
+ """
+ Get a session object that can be used for opening machine sessions.
+
+ The oIVBox parameter is an getVirtualBox() return value, i.e. an
+ IVirtualBox reference.
+
+ See also openMachineSession.
+ """
+ return None
+
+ def getType(self):
+ """ Returns the platform type (class name sans 'Platform'). """
+ return None
+
+ def isRemote(self):
+ """
+ Returns True if remote (web services) and False if local (COM/XPCOM).
+ """
+ return False
+
+ def getArray(self, oInterface, sAttrib):
+ """
+ Retrives the value of the array attribute 'sAttrib' from
+ interface 'oInterface'.
+
+ This is for hiding platform specific differences in attributes
+ returning arrays.
+ """
+ _ = oInterface
+ _ = sAttrib
+ return None
+
+ def setArray(self, oInterface, sAttrib, aoArray):
+ """
+ Sets the value (aoArray) of the array attribute 'sAttrib' in
+ interface 'oInterface'.
+
+ This is for hiding platform specific differences in attributes
+ setting arrays.
+ """
+ _ = oInterface
+ _ = sAttrib
+ _ = aoArray
+ return None
+
+ def initPerThread(self):
+ """
+ Does backend specific initialization for the calling thread.
+ """
+ return True
+
+ def deinitPerThread(self):
+ """
+ Does backend specific uninitialization for the calling thread.
+ """
+ return True
+
+ def createListener(self, oImplClass, dArgs):
+ """
+ Instantiates and wraps an active event listener class so it can be
+ passed to an event source for registration.
+
+ oImplClass is a class (type, not instance) which implements
+ IEventListener.
+
+ dArgs is a dictionary with string indexed variables. This may be
+ modified by the method to pass platform specific parameters. Can
+ be None.
+
+ This currently only works on XPCOM. COM support is not possible due to
+ shortcuts taken in the COM bridge code, which is not under our control.
+ Use passive listeners for COM and web services.
+ """
+ _ = oImplClass
+ _ = dArgs
+ raise Exception("No active listeners for this platform")
+
+ def waitForEvents(self, cMsTimeout):
+ """
+ Wait for events to arrive and process them.
+
+ The timeout (cMsTimeout) is in milliseconds for how long to wait for
+ events to arrive. A negative value means waiting for ever, while 0
+ does not wait at all.
+
+ Returns 0 if events was processed.
+ Returns 1 if timed out or interrupted in some way.
+ Returns 2 on error (like not supported for web services).
+
+ Raises an exception if the calling thread is not the main thread (the one
+ that initialized VirtualBoxManager) or if the time isn't an integer.
+ """
+ _ = cMsTimeout
+ return 2
+
+ def interruptWaitEvents(self):
+ """
+ Interrupt a waitForEvents call.
+ This is normally called from a worker thread to wake up the main thread.
+
+ Returns True on success, False on failure.
+ """
+ return False
+
+ def deinit(self):
+ """
+ Unitializes the platform specific backend.
+ """
+ return None
+
+ def queryInterface(self, oIUnknown, sClassName):
+ """
+ IUnknown::QueryInterface wrapper.
+
+ oIUnknown is who to ask.
+ sClassName is the name of the interface we're asking for.
+ """
+ return None
+
+ #
+ # Error (exception) access methods.
+ #
+
+ def xcptGetStatus(self, oXcpt):
+ """
+ Returns the COM status code from the VBox API given exception.
+ """
+ return None
+
+ def xcptIsDeadInterface(self, oXcpt):
+ """
+ Returns True if the exception indicates that the interface is dead, False if not.
+ """
+ return False
+
+ def xcptIsEqual(self, oXcpt, hrStatus):
+ """
+ Checks if the exception oXcpt is equal to the COM/XPCOM status code
+ hrStatus.
+
+ The oXcpt parameter can be any kind of object, we'll just return True
+ if it doesn't behave like a our exception class.
+
+ Will not raise any exception as long as hrStatus and self are not bad.
+ """
+ try:
+ hrXcpt = self.xcptGetStatus(oXcpt)
+ except AttributeError:
+ return False
+ if hrXcpt == hrStatus:
+ return True
+
+ # Fudge for 32-bit signed int conversion.
+ if 0x7fffffff < hrStatus <= 0xffffffff and hrXcpt < 0:
+ if (hrStatus - 0x100000000) == hrXcpt:
+ return True
+ return False
+
+ def xcptGetMessage(self, oXcpt):
+ """
+ Returns the best error message found in the COM-like exception.
+ Returns None to fall back on xcptToString.
+ Raises exception if oXcpt isn't our kind of exception object.
+ """
+ return None
+
+ def xcptGetBaseXcpt(self):
+ """
+ Returns the base exception class.
+ """
+ return None
+
+ def xcptSetupConstants(self, oDst):
+ """
+ Copy/whatever all error constants onto oDst.
+ """
+ return oDst
+
+ @staticmethod
+ def xcptCopyErrorConstants(oDst, oSrc):
+ """
+ Copy everything that looks like error constants from oDst to oSrc.
+ """
+ for sAttr in dir(oSrc):
+ if sAttr[0].isupper() and (sAttr[1].isupper() or sAttr[1] == '_'):
+ oAttr = getattr(oSrc, sAttr)
+ if type(oAttr) is int:
+ setattr(oDst, sAttr, oAttr)
+ return oDst
+
+
+class PlatformMSCOM(PlatformBase):
+ """
+ Platform specific code for MS COM.
+ """
+
+ ## @name VirtualBox COM Typelib definitions (should be generate)
+ #
+ # @remarks Must be updated when the corresponding VirtualBox.xidl bits
+ # are changed. Fortunately this isn't very often.
+ # @{
+ VBOX_TLB_GUID = '{D7569351-1750-46F0-936E-BD127D5BC264}'
+ VBOX_TLB_LCID = 0
+ VBOX_TLB_MAJOR = 1
+ VBOX_TLB_MINOR = 3
+ ## @}
+
+ def __init__(self, dParams):
+ PlatformBase.__init__(self, dParams)
+
+ #
+ # Since the code runs on all platforms, we have to do a lot of
+ # importing here instead of at the top of the file where it's normally located.
+ #
+ from win32com import universal
+ from win32com.client import gencache, DispatchBaseClass
+ from win32com.client import constants, getevents
+ import win32com
+ import pythoncom
+ import win32api
+ import winerror
+ from win32con import DUPLICATE_SAME_ACCESS
+ from win32api import GetCurrentThread, GetCurrentThreadId, DuplicateHandle, GetCurrentProcess
+ import threading
+
+ self.winerror = winerror
+
+ # Setup client impersonation in COM calls.
+ try:
+ pythoncom.CoInitializeSecurity(None,
+ None,
+ None,
+ pythoncom.RPC_C_AUTHN_LEVEL_DEFAULT,
+ pythoncom.RPC_C_IMP_LEVEL_IMPERSONATE,
+ None,
+ pythoncom.EOAC_NONE,
+ None)
+ except:
+ _, oXcpt, _ = sys.exc_info();
+ if isinstance(oXcpt, pythoncom.com_error) and self.xcptGetStatus(oXcpt) == -2147417831: # RPC_E_TOO_LATE
+ print("Warning: CoInitializeSecurity was already called");
+ else:
+ print("Warning: CoInitializeSecurity failed: ", oXcpt);
+
+ # Remember this thread ID and get its handle so we can wait on it in waitForEvents().
+ self.tid = GetCurrentThreadId()
+ pid = GetCurrentProcess()
+ self.aoHandles = [DuplicateHandle(pid, GetCurrentThread(), pid, 0, 0, DUPLICATE_SAME_ACCESS),] # type: list[PyHANDLE]
+
+ # Hack the COM dispatcher base class so we can modify method and
+ # attribute names to match those in xpcom.
+ if _g_dCOMForward['setattr'] is None:
+ _g_dCOMForward['getattr'] = DispatchBaseClass.__dict__['__getattr__']
+ _g_dCOMForward['setattr'] = DispatchBaseClass.__dict__['__setattr__']
+ setattr(DispatchBaseClass, '__getattr__', _CustomGetAttr)
+ setattr(DispatchBaseClass, '__setattr__', _CustomSetAttr)
+
+ # Hack the exception base class so the users doesn't need to check for
+ # XPCOM or COM and do different things.
+ ## @todo
+
+ #
+ # Make sure the gencache is correct (we don't quite follow the COM
+ # versioning rules).
+ #
+ self.flushGenPyCache(win32com.client.gencache)
+ win32com.client.gencache.EnsureDispatch('VirtualBox.Session')
+ win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBox')
+ win32com.client.gencache.EnsureDispatch('VirtualBox.VirtualBoxClient')
+
+ self.oClient = None ##< instance of client used to support lifetime of VBoxSDS
+ self.oIntCv = threading.Condition()
+ self.fInterrupted = False
+
+ _ = dParams
+
+ def flushGenPyCache(self, oGenCache):
+ """
+ Flushes VBox related files in the win32com gen_py cache.
+
+ This is necessary since we don't follow the typelib versioning rules
+ that everyeone else seems to subscribe to.
+ """
+ #
+ # The EnsureModule method have broken validation code, it doesn't take
+ # typelib module directories into account. So we brute force them here.
+ # (It's possible the directory approach is from some older pywin
+ # version or the result of runnig makepy or gencache manually, but we
+ # need to cover it as well.)
+ #
+ sName = oGenCache.GetGeneratedFileName(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID,
+ self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR)
+ sGenPath = oGenCache.GetGeneratePath()
+ if len(sName) > 36 and len(sGenPath) > 5:
+ sTypelibPath = os.path.join(sGenPath, sName)
+ if os.path.isdir(sTypelibPath):
+ import shutil
+ shutil.rmtree(sTypelibPath, ignore_errors=True)
+
+ #
+ # Ensure that our typelib is valid.
+ #
+ return oGenCache.EnsureModule(self.VBOX_TLB_GUID, self.VBOX_TLB_LCID, self.VBOX_TLB_MAJOR, self.VBOX_TLB_MINOR)
+
+ def getSessionObject(self):
+ import win32com
+ from win32com.client import Dispatch
+ return win32com.client.Dispatch("VirtualBox.Session")
+
+ def getVirtualBox(self):
+ # Caching self.oClient is the trick for SDS. It allows to keep the
+ # VBoxSDS in the memory until the end of PlatformMSCOM lifetme.
+ if self.oClient is None:
+ import win32com
+ from win32com.client import Dispatch
+ self.oClient = win32com.client.Dispatch("VirtualBox.VirtualBoxClient")
+ return self.oClient.virtualBox
+
+ def getType(self):
+ return 'MSCOM'
+
+ def getArray(self, oInterface, sAttrib):
+ return oInterface.__getattr__(sAttrib)
+
+ def setArray(self, oInterface, sAttrib, aoArray):
+ #
+ # HACK ALERT!
+ #
+ # With pywin32 build 218, we're seeing type mismatch errors here for
+ # IGuestSession::environmentChanges (safearray of BSTRs). The Dispatch
+ # object (_oleobj_) seems to get some type conversion wrong and COM
+ # gets upset. So, we redo some of the dispatcher work here, picking
+ # the missing type information from the getter.
+ #
+ oOleObj = getattr(oInterface, '_oleobj_')
+ aPropMapGet = getattr(oInterface, '_prop_map_get_')
+ aPropMapPut = getattr(oInterface, '_prop_map_put_')
+ sComAttrib = sAttrib if sAttrib in aPropMapGet else ComifyName(sAttrib)
+ try:
+ aArgs, aDefaultArgs = aPropMapPut[sComAttrib]
+ aGetArgs = aPropMapGet[sComAttrib]
+ except KeyError: # fallback.
+ return oInterface.__setattr__(sAttrib, aoArray)
+
+ import pythoncom
+ oOleObj.InvokeTypes(aArgs[0], # dispid
+ aArgs[1], # LCID
+ aArgs[2], # DISPATCH_PROPERTYPUT
+ (pythoncom.VT_HRESULT, 0), # retType - or void?
+ (aGetArgs[2],), # argTypes - trick: we get the type from the getter.
+ aoArray,) # The array
+
+ def initPerThread(self):
+ import pythoncom
+ pythoncom.CoInitializeEx(0)
+
+ def deinitPerThread(self):
+ import pythoncom
+ pythoncom.CoUninitialize()
+
+ def createListener(self, oImplClass, dArgs):
+ if True:
+ raise Exception('no active listeners on Windows as PyGatewayBase::QueryInterface() '
+ 'returns new gateway objects all the time, thus breaking EventQueue '
+ 'assumptions about the listener interface pointer being constants between calls ')
+ # Did this code ever really work?
+ d = {}
+ d['BaseClass'] = oImplClass
+ d['dArgs'] = dArgs
+ d['tlb_guid'] = PlatformMSCOM.VBOX_TLB_GUID
+ d['tlb_major'] = PlatformMSCOM.VBOX_TLB_MAJOR
+ d['tlb_minor'] = PlatformMSCOM.VBOX_TLB_MINOR
+ str_ = ""
+ str_ += "import win32com.server.util\n"
+ str_ += "import pythoncom\n"
+
+ str_ += "class ListenerImpl(BaseClass):\n"
+ str_ += " _com_interfaces_ = ['IEventListener']\n"
+ str_ += " _typelib_guid_ = tlb_guid\n"
+ str_ += " _typelib_version_ = tlb_major, tlb_minor\n"
+ str_ += " _reg_clsctx_ = pythoncom.CLSCTX_INPROC_SERVER\n"
+ # Maybe we'd better implement Dynamic invoke policy, to be more flexible here
+ str_ += " _reg_policy_spec_ = 'win32com.server.policy.EventHandlerPolicy'\n"
+
+ # capitalized version of listener method
+ str_ += " HandleEvent=BaseClass.handleEvent\n"
+ str_ += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
+ str_ += "result = win32com.server.util.wrap(ListenerImpl())\n"
+ exec(str_, d, d)
+ return d['result']
+
+ def waitForEvents(self, timeout):
+ from win32api import GetCurrentThreadId
+ from win32event import INFINITE
+ from win32event import MsgWaitForMultipleObjects, QS_ALLINPUT, WAIT_TIMEOUT, WAIT_OBJECT_0
+ from pythoncom import PumpWaitingMessages
+ import types
+
+ if not isinstance(timeout, int):
+ raise TypeError("The timeout argument is not an integer")
+ if self.tid != GetCurrentThreadId():
+ raise Exception("wait for events from the same thread you inited!")
+
+ if timeout < 0:
+ cMsTimeout = INFINITE
+ else:
+ cMsTimeout = timeout
+ rc = MsgWaitForMultipleObjects(self.aoHandles, 0, cMsTimeout, QS_ALLINPUT)
+ if WAIT_OBJECT_0 <= rc < WAIT_OBJECT_0 + len(self.aoHandles):
+ # is it possible?
+ rc = 2
+ elif rc == WAIT_OBJECT_0 + len(self.aoHandles):
+ # Waiting messages
+ PumpWaitingMessages()
+ rc = 0
+ else:
+ # Timeout
+ rc = 1
+
+ # check for interruption
+ self.oIntCv.acquire()
+ if self.fInterrupted:
+ self.fInterrupted = False
+ rc = 1
+ self.oIntCv.release()
+
+ return rc
+
+ def interruptWaitEvents(self):
+ """
+ Basically a python implementation of NativeEventQueue::postEvent().
+
+ The magic value must be in sync with the C++ implementation or this
+ won't work.
+
+ Note that because of this method we cannot easily make use of a
+ non-visible Window to handle the message like we would like to do.
+ """
+ from win32api import PostThreadMessage
+ from win32con import WM_USER
+
+ self.oIntCv.acquire()
+ self.fInterrupted = True
+ self.oIntCv.release()
+ try:
+ PostThreadMessage(self.tid, WM_USER, None, 0xf241b819)
+ except:
+ return False
+ return True
+
+ def deinit(self):
+ for oHandle in self.aoHandles:
+ if oHandle is not None:
+ oHandle.Close();
+ self.oHandle = None;
+
+ del self.oClient;
+ self.oClient = None;
+
+ # This non-sense doesn't pair up with any pythoncom.CoInitialize[Ex].
+ # See @bugref{9037}.
+ #import pythoncom
+ #pythoncom.CoUninitialize()
+
+ def queryInterface(self, oIUnknown, sClassName):
+ from win32com.client import CastTo
+ return CastTo(oIUnknown, sClassName)
+
+ def xcptGetStatus(self, oXcpt):
+ # The DISP_E_EXCEPTION + excptinfo fun needs checking up, only
+ # empirical info on it so far.
+ hrXcpt = oXcpt.hresult
+ if hrXcpt == self.winerror.DISP_E_EXCEPTION:
+ try:
+ hrXcpt = oXcpt.excepinfo[5]
+ except:
+ pass
+ return hrXcpt
+
+ def xcptIsDeadInterface(self, oXcpt):
+ return self.xcptGetStatus(oXcpt) in [
+ 0x800706ba, -2147023174, # RPC_S_SERVER_UNAVAILABLE.
+ 0x800706be, -2147023170, # RPC_S_CALL_FAILED.
+ 0x800706bf, -2147023169, # RPC_S_CALL_FAILED_DNE.
+ 0x80010108, -2147417848, # RPC_E_DISCONNECTED.
+ 0x800706b5, -2147023179, # RPC_S_UNKNOWN_IF
+ ]
+
+ def xcptGetMessage(self, oXcpt):
+ if hasattr(oXcpt, 'excepinfo'):
+ try:
+ if len(oXcpt.excepinfo) >= 3:
+ sRet = oXcpt.excepinfo[2]
+ if len(sRet) > 0:
+ return sRet[0:]
+ except:
+ pass
+ if hasattr(oXcpt, 'strerror'):
+ try:
+ sRet = oXcpt.strerror
+ if len(sRet) > 0:
+ return sRet
+ except:
+ pass
+ return None
+
+ def xcptGetBaseXcpt(self):
+ import pythoncom
+
+ return pythoncom.com_error
+
+ def xcptSetupConstants(self, oDst):
+ import winerror
+
+ oDst = self.xcptCopyErrorConstants(oDst, winerror)
+
+ # XPCOM compatability constants.
+ oDst.NS_OK = oDst.S_OK
+ oDst.NS_ERROR_FAILURE = oDst.E_FAIL
+ oDst.NS_ERROR_ABORT = oDst.E_ABORT
+ oDst.NS_ERROR_NULL_POINTER = oDst.E_POINTER
+ oDst.NS_ERROR_NO_INTERFACE = oDst.E_NOINTERFACE
+ oDst.NS_ERROR_INVALID_ARG = oDst.E_INVALIDARG
+ oDst.NS_ERROR_OUT_OF_MEMORY = oDst.E_OUTOFMEMORY
+ oDst.NS_ERROR_NOT_IMPLEMENTED = oDst.E_NOTIMPL
+ oDst.NS_ERROR_UNEXPECTED = oDst.E_UNEXPECTED
+ return oDst
+
+
+class PlatformXPCOM(PlatformBase):
+ """
+ Platform specific code for XPCOM.
+ """
+
+ def __init__(self, dParams):
+ PlatformBase.__init__(self, dParams)
+ sys.path.append(VBoxSdkDir + '/bindings/xpcom/python/')
+ import xpcom.vboxxpcom
+ import xpcom
+ import xpcom.components
+ _ = dParams
+
+ def getSessionObject(self):
+ import xpcom.components
+ return xpcom.components.classes["@virtualbox.org/Session;1"].createInstance()
+
+ def getVirtualBox(self):
+ import xpcom.components
+ client = xpcom.components.classes["@virtualbox.org/VirtualBoxClient;1"].createInstance()
+ return client.virtualBox
+
+ def getType(self):
+ return 'XPCOM'
+
+ def getArray(self, oInterface, sAttrib):
+ return oInterface.__getattr__('get' + ComifyName(sAttrib))()
+
+ def setArray(self, oInterface, sAttrib, aoArray):
+ return oInterface.__getattr__('set' + ComifyName(sAttrib))(aoArray)
+
+ def initPerThread(self):
+ import xpcom
+ xpcom._xpcom.AttachThread()
+
+ def deinitPerThread(self):
+ import xpcom
+ xpcom._xpcom.DetachThread()
+
+ def createListener(self, oImplClass, dArgs):
+ d = {}
+ d['BaseClass'] = oImplClass
+ d['dArgs'] = dArgs
+ str = ""
+ str += "import xpcom.components\n"
+ str += "class ListenerImpl(BaseClass):\n"
+ str += " _com_interfaces_ = xpcom.components.interfaces.IEventListener\n"
+ str += " def __init__(self): BaseClass.__init__(self, dArgs)\n"
+ str += "result = ListenerImpl()\n"
+ exec(str, d, d)
+ return d['result']
+
+ def waitForEvents(self, timeout):
+ import xpcom
+ return xpcom._xpcom.WaitForEvents(timeout)
+
+ def interruptWaitEvents(self):
+ import xpcom
+ return xpcom._xpcom.InterruptWait()
+
+ def deinit(self):
+ import xpcom
+ xpcom._xpcom.DeinitCOM()
+
+ def queryInterface(self, oIUnknown, sClassName):
+ import xpcom.components
+ return oIUnknown.queryInterface(getattr(xpcom.components.interfaces, sClassName))
+
+ def xcptGetStatus(self, oXcpt):
+ return oXcpt.errno
+
+ def xcptIsDeadInterface(self, oXcpt):
+ return self.xcptGetStatus(oXcpt) in [
+ 0x80004004, -2147467260, # NS_ERROR_ABORT
+ 0x800706be, -2147023170, # NS_ERROR_CALL_FAILED (RPC_S_CALL_FAILED)
+ ]
+
+ def xcptGetMessage(self, oXcpt):
+ if hasattr(oXcpt, 'msg'):
+ try:
+ sRet = oXcpt.msg
+ if len(sRet) > 0:
+ return sRet
+ except:
+ pass
+ return None
+
+ def xcptGetBaseXcpt(self):
+ import xpcom
+ return xpcom.Exception
+
+ def xcptSetupConstants(self, oDst):
+ import xpcom
+ oDst = self.xcptCopyErrorConstants(oDst, xpcom.nsError)
+
+ # COM compatability constants.
+ oDst.E_ACCESSDENIED = -2147024891 # see VBox/com/defs.h
+ oDst.S_OK = oDst.NS_OK
+ oDst.E_FAIL = oDst.NS_ERROR_FAILURE
+ oDst.E_ABORT = oDst.NS_ERROR_ABORT
+ oDst.E_POINTER = oDst.NS_ERROR_NULL_POINTER
+ oDst.E_NOINTERFACE = oDst.NS_ERROR_NO_INTERFACE
+ oDst.E_INVALIDARG = oDst.NS_ERROR_INVALID_ARG
+ oDst.E_OUTOFMEMORY = oDst.NS_ERROR_OUT_OF_MEMORY
+ oDst.E_NOTIMPL = oDst.NS_ERROR_NOT_IMPLEMENTED
+ oDst.E_UNEXPECTED = oDst.NS_ERROR_UNEXPECTED
+ oDst.DISP_E_EXCEPTION = -2147352567 # For COM compatability only.
+ return oDst
+
+
+class PlatformWEBSERVICE(PlatformBase):
+ """
+ VirtualBox Web Services API specific code.
+ """
+
+ def __init__(self, dParams):
+ PlatformBase.__init__(self, dParams)
+ # Import web services stuff. Fix the sys.path the first time.
+ sWebServLib = os.path.join(VBoxSdkDir, 'bindings', 'webservice', 'python', 'lib')
+ if sWebServLib not in sys.path:
+ sys.path.append(sWebServLib)
+ import VirtualBox_wrappers
+ from VirtualBox_wrappers import IWebsessionManager2
+
+ # Initialize instance variables from parameters.
+ if dParams is not None:
+ self.user = dParams.get("user", "")
+ self.password = dParams.get("password", "")
+ self.url = dParams.get("url", "")
+ else:
+ self.user = ""
+ self.password = ""
+ self.url = None
+ self.vbox = None
+ self.wsmgr = None
+
+ #
+ # Base class overrides.
+ #
+
+ def getSessionObject(self):
+ return self.wsmgr.getSessionObject(self.vbox)
+
+ def getVirtualBox(self):
+ return self.connect(self.url, self.user, self.password)
+
+ def getType(self):
+ return 'WEBSERVICE'
+
+ def isRemote(self):
+ """ Returns True if remote VBox host, False if local. """
+ return True
+
+ def getArray(self, oInterface, sAttrib):
+ return oInterface.__getattr__(sAttrib)
+
+ def setArray(self, oInterface, sAttrib, aoArray):
+ return oInterface.__setattr__(sAttrib, aoArray)
+
+ def waitForEvents(self, timeout):
+ # Webservices cannot do that yet
+ return 2
+
+ def interruptWaitEvents(self, timeout):
+ # Webservices cannot do that yet
+ return False
+
+ def deinit(self):
+ try:
+ self.disconnect()
+ except:
+ pass
+
+ def queryInterface(self, oIUnknown, sClassName):
+ d = {}
+ d['oIUnknown'] = oIUnknown
+ str = ""
+ str += "from VirtualBox_wrappers import " + sClassName + "\n"
+ str += "result = " + sClassName + "(oIUnknown.mgr, oIUnknown.handle)\n"
+ # wrong, need to test if class indeed implements this interface
+ exec(str, d, d)
+ return d['result']
+
+ #
+ # Web service specific methods.
+ #
+
+ def connect(self, url, user, passwd):
+ if self.vbox is not None:
+ self.disconnect()
+ from VirtualBox_wrappers import IWebsessionManager2
+
+ if url is None:
+ url = ""
+ self.url = url
+ if user is None:
+ user = ""
+ self.user = user
+ if passwd is None:
+ passwd = ""
+ self.password = passwd
+ self.wsmgr = IWebsessionManager2(self.url)
+ self.vbox = self.wsmgr.logon(self.user, self.password)
+ if not self.vbox.handle:
+ raise Exception("cannot connect to '" + self.url + "' as '" + self.user + "'")
+ return self.vbox
+
+ def disconnect(self):
+ if self.vbox is not None and self.wsmgr is not None:
+ self.wsmgr.logoff(self.vbox)
+ self.vbox = None
+ self.wsmgr = None
+
+
+## The current (last) exception class.
+# This is reinitalized whenever VirtualBoxManager is called, so it will hold
+# the reference to the error exception class for the last platform/style that
+# was used. Most clients does talk to multiple VBox instance on different
+# platforms at the same time, so this should be sufficent for most uses and
+# be way simpler to use than VirtualBoxManager::oXcptClass.
+CurXcptClass = None
+
+
+class VirtualBoxManager(object):
+ """
+ VirtualBox API manager class.
+
+ The API users will have to instantiate this. If no parameters are given,
+ it will default to interface with the VirtualBox running on the local
+ machine. sStyle can be None (default), MSCOM, XPCOM or WEBSERVICES. Most
+ users will either be specifying None or WEBSERVICES.
+
+ The dPlatformParams is an optional dictionary for passing parameters to the
+ WEBSERVICE backend.
+ """
+
+ class Statuses(object):
+ def __init__(self):
+ pass
+
+ def __init__(self, sStyle=None, dPlatformParams=None):
+ if sStyle is None:
+ if sys.platform == 'win32':
+ sStyle = "MSCOM"
+ else:
+ sStyle = "XPCOM"
+ if sStyle == 'XPCOM':
+ self.platform = PlatformXPCOM(dPlatformParams)
+ elif sStyle == 'MSCOM':
+ self.platform = PlatformMSCOM(dPlatformParams)
+ elif sStyle == 'WEBSERVICE':
+ self.platform = PlatformWEBSERVICE(dPlatformParams)
+ else:
+ raise Exception('Unknown sStyle=%s' % (sStyle,))
+ self.style = sStyle
+ self.type = self.platform.getType()
+ self.remote = self.platform.isRemote()
+ ## VirtualBox API constants (for webservices, enums are symbolic).
+ self.constants = VirtualBoxReflectionInfo(sStyle == "WEBSERVICE")
+
+ ## Status constants.
+ self.statuses = self.platform.xcptSetupConstants(VirtualBoxManager.Statuses())
+ ## @todo Add VBOX_E_XXX to statuses? They're already in constants...
+ ## Dictionary for errToString, built on demand.
+ self._dErrorValToName = None
+
+ ## Dictionary for resolving enum values to names, two levels of dictionaries.
+ ## First level is indexed by enum name, the next by value.
+ self._ddEnumValueToName = {};
+
+ ## The exception class for the selected platform.
+ self.oXcptClass = self.platform.xcptGetBaseXcpt()
+ global CurXcptClass
+ CurXcptClass = self.oXcptClass
+
+ # Get the virtualbox singleton.
+ try:
+ vbox = self.platform.getVirtualBox()
+ except NameError:
+ print("Installation problem: check that appropriate libs in place")
+ traceback.print_exc()
+ raise
+ except Exception:
+ _, e, _ = sys.exc_info()
+ print("init exception: ", e)
+ traceback.print_exc()
+
+ def __del__(self):
+ self.deinit()
+
+ def getPythonApiRevision(self):
+ """
+ Returns a Python API revision number.
+ This will be incremented when features are added to this file.
+ """
+ return 3
+
+ @property
+ def mgr(self):
+ """
+ This used to be an attribute referring to a session manager class with
+ only one method called getSessionObject. It moved into this class.
+ """
+ return self
+
+ #
+ # Wrappers for self.platform methods.
+ #
+ def getVirtualBox(self):
+ """ See PlatformBase::getVirtualBox(). """
+ return self.platform.getVirtualBox()
+
+ def getSessionObject(self, oIVBox = None):
+ """ See PlatformBase::getSessionObject(). """
+ # ignore parameter which was never needed
+ _ = oIVBox
+ return self.platform.getSessionObject()
+
+ def getArray(self, oInterface, sAttrib):
+ """ See PlatformBase::getArray(). """
+ return self.platform.getArray(oInterface, sAttrib)
+
+ def setArray(self, oInterface, sAttrib, aoArray):
+ """ See PlatformBase::setArray(). """
+ return self.platform.setArray(oInterface, sAttrib, aoArray)
+
+ def createListener(self, oImplClass, dArgs=None):
+ """ See PlatformBase::createListener(). """
+ return self.platform.createListener(oImplClass, dArgs)
+
+ def waitForEvents(self, cMsTimeout):
+ """ See PlatformBase::waitForEvents(). """
+ return self.platform.waitForEvents(cMsTimeout)
+
+ def interruptWaitEvents(self):
+ """ See PlatformBase::interruptWaitEvents(). """
+ return self.platform.interruptWaitEvents()
+
+ def queryInterface(self, oIUnknown, sClassName):
+ """ See PlatformBase::queryInterface(). """
+ return self.platform.queryInterface(oIUnknown, sClassName)
+
+ #
+ # Init and uninit.
+ #
+ def initPerThread(self):
+ """ See PlatformBase::deinitPerThread(). """
+ self.platform.initPerThread()
+
+ def deinitPerThread(self):
+ """ See PlatformBase::deinitPerThread(). """
+ return self.platform.deinitPerThread()
+
+ def deinit(self):
+ """
+ For unitializing the manager.
+ Do not access it after calling this method.
+ """
+ if hasattr(self, "platform") and self.platform is not None:
+ self.platform.deinit()
+ self.platform = None
+ return True
+
+ #
+ # Utility methods.
+ #
+ def openMachineSession(self, oIMachine, fPermitSharing=True):
+ """
+ Attempts to open the a session to the machine.
+ Returns a session object on success.
+ Raises exception on failure.
+ """
+ oSession = self.getSessionObject()
+ if fPermitSharing:
+ eType = self.constants.LockType_Shared
+ else:
+ eType = self.constants.LockType_Write
+ oIMachine.lockMachine(oSession, eType)
+ return oSession
+
+ def closeMachineSession(self, oSession):
+ """
+ Closes a session opened by openMachineSession.
+ Ignores None parameters.
+ """
+ if oSession is not None:
+ oSession.unlockMachine()
+ return True
+
+ def getPerfCollector(self, oIVBox):
+ """
+ Returns a helper class (PerfCollector) for accessing performance
+ collector goodies. See PerfCollector for details.
+ """
+ return PerfCollector(self, oIVBox)
+
+ def getBinDir(self):
+ """
+ Returns the VirtualBox binary directory.
+ """
+ global VBoxBinDir
+ return VBoxBinDir
+
+ def getSdkDir(self):
+ """
+ Returns the VirtualBox SDK directory.
+ """
+ global VBoxSdkDir
+ return VBoxSdkDir
+
+ def getEnumValueName(self, sEnumTypeNm, oEnumValue, fTypePrefix = False):
+ """
+ Returns the name (string) for the corresponding enum value.
+ """
+ # Cache lookup:
+ dValueNames = self._ddEnumValueToName.get(sEnumTypeNm);
+ if dValueNames is not None:
+ sValueName = dValueNames.get(oEnumValue);
+ if sValueName:
+ return sValueName if not fTypePrefix else '%s_%s' % (sEnumTypeNm, sValueName);
+ else:
+ # Cache miss. Build the reverse lookup dictionary and add it to the cache:
+ dNamedValues = self.constants.all_values(sEnumTypeNm);
+ if len(dNamedValues) > 0:
+
+ dValueNames = dict();
+ for sName in dNamedValues:
+ dValueNames[dNamedValues[sName]] = sName;
+ self._ddEnumValueToName[sEnumTypeNm] = dValueNames;
+
+ # Lookup the value:
+ sValueName = dValueNames.get(oEnumValue);
+ if sValueName:
+ return sValueName if not fTypePrefix else '%s_%s' % (sEnumTypeNm, sValueName);
+
+ # Fallback:
+ return '%s_Unknown_%s' % (sEnumTypeNm, oEnumValue);
+
+ #
+ # Error code utilities.
+ #
+ ## @todo port to webservices!
+ def xcptGetStatus(self, oXcpt=None):
+ """
+ Gets the status code from an exception. If the exception parameter
+ isn't specified, the current exception is examined.
+ """
+ if oXcpt is None:
+ oXcpt = sys.exc_info()[1]
+ return self.platform.xcptGetStatus(oXcpt)
+
+ def xcptIsDeadInterface(self, oXcpt=None):
+ """
+ Returns True if the exception indicates that the interface is dead,
+ False if not. If the exception parameter isn't specified, the current
+ exception is examined.
+ """
+ if oXcpt is None:
+ oXcpt = sys.exc_info()[1]
+ return self.platform.xcptIsDeadInterface(oXcpt)
+
+ def xcptIsOurXcptKind(self, oXcpt=None):
+ """
+ Checks if the exception is one that could come from the VBox API. If
+ the exception parameter isn't specified, the current exception is
+ examined.
+ """
+ if self.oXcptClass is None: # @todo find the exception class for web services!
+ return False
+ if oXcpt is None:
+ oXcpt = sys.exc_info()[1]
+ return isinstance(oXcpt, self.oXcptClass)
+
+ def xcptIsEqual(self, oXcpt, hrStatus):
+ """
+ Checks if the exception oXcpt is equal to the COM/XPCOM status code
+ hrStatus.
+
+ The oXcpt parameter can be any kind of object, we'll just return True
+ if it doesn't behave like a our exception class. If it's None, we'll
+ query the current exception and examine that.
+
+ Will not raise any exception as long as hrStatus and self are not bad.
+ """
+ if oXcpt is None:
+ oXcpt = sys.exc_info()[1]
+ return self.platform.xcptIsEqual(oXcpt, hrStatus)
+
+ def xcptIsNotEqual(self, oXcpt, hrStatus):
+ """
+ Negated xcptIsEqual.
+ """
+ return not self.xcptIsEqual(oXcpt, hrStatus)
+
+ def xcptToString(self, hrStatusOrXcpt=None):
+ """
+ Converts the specified COM status code, or the status code of the
+ specified exception, to a C constant string. If the parameter isn't
+ specified (is None), the current exception is examined.
+ """
+
+ # Deal with exceptions.
+ if hrStatusOrXcpt is None or self.xcptIsOurXcptKind(hrStatusOrXcpt):
+ hrStatus = self.xcptGetStatus(hrStatusOrXcpt)
+ else:
+ hrStatus = hrStatusOrXcpt
+
+ # Build the dictionary on demand.
+ if self._dErrorValToName is None:
+ dErrorValToName = dict()
+ for sKey in dir(self.statuses):
+ if sKey[0].isupper():
+ oValue = getattr(self.statuses, sKey)
+ if isinstance(oValue, (int, long)):
+ dErrorValToName[int(oValue)] = sKey
+ # Always prefer the COM names (see aliasing in platform specific code):
+ for sKey in ('S_OK', 'E_FAIL', 'E_ABORT', 'E_POINTER', 'E_NOINTERFACE', 'E_INVALIDARG',
+ 'E_OUTOFMEMORY', 'E_NOTIMPL', 'E_UNEXPECTED',):
+ oValue = getattr(self.statuses, sKey, None)
+ if oValue is not None:
+ dErrorValToName[oValue] = sKey
+ self._dErrorValToName = dErrorValToName
+
+ # Do the lookup, falling back on formatting the status number.
+ try:
+ sStr = self._dErrorValToName[int(hrStatus)]
+ except KeyError:
+ hrLong = long(hrStatus)
+ sStr = '%#x (%d)' % (hrLong & 0xffffffff, hrLong)
+ return sStr
+
+ def xcptGetMessage(self, oXcpt=None):
+ """
+ Returns the best error message found in the COM-like exception. If the
+ exception parameter isn't specified, the current exception is examined.
+ """
+ if oXcpt is None:
+ oXcpt = sys.exc_info()[1]
+ sRet = self.platform.xcptGetMessage(oXcpt)
+ if sRet is None:
+ sRet = self.xcptToString(oXcpt)
+ return sRet
+
diff --git a/src/VBox/Main/glue/xpcom/Makefile.kup b/src/VBox/Main/glue/xpcom/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Main/glue/xpcom/Makefile.kup
diff --git a/src/VBox/Main/glue/xpcom/helpers.cpp b/src/VBox/Main/glue/xpcom/helpers.cpp
new file mode 100644
index 00000000..bdfa3591
--- /dev/null
+++ b/src/VBox/Main/glue/xpcom/helpers.cpp
@@ -0,0 +1,204 @@
+/* $Id: helpers.cpp $ */
+/** @file
+ * COM helper functions for 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>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "VBox/com/defs.h"
+#include <nsMemory.h>
+#include <iprt/assertcompile.h>
+#include <iprt/utf16.h>
+
+
+//
+// OLE Automation string APIs
+//
+
+// Note: on Windows, every BSTR stores its length in the
+// byte just before the pointer you get. If we do it like this,
+// the caller cannot just use nsMemory::Free() on our strings.
+// Therefore we'll try to implement it differently and hope that
+// we don't run into problems.
+
+/**
+ * Copies a string into a new memory block including the terminating UCS2 NULL.
+ *
+ * @param pwsz Source string to duplicate.
+ * @returns New BSTR string buffer.
+ */
+BSTR SysAllocString(const OLECHAR *pwszSrc)
+{
+ AssertCompile(sizeof(*pwszSrc) == sizeof(PRUnichar));
+ if (pwszSrc)
+ return SysAllocStringLen(pwszSrc, RTUtf16Len((PCRTUTF16)pwszSrc));
+ return NULL;
+}
+
+/**
+ * Duplicates an ANSI string into a BSTR / allocates a BSTR with a size given in
+ * bytes.
+ *
+ * No conversion is done.
+ *
+ * @param pszSrc Source string to copy, optional.
+ * @param cbSrcReq Length of the source string / memory request in bytes.
+ * @returns new BSTR string buffer, NULL on failure.
+ */
+BSTR SysAllocStringByteLen(char const *pszSrc, unsigned int cbSrcReq)
+{
+ BSTR pBstrNew = (BSTR)nsMemory::Alloc(RT_ALIGN_Z(cbSrcReq + sizeof(OLECHAR), sizeof(OLECHAR)));
+ AssertCompile(sizeof(*pBstrNew) == sizeof(OLECHAR));
+ if (pBstrNew)
+ {
+ if (!pszSrc)
+ memset(pBstrNew, 0, cbSrcReq + sizeof(OLECHAR));
+ else
+ {
+ // Copy the string and make sure it is terminated.
+ memcpy(pBstrNew, pszSrc, cbSrcReq);
+ char *pchTerminator = (char *)pBstrNew;
+ pchTerminator[cbSrcReq] = '\0';
+ pchTerminator[cbSrcReq + 1] = '\0';
+ }
+ }
+ return pBstrNew;
+}
+
+/**
+ * Duplicates a UTF-16 string into a BSTR / Allocates a BSTR with a size given
+ * in UTF-16 characters.
+ *
+ * @param pwszSrc Pointer to the source string, optional.
+ * @param cwcSrcReq Length of the source string / memory request in UTF-16
+ * characters.
+ * @returns new BSTR string buffer, NULL on failure.
+ */
+BSTR SysAllocStringLen(const OLECHAR *pwszSrc, unsigned int cwcSrcReq)
+{
+ size_t const cbReq = (cwcSrcReq + 1) * sizeof(OLECHAR);
+ BSTR pBstrNew = (BSTR)nsMemory::Alloc(cbReq);
+ AssertCompile(sizeof(*pBstrNew) == sizeof(OLECHAR));
+ if (pBstrNew)
+ {
+ if (!pwszSrc)
+ memset(pBstrNew, 0, cbReq);
+ else
+ {
+ // Copy the string and make sure it is terminated.
+ memcpy(pBstrNew, pwszSrc, cbReq - sizeof(OLECHAR));
+ pBstrNew[cwcSrcReq] = L'\0';
+ }
+ }
+ return pBstrNew;
+}
+
+/**
+ * Frees the memory associated with the given BSTR.
+ *
+ * @param pBstr The string to free. NULL is ignored.
+ */
+void SysFreeString(BSTR pBstr)
+{
+ if (pBstr)
+ nsMemory::Free(pBstr);
+}
+
+/**
+ * Duplicates @a pwszSrc into an exsting BSTR, adjust its size to make it fit.
+ *
+ * @param ppBstr The existing BSTR buffer pointer.
+ * @param pwszSrc Source string to copy. If NULL, the existing BSTR is freed.
+ * @returns success indicator (TRUE/FALSE)
+ */
+int SysReAllocString(BSTR *ppBstr, const OLECHAR *pwszSrc)
+{
+ if (pwszSrc)
+ return SysReAllocStringLen(ppBstr, pwszSrc, RTUtf16Len((PCRTUTF16)pwszSrc));
+ SysFreeString(*ppBstr);
+ *ppBstr = NULL;
+ return 1;
+}
+
+/**
+ * Duplicates @a pwszSrc into an exsting BSTR / resizing an existing BSTR buffer
+ * into the given size (@a cwcSrcReq).
+ *
+ * @param ppBstr The existing BSTR buffer pointer.
+ * @param pwszSrc Source string to copy into the adjusted pbstr, optional.
+ * @param cwcSrcReq Length of the source string / request in UCS2
+ * characters, a zero terminator is always added.
+ * @returns success indicator (TRUE/FALSE)
+ */
+int SysReAllocStringLen(BSTR *ppBstr, const OLECHAR *pwszSrc, unsigned int cwcSrcReq)
+{
+ BSTR pBstrOld = *ppBstr;
+ AssertCompile(sizeof(*pBstrOld) == sizeof(OLECHAR));
+ if (pBstrOld)
+ {
+ if ((BSTR)pwszSrc == pBstrOld)
+ pwszSrc = NULL;
+
+ size_t cbReq = (cwcSrcReq + 1) * sizeof(OLECHAR);
+ BSTR pBstrNew = (BSTR)nsMemory::Realloc(pBstrOld, cbReq);
+ if (pBstrNew)
+ {
+ if (pwszSrc)
+ memcpy(pBstrNew, pwszSrc, cbReq - sizeof(OLECHAR));
+ pBstrNew[cwcSrcReq] = L'\0';
+ *ppBstr = pBstrNew;
+ return 1;
+ }
+ }
+ else
+ {
+ // allocate a new string
+ *ppBstr = SysAllocStringLen(pwszSrc, cwcSrcReq);
+ if (*ppBstr)
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Returns the string length in bytes without the terminator.
+ *
+ * @param pBstr The BSTR to get the byte length of.
+ * @returns String length in bytes.
+ */
+unsigned int SysStringByteLen(BSTR pBstr)
+{
+ AssertCompile(sizeof(OLECHAR) == sizeof(*pBstr));
+ return RTUtf16Len(pBstr) * sizeof(OLECHAR);
+}
+
+/**
+ * Returns the string length in OLECHARs without the terminator.
+ *
+ * @param pBstr The BSTR to get the length of.
+ * @returns String length in OLECHARs.
+ */
+unsigned int SysStringLen(BSTR pBstr)
+{
+ return RTUtf16Len(pBstr);
+}