VirtualBox

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

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

VBoxServiceNT: Temporarily a duplicate of VBoxService.

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

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