Index: /trunk/src/VBox/Main/src-global/VirtualBoxSDSImpl.cpp
===================================================================
--- /trunk/src/VBox/Main/src-global/VirtualBoxSDSImpl.cpp	(revision 70507)
+++ /trunk/src/VBox/Main/src-global/VirtualBoxSDSImpl.cpp	(revision 70507)
@@ -0,0 +1,462 @@
+/* $Id$ */
+/** @file
+ * VBox Global COM Class implementation.
+ */
+
+/*
+ * Copyright (C) 2015-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#include <VBox/com/VirtualBox.h>
+#include "VirtualBoxSDSImpl.h"
+
+#include "AutoCaller.h"
+#include "Logging.h"
+
+#include <VBox/err.h>
+#include <iprt/asm.h>
+#include <iprt/critsect.h>
+
+#include <rpcasync.h>
+#include <rpcdcep.h>
+#include <sddl.h>
+#include <lmcons.h> /* UNLEN */
+
+
+/**
+ * Per user data.
+ *
+ * @note We never delete instances of this class, except in case of an insertion
+ *       race.  This allows us to separate the map lock from the user data lock
+ *       and avoid DoS issues.
+ */
+class VBoxSDSPerUserData
+{
+public:
+    /** The SID (secure identifier) for the user.  This is the key. */
+    com::Utf8Str                    m_strUserSid;
+    /** The user name (if we could get it). */
+    com::Utf8Str                    m_strUsername;
+    /** The VBoxSVC chosen to instantiate CLSID_VirtualBox.
+     * This is NULL if not set. */
+    ComPtr<IVBoxSVCRegistration>    m_ptrTheChosenOne;
+private:
+    /** Reference count to make destruction safe wrt hung callers.
+     * (References are retain while holding the map lock in some form, but
+     * released while holding no locks.) */
+    uint32_t volatile               m_cRefs;
+    /** Critical section protecting everything here. */
+    RTCRITSECT                      m_Lock;
+
+public:
+    VBoxSDSPerUserData(com::Utf8Str const &a_rStrUserSid, com::Utf8Str const &a_rStrUsername)
+        : m_strUserSid(a_rStrUserSid), m_strUsername(a_rStrUsername), m_cRefs(1)
+    {
+        RTCritSectInit(&m_Lock);
+    }
+
+    ~VBoxSDSPerUserData()
+    {
+        RTCritSectDelete(&m_Lock);
+    }
+
+    uint32_t i_retain()
+    {
+        uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
+        Assert(cRefs > 1);
+        return cRefs;
+    }
+
+    uint32_t i_release()
+    {
+        uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
+        Assert(cRefs < _1K);
+        if (cRefs == 0)
+            delete this;
+        return cRefs;
+    }
+
+    void i_lock()
+    {
+        RTCritSectEnter(&m_Lock);
+    }
+
+    void i_unlock()
+    {
+        RTCritSectLeave(&m_Lock);
+    }
+};
+
+
+
+
+// constructor / destructor
+/////////////////////////////////////////////////////////////////////////////
+
+DEFINE_EMPTY_CTOR_DTOR(VirtualBoxSDS)
+
+HRESULT VirtualBoxSDS::FinalConstruct()
+{
+    LogRelFlowThisFuncEnter();
+
+    int vrc = RTCritSectRwInit(&m_MapCritSect);
+    AssertLogRelRCReturn(vrc, E_FAIL);
+
+    LogRelFlowThisFuncLeave();
+    return S_OK;
+}
+
+
+void VirtualBoxSDS::FinalRelease()
+{
+    LogRelFlowThisFuncEnter();
+
+    RTCritSectRwDelete(&m_MapCritSect);
+
+    for (UserDataMap_T::iterator it = m_UserDataMap.begin(); it != m_UserDataMap.end(); ++it)
+    {
+        VBoxSDSPerUserData *pUserData = it->second;
+        if (pUserData)
+        {
+            it->second = NULL;
+            pUserData->i_release();
+        }
+    }
+
+    LogRelFlowThisFuncLeave();
+}
+
+
+
+// IVirtualBoxSDS methods
+/////////////////////////////////////////////////////////////////////////////
+
+
+/* SDS plan B interfaces: */
+STDMETHODIMP_(HRESULT) VirtualBoxSDS::RegisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid, IUnknown **aExistingVirtualBox)
+{
+    LogRel(("VirtualBoxSDS::registerVBoxSVC: aVBoxSVC=%p aPid=%u\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid));
+    HRESULT hrc;
+    if (   RT_VALID_PTR(aVBoxSVC)
+        && RT_VALID_PTR(aExistingVirtualBox))
+    {
+        *aExistingVirtualBox = NULL;
+
+        /* Get the client user SID and name. */
+        com::Utf8Str strSid;
+        com::Utf8Str strUsername;
+        if (i_getClientUserSid(&strSid, &strUsername))
+        {
+            VBoxSDSPerUserData *pUserData = i_lookupOrCreatePerUserData(strSid, strUsername);
+            if (pUserData)
+            {
+                if (pUserData->m_ptrTheChosenOne.isNotNull())
+                {
+                    try
+                    {
+                        hrc = pUserData->m_ptrTheChosenOne->GetVirtualBox(aExistingVirtualBox);
+                    }
+                    catch (...)
+                    {
+                        LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception calling GetVirtualBox.\n"));
+                        hrc = E_FAIL;
+                    }
+                    if (FAILED_DEAD_INTERFACE(hrc))
+                    {
+                        LogRel(("VirtualBoxSDS::registerVBoxSVC: Seems VBoxSVC instance died.  Dropping it and letting caller take over.\n"));
+                        pUserData->m_ptrTheChosenOne.setNull();
+                    }
+                }
+                else
+                    hrc = S_OK;
+
+                if (pUserData->m_ptrTheChosenOne.isNull())
+                {
+                    LogRel(("VirtualBoxSDS::registerVBoxSVC: Making aPid=%u the chosen one for user %s (%s)!\n",
+                            aPid, pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
+                    try
+                    {
+                        pUserData->m_ptrTheChosenOne = aVBoxSVC;
+                        hrc = S_OK;
+                    }
+                    catch (...)
+                    {
+                        LogRel(("VirtualBoxSDS::registerVBoxSVC: unexpected exception setting the chosen one.\n"));
+                        hrc = E_FAIL;
+                    }
+                }
+
+                pUserData->i_unlock();
+                pUserData->i_release();
+            }
+            else
+                hrc = E_OUTOFMEMORY;
+        }
+        else
+            hrc = E_FAIL;
+    }
+    else
+        hrc = E_INVALIDARG;
+    LogRel2(("VirtualBoxSDS::registerVBoxSVC: returns %Rhrc aExistingVirtualBox=%p\n", hrc, (IUnknown *)aExistingVirtualBox));
+    return hrc;
+}
+
+STDMETHODIMP_(HRESULT) VirtualBoxSDS::DeregisterVBoxSVC(IVBoxSVCRegistration *aVBoxSVC, LONG aPid)
+{
+    LogRel(("VirtualBoxSDS::deregisterVBoxSVC: aVBoxSVC=%p aPid=%u\n", (IVBoxSVCRegistration *)aVBoxSVC, aPid));
+    HRESULT hrc;
+    if (RT_VALID_PTR(aVBoxSVC))
+    {
+        /* Get the client user SID and name. */
+        com::Utf8Str strSid;
+        com::Utf8Str strUsername;
+        if (i_getClientUserSid(&strSid, &strUsername))
+        {
+            VBoxSDSPerUserData *pUserData = i_lookupPerUserData(strSid);
+            if (pUserData)
+            {
+                if (   (IVBoxSVCRegistration *)aVBoxSVC
+                    == (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne)
+                {
+                    LogRel(("VirtualBoxSDS::deregisterVBoxSVC: It's the chosen one for %s (%s)!\n",
+                            pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str()));
+                    pUserData->m_ptrTheChosenOne.setNull();
+                    /** @todo consider evicting the user from the table...   */
+                }
+                else
+                    LogRel(("VirtualBoxSDS::deregisterVBoxSVC: not the choosen one (%p != %p)\n",
+                            (IVBoxSVCRegistration *)aVBoxSVC, (IVBoxSVCRegistration *)pUserData->m_ptrTheChosenOne));
+                pUserData->i_unlock();
+                pUserData->i_release();
+
+                hrc = S_OK;
+            }
+            else
+            {
+                LogRel(("VirtualBoxSDS::deregisterVBoxSVC: Found no user data for %s (%s) (pid %u)\n",
+                        strSid.c_str(), strUsername.c_str(), aPid));
+                hrc = S_OK;
+            }
+        }
+        else
+            hrc = E_FAIL;
+    }
+    else
+        hrc = E_INVALIDARG;
+    LogRel2(("VirtualBoxSDS::deregisterVBoxSVC: returns %Rhrc\n", hrc));
+    return hrc;
+}
+
+
+// private methods
+///////////////////////////////////////////////////////////////////////////////
+
+/*static*/ bool VirtualBoxSDS::i_getClientUserSid(com::Utf8Str *a_pStrSid, com::Utf8Str *a_pStrUsername)
+{
+    bool fRet = false;
+    a_pStrSid->setNull();
+    a_pStrUsername->setNull();
+
+    CoInitializeEx(NULL, COINIT_MULTITHREADED); // is this necessary?
+    HRESULT hrc = CoImpersonateClient();
+    if (SUCCEEDED(hrc))
+    {
+        HANDLE hToken = INVALID_HANDLE_VALUE;
+        if (::OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE /*OpenAsSelf*/, &hToken))
+        {
+            CoRevertToSelf();
+
+            union
+            {
+                TOKEN_USER  TokenUser;
+                uint8_t     abPadding[SECURITY_MAX_SID_SIZE + 256];
+                WCHAR       wszUsername[UNLEN + 1];
+            } uBuf;
+            RT_ZERO(uBuf);
+            DWORD cbActual = 0;
+            if (::GetTokenInformation(hToken, TokenUser, &uBuf, sizeof(uBuf), &cbActual))
+            {
+                WCHAR *pwszString;
+                if (ConvertSidToStringSidW(uBuf.TokenUser.User.Sid, &pwszString))
+                {
+                    try
+                    {
+                        *a_pStrSid = pwszString;
+                        a_pStrSid->toUpper(); /* (just to be on the safe side) */
+                        fRet = true;
+                    }
+                    catch (std::bad_alloc)
+                    {
+                        LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rstrSid.\n"));
+                    }
+                    LocalFree((HLOCAL)pwszString);
+
+                    /*
+                     * Get the username too.  We don't care if this step fails.
+                     */
+                    if (fRet)
+                    {
+                        WCHAR           wszUsername[UNLEN * 2 + 1];
+                        DWORD           cwcUsername = RT_ELEMENTS(wszUsername);
+                        WCHAR           wszDomain[UNLEN * 2 + 1];
+                        DWORD           cwcDomain = RT_ELEMENTS(wszDomain);
+                        SID_NAME_USE    enmNameUse;
+                        if (LookupAccountSidW(NULL, uBuf.TokenUser.User.Sid, wszUsername, &cwcUsername,
+                                              wszDomain, &cwcDomain, &enmNameUse))
+                        {
+                            wszUsername[RT_ELEMENTS(wszUsername) - 1] = '\0';
+                            wszDomain[RT_ELEMENTS(wszDomain) - 1] = '\0';
+                            try
+                            {
+                                *a_pStrUsername = wszDomain;
+                                a_pStrUsername->append('/');
+                                a_pStrUsername->append(Utf8Str(wszUsername));
+                            }
+                            catch (std::bad_alloc)
+                            {
+                                LogRel(("VirtualBoxSDS::i_GetClientUserSID: std::bad_alloc setting rStrUsername.\n"));
+                                a_pStrUsername->setNull();
+                            }
+                        }
+                        else
+                            LogRel(("VirtualBoxSDS::i_GetClientUserSID: LookupAccountSidW failed: %u/%x (cwcUsername=%u, cwcDomain=%u)\n",
+                                   GetLastError(), cwcUsername, cwcDomain));
+                    }
+                }
+                else
+                    LogRel(("VirtualBoxSDS::i_GetClientUserSID: ConvertSidToStringSidW failed: %u\n", GetLastError()));
+            }
+            else
+                LogRel(("VirtualBoxSDS::i_GetClientUserSID: GetTokenInformation/TokenUser failed: %u\n", GetLastError()));
+            CloseHandle(hToken);
+        }
+        else
+        {
+            CoRevertToSelf();
+            LogRel(("VirtualBoxSDS::i_GetClientUserSID: OpenThreadToken failed: %u\n", GetLastError()));
+        }
+    }
+    else
+        LogRel(("VirtualBoxSDS::i_GetClientUserSID: CoImpersonateClient failed: %Rhrc\n", hrc));
+    CoUninitialize();
+    return fRet;
+}
+
+
+/**
+ * Looks up the given user.
+ *
+ * @returns Pointer to the LOCKED and RETAINED per user data.
+ *          NULL if not found.
+ * @param   a_rStrUserSid   The user SID.
+ */
+VBoxSDSPerUserData *VirtualBoxSDS::i_lookupPerUserData(com::Utf8Str const &a_rStrUserSid)
+{
+    int vrc = RTCritSectRwEnterShared(&m_MapCritSect);
+    if (RT_SUCCESS(vrc))
+    {
+
+        UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
+        if (it != m_UserDataMap.end())
+        {
+            VBoxSDSPerUserData *pUserData = it->second;
+            pUserData->i_retain();
+
+            RTCritSectRwLeaveShared(&m_MapCritSect);
+
+            pUserData->i_lock();
+            return pUserData;
+        }
+
+        RTCritSectRwLeaveShared(&m_MapCritSect);
+    }
+    return NULL;
+}
+
+
+/**
+ * Looks up the given user, creating it if not found
+ *
+ * @returns Pointer to the LOCKED and RETAINED per user data.
+ *          NULL on allocation error.
+ * @param   a_rStrUserSid   The user SID.
+ * @param   a_rStrUsername  The user name if available.
+ */
+VBoxSDSPerUserData *VirtualBoxSDS::i_lookupOrCreatePerUserData(com::Utf8Str const &a_rStrUserSid,
+                                                               com::Utf8Str const &a_rStrUsername)
+{
+    /*
+     * Try do a simple lookup first.
+     */
+    VBoxSDSPerUserData *pUserData = i_lookupPerUserData(a_rStrUserSid);
+    if (!pUserData)
+    {
+        /*
+         * SID is not in map, create a new one.
+         */
+        try
+        {
+            pUserData = new VBoxSDSPerUserData(a_rStrUserSid, a_rStrUsername);
+        }
+        catch (std::bad_alloc)
+        {
+            pUserData = NULL;
+        }
+        if (pUserData)
+        {
+            /*
+             * Insert it.  We must check if someone raced us here.
+             */
+            VBoxSDSPerUserData *pUserDataFree = pUserData;
+            pUserData->i_lock();
+
+            int vrc = RTCritSectRwEnterExcl(&m_MapCritSect);
+            if (RT_SUCCESS(vrc))
+            {
+
+                UserDataMap_T::iterator it = m_UserDataMap.find(a_rStrUserSid);
+                if (it == m_UserDataMap.end())
+                {
+                    try
+                    {
+                        m_UserDataMap[a_rStrUserSid] = pUserData;
+                        pUserData->i_retain();
+                    }
+                    catch (std::bad_alloc)
+                    {
+                        pUserData = NULL;
+                    }
+                }
+                else
+                    pUserData = NULL;
+
+                RTCritSectRwLeaveExcl(&m_MapCritSect);
+
+                if (pUserData)
+                    LogRel(("VirtualBoxSDS::i_lookupOrCreatePerUserData: Created new entry for %s (%s)\n",
+                            pUserData->m_strUserSid.c_str(), pUserData->m_strUsername.c_str() ));
+                else
+                {
+                    pUserDataFree->i_unlock();
+                    delete pUserDataFree;
+                }
+            }
+        }
+    }
+
+    return pUserData;
+}
+
+/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Index: /trunk/src/VBox/Main/src-global/win/VBoxSDS.cpp
===================================================================
--- /trunk/src/VBox/Main/src-global/win/VBoxSDS.cpp	(revision 70507)
+++ /trunk/src/VBox/Main/src-global/win/VBoxSDS.cpp	(revision 70507)
@@ -0,0 +1,960 @@
+/* $Id$ */
+/** @file
+ * VBoxSDS - COM global service main entry (System Directory Service)
+ */
+
+/*
+ * Copyright (C) 2015-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/** @page pg_VBoxSDS    VBoxSDS - Per user CLSID_VirtualBox coordinater
+ *
+ * VBoxSDS is short for VirtualBox System Directory Service (SDS).  It's purpose
+ * is to make sure there only one CLSID_VirtualBox object running for each user
+ * uisng VirtualBox on a Windows host system.
+ *
+ *
+ * @section sec_vboxsds_backgroud   Background
+ *
+ * COM is desktop oriented when it comes to activate-as-activator (AAA) COM
+ * servers.  This means that if the users has two logins to the same box (e.g.
+ * physical console, RDP, SSHD) and tries to use an AAA COM server, a new server
+ * will be instantiated for each login.  With the introduction of User Account
+ * Control (UAC) in Windows Vista, this was taken a step further and a user
+ * would talk different AAA COM server instances depending on the elevation
+ * level too.
+ *
+ * VBoxSVC is a service affected by this issue.  Using VirtualBox accross logins
+ * or between user elevation levels was impossible to do simultaneously.  This
+ * was confusing and illogical to the user.
+ *
+ *
+ * @section sec_vboxsds_how         How it works
+ *
+ * VBoxSDS assists in working around this problem by tracking which VBoxSVC
+ * server is currently providing CLSID_VirtualBox for a user.  Each VBoxSVC
+ * instance will register itself with VBoxSDS when the CLSID_VirtualBox object
+ * is requested via their class factory.  The first VBoxSVC registering for a
+ * given user will be allowed to instantate CLSID_VirtualBox.  We will call this
+ * the chosen one.  Subsequent VBoxSVC instance for the given user, regardless
+ * of elevation, session, windows station, or whatever else, will be told to use
+ * the instance from the first VBoxSVC.
+ *
+ * The registration call passes along an IVBoxSVCRegistration interface from
+ * VBoxSVC.  VBoxSDS keeps this around for the chosen one only.  When other
+ * VBoxSVC instances for the same user tries to register, VBoxSDS will ask the
+ * choosen one for its CLSID_VirtualBox object and return it to the new
+ * registrant.
+ *
+ * The chosen one will deregister with VBoxSDS before it terminates.  Should it
+ * terminate abnormally, VBoxSDS will (probably) notice the next time it tries
+ * to request CLSID_VirtualBox from it and replace it as the chosen one with the
+ * new registrant.
+ *
+ *
+ * @section sec_vboxsds_locking     Locking
+ *
+ * VBoxSDS stores data in a map indexed by the stringified secure identifier
+ * (SID) for each user.  The map is protected by a shared critical section, so
+ * only inserting new users requires exclusive access.
+ *
+ * Each user data entry has it own lock (regular, not shared), so that it won't
+ * be necessary to hold down the map lock while accessing per user data.  Thus
+ * preventing a user from blocking all others from using VirtualBox by
+ * suspending or debugging their chosen VBoxSVC process.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+*   Header Files                                                                                                                 *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/win/shlobj.h>
+
+#include "VBox/com/defs.h"
+#include "VBox/com/com.h"
+#include "VBox/com/VirtualBox.h"
+
+#include "VirtualBoxSDSImpl.h"
+#include "Logging.h"
+
+#include <VBox/err.h>
+#include <iprt/asm.h>
+#include <iprt/buildconfig.h>
+#include <iprt/dir.h>
+#include <iprt/env.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/path.h>
+#include <iprt/message.h>
+#include <iprt/string.h>
+
+#include <VBox/com/microatl.h>
+
+#define _ATL_FREE_THREADED
+
+/**
+ * Implements Windows Service
+ */
+class ATL_NO_VTABLE CWindowsServiceModule
+{
+protected:
+    // data members
+    WCHAR                   m_wszServiceName[256];
+    WCHAR                   m_wszServiceDisplayName[256];
+    WCHAR                   m_wszServiceDescription[256];
+    SERVICE_STATUS_HANDLE   m_hServiceStatus;
+    SERVICE_STATUS          m_Status;
+    DWORD                   m_dwThreadID;
+
+    /** Pointer to the instance, for use by staticServiceMain and staticHandler.  */
+    static CWindowsServiceModule *s_pInstance;
+
+public:
+    CWindowsServiceModule() throw()
+    {
+        // set up the initial service status
+        m_hServiceStatus = NULL;
+        m_Status.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
+        m_Status.dwCurrentState            = SERVICE_STOPPED;
+        m_Status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
+        m_Status.dwWin32ExitCode           = 0;
+        m_Status.dwServiceSpecificExitCode = 0;
+        m_Status.dwCheckPoint              = 0;
+        m_Status.dwWaitHint                = 3000;
+
+        s_pInstance = this;
+    }
+
+    virtual ~CWindowsServiceModule()
+    {
+        s_pInstance = NULL;
+    }
+
+    /** @todo Nobody calls this method and it doesn't do anythin...   */
+    virtual HRESULT InitializeSecurity() throw()
+    {
+        return S_OK;
+    }
+
+    HRESULT startService(int /*nShowCmd*/) throw()
+    {
+        SERVICE_TABLE_ENTRY aServiceTable[] =
+        {
+            { m_wszServiceName, staticServiceMain },
+            { NULL, NULL }
+        };
+
+        if (::StartServiceCtrlDispatcher(aServiceTable) == 0)
+        {
+            m_Status.dwWin32ExitCode = ::GetLastError();
+            LogRelFunc(("Error: Cannot start service in console mode. Code: %u\n", m_Status.dwWin32ExitCode));
+        }
+
+        return m_Status.dwWin32ExitCode;
+    }
+
+    virtual HRESULT registerService() throw()
+    {
+        HRESULT hrc;
+        if (uninstallService())
+        {
+            hrc = onRegisterService();
+            if (SUCCEEDED(hrc))
+            {
+                if (installService())
+                    hrc = S_OK;
+                else
+                    hrc = E_FAIL;
+            }
+        }
+        else
+            hrc = E_FAIL;
+        return hrc;
+    }
+
+    virtual HRESULT unregisterService() throw()
+    {
+        HRESULT hrc = E_FAIL;
+        if (uninstallService())
+            hrc = onUnregisterService();
+        return hrc;
+    }
+
+private:
+    void serviceMain( DWORD , LPTSTR* ) throw()
+    {
+        LogFunc(("Enter into serviceMain\n"));
+        // Register the control request handler
+        m_Status.dwCurrentState = SERVICE_START_PENDING;
+        m_dwThreadID = ::GetCurrentThreadId();
+        m_hServiceStatus = ::RegisterServiceCtrlHandler(m_wszServiceName, staticHandler);
+        if (m_hServiceStatus == NULL)
+        {
+            LogWarnFunc(("Handler not installed\n"));
+            return;
+        }
+        setServiceStatus(SERVICE_START_PENDING);
+
+        m_Status.dwWin32ExitCode = S_OK;
+        m_Status.dwCheckPoint = 0;
+        m_Status.dwWaitHint = 0;
+
+        // When the Run function returns, the service has stopped.
+        m_Status.dwWin32ExitCode = runService(SW_HIDE);
+
+        setServiceStatus(SERVICE_STOPPED);
+        LogFunc(("Windows Service stopped\n"));
+    }
+
+    /** Service table callback. */
+    static void WINAPI staticServiceMain(DWORD cArgs, LPTSTR *papwszArgs) throw()
+    {
+        AssertPtrReturnVoid(s_pInstance);
+        s_pInstance->serviceMain(cArgs, papwszArgs);
+    }
+
+    HRESULT runService(int nShowCmd = SW_HIDE) throw()
+    {
+        HRESULT hr = preMessageLoop(nShowCmd);
+
+        if (hr == S_OK)
+            runMessageLoop();
+
+        if (SUCCEEDED(hr))
+            hr = postMessageLoop();
+
+        return hr;
+    }
+
+protected:
+    /** Hook that's called before the message loop starts.
+     * Must return S_OK for it to start. */
+    virtual HRESULT preMessageLoop(int /*nShowCmd*/) throw()
+    {
+        LogFunc(("Enter\n"));
+        if (::InterlockedCompareExchange(&m_Status.dwCurrentState, SERVICE_RUNNING, SERVICE_START_PENDING) == SERVICE_START_PENDING)
+        {
+            LogFunc(("VBoxSDS Service started/resumed without delay\n"));
+            ::SetServiceStatus(m_hServiceStatus, &m_Status);
+        }
+        return S_OK;
+    }
+
+    /** Your typical windows message loop. */
+    virtual void runMessageLoop()
+    {
+        MSG msg;
+        while (::GetMessage(&msg, 0, 0, 0) > 0)
+        {
+            ::TranslateMessage(&msg);
+            ::DispatchMessage(&msg);
+        }
+    }
+
+    /** Hook that's called after the message loop ends. */
+    virtual HRESULT postMessageLoop()
+    {
+        return S_OK;
+    }
+
+    /** @name Overridable status change handlers
+     * @{ */
+    virtual void onStop() throw()
+    {
+        setServiceStatus(SERVICE_STOP_PENDING);
+        ::PostThreadMessage(m_dwThreadID, WM_QUIT, 0, 0);
+        LogFunc(("Windows Service stopped\n"));
+    }
+
+    virtual void onPause() throw()
+    {
+    }
+
+    virtual void onContinue() throw()
+    {
+    }
+
+    virtual void onInterrogate() throw()
+    {
+    }
+
+    virtual void onShutdown() throw()
+    {
+    }
+
+    virtual void onUnknownRequest(DWORD dwOpcode) throw()
+    {
+        LogRelFunc(("Bad service request: %u (%#x)\n", dwOpcode, dwOpcode));
+    }
+
+    virtual HRESULT onRegisterService()
+    {
+        return S_OK;
+    }
+
+    virtual HRESULT onUnregisterService()
+    {
+        return S_OK;
+    }
+    /** @} */
+
+private:
+    void handler(DWORD dwOpcode) throw()
+    {
+
+        switch (dwOpcode)
+        {
+            case SERVICE_CONTROL_STOP:
+                onStop();
+                break;
+            case SERVICE_CONTROL_PAUSE:
+                onPause();
+                break;
+            case SERVICE_CONTROL_CONTINUE:
+                onContinue();
+                break;
+            case SERVICE_CONTROL_INTERROGATE:
+                onInterrogate();
+                break;
+            case SERVICE_CONTROL_SHUTDOWN:
+                onShutdown();
+                break;
+            default:
+                onUnknownRequest(dwOpcode);
+        }
+    }
+
+    static void WINAPI staticHandler(DWORD dwOpcode) throw()
+    {
+        AssertPtrReturnVoid(s_pInstance);
+        s_pInstance->handler(dwOpcode);
+    }
+
+protected:
+    void setServiceStatus(DWORD dwState) throw()
+    {
+        uint32_t const uPrevState = ASMAtomicXchgU32((uint32_t volatile *)&m_Status.dwCurrentState, dwState);
+        if (!::SetServiceStatus(m_hServiceStatus, &m_Status))
+            LogRel(("Error: SetServiceStatus(%p, %u) failed: %u (uPrevState=%u)\n",
+                    m_hServiceStatus, dwState, GetLastError(), uPrevState));
+    }
+
+
+public:
+    /** @note unused */
+    BOOL IsInstalled() throw()
+    {
+        BOOL fResult = FALSE;
+
+        SC_HANDLE hSCM = ::OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+        if (hSCM != NULL)
+        {
+            SC_HANDLE hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_QUERY_CONFIG);
+            if (hService != NULL)
+            {
+                fResult = TRUE;
+                ::CloseServiceHandle(hService);
+            }
+            ::CloseServiceHandle(hSCM);
+        }
+
+        return fResult;
+    }
+
+    BOOL installService() throw()
+    {
+        BOOL fResult = FALSE;
+        SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+        if (hSCM != NULL)
+        {
+            SC_HANDLE hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_QUERY_CONFIG);
+            if (hService != NULL)
+            {
+                fResult = TRUE; /* Already installed. */
+
+                ::CloseServiceHandle(hService);
+            }
+            else
+            {
+                // Get the executable file path and quote it.
+                const int QUOTES_SPACE = 2;
+                WCHAR wszFilePath[MAX_PATH + QUOTES_SPACE];
+                DWORD cwcFilePath = ::GetModuleFileNameW(NULL, wszFilePath + 1, MAX_PATH);
+                if (cwcFilePath != 0 && cwcFilePath < MAX_PATH)
+                {
+                    wszFilePath[0] = L'\"';
+                    wszFilePath[cwcFilePath + 1] = L'\"';
+                    wszFilePath[cwcFilePath + 2] = L'\0';
+
+                    SC_HANDLE hService = ::CreateServiceW(hSCM, m_wszServiceName, m_wszServiceDisplayName,
+                                                          SERVICE_CHANGE_CONFIG,
+                                                          SERVICE_WIN32_OWN_PROCESS,
+                                                          SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+                                                          wszFilePath, NULL, NULL, L"RPCSS\0", NULL, NULL);
+                    if (hService != NULL)
+                    {
+                        SERVICE_DESCRIPTIONW sd;
+                        sd.lpDescription = m_wszServiceDescription;
+                        if (!::ChangeServiceConfig2W(hService, SERVICE_CONFIG_DESCRIPTION, &sd))
+                            AssertLogRelMsgFailed(("Error: could not set service description: %u\n", GetLastError()));
+
+                        fResult = TRUE;
+
+                        ::CloseServiceHandle(hService);
+                    }
+                    else
+                        AssertLogRelMsgFailed(("Error: Could not create service '%ls': %u\n", m_wszServiceName, GetLastError()));
+                }
+                else
+                    AssertLogRelMsgFailed(("Error: GetModuleFileNameW returned %u: %u\n", cwcFilePath, GetLastError()));
+            }
+        }
+        else
+            AssertLogRelMsgFailed(("Error: Could not open the service control manager: %u\n", GetLastError()));
+        return fResult;
+    }
+
+    BOOL uninstallService() throw()
+    {
+        BOOL fResult = FALSE;
+        SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
+        if (hSCM != NULL)
+        {
+            SC_HANDLE hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_STOP | DELETE);
+            if (hService == NULL)
+            {
+                DWORD dwErr = GetLastError();
+                hService = ::OpenService(hSCM, m_wszServiceName, SERVICE_QUERY_CONFIG);
+                if (hService == NULL)
+                    fResult = TRUE; /* Probably not installed or some access problem. */
+                else
+                {
+                    ::CloseServiceHandle(hService);
+                    AssertLogRelMsgFailed(("Error: Failed to open '%ls' for stopping and deletion: %u\n", m_wszServiceName, dwErr));
+                }
+            }
+            else
+            {
+                /* Try stop it. */
+                SERVICE_STATUS status;
+                RT_ZERO(status);
+                if (!::ControlService(hService, SERVICE_CONTROL_STOP, &status))
+                {
+                    DWORD dwErr = GetLastError();
+                    AssertLogRelMsg(       dwErr == ERROR_SERVICE_NOT_ACTIVE
+                                    || (   dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL
+                                        && status.dwCurrentState == SERVICE_STOP_PENDING)
+                                    , ("Error: Failed to stop serive '%ls': dwErr=%u dwCurrentState=%u\n",
+                                       m_wszServiceName, dwErr, status.dwCurrentState));
+                }
+
+                /* Try delete it. */
+                fResult = ::DeleteService(hService);
+                AssertLogRelMsg(fResult, ("Error: Failed to delete serivce '%ls': %u\n", m_wszServiceName, GetLastError()));
+
+                ::CloseServiceHandle(hService);
+            }
+            ::CloseServiceHandle(hSCM);
+        }
+        else
+            AssertLogRelMsgFailed(("Error: Could not open the service control manager: %u\n", GetLastError()));
+        return fResult;
+    }
+};
+
+/*static*/ CWindowsServiceModule *CWindowsServiceModule::s_pInstance = NULL;
+
+
+/**
+ * Implements COM Module that used within Windows Service.
+ *
+ * It is derived from ComModule to intercept Unlock() and derived from
+ * CWindowsServiceModule to implement Windows Service
+ */
+class CComServiceModule : public CWindowsServiceModule, public ATL::CComModule
+{
+private:
+    /** Tracks whether Init() has been called for debug purposes. */
+    bool m_fInitialized;
+    /** Tracks COM init status for no visible purpose other than debugging. */
+    bool m_fComInitialized;
+    /** Part of the shutdown monitoring logic. */
+    bool volatile m_fActivity;
+    /** Auto reset event for communicating with the shutdown thread.
+     * This is created by startMonitor(). */
+    HANDLE m_hEventShutdown;
+    /** The main thread ID.
+     * The monitorShutdown code needs this to post a WM_QUIT message. */
+    DWORD m_dwMainThreadID;
+
+public:
+    /** Time for EXE to be idle before shutting down.
+     * Can be decreased at system shutdown phase. */
+    volatile uint32_t m_cMsShutdownTimeOut;
+
+public:
+    CComServiceModule(DWORD cMsShutdownTimeout = 10000)
+        : m_fInitialized(false)
+        , m_fComInitialized(false)
+        , m_fActivity(false)
+        , m_cMsShutdownTimeOut(cMsShutdownTimeout)
+        , m_hEventShutdown(INVALID_HANDLE_VALUE)
+        , m_dwMainThreadID(~(DWORD)42)
+    {
+    }
+
+    /**
+     * Initialization function.
+     */
+    HRESULT init(ATL::_ATL_OBJMAP_ENTRY *p, HINSTANCE h, const GUID *pLibID,
+                 wchar_t const *p_wszServiceName, wchar_t const *p_wszDisplayName, wchar_t const *p_wszDescription)
+    {
+        HRESULT hrc = ATL::CComModule::Init(p, h, pLibID);
+        if (SUCCEEDED(hrc))
+        {
+
+            // copy service name
+            int rc = ::RTUtf16Copy(m_wszServiceName, sizeof(m_wszServiceName), p_wszServiceName);
+            AssertRCReturn(rc, E_NOT_SUFFICIENT_BUFFER);
+            rc = ::RTUtf16Copy(m_wszServiceDisplayName, sizeof(m_wszServiceDisplayName), p_wszDisplayName);
+            AssertRCReturn(rc, E_NOT_SUFFICIENT_BUFFER);
+            rc = ::RTUtf16Copy(m_wszServiceDescription, sizeof(m_wszServiceDescription), p_wszDescription);
+            AssertRCReturn(rc, E_NOT_SUFFICIENT_BUFFER);
+
+            m_fInitialized = true;
+        }
+
+        return hrc;
+    }
+
+    /**
+     * Overload CAtlModule::Unlock to trigger delayed automatic shutdown action.
+     */
+    virtual LONG Unlock() throw()
+    {
+        LONG cLocks = ATL::CComModule::Unlock();
+        LogFunc(("Unlock() called. Ref=%d\n", cLocks));
+        if (cLocks == 0)
+        {
+            m_fActivity = true;
+            ::SetEvent(m_hEventShutdown); // tell monitor that we transitioned to zero
+        }
+        return cLocks;
+    }
+
+protected:
+
+    bool hasActiveConnection()
+    {
+        return m_fActivity || GetLockCount() > 0;
+    }
+
+    void monitorShutdown() throw()
+    {
+        for (;;)
+        {
+            ::WaitForSingleObject(m_hEventShutdown, INFINITE);
+            DWORD dwWait;
+            do
+            {
+                m_fActivity = false;
+                dwWait = ::WaitForSingleObject(m_hEventShutdown, m_cMsShutdownTimeOut);
+            } while (dwWait == WAIT_OBJECT_0);
+
+            /* timed out */
+            if (!hasActiveConnection()) /* if no activity let's really bail */
+            {
+                /*
+                 * Disable log rotation at this point, worst case a log file
+                 * becomes slightly bigger than it should. Avoids quirks with
+                 * log rotation: there might be another API service process
+                 * running at this point which would rotate the logs concurrently,
+                 * creating a mess.
+                 */
+                PRTLOGGER pReleaseLogger = ::RTLogRelGetDefaultInstance();
+                if (pReleaseLogger)
+                {
+                    char szDest[1024];
+                    int rc = ::RTLogGetDestinations(pReleaseLogger, szDest, sizeof(szDest));
+                    if (RT_SUCCESS(rc))
+                    {
+                        rc = ::RTStrCat(szDest, sizeof(szDest), " nohistory");
+                        if (RT_SUCCESS(rc))
+                        {
+                            rc = ::RTLogDestinations(pReleaseLogger, szDest);
+                            AssertRC(rc);
+                        }
+                    }
+                }
+                ::CoSuspendClassObjects();
+                if (!hasActiveConnection())
+                    break;
+                LogRel(("Still got active connection(s)...\n"));
+            }
+        }
+
+        LogRel(("Shutting down\n"));
+        if (m_hEventShutdown)
+        {
+            ::CloseHandle(m_hEventShutdown);
+            m_hEventShutdown = NULL;
+        }
+        ::PostThreadMessage(m_dwMainThreadID, WM_QUIT, 0, 0);
+    }
+
+    static DECLCALLBACK(int) monitorThreadProc(RTTHREAD hThreadSelf, void *pvUser) throw()
+    {
+        RT_NOREF(hThreadSelf);
+        CComServiceModule *p = static_cast<CComServiceModule *>(pvUser);
+        p->monitorShutdown();
+        return VINF_SUCCESS;
+    }
+
+    void startMonitor()
+    {
+        m_dwMainThreadID = ::GetCurrentThreadId();
+        m_hEventShutdown = ::CreateEvent(NULL, false, false, NULL);
+        AssertLogRelMsg(m_hEventShutdown != NULL, ("GetLastError => %u\n", GetLastError()));
+
+        int vrc = RTThreadCreate(NULL, monitorThreadProc, this, 0 /*cbStack*/, RTTHREADTYPE_DEFAULT, 0 /*fFlags*/, "MonShdwn");
+        if (RT_FAILURE(vrc))
+        {
+            ::CloseHandle(m_hEventShutdown);
+            m_hEventShutdown = NULL;
+            LogRel(("Error: RTThreadCreate failed to create shutdown monitor thread: %Rrc\n", vrc));
+        }
+    }
+
+    virtual HRESULT preMessageLoop(int nShowCmd)
+    {
+        Assert(m_fInitialized);
+        LogFunc(("Enter\n"));
+
+        HRESULT hrc = com::Initialize();
+        if (SUCCEEDED(hrc))
+        {
+            m_fComInitialized = true;
+            hrc = ATL::CComModule::RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
+            if (SUCCEEDED(hrc))
+            {
+                // Start Shutdown monitor here
+                startMonitor();
+
+                hrc = CWindowsServiceModule::preMessageLoop(nShowCmd);
+                if (FAILED(hrc))
+                    LogRelFunc(("Warning: preMessageLoop failed: %Rhrc\n", hrc));
+
+                hrc = CoResumeClassObjects();
+                if (FAILED(hrc))
+                {
+                    ATL::CComModule::RevokeClassObjects();
+                    LogRelFunc(("Error: CoResumeClassObjects failed: %Rhrc\n", hrc));
+                }
+            }
+            else
+                LogRel(("Error: ATL::CComModule::RegisterClassObjects: %Rhrc\n", hrc));
+        }
+        else
+            LogRel(("Error: com::Initialize failed\n", hrc));
+        return hrc;
+    }
+
+    virtual HRESULT postMessageLoop()
+    {
+        com::Shutdown();
+        m_fComInitialized = false;
+        return S_OK;
+    }
+};
+
+
+/**
+ * Main function for the VBoxSDS process.
+ *
+ * @param   hInstance       The process instance.
+ * @param   hPrevInstance   Previous instance (not used here).
+ * @param   nShowCmd        The show flags.
+ * @param   lpCmdLine       The command line (not used here, we get it from the
+ *                          C runtime library).
+ *
+ * @return  Exit code
+ */
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
+{
+    RT_NOREF(hPrevInstance, lpCmdLine);
+    int    argc = __argc;
+    char **argv = __argv;
+
+    /*
+     * Initialize the VBox runtime without loading the support driver.
+     */
+    RTR3InitExe(argc, &argv, 0);
+
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        { "--embedding",    'e',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "-embedding",     'e',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "/embedding",     'e',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "--unregservice", 'u',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "-unregservice",  'u',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "/unregservice",  'u',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "--regservice",   'r',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "-regservice",    'r',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "/regservice",    'r',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "--reregservice", 'f',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "-reregservice",  'f',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "/reregservice",  'f',    RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+        { "--logfile",      'F',    RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+        { "-logfile",       'F',    RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+        { "/logfile",       'F',    RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+        { "--logrotate",    'R',    RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+        { "-logrotate",     'R',    RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+        { "/logrotate",     'R',    RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+        { "--logsize",      'S',    RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
+        { "-logsize",       'S',    RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
+        { "/logsize",       'S',    RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
+        { "--loginterval",  'I',    RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+        { "-loginterval",   'I',    RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+        { "/loginterval",   'I',    RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+    };
+
+    bool            fRun = true;
+    bool            fRegister = false;
+    bool            fUnregister = false;
+    const char      *pszLogFile = NULL;
+    uint32_t        cHistory = 10;                  // enable log rotation, 10 files
+    uint32_t        uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
+    uint64_t        uHistoryFileSize = 100 * _1M;   // max 100MB per file
+
+    RTGETOPTSTATE   GetOptState;
+    int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
+    AssertRC(vrc);
+
+    RTGETOPTUNION   ValueUnion;
+    while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
+    {
+        switch (vrc)
+        {
+            case 'e':
+                break;
+
+            case 'u':
+                fUnregister = true;
+                fRun = false;
+                break;
+
+            case 'r':
+                fRegister = true;
+                fRun = false;
+                break;
+
+            case 'f':
+                fUnregister = true;
+                fRegister = true;
+                fRun = false;
+                break;
+
+            case 'F':
+                pszLogFile = ValueUnion.psz;
+                break;
+
+            case 'R':
+                cHistory = ValueUnion.u32;
+                break;
+
+            case 'S':
+                uHistoryFileSize = ValueUnion.u64;
+                break;
+
+            case 'I':
+                uHistoryFileTime = ValueUnion.u32;
+                break;
+
+            case 'h':
+            {
+                static WCHAR const s_wszHelpText[] =
+                    L"Options:\n"
+                    L"\n"
+                    L"/RegService\t"   L"register COM out-of-proc service\n"
+                    L"/UnregService\t" L"unregister COM out-of-proc service\n"
+                    L"/ReregService\t" L"unregister and register COM service\n"
+                    L"no options\t"    L"run the service";
+                MessageBoxW(NULL, s_wszHelpText, L"VBoxSDS - Usage", MB_OK);
+                return 0;
+            }
+
+            case 'V':
+            {
+                char *pszText = NULL;
+                RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+
+                PRTUTF16 pwszText = NULL;
+                RTStrToUtf16(pszText, &pwszText);
+
+                MessageBoxW(NULL, pwszText, L"VBoxSDS - Version", MB_OK);
+
+                RTStrFree(pszText);
+                RTUtf16Free(pwszText);
+                return 0;
+            }
+
+            default:
+            {
+                char szTmp[256];
+                RTGetOptFormatError(szTmp, sizeof(szTmp), vrc, &ValueUnion);
+
+                PRTUTF16 pwszText = NULL;
+                RTStrToUtf16(szTmp, &pwszText);
+
+                MessageBoxW(NULL, pwszText, L"VBoxSDS - Syntax error", MB_OK | MB_ICONERROR);
+
+                RTUtf16Free(pwszText);
+                return RTEXITCODE_SYNTAX;
+            }
+        }
+    }
+
+    /*
+     * Default log location is %ProgramData%\VirtualBox\VBoxSDS.log, falling back
+     * on %_CWD%\VBoxSDS.log (where _CWD typicaly is 'C:\Windows\System32').
+     *
+     * We change the current directory to %ProgramData%\VirtualBox\ if possible.
+     *
+     * We only create the log file when running VBoxSDS normally, but not
+     * when registering/unregistering, at least for now.
+     */
+    if (fRun)
+    {
+        char szLogFile[RTPATH_MAX];
+        if (!pszLogFile || !*pszLogFile)
+        {
+            WCHAR wszAppData[MAX_PATH + 16];
+            if (SHGetSpecialFolderPathW(NULL, wszAppData, CSIDL_COMMON_APPDATA, TRUE /*fCreate*/))
+            {
+                char *pszConv = szLogFile;
+                vrc = RTUtf16ToUtf8Ex(wszAppData, RTSTR_MAX, &pszConv, sizeof(szLogFile) - 12, NULL);
+            }
+            else
+                vrc = RTEnvGetUtf8("ProgramData", szLogFile, sizeof(szLogFile) - sizeof("VBoxSDS.log"), NULL);
+            if (RT_SUCCESS(vrc))
+            {
+                vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VirtualBox\\");
+                if (RT_SUCCESS(vrc))
+                {
+                    /* Make sure it exists. */
+                    if (!RTDirExists(szLogFile))
+                        vrc = RTDirCreate(szLogFile, 0755, RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET);
+                    if (RT_SUCCESS(vrc))
+                    {
+                        /* Change into it. */
+                        RTPathSetCurrent(szLogFile);
+                    }
+                }
+            }
+            if (RT_FAILURE(vrc))     /* ignore any failure above */
+                szLogFile[0] = '\0';
+            vrc = RTStrCat(szLogFile, sizeof(szLogFile), "VBoxSDS.log");
+            if (RT_FAILURE(vrc))
+                return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct release log filename: %Rrc", vrc);
+            pszLogFile = szLogFile;
+        }
+
+        RTERRINFOSTATIC ErrInfo;
+        vrc = com::VBoxLogRelCreate("COM Service", pszLogFile,
+                                    RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
+                                    VBOXSDS_LOG_DEFAULT, "VBOXSDS_RELEASE_LOG",
+                                    RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */,
+                                    cHistory, uHistoryFileTime, uHistoryFileSize,
+                                    RTErrInfoInitStatic(&ErrInfo));
+        if (RT_FAILURE(vrc))
+            return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
+    }
+
+
+    /*
+     * Initialize COM.
+     */
+    HRESULT hrcExit = com::Initialize();
+    if (SUCCEEDED(hrcExit))
+    {
+        HRESULT hrcSec = CoInitializeSecurity(NULL,
+                                              -1,
+                                              NULL,
+                                              NULL,
+                                              RPC_C_AUTHN_LEVEL_DEFAULT,
+                                              RPC_C_IMP_LEVEL_IMPERSONATE,//RPC_C_IMP_LEVEL_IMPERSONATE, RPC_C_IMP_LEVEL_DELEGATE
+                                              NULL,
+                                              EOAC_NONE, //EOAC_DYNAMIC_CLOAKING,//EOAC_STATIC_CLOAKING, //EOAC_NONE,
+                                              NULL);
+        LogRelFunc(("VBoxSDS: InitializeSecurity: %x\n", hrcSec));
+
+        /*
+         * Instantiate our COM service class.
+         */
+        CComServiceModule *pServiceModule = new CComServiceModule();
+        if (pServiceModule)
+        {
+            BEGIN_OBJECT_MAP(s_aObjectMap)
+                OBJECT_ENTRY(CLSID_VirtualBoxSDS, VirtualBoxSDS)
+            END_OBJECT_MAP()
+            hrcExit = pServiceModule->init(s_aObjectMap, hInstance, &LIBID_VirtualBox,
+                                           L"VBoxSDS",
+                                           L"VirtualBox system service",
+                                           L"Used as a COM server for VirtualBox API.");
+
+            if (SUCCEEDED(hrcExit))
+            {
+                if (!fRun)
+                {
+                    /*
+                     * Do registration work and quit.
+                     */
+                    // The VBoxProxyStub should do all work for COM registration
+                    if (fUnregister)
+                        hrcExit = pServiceModule->unregisterService();
+                    if (fRegister)
+                        hrcExit = pServiceModule->registerService();
+                }
+                else
+                {
+                    /*
+                     * Run service.
+                     */
+                    hrcExit = pServiceModule->startService(nShowCmd);
+                    LogRelFunc(("VBoxSDS: Calling _ServiceModule.RevokeClassObjects()...\n"));
+                    pServiceModule->RevokeClassObjects();
+                }
+
+                LogRelFunc(("VBoxSDS: Calling _ServiceModule.Term()...\n"));
+                pServiceModule->Term();
+            }
+            else
+                LogRelFunc(("VBoxSDS: new CComServiceModule::Init failed: %Rhrc\n", hrcExit));
+
+            LogRelFunc(("VBoxSDS: deleting pServiceModule (%p)\n", pServiceModule));
+            delete pServiceModule;
+            pServiceModule = NULL;
+        }
+        else
+            LogRelFunc(("VBoxSDS: new CComServiceModule() failed\n"));
+
+        LogRelFunc(("VBoxSDS: Calling com::Shutdown\n"));
+        com::Shutdown();
+    }
+    else
+        LogRelFunc(("VBoxSDS: COM initialization failed: %Rrc\n", hrcExit));
+
+    LogRelFunc(("VBoxSDS: COM service process ends: hrcExit=%Rhrc (%#x)\n", hrcExit, hrcExit));
+    return (int)hrcExit;
+}
Index: /trunk/src/VBox/Main/src-global/win/VBoxSDS.rc
===================================================================
--- /trunk/src/VBox/Main/src-global/win/VBoxSDS.rc	(revision 70507)
+++ /trunk/src/VBox/Main/src-global/win/VBoxSDS.rc	(revision 70507)
@@ -0,0 +1,78 @@
+/* $Id$ */
+/** @file
+ * VBoxSDS - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-2017 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#include "win/resource.h"
+
+VS_VERSION_INFO VERSIONINFO
+  FILEVERSION      VBOX_RC_FILE_VERSION
+  PRODUCTVERSION   VBOX_RC_FILE_VERSION
+  FILEFLAGSMASK    VS_FFI_FILEFLAGSMASK
+  FILEFLAGS        VBOX_RC_FILE_FLAGS
+  FILEOS           VBOX_RC_FILE_OS
+  FILETYPE         VBOX_RC_TYPE_APP
+  FILESUBTYPE      VFT2_UNKNOWN
+BEGIN
+  BLOCK "StringFileInfo"
+  BEGIN
+    BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
+    BEGIN
+      VALUE "FileDescription",  "VirtualBox Global Interface\0"
+      VALUE "InternalName",     "VBoxSDS\0"
+      VALUE "OriginalFilename", "VBoxSDS.exe\0"
+      VALUE "CompanyName",      VBOX_RC_COMPANY_NAME
+      VALUE "FileVersion",      VBOX_RC_FILE_VERSION_STR
+      VALUE "LegalCopyright",   VBOX_RC_LEGAL_COPYRIGHT
+      VALUE "ProductName",      VBOX_RC_PRODUCT_NAME_STR
+      VALUE "ProductVersion",   VBOX_RC_PRODUCT_VERSION_STR
+      VBOX_RC_MORE_STRINGS
+
+      VALUE "OLESelfRegister",  "\0"
+    END
+  END
+  BLOCK "VarFileInfo"
+  BEGIN
+    VALUE "Translation", 0x409, 1252
+  END
+END
+
+/* Creates the application icon. */
+#include "VBoxSDS-icon.rc"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// REGISTRY
+//
+
+// IDR_VIRTUALBOX REGISTRY "VBoxSDS.rgs"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+  IDS_SERVICENAME "VBoxSDS"
+  -100 "VirtualBox Global Service"
+  -101 "Workaround..."
+END
+
+1 TYPELIB "VirtualBox.tlb"
