VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp@ 70171

Last change on this file since 70171 was 70171, checked in by vboxsync, 7 years ago

VBoxService: Working on removing TARGET_NT4 here too...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 21.3 KB
Line 
1/* $Id: VBoxService-win.cpp 70171 2017-12-15 22:08:07Z vboxsync $ */
2/** @file
3 * VBoxService - Guest Additions Service Skeleton, Windows Specific Parts.
4 */
5
6/*
7 * Copyright (C) 2009-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#undef _WIN32_WINNT /// REMOVE WHEN VBoxServiceNT IS GONE
23#define _WIN32_WINNT 0x0501 /// REMOVE WHEN VBoxServiceNT IS GONE
24#include <iprt/assert.h>
25#include <iprt/err.h>
26#include <iprt/ldr.h>
27#include <iprt/system.h> /* For querying OS version. */
28#include <VBox/VBoxGuestLib.h>
29
30#include <iprt/nt/nt-and-windows.h>
31#include <process.h>
32#include <aclapi.h>
33#include <tlhelp32.h>
34
35#include "VBoxServiceInternal.h"
36
37
38/*********************************************************************************************************************************
39* Internal Functions *
40*********************************************************************************************************************************/
41static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv);
42
43
44/*********************************************************************************************************************************
45* Global Variables *
46*********************************************************************************************************************************/
47static DWORD g_dwWinServiceLastStatus = 0;
48SERVICE_STATUS_HANDLE g_hWinServiceStatus = NULL;
49/** The semaphore for the dummy Windows service. */
50static RTSEMEVENT g_WindowsEvent = NIL_RTSEMEVENT;
51
52static SERVICE_TABLE_ENTRY const g_aServiceTable[] =
53{
54 { VBOXSERVICE_NAME, vgsvcWinMain },
55 { NULL, NULL}
56};
57
58/** @name APIs from ADVAPI32.DLL.
59 * @{ */
60decltype(RegisterServiceCtrlHandlerExA) *g_pfnRegisterServiceCtrlHandlerExA; /**< W2K+ */
61decltype(ChangeServiceConfig2A) *g_pfnChangeServiceConfig2A; /**< W2K+ */
62/** @} */
63
64/** @name API from KERNEL32.DLL
65 * @{ */
66decltype(CreateToolhelp32Snapshot) *g_pfnCreateToolhelp32Snapshot; /**< W2K+, but Geoff says NT4. Hmm. */
67decltype(Process32First) *g_pfnProcess32First; /**< W2K+, but Geoff says NT4. Hmm. */
68decltype(Process32Next) *g_pfnProcess32Next; /**< W2K+, but Geoff says NT4. Hmm. */
69decltype(Module32First) *g_pfnModule32First; /**< W2K+, but Geoff says NT4. Hmm. */
70decltype(Module32Next) *g_pfnModule32Next; /**< W2K+, but Geoff says NT4. Hmm. */
71/** @} */
72
73/** @name API from NTDLL.DLL
74 * @{ */
75decltype(ZwQuerySystemInformation) *g_pfnZwQuerySystemInformation; /**< NT4 (where as NtQuerySystemInformation is W2K). */
76/** @} */
77
78
79/**
80 * Resolve APIs not present on older windows versions.
81 */
82void VGSvcWinResolveApis(void)
83{
84 RTLDRMOD hLdrMod;
85#define RESOLVE_SYMBOL(a_fn) do { RT_CONCAT(g_pfn, a_fn) = (decltype(a_fn) *)RTLdrGetFunction(hLdrMod, #a_fn); } while (0)
86
87 /* From ADVAPI32.DLL: */
88 int rc = RTLdrLoadSystem("advapi32.dll", false /*fNoUnload*/, &hLdrMod);
89 AssertRC(rc);
90 if (RT_SUCCESS(rc))
91 {
92 RESOLVE_SYMBOL(RegisterServiceCtrlHandlerExA);
93 RESOLVE_SYMBOL(ChangeServiceConfig2A);
94 RTLdrClose(hLdrMod);
95 }
96
97 /* From KERNEL32.DLL: */
98 rc = RTLdrLoadSystem("kernel32.dll", false /*fNoUnload*/, &hLdrMod);
99 AssertRC(rc);
100 if (RT_SUCCESS(rc))
101 {
102 RESOLVE_SYMBOL(CreateToolhelp32Snapshot);
103 RESOLVE_SYMBOL(Process32First);
104 RESOLVE_SYMBOL(Process32Next);
105 RESOLVE_SYMBOL(Module32First);
106 RESOLVE_SYMBOL(Module32Next);
107 RTLdrClose(hLdrMod);
108 }
109
110 /* From NTDLL.DLL: */
111 rc = RTLdrLoadSystem("ntdll.dll", false /*fNoUnload*/, &hLdrMod);
112 AssertRC(rc);
113 if (RT_SUCCESS(rc))
114 {
115 RESOLVE_SYMBOL(ZwQuerySystemInformation);
116 RTLdrClose(hLdrMod);
117 }
118}
119
120
121/**
122 * @todo Format code style.
123 * @todo Add full unicode support.
124 * @todo Add event log capabilities / check return values.
125 */
126static DWORD vgsvcWinAddAceToObjectsSecurityDescriptor(LPTSTR pszObjName,
127 SE_OBJECT_TYPE ObjectType,
128 LPTSTR pszTrustee,
129 TRUSTEE_FORM TrusteeForm,
130 DWORD dwAccessRights,
131 ACCESS_MODE AccessMode,
132 DWORD dwInheritance)
133{
134 PACL pOldDACL = NULL, pNewDACL = NULL;
135 PSECURITY_DESCRIPTOR pSD = NULL;
136 EXPLICIT_ACCESS ea;
137
138 if (NULL == pszObjName)
139 return ERROR_INVALID_PARAMETER;
140
141 /* Get a pointer to the existing DACL. */
142 DWORD dwRes = GetNamedSecurityInfo(pszObjName, ObjectType, DACL_SECURITY_INFORMATION, NULL, NULL, &pOldDACL, NULL, &pSD);
143 if (ERROR_SUCCESS != dwRes)
144 {
145 if (dwRes == ERROR_FILE_NOT_FOUND)
146 VGSvcError("AddAceToObjectsSecurityDescriptor: Object not found/installed: %s\n", pszObjName);
147 else
148 VGSvcError("AddAceToObjectsSecurityDescriptor: GetNamedSecurityInfo: Error %u\n", dwRes);
149 goto l_Cleanup;
150 }
151
152 /* Initialize an EXPLICIT_ACCESS structure for the new ACE. */
153 ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
154 ea.grfAccessPermissions = dwAccessRights;
155 ea.grfAccessMode = AccessMode;
156 ea.grfInheritance= dwInheritance;
157 ea.Trustee.TrusteeForm = TrusteeForm;
158 ea.Trustee.ptstrName = pszTrustee;
159
160 /* Create a new ACL that merges the new ACE into the existing DACL. */
161 dwRes = SetEntriesInAcl(1, &ea, pOldDACL, &pNewDACL);
162 if (ERROR_SUCCESS != dwRes)
163 {
164 VGSvcError("AddAceToObjectsSecurityDescriptor: SetEntriesInAcl: Error %u\n", dwRes);
165 goto l_Cleanup;
166 }
167
168 /* Attach the new ACL as the object's DACL. */
169 dwRes = SetNamedSecurityInfo(pszObjName, ObjectType, DACL_SECURITY_INFORMATION, NULL, NULL, pNewDACL, NULL);
170 if (ERROR_SUCCESS != dwRes)
171 {
172 VGSvcError("AddAceToObjectsSecurityDescriptor: SetNamedSecurityInfo: Error %u\n", dwRes);
173 goto l_Cleanup;
174 }
175
176 /** @todo get rid of that spaghetti jump ... */
177l_Cleanup:
178
179 if(pSD != NULL)
180 LocalFree((HLOCAL) pSD);
181 if(pNewDACL != NULL)
182 LocalFree((HLOCAL) pNewDACL);
183
184 return dwRes;
185}
186
187
188/** Reports our current status to the SCM. */
189static BOOL vgsvcWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint)
190{
191 if (g_hWinServiceStatus == NULL) /* Program could be in testing mode, so no service environment available. */
192 return FALSE;
193
194 VGSvcVerbose(2, "Setting service status to: %ld\n", dwStatus);
195 g_dwWinServiceLastStatus = dwStatus;
196
197 SERVICE_STATUS ss;
198 RT_ZERO(ss);
199
200 ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
201 ss.dwCurrentState = dwStatus;
202 /* Don't accept controls when in start pending state. */
203 if (ss.dwCurrentState != SERVICE_START_PENDING)
204 {
205 ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
206
207 /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000 or earlier. This makes SCM angry. */
208 char szOSVersion[32];
209 int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szOSVersion, sizeof(szOSVersion));
210 if (RT_SUCCESS(rc))
211 {
212 if (RTStrVersionCompare(szOSVersion, "5.1") >= 0)
213 ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE;
214 }
215 else
216 VGSvcError("Error determining OS version, rc=%Rrc\n", rc);
217 }
218
219 ss.dwWin32ExitCode = NO_ERROR;
220 ss.dwServiceSpecificExitCode = 0; /* Not used */
221 ss.dwCheckPoint = dwCheckPoint;
222 ss.dwWaitHint = 3000;
223
224 BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss);
225 if (!fStatusSet)
226 VGSvcError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n",
227 dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError());
228 return fStatusSet;
229}
230
231
232/**
233 * Reports SERVICE_STOP_PENDING to SCM.
234 *
235 * @param uCheckPoint Some number.
236 */
237void VGSvcWinSetStopPendingStatus(uint32_t uCheckPoint)
238{
239 vgsvcWinSetStatus(SERVICE_STOP_PENDING, uCheckPoint);
240}
241
242
243static RTEXITCODE vgsvcWinSetDesc(SC_HANDLE hService)
244{
245 /* On W2K+ there's ChangeServiceConfig2() which lets us set some fields
246 like a longer service description. */
247 if (g_pfnChangeServiceConfig2A)
248 {
249 /** @todo On Vista+ SERVICE_DESCRIPTION also supports localized strings! */
250 SERVICE_DESCRIPTION desc;
251 desc.lpDescription = VBOXSERVICE_DESCRIPTION;
252 if (!g_pfnChangeServiceConfig2A(hService, SERVICE_CONFIG_DESCRIPTION, &desc))
253 {
254 VGSvcError("Cannot set the service description! Error: %ld\n", GetLastError());
255 return RTEXITCODE_FAILURE;
256 }
257 }
258 return RTEXITCODE_SUCCESS;
259}
260
261
262/**
263 * Installs the service.
264 */
265RTEXITCODE VGSvcWinInstall(void)
266{
267 VGSvcVerbose(1, "Installing service ...\n");
268
269 TCHAR imagePath[MAX_PATH] = { 0 };
270 GetModuleFileName(NULL, imagePath, sizeof(imagePath));
271
272 SC_HANDLE hSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
273 if (hSCManager == NULL)
274 {
275 VGSvcError("Could not open SCM! Error: %ld\n", GetLastError());
276 return RTEXITCODE_FAILURE;
277 }
278
279 RTEXITCODE rc = RTEXITCODE_SUCCESS;
280 SC_HANDLE hService = CreateService(hSCManager,
281 VBOXSERVICE_NAME, VBOXSERVICE_FRIENDLY_NAME,
282 SERVICE_ALL_ACCESS,
283 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
284 SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
285 imagePath, NULL, NULL, NULL, NULL, NULL);
286 if (hService != NULL)
287 VGSvcVerbose(0, "Service successfully installed!\n");
288 else
289 {
290 DWORD dwErr = GetLastError();
291 switch (dwErr)
292 {
293 case ERROR_SERVICE_EXISTS:
294 VGSvcVerbose(1, "Service already exists, just updating the service config.\n");
295 hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS);
296 if (hService)
297 {
298 if (ChangeServiceConfig (hService,
299 SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS,
300 SERVICE_DEMAND_START,
301 SERVICE_ERROR_NORMAL,
302 imagePath,
303 NULL,
304 NULL,
305 NULL,
306 NULL,
307 NULL,
308 VBOXSERVICE_FRIENDLY_NAME))
309 VGSvcVerbose(1, "The service config has been successfully updated.\n");
310 else
311 rc = VGSvcError("Could not change service config! Error: %ld\n", GetLastError());
312 }
313 else
314 rc = VGSvcError("Could not open service! Error: %ld\n", GetLastError());
315 break;
316
317 default:
318 rc = VGSvcError("Could not create service! Error: %ld\n", dwErr);
319 break;
320 }
321 }
322
323 if (rc == RTEXITCODE_SUCCESS)
324 rc = vgsvcWinSetDesc(hService);
325
326 CloseServiceHandle(hService);
327 CloseServiceHandle(hSCManager);
328 return rc;
329}
330
331/**
332 * Uninstalls the service.
333 */
334RTEXITCODE VGSvcWinUninstall(void)
335{
336 VGSvcVerbose(1, "Uninstalling service ...\n");
337
338 SC_HANDLE hSCManager = OpenSCManager(NULL,NULL,SC_MANAGER_ALL_ACCESS);
339 if (hSCManager == NULL)
340 {
341 VGSvcError("Could not open SCM! Error: %d\n", GetLastError());
342 return RTEXITCODE_FAILURE;
343 }
344
345 RTEXITCODE rcExit;
346 SC_HANDLE hService = OpenService(hSCManager, VBOXSERVICE_NAME, SERVICE_ALL_ACCESS );
347 if (hService != NULL)
348 {
349 if (DeleteService(hService))
350 {
351 /*
352 * ???
353 */
354 HKEY hKey = NULL;
355 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
356 "SYSTEM\\CurrentControlSet\\Services\\EventLog\\System",
357 0,
358 KEY_ALL_ACCESS,
359 &hKey)
360 == ERROR_SUCCESS)
361 {
362 RegDeleteKey(hKey, VBOXSERVICE_NAME);
363 RegCloseKey(hKey);
364 }
365
366 VGSvcVerbose(0, "Service successfully uninstalled!\n");
367 rcExit = RTEXITCODE_SUCCESS;
368 }
369 else
370 rcExit = VGSvcError("Could not remove service! Error: %d\n", GetLastError());
371 CloseServiceHandle(hService);
372 }
373 else
374 rcExit = VGSvcError("Could not open service! Error: %d\n", GetLastError());
375 CloseServiceHandle(hSCManager);
376
377 return rcExit;
378}
379
380
381static int vgsvcWinStart(void)
382{
383 int rc = VINF_SUCCESS;
384
385 /* Create a well-known SID for the "Builtin Users" group. */
386 PSID pBuiltinUsersSID = NULL;
387 SID_IDENTIFIER_AUTHORITY SIDAuthWorld = SECURITY_LOCAL_SID_AUTHORITY;
388 if (AllocateAndInitializeSid(&SIDAuthWorld, 1, SECURITY_LOCAL_RID, 0, 0, 0, 0, 0, 0, 0, &pBuiltinUsersSID))
389 {
390 DWORD dwRes = vgsvcWinAddAceToObjectsSecurityDescriptor(TEXT("\\\\.\\VBoxMiniRdrDN"),
391 SE_FILE_OBJECT,
392 (LPTSTR)pBuiltinUsersSID,
393 TRUSTEE_IS_SID,
394 FILE_GENERIC_READ | FILE_GENERIC_WRITE,
395 SET_ACCESS,
396 NO_INHERITANCE);
397 if (dwRes != ERROR_SUCCESS)
398 {
399 if (dwRes == ERROR_FILE_NOT_FOUND)
400 {
401 /* If we don't find our "VBoxMiniRdrDN" (for Shared Folders) object above,
402 don't report an error; it just might be not installed. Otherwise this
403 would cause the SCM to hang on starting up the service. */
404 rc = VINF_SUCCESS;
405 }
406 else
407 rc = RTErrConvertFromWin32(dwRes);
408 }
409 }
410 else
411 rc = RTErrConvertFromWin32(GetLastError());
412
413 if (RT_SUCCESS(rc))
414 {
415 vgsvcWinSetStatus(SERVICE_START_PENDING, 0);
416
417 rc = VGSvcStartServices();
418 if (RT_SUCCESS(rc))
419 {
420 vgsvcWinSetStatus(SERVICE_RUNNING, 0);
421 VGSvcMainWait();
422 }
423 else
424 {
425 vgsvcWinSetStatus(SERVICE_STOPPED, 0);
426#if 0 /** @todo r=bird: Enable this if SERVICE_CONTROL_STOP isn't triggered automatically */
427 VGSvcStopServices();
428#endif
429 }
430 }
431 else
432 vgsvcWinSetStatus(SERVICE_STOPPED, 0);
433
434 if (RT_FAILURE(rc))
435 VGSvcError("Service failed to start with rc=%Rrc!\n", rc);
436
437 return rc;
438}
439
440
441/**
442 * Call StartServiceCtrlDispatcher.
443 *
444 * The main() thread invokes this when not started in foreground mode. It
445 * won't return till the service is being shutdown (unless start up fails).
446 *
447 * @returns RTEXITCODE_SUCCESS on normal return after service shutdown.
448 * Something else on failure, error will have been reported.
449 */
450RTEXITCODE VGSvcWinEnterCtrlDispatcher(void)
451{
452 if (!StartServiceCtrlDispatcher(&g_aServiceTable[0]))
453 return VGSvcError("StartServiceCtrlDispatcher: %u. Please start %s with option -f (foreground)!\n",
454 GetLastError(), g_pszProgName);
455 return RTEXITCODE_SUCCESS;
456}
457
458
459/**
460 * Event code to description.
461 *
462 * @returns String.
463 * @param dwEvent The event code.
464 */
465static const char *vgsvcWTSStateToString(DWORD dwEvent)
466{
467 switch (dwEvent)
468 {
469 case WTS_CONSOLE_CONNECT: return "A session was connected to the console terminal";
470 case WTS_CONSOLE_DISCONNECT: return "A session was disconnected from the console terminal";
471 case WTS_REMOTE_CONNECT: return "A session connected to the remote terminal";
472 case WTS_REMOTE_DISCONNECT: return "A session was disconnected from the remote terminal";
473 case WTS_SESSION_LOGON: return "A user has logged on to a session";
474 case WTS_SESSION_LOGOFF: return "A user has logged off the session";
475 case WTS_SESSION_LOCK: return "A session has been locked";
476 case WTS_SESSION_UNLOCK: return "A session has been unlocked";
477 case WTS_SESSION_REMOTE_CONTROL: return "A session has changed its remote controlled status";
478#ifdef WTS_SESSION_CREATE
479 case WTS_SESSION_CREATE: return "A session has been created";
480#endif
481#ifdef WTS_SESSION_TERMINATE
482 case WTS_SESSION_TERMINATE: return "The session has been terminated";
483#endif
484 default: return "Uknonwn state";
485 }
486}
487
488
489/**
490 * Common control handler.
491 *
492 * @returns Return code for NT5+.
493 * @param dwControl The control code.
494 */
495static DWORD vgsvcWinCtrlHandlerCommon(DWORD dwControl)
496{
497 DWORD rcRet = NO_ERROR;
498 switch (dwControl)
499 {
500 case SERVICE_CONTROL_INTERROGATE:
501 vgsvcWinSetStatus(g_dwWinServiceLastStatus, 0);
502 break;
503
504 case SERVICE_CONTROL_STOP:
505 case SERVICE_CONTROL_SHUTDOWN:
506 {
507 vgsvcWinSetStatus(SERVICE_STOP_PENDING, 0);
508
509 int rc2 = VGSvcStopServices();
510 if (RT_FAILURE(rc2))
511 rcRet = ERROR_GEN_FAILURE;
512 else
513 {
514 rc2 = VGSvcReportStatus(VBoxGuestFacilityStatus_Terminated);
515 AssertRC(rc2);
516 }
517
518 vgsvcWinSetStatus(SERVICE_STOPPED, 0);
519 break;
520 }
521
522 default:
523 VGSvcVerbose(1, "Control handler: Function not implemented: %#x\n", dwControl);
524 rcRet = ERROR_CALL_NOT_IMPLEMENTED;
525 break;
526 }
527
528 return rcRet;
529}
530
531
532/**
533 * Callback registered by RegisterServiceCtrlHandler on NT4 and earlier.
534 */
535static VOID WINAPI vgsvcWinCtrlHandlerNt4(DWORD dwControl)
536{
537 VGSvcVerbose(2, "Control handler (NT4): dwControl=%#x\n", dwControl);
538 vgsvcWinCtrlHandlerCommon(dwControl);
539}
540
541
542/**
543 * Callback registered by RegisterServiceCtrlHandler on NT5 and later.
544 */
545static DWORD WINAPI vgsvcWinCtrlHandlerNt5Plus(DWORD dwControl, DWORD dwEventType, LPVOID lpEventData, LPVOID lpContext)
546{
547 VGSvcVerbose(2, "Control handler: dwControl=%#x, dwEventType=%#x\n", dwControl, dwEventType);
548 RT_NOREF1(lpContext);
549
550 switch (dwControl)
551 {
552 default:
553 return vgsvcWinCtrlHandlerCommon(dwControl);
554
555 case SERVICE_CONTROL_SESSIONCHANGE: /* Only Windows 2000 and up. */
556 {
557 AssertPtr(lpEventData);
558 PWTSSESSION_NOTIFICATION pNotify = (PWTSSESSION_NOTIFICATION)lpEventData;
559 Assert(pNotify->cbSize == sizeof(WTSSESSION_NOTIFICATION));
560
561 VGSvcVerbose(1, "Control handler: %s (Session=%ld, Event=%#x)\n",
562 vgsvcWTSStateToString(dwEventType), pNotify->dwSessionId, dwEventType);
563
564 /* Handle all events, regardless of dwEventType. */
565 int rc2 = VGSvcVMInfoSignal();
566 AssertRC(rc2);
567
568 return NO_ERROR;
569 }
570 }
571}
572
573
574static void WINAPI vgsvcWinMain(DWORD argc, LPTSTR *argv)
575{
576 RT_NOREF2(argc, argv);
577 VGSvcVerbose(2, "Registering service control handler ...\n");
578 if (g_pfnRegisterServiceCtrlHandlerExA)
579 g_hWinServiceStatus = g_pfnRegisterServiceCtrlHandlerExA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt5Plus, NULL);
580 else
581 g_hWinServiceStatus = RegisterServiceCtrlHandlerA(VBOXSERVICE_NAME, vgsvcWinCtrlHandlerNt4);
582 if (g_hWinServiceStatus != NULL)
583 {
584 VGSvcVerbose(2, "Service control handler registered.\n");
585 vgsvcWinStart();
586 }
587 else
588 {
589 DWORD dwErr = GetLastError();
590 switch (dwErr)
591 {
592 case ERROR_INVALID_NAME:
593 VGSvcError("Invalid service name!\n");
594 break;
595 case ERROR_SERVICE_DOES_NOT_EXIST:
596 VGSvcError("Service does not exist!\n");
597 break;
598 default:
599 VGSvcError("Could not register service control handle! Error: %ld\n", dwErr);
600 break;
601 }
602 }
603}
604
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette