summaryrefslogtreecommitdiffstats
path: root/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Additions/common/VBoxService/VBoxService-win.cpp')
-rw-r--r--src/VBox/Additions/common/VBoxService/VBoxService-win.cpp670
1 files changed, 670 insertions, 0 deletions
diff --git a/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp b/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp
new file mode 100644
index 00000000..fb2769fb
--- /dev/null
+++ b/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp
@@ -0,0 +1,670 @@
+/* $Id: VBoxService-win.cpp $ */
+/** @file
+ * VBoxService - Guest Additions Service Skeleton, Windows Specific Parts.
+ */
+
+/*
+ * 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
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/system.h> /* For querying OS version. */
+#include <VBox/VBoxGuestLib.h>
+
+#define WIN32_NO_STATUS
+#include <iprt/win/ws2tcpip.h>
+#include <iprt/win/winsock2.h>
+#undef WIN32_NO_STATUS
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/win/iphlpapi.h>
+#include <aclapi.h>
+#include <tlhelp32.h>
+#define _NTDEF_
+#include <Ntsecapi.h>
+
+#include "VBoxServiceInternal.h"
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static DWORD g_dwWinServiceLastStatus = 0;
+SERVICE_STATUS_HANDLE g_hWinServiceStatus = NULL;
+/** The semaphore for the dummy Windows service. */
+static RTSEMEVENT g_WindowsEvent = NIL_RTSEMEVENT;
+
+static SERVICE_TABLE_ENTRY const g_aServiceTable[] =
+{
+ { VBOXSERVICE_NAME, vgsvcWinMain },
+ { NULL, NULL}
+};
+
+/** @name APIs from ADVAPI32.DLL.
+ * @{ */
+decltype(RegisterServiceCtrlHandlerExA) *g_pfnRegisterServiceCtrlHandlerExA; /**< W2K+ */
+decltype(ChangeServiceConfig2A) *g_pfnChangeServiceConfig2A; /**< W2K+ */
+decltype(GetNamedSecurityInfoA) *g_pfnGetNamedSecurityInfoA; /**< NT4+ */
+decltype(SetEntriesInAclA) *g_pfnSetEntriesInAclA; /**< NT4+ */
+decltype(SetNamedSecurityInfoA) *g_pfnSetNamedSecurityInfoA; /**< NT4+ */
+decltype(LsaNtStatusToWinError) *g_pfnLsaNtStatusToWinError; /**< NT3.51+ */
+/** @} */
+
+/** @name API from KERNEL32.DLL
+ * @{ */
+decltype(CreateToolhelp32Snapshot) *g_pfnCreateToolhelp32Snapshot; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Process32First) *g_pfnProcess32First; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Process32Next) *g_pfnProcess32Next; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Module32First) *g_pfnModule32First; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(Module32Next) *g_pfnModule32Next; /**< W2K+, but Geoff says NT4. Hmm. */
+decltype(GetSystemTimeAdjustment) *g_pfnGetSystemTimeAdjustment; /**< NT 3.50+ */
+decltype(SetSystemTimeAdjustment) *g_pfnSetSystemTimeAdjustment; /**< NT 3.50+ */
+/** @} */
+
+/** @name API from NTDLL.DLL
+ * @{ */
+decltype(ZwQuerySystemInformation) *g_pfnZwQuerySystemInformation; /**< NT4 (where as NtQuerySystemInformation is W2K). */
+/** @} */
+
+/** @name API from IPHLPAPI.DLL
+ * @{ */
+decltype(GetAdaptersInfo) *g_pfnGetAdaptersInfo;
+/** @} */
+
+/** @name APIs from WS2_32.DLL
+ * @note WSAIoctl is not present in wsock32.dll, so no point in trying the
+ * fallback here.
+ * @{ */
+decltype(WSAStartup) *g_pfnWSAStartup;
+decltype(WSACleanup) *g_pfnWSACleanup;
+decltype(WSASocketA) *g_pfnWSASocketA;
+decltype(WSAIoctl) *g_pfnWSAIoctl;
+decltype(WSAGetLastError) *g_pfnWSAGetLastError;
+decltype(closesocket) *g_pfnclosesocket;
+decltype(inet_ntoa) *g_pfninet_ntoa;
+
+/** @} */
+
+/**
+ * Resolve APIs not present on older windows versions.
+ */
+void VGSvcWinResolveApis(void)
+{
+ RTLDRMOD hLdrMod;
+#define RESOLVE_SYMBOL(a_fn) do { RT_CONCAT(g_pfn, a_fn) = (decltype(a_fn) *)RTLdrGetFunction(hLdrMod, #a_fn); } while (0)
+
+ /* From ADVAPI32.DLL: */
+ int rc = RTLdrLoadSystem("advapi32.dll", true /*fNoUnload*/, &hLdrMod);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(RegisterServiceCtrlHandlerExA);
+ RESOLVE_SYMBOL(ChangeServiceConfig2A);
+ RESOLVE_SYMBOL(GetNamedSecurityInfoA);
+ RESOLVE_SYMBOL(SetEntriesInAclA);
+ RESOLVE_SYMBOL(SetNamedSecurityInfoA);
+ RESOLVE_SYMBOL(LsaNtStatusToWinError);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From KERNEL32.DLL: */
+ rc = RTLdrLoadSystem("kernel32.dll", true /*fNoUnload*/, &hLdrMod);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(CreateToolhelp32Snapshot);
+ RESOLVE_SYMBOL(Process32First);
+ RESOLVE_SYMBOL(Process32Next);
+ RESOLVE_SYMBOL(Module32First);
+ RESOLVE_SYMBOL(Module32Next);
+ RESOLVE_SYMBOL(GetSystemTimeAdjustment);
+ RESOLVE_SYMBOL(SetSystemTimeAdjustment);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From NTDLL.DLL: */
+ rc = RTLdrLoadSystem("ntdll.dll", true /*fNoUnload*/, &hLdrMod);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(ZwQuerySystemInformation);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From IPHLPAPI.DLL: */
+ rc = RTLdrLoadSystem("iphlpapi.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(GetAdaptersInfo);
+ RTLdrClose(hLdrMod);
+ }
+
+ /* From WS2_32.DLL: */
+ rc = RTLdrLoadSystem("ws2_32.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ RESOLVE_SYMBOL(WSAStartup);
+ RESOLVE_SYMBOL(WSACleanup);
+ RESOLVE_SYMBOL(WSASocketA);
+ RESOLVE_SYMBOL(WSAIoctl);
+ RESOLVE_SYMBOL(WSAGetLastError);
+ RESOLVE_SYMBOL(closesocket);
+ RESOLVE_SYMBOL(inet_ntoa);
+ RTLdrClose(hLdrMod);
+ }
+}
+
+
+/**
+ * @todo Add full unicode support.
+ * @todo Add event log capabilities / check return values.
+ */
+static int vgsvcWinAddAceToObjectsSecurityDescriptor(LPTSTR pszObjName, SE_OBJECT_TYPE enmObjectType, const char *pszTrustee,
+ TRUSTEE_FORM enmTrusteeForm, DWORD dwAccessRights, ACCESS_MODE fAccessMode,
+ DWORD dwInheritance)
+{
+ int rc;
+ if ( g_pfnGetNamedSecurityInfoA
+ && g_pfnSetEntriesInAclA
+ && g_pfnSetNamedSecurityInfoA)
+ {
+ /* Get a pointer to the existing DACL. */
+ PSECURITY_DESCRIPTOR pSD = NULL;
+ PACL pOldDACL = NULL;
+ DWORD rcWin = g_pfnGetNamedSecurityInfoA(pszObjName, enmObjectType, DACL_SECURITY_INFORMATION,
+ NULL, NULL, &pOldDACL, NULL, &pSD);
+ if (rcWin == ERROR_SUCCESS)
+ {
+ /* Initialize an EXPLICIT_ACCESS structure for the new ACE. */
+ EXPLICIT_ACCESSA ExplicitAccess;
+ RT_ZERO(ExplicitAccess);
+ ExplicitAccess.grfAccessPermissions = dwAccessRights;
+ ExplicitAccess.grfAccessMode = fAccessMode;
+ ExplicitAccess.grfInheritance = dwInheritance;
+ ExplicitAccess.Trustee.TrusteeForm = enmTrusteeForm;
+ ExplicitAccess.Trustee.ptstrName = (char *)pszTrustee;
+
+ /* Create a new ACL that merges the new ACE into the existing DACL. */
+ PACL pNewDACL = NULL;
+ rcWin = g_pfnSetEntriesInAclA(1, &ExplicitAccess, pOldDACL, &pNewDACL);
+ if (rcWin == ERROR_SUCCESS)
+ {
+ /* Attach the new ACL as the object's DACL. */
+ rcWin = g_pfnSetNamedSecurityInfoA(pszObjName, enmObjectType, DACL_SECURITY_INFORMATION,
+ NULL, NULL, pNewDACL, NULL);
+ if (rcWin == ERROR_SUCCESS)
+ rc = VINF_SUCCESS;
+ else
+ {
+ VGSvcError("AddAceToObjectsSecurityDescriptor: SetNamedSecurityInfo: Error %u\n", rcWin);
+ rc = RTErrConvertFromWin32(rcWin);
+ }
+ if (pNewDACL)
+ LocalFree(pNewDACL);
+ }
+ else
+ {
+ VGSvcError("AddAceToObjectsSecurityDescriptor: SetEntriesInAcl: Error %u\n", rcWin);
+ rc = RTErrConvertFromWin32(rcWin);
+ }
+ if (pSD)
+ LocalFree(pSD);
+ }
+ else
+ {
+ if (rcWin == ERROR_FILE_NOT_FOUND)
+ VGSvcError("AddAceToObjectsSecurityDescriptor: Object not found/installed: %s\n", pszObjName);
+ else
+ VGSvcError("AddAceToObjectsSecurityDescriptor: GetNamedSecurityInfo: Error %u\n", rcWin);
+ rc = RTErrConvertFromWin32(rcWin);
+ }
+ }
+ else
+ rc = VINF_SUCCESS; /* fake it */
+ return rc;
+}
+
+
+/** Reports our current status to the SCM. */
+static BOOL vgsvcWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint)
+{
+ if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */
+ return FALSE;
+
+ VGSvcVerbose(2, "Setting service status to: %ld\n", dwStatus);
+ g_dwWinServiceLastStatus = dwStatus;
+
+ SERVICE_STATUS ss;
+ RT_ZERO(ss);
+
+ ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ ss.dwCurrentState = dwStatus;
+ /* Don't accept controls when in start pending state. */
+ if (ss.dwCurrentState != SERVICE_START_PENDING)
+ {
+ ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+
+ /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000 or earlier. This makes SCM angry. */
+ char szOSVersion[32];
+ int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSVersion, sizeof(szOSVersion));
+ if (RT_SUCCESS(rc))
+ {
+ if (RTStrVersionCompare(szOSVersion, "5.1") >= 0)
+ ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
+ }
+ else
+ VGSvcError("Error determining OS version, rc=%Rrc\n", rc);
+ }
+
+ ss.dwWin32ExitCode = NO_ERROR;
+ ss.dwServiceSpecificExitCode = 0; /* Not used */
+ ss.dwCheckPoint = dwCheckPoint;
+ ss.dwWaitHint = 3000;
+
+ BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss);
+ if (!fStatusSet)
+ VGSvcError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n",
+ dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError());
+ return fStatusSet;
+}
+
+
+/**
+ * Reports SERVICE_STOP_PENDING to SCM.
+ *
+ * @param uCheckPoint Some number.
+ */
+void VGSvcWinSetStopPendingStatus(uint32_t uCheckPoint)
+{
+ vgsvcWinSetStatus(SERVICE_STOP_PENDING, uCheckPoint);
+}
+
+
+static RTEXITCODE vgsvcWinSetDesc(SC_HANDLE hService)
+{
+ /* On W2K+ there's ChangeServiceConfig2() which lets us set some fields
+ like a longer service description. */
+ if (g_pfnChangeServiceConfig2A)
+ {
+ /** @todo On Vista+ SERVICE_DESCRIPTION also supports localized strings! */
+ SERVICE_DESCRIPTION desc;
+ desc.lpDescription = VBOXSERVICE_DESCRIPTION;
+ if (!g_pfnChangeServiceConfig2A(hService, SERVICE_CONFIG_DESCRIPTION, &desc))
+ {
+ VGSvcError("Cannot set the service description! Error: %ld\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+ }
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Installs the service.
+ */
+RTEXITCODE VGSvcWinInstall(void)
+{
+ VGSvcVerbose(1, "Installing service ...\n");
+
+ TCHAR imagePath[MAX_PATH] = { 0 };
+ GetModuleFileName(NULL, imagePath, sizeof(imagePath));
+
+ SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+ if (hSCManager == NULL)
+ {
+ VGSvcError("Could not open SCM! Error: %ld\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+
+ RTEXITCODE rc = RTEXITCODE_SUCCESS;
+ SC_HANDLE hService = CreateService(hSCManager,
+ VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
+ SERVICE_ALL_ACCESS,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+ imagePath, NULL, NULL, NULL, NULL, NULL);
+ if (hService != NULL)
+ VGSvcVerbose(0, "Service successfully installed!\n");
+ else
+ {
+ DWORD dwErr = GetLastError();
+ switch (dwErr)
+ {
+ case ERROR_SERVICE_EXISTS:
+ VGSvcVerbose(1, "Service already exists, just updating the service config.\n");
+ hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS);
+ if (hService)
+ {
+ if (ChangeServiceConfig(hService,
+ SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
+ SERVICE_DEMAND_START,
+ SERVICE_ERROR_NORMAL,
+ imagePath,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ VBOXSERVICE_FRIENDLY_NAME))
+ VGSvcVerbose(1, "The service config has been successfully updated.\n");
+ else
+ rc = VGSvcError("Could not change service config! Error: %ld\n", GetLastError());
+ }
+ else
+ rc = VGSvcError("Could not open service! Error: %ld\n", GetLastError());
+ break;
+
+ default:
+ rc = VGSvcError("Could not create service! Error: %ld\n", dwErr);
+ break;
+ }
+ }
+
+ if (rc == RTEXITCODE_SUCCESS)
+ rc = vgsvcWinSetDesc(hService);
+
+ CloseServiceHandle(hService);
+ CloseServiceHandle(hSCManager);
+ return rc;
+}
+
+/**
+ * Uninstalls the service.
+ */
+RTEXITCODE VGSvcWinUninstall(void)
+{
+ VGSvcVerbose(1, "Uninstalling service ...\n");
+
+ SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
+ if (hSCManager == NULL)
+ {
+ VGSvcError("Could not open SCM! Error: %d\n", GetLastError());
+ return RTEXITCODE_FAILURE;
+ }
+
+ RTEXITCODE rcExit;
+ SC_HANDLE hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS );
+ if (hService != NULL)
+ {
+ if (DeleteService(hService))
+ {
+ /*
+ * ???
+ */
+ HKEY hKey = NULL;
+ if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System",
+ 0,
+ KEY_ALL_ACCESS,
+ &hKey)
+ == ERROR_SUCCESS)
+ {
+ RegDeleteKey(hKey, VBOXSERVICE_NAME);
+ RegCloseKey(hKey);
+ }
+
+ VGSvcVerbose(0, "Service successfully uninstalled!\n");
+ rcExit = RTEXITCODE_SUCCESS;
+ }
+ else
+ rcExit = VGSvcError("Could not remove service! Error: %d\n", GetLastError());
+ CloseServiceHandle(hService);
+ }
+ else
+ rcExit = VGSvcError("Could not open service! Error: %d\n", GetLastError());
+ CloseServiceHandle(hSCManager);
+
+ return rcExit;
+}
+
+
+static int vgsvcWinStart(void)
+{
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Create a well-known SID for the "Builtin Users" group and modify the ACE
+ * for the shared folders miniport redirector DN (whatever DN means).
+ */
+ PSID pBuiltinUsersSID = NULL;
+ SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
+ if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &pBuiltinUsersSID))
+ {
+ rc = vgsvcWinAddAceToObjectsSecurityDescriptor(TEXT("\\\\.\\VBoxMiniRdrDN"), SE_FILE_OBJECT,
+ (LPTSTR)pBuiltinUsersSID, TRUSTEE_IS_SID,
+ FILE_GENERIC_READ | FILE_GENERIC_WRITE, SET_ACCESS, NO_INHERITANCE);
+ /* If we don't find our "VBoxMiniRdrDN" (for Shared Folders) object above,
+ don't report an error; it just might be not installed. Otherwise this
+ would cause the SCM to hang on starting up the service. */
+ if (rc == VERR_FILE_NOT_FOUND || rc == VERR_PATH_NOT_FOUND)
+ rc = VINF_SUCCESS;
+
+ FreeSid(pBuiltinUsersSID);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Start the service.
+ */
+ vgsvcWinSetStatus(SERVICE_START_PENDING, 0);
+
+ rc = VGSvcStartServices();
+ if (RT_SUCCESS(rc))
+ {
+ vgsvcWinSetStatus(SERVICE_RUNNING, 0);
+ VGSvcMainWait();
+ }
+ else
+ {
+ vgsvcWinSetStatus(SERVICE_STOPPED, 0);
+#if 0 /** @todo r=bird: Enable this if SERVICE_CONTROL_STOP isn't triggered automatically */
+ VGSvcStopServices();
+#endif
+ }
+ }
+ else
+ vgsvcWinSetStatus(SERVICE_STOPPED, 0);
+
+ if (RT_FAILURE(rc))
+ VGSvcError("Service failed to start with rc=%Rrc!\n", rc);
+
+ return rc;
+}
+
+
+/**
+ * Call StartServiceCtrlDispatcher.
+ *
+ * The main() thread invokes this when not started in foreground mode. It
+ * won't return till the service is being shutdown (unless start up fails).
+ *
+ * @returns RTEXITCODE_SUCCESS on normal return after service shutdown.
+ * Something else on failure, error will have been reported.
+ */
+RTEXITCODE VGSvcWinEnterCtrlDispatcher(void)
+{
+ if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
+ return VGSvcError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!\n",
+ GetLastError(), g_pszProgName);
+ return RTEXITCODE_SUCCESS;
+}
+
+
+/**
+ * Event code to description.
+ *
+ * @returns String.
+ * @param dwEvent The event code.
+ */
+static const char *vgsvcWTSStateToString(DWORD dwEvent)
+{
+ switch (dwEvent)
+ {
+ case WTS_CONSOLE_CONNECT: return "A session was connected to the console terminal";
+ case WTS_CONSOLE_DISCONNECT: return "A session was disconnected from the console terminal";
+ case WTS_REMOTE_CONNECT: return "A session connected to the remote terminal";
+ case WTS_REMOTE_DISCONNECT: return "A session was disconnected from the remote terminal";
+ case WTS_SESSION_LOGON: return "A user has logged on to a session";
+ case WTS_SESSION_LOGOFF: return "A user has logged off the session";
+ case WTS_SESSION_LOCK: return "A session has been locked";
+ case WTS_SESSION_UNLOCK: return "A session has been unlocked";
+ case WTS_SESSION_REMOTE_CONTROL: return "A session has changed its remote controlled status";
+#ifdef WTS_SESSION_CREATE
+ case WTS_SESSION_CREATE: return "A session has been created";
+#endif
+#ifdef WTS_SESSION_TERMINATE
+ case WTS_SESSION_TERMINATE: return "The session has been terminated";
+#endif
+ default: return "Uknonwn state";
+ }
+}
+
+
+/**
+ * Common control handler.
+ *
+ * @returns Return code for NT5+.
+ * @param dwControl The control code.
+ */
+static DWORD vgsvcWinCtrlHandlerCommon(DWORD dwControl)
+{
+ DWORD rcRet = NO_ERROR;
+ switch (dwControl)
+ {
+ case SERVICE_CONTROL_INTERROGATE:
+ vgsvcWinSetStatus(g_dwWinServiceLastStatus, 0);
+ break;
+
+ case SERVICE_CONTROL_STOP:
+ case SERVICE_CONTROL_SHUTDOWN:
+ {
+ vgsvcWinSetStatus(SERVICE_STOP_PENDING, 0);
+
+ int rc2 = VGSvcStopServices();
+ if (RT_FAILURE(rc2))
+ rcRet = ERROR_GEN_FAILURE;
+ else
+ {
+ rc2 = VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
+ AssertRC(rc2);
+ }
+
+ vgsvcWinSetStatus(SERVICE_STOPPED, 0);
+ break;
+ }
+
+ default:
+ VGSvcVerbose(1, "Control handler: Function not implemented: %#x\n", dwControl);
+ rcRet = ERROR_CALL_NOT_IMPLEMENTED;
+ break;
+ }
+
+ return rcRet;
+}
+
+
+/**
+ * Callback registered by RegisterServiceCtrlHandler on NT4 and earlier.
+ */
+static VOID WINAPI vgsvcWinCtrlHandlerNt4(DWORD dwControl) RT_NOTHROW_DEF
+{
+ VGSvcVerbose(2, "Control handler (NT4): dwControl=%#x\n", dwControl);
+ vgsvcWinCtrlHandlerCommon(dwControl);
+}
+
+
+/**
+ * Callback registered by RegisterServiceCtrlHandler on NT5 and later.
+ */
+static DWORD WINAPI
+vgsvcWinCtrlHandlerNt5Plus(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext) RT_NOTHROW_DEF
+{
+ VGSvcVerbose(2, "Control handler: dwControl=%#x, dwEventType=%#x\n", dwControl, dwEventType);
+ RT_NOREF1(lpContext);
+
+ switch (dwControl)
+ {
+ default:
+ return vgsvcWinCtrlHandlerCommon(dwControl);
+
+ case SERVICE_CONTROL_SESSIONCHANGE: /* Only Windows 2000 and up. */
+ {
+ AssertPtr(lpEventData);
+ PWTSSESSION_NOTIFICATION pNotify = (PWTSSESSION_NOTIFICATION)lpEventData;
+ Assert(pNotify->cbSize == sizeof(WTSSESSION_NOTIFICATION));
+
+ VGSvcVerbose(1, "Control handler: %s (Session=%ld, Event=%#x)\n",
+ vgsvcWTSStateToString(dwEventType), pNotify->dwSessionId, dwEventType);
+
+ /* Handle all events, regardless of dwEventType. */
+ int rc2 = VGSvcVMInfoSignal();
+ AssertRC(rc2);
+
+ return NO_ERROR;
+ }
+ }
+}
+
+
+static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv)
+{
+ RT_NOREF2(argc, argv);
+ VGSvcVerbose(2, "Registering service control handler ...\n");
+ if (g_pfnRegisterServiceCtrlHandlerExA)
+ g_hWinServiceStatus = g_pfnRegisterServiceCtrlHandlerExA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt5Plus, NULL);
+ else
+ g_hWinServiceStatus = RegisterServiceCtrlHandlerA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt4);
+ if (g_hWinServiceStatus != NULL)
+ {
+ VGSvcVerbose(2, "Service control handler registered.\n");
+ vgsvcWinStart();
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ switch (dwErr)
+ {
+ case ERROR_INVALID_NAME:
+ VGSvcError("Invalid service name!\n");
+ break;
+ case ERROR_SERVICE_DOES_NOT_EXIST:
+ VGSvcError("Service does not exist!\n");
+ break;
+ default:
+ VGSvcError("Could not register service control handle! Error: %ld\n", dwErr);
+ break;
+ }
+ }
+}
+