VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Installer/VBoxDrvInst.cpp@ 96686

Last change on this file since 96686 was 96605, checked in by vboxsync, 2 years ago

Add/NT/Inst: Merged the NT4 video driver helper executable VBoxGuestDrvInst.exe into VBoxDrvInst.exe, it only had one function and one structure we need to be careful with. bugref:10261

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 87.2 KB
Line 
1/* $Id: VBoxDrvInst.cpp 96605 2022-09-05 19:08:21Z vboxsync $ */
2/** @file
3 * VBoxDrvInst - Driver and service installation helper for Windows guests.
4 */
5
6/*
7 * Copyright (C) 2011-2022 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#ifndef UNICODE
33# define UNICODE
34#endif
35
36#include <iprt/alloca.h>
37#include <VBox/version.h>
38
39#include <iprt/win/windows.h>
40#include <iprt/win/setupapi.h>
41#include <devguid.h>
42#include <RegStr.h>
43
44#include <iprt/asm.h>
45#include <iprt/mem.h>
46#include <iprt/path.h> /* RTPATH_IS_SEP */
47#include <iprt/string.h>
48#include <iprt/utf16.h>
49
50/* Exit codes */
51#define EXIT_OK (0)
52#define EXIT_REBOOT (1)
53#define EXIT_FAIL (2)
54#define EXIT_USAGE (3)
55
56/* Must include after EXIT_FAIL was defined! Sorry for the mixing up of thing. */
57#include "NoCrtOutput.h"
58
59
60/*********************************************************************************************************************************
61* Defines *
62*********************************************************************************************************************************/
63/* Defines */
64#define DRIVER_PACKAGE_REPAIR 0x00000001
65#define DRIVER_PACKAGE_SILENT 0x00000002
66#define DRIVER_PACKAGE_FORCE 0x00000004
67#define DRIVER_PACKAGE_ONLY_IF_DEVICE_PRESENT 0x00000008
68#define DRIVER_PACKAGE_LEGACY_MODE 0x00000010
69#define DRIVER_PACKAGE_DELETE_FILES 0x00000020
70
71/* DIFx error codes */
72/** @todo any reason why we're not using difxapi.h instead of these redefinitions? */
73#ifndef ERROR_DRIVER_STORE_ADD_FAILED
74# define ERROR_DRIVER_STORE_ADD_FAILED (APPLICATION_ERROR_MASK | ERROR_SEVERITY_ERROR | 0x0247L)
75#endif
76#define ERROR_DEPENDENT_APPLICATIONS_EXIST (APPLICATION_ERROR_MASK | ERROR_SEVERITY_ERROR|0x300)
77#define ERROR_DRIVER_PACKAGE_NOT_IN_STORE (APPLICATION_ERROR_MASK | ERROR_SEVERITY_ERROR | 0x302)
78
79/* Registry string list flags */
80#define VBOX_REG_STRINGLIST_NONE 0x00000000 /**< No flags set. */
81#define VBOX_REG_STRINGLIST_ALLOW_DUPLICATES 0x00000001 /**< Allows duplicates in list when adding a value. */
82
83#ifdef DEBUG
84# define VBOX_DRVINST_LOGFILE "C:\\Temp\\VBoxDrvInstDIFx.log"
85#endif
86
87/** NT4: The video service name. */
88#define VBOXGUEST_NT4_VIDEO_NAME "VBoxVideo"
89/** NT4: The video inf file name */
90#define VBOXGUEST_NT4_VIDEO_INF_NAME "VBoxVideo.inf"
91
92
93/*********************************************************************************************************************************
94* Structures and Typedefs *
95*********************************************************************************************************************************/
96typedef struct
97{
98 PWSTR pApplicationId;
99 PWSTR pDisplayName;
100 PWSTR pProductName;
101 PWSTR pMfgName;
102} INSTALLERINFO, *PINSTALLERINFO;
103typedef const PINSTALLERINFO PCINSTALLERINFO;
104
105typedef enum
106{
107 DIFXAPI_SUCCESS,
108 DIFXAPI_INFO,
109 DIFXAPI_WARNING,
110 DIFXAPI_ERROR
111} DIFXAPI_LOG;
112
113typedef void (__cdecl * DIFXAPILOGCALLBACK_W)(DIFXAPI_LOG Event, DWORD Error, PCWSTR EventDescription, PVOID CallbackContext);
114
115typedef DWORD (WINAPI *fnDriverPackageInstall)(PCTSTR DriverPackageInfPath, DWORD Flags, PCINSTALLERINFO pInstallerInfo, BOOL *pNeedReboot);
116fnDriverPackageInstall g_pfnDriverPackageInstall = NULL;
117
118typedef DWORD (WINAPI *fnDriverPackageUninstall)(PCTSTR DriverPackageInfPath, DWORD Flags, PCINSTALLERINFO pInstallerInfo, BOOL *pNeedReboot);
119fnDriverPackageUninstall g_pfnDriverPackageUninstall = NULL;
120
121typedef VOID (WINAPI *fnDIFXAPISetLogCallback)(DIFXAPILOGCALLBACK_W LogCallback, PVOID CallbackContext);
122fnDIFXAPISetLogCallback g_pfnDIFXAPISetLogCallback = NULL;
123
124
125
126static char *ArgToUtf8(wchar_t const *pwszString, const char *pszArgName)
127{
128 char *pszUtf8 = NULL;
129 int rc = RTUtf16ToUtf8(pwszString, &pszUtf8);
130 if (RT_SUCCESS(rc))
131 return pszUtf8;
132 ErrorMsgBegin("RTUtf16ToUtf8 failed on '");
133 ErrorMsgStr(pszArgName);
134 ErrorMsgStr("': ");
135 ErrorMsgErrVal(rc, true);
136 ErrorMsgEnd(NULL);
137 return NULL;
138}
139
140/**
141 * @returns false.
142 * @note Frees pszValue
143 */
144static bool ErrorArtToNum(int rc, const char *pszArgName, char *pszValue)
145{
146 ErrorMsgBegin("Failed to convert the '");
147 ErrorMsgStr(pszArgName);
148 ErrorMsgStr("' value '");
149 ErrorMsgStr(pszValue);
150 ErrorMsgStr("' to a number: ");
151 ErrorMsgErrVal(rc, true);
152 ErrorMsgEnd(NULL);
153 return false;
154}
155
156
157static bool ArgToUInt32Full(wchar_t const *pwszString, const char *pszArgName, uint32_t *puValue)
158{
159 char *pszValue = ArgToUtf8(pwszString, pszArgName);
160 if (!pszValue)
161 return false;
162 int rc = RTStrToUInt32Full(pszValue, 0, puValue);
163 if (RT_FAILURE(rc))
164 return ErrorArtToNum(rc, pszArgName, pszValue);
165 RTStrFree(pszValue);
166 return true;
167}
168
169
170static bool ArgToUInt64Full(wchar_t const *pwszString, const char *pszArgName, uint64_t *puValue)
171{
172 char *pszValue = ArgToUtf8(pwszString, pszArgName);
173 if (!pszValue)
174 return false;
175 int rc = RTStrToUInt64Full(pszValue, 0, puValue);
176 if (rc != VINF_SUCCESS)
177 return ErrorArtToNum(rc, pszArgName, pszValue);
178 RTStrFree(pszValue);
179 return true;
180}
181
182
183
184static bool GetErrorMsg(DWORD dwLastError, wchar_t *pwszMsg, DWORD cwcMsg)
185{
186 if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwLastError, 0, pwszMsg, cwcMsg, NULL) == 0)
187 return false;
188 wchar_t *pwc = RTUtf16Chr(pwszMsg, '\r');
189 if (pwc)
190 *pwc = '\0';
191 return true;
192}
193
194
195/**
196 * Log callback for DIFxAPI calls.
197 *
198 * @param enmEvent Event logging level.
199 * @param dwError Event error number.
200 * @param pwszEventDesc Event description text.
201 * @param pvCtx Log file handle, if we've got one.
202 */
203static void __cdecl VBoxDIFxLogCallback(DIFXAPI_LOG enmEvent, DWORD dwError, PCWSTR pwszEventDesc, PVOID pvCtx)
204{
205 const char *pszEvent;
206 switch (enmEvent)
207 {
208 case DIFXAPI_SUCCESS: pszEvent = "DIFXAPI_SUCCESS"; break;
209 case DIFXAPI_INFO: pszEvent = "DIFXAPI_INFO"; break;
210 case DIFXAPI_WARNING: pszEvent = "DIFXAPI_WARNING"; break;
211 case DIFXAPI_ERROR: pszEvent = "DIFXAPI_ERROR"; break;
212 default: pszEvent = "DIFXAPI_<unknown>"; break;
213 }
214
215 /*
216 * Log to standard output:
217 */
218 PrintStr(pszEvent);
219 if (dwError == 0)
220 PrintStr(": ");
221 else
222 {
223 PrintStr(": ERROR: ");
224 PrintX64(dwError);
225 PrintStr(" - ");
226 }
227 PrintWStr(pwszEventDesc);
228 PrintStr("\r\n");
229
230 /*
231 * Write to the log file if we have one (wide char format).
232 */
233 HANDLE const hLogFile = (HANDLE)pvCtx;
234 if (hLogFile != INVALID_HANDLE_VALUE)
235 {
236 /* "event: err - desc\r\n" */
237 wchar_t wszBuf[168];
238 RTUtf16CopyAscii(wszBuf, RT_ELEMENTS(wszBuf), pszEvent);
239 RTUtf16CatAscii(wszBuf, RT_ELEMENTS(wszBuf), ": ");
240 char szVal[128];
241 RTStrFormatU32(szVal, sizeof(szVal), dwError, 10, 0, 0, 0);
242 RTUtf16CatAscii(wszBuf, RT_ELEMENTS(wszBuf), szVal);
243 RTUtf16CatAscii(wszBuf, RT_ELEMENTS(wszBuf), " - ");
244
245 DWORD dwIgn;
246 WriteFile(hLogFile, wszBuf, (DWORD)(RTUtf16Len(wszBuf) * sizeof(wchar_t)), &dwIgn, NULL);
247 WriteFile(hLogFile, pwszEventDesc, (DWORD)(RTUtf16Len(pwszEventDesc) * sizeof(wchar_t)), &dwIgn, NULL);
248 WriteFile(hLogFile, L"\r\n", 2 * sizeof(wchar_t), &dwIgn, NULL);
249 }
250}
251
252
253/**
254 * Loads a DLL from the same directory as the installer.
255 *
256 * @returns Module handle, NULL on failure (fully messaged).
257 * @param pwszName The DLL name.
258 */
259static HMODULE LoadAppDll(const wchar_t *pwszName)
260{
261 /* Get the process image path. */
262 WCHAR wszPath[MAX_PATH];
263 UINT cwcPath = GetModuleFileNameW(NULL, wszPath, MAX_PATH);
264 if (!cwcPath || cwcPath >= MAX_PATH)
265 {
266 ErrorMsgLastErr("LoadAppDll: GetModuleFileNameW failed");
267 return NULL;
268 }
269
270 /* Drop the image filename. */
271 do
272 {
273 cwcPath--;
274 if (RTPATH_IS_SEP(wszPath[cwcPath]))
275 {
276 cwcPath++;
277 wszPath[cwcPath] = '\0';
278 break;
279 }
280 } while (cwcPath > 0);
281
282 if (!cwcPath) /* This should be impossible */
283 {
284 ErrorMsg("LoadAppDll: GetModuleFileNameW returned no path!");
285 return NULL;
286 }
287
288 /* Append the dll name if we can. */
289 size_t const cwcName = RTUtf16Len(pwszName);
290 if (cwcPath + cwcName >= RT_ELEMENTS(wszPath))
291 {
292 ErrorMsgSWSWS("LoadAppDll: Path '", wszPath, "' too long when adding '", pwszName, "'");
293 return NULL;
294 }
295 memcpy(&wszPath[cwcPath], pwszName, (cwcName + 1) * sizeof(wszPath[0]));
296
297 /* Try load the module. We will try restrict the library search to the
298 system32 directory if supported by the OS. Older OSes doesn't support
299 this, so we fall back on full search in that case. */
300 HMODULE hMod = LoadLibraryExW(wszPath, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
301 if (hMod == NULL && GetLastError() == ERROR_INVALID_PARAMETER)
302 hMod = LoadLibraryExW(wszPath, NULL, 0);
303 if (!hMod)
304 ErrorMsgLastErrSWS("LoadAppDll: LoadLibraryExW failed on '", wszPath, "'");
305 return hMod;
306}
307
308
309/**
310 * Installs or uninstalls a driver.
311 *
312 * @return Exit code (EXIT_OK, EXIT_FAIL)
313 * @param fInstall Set to @c true for installation, and @c false
314 * for uninstallation.
315 * @param pwszDriverPath Path to the driver's .INF file.
316 * @param fSilent Set to @c true for silent installation.
317 * @param pwszLogFile Pointer to full qualified path to log file to be
318 * written during installation. Optional.
319 */
320static int VBoxInstallDriver(const BOOL fInstall, const wchar_t *pwszDriverPath, bool fSilent, const wchar_t *pwszLogFile)
321{
322 /*
323 * Windows 2000 and later.
324 */
325 OSVERSIONINFO VerInfo = { sizeof(VerInfo) };
326 GetVersionEx(&VerInfo);
327 if (VerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT)
328 return ErrorMsg("Platform not supported for driver (un)installation!");
329 if (VerInfo.dwMajorVersion < 5)
330 return ErrorMsg("Platform too old to be supported for driver (un)installation!");
331
332 /*
333 * Get the full path to the INF file.
334 */
335 wchar_t wszFullDriverInf[MAX_PATH];
336 if (GetFullPathNameW(pwszDriverPath, MAX_PATH, wszFullDriverInf, NULL) ==0 )
337 return ErrorMsgLastErrSWS("GetFullPathNameW failed on '", pwszDriverPath, "'");
338
339 /*
340 * Load DIFxAPI.dll from our application directory and resolve the symbols we need
341 * from it. We always resolve all for reasons of simplicity and general paranoia.
342 */
343 HMODULE hModDifXApi = LoadAppDll(L"DIFxAPI.dll");
344 if (!hModDifXApi)
345 return EXIT_FAIL;
346
347 static struct { FARPROC *ppfn; const char *pszName; } const s_aFunctions[] =
348 {
349 { (FARPROC *)&g_pfnDriverPackageInstall, "DriverPackageInstallW" },
350 { (FARPROC *)&g_pfnDriverPackageUninstall, "DriverPackageUninstallW" },
351 { (FARPROC *)&g_pfnDIFXAPISetLogCallback, "DIFXAPISetLogCallbackW" },
352 };
353 for (size_t i = 0; i < RT_ELEMENTS(s_aFunctions); i++)
354 {
355 FARPROC pfn = *s_aFunctions[i].ppfn = GetProcAddress(hModDifXApi, s_aFunctions[i].pszName);
356 if (!pfn)
357 return ErrorMsgLastErrSSS("Failed to find symbol '", s_aFunctions[i].pszName, "' in DIFxAPI.dll");
358 }
359
360 /*
361 * Try open the log file and register a logger callback with DIFx.
362 * Failures here are non-fatal.
363 */
364 HANDLE hLogFile = INVALID_HANDLE_VALUE;
365 if (pwszLogFile)
366 {
367 hLogFile = CreateFileW(pwszLogFile, FILE_GENERIC_WRITE & ~FILE_WRITE_DATA /* append mode */, FILE_SHARE_READ,
368 NULL /*pSecAttr*/, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/);
369 if (hLogFile == INVALID_HANDLE_VALUE)
370 ErrorMsgLastErrSWS("Failed to open/create log file '", pwszLogFile, "'");
371 g_pfnDIFXAPISetLogCallback(VBoxDIFxLogCallback, (void *)hLogFile);
372 }
373
374 PrintStr(fInstall ? "Installing driver ...\r\n" : "Uninstalling driver ...\r\n");
375 PrintSWS("INF-File: '", wszFullDriverInf, "'\r\n");
376
377 INSTALLERINFO InstInfo =
378 {
379 L"{7d2c708d-c202-40ab-b3e8-de21da1dc629}", /* Our GUID for representing this installation tool. */
380 L"VirtualBox Guest Additions Install Helper",
381 L"VirtualBox Guest Additions", /** @todo Add version! */
382 L"Oracle Corporation"
383 };
384
385 /* Flags */
386 DWORD dwFlags = DRIVER_PACKAGE_FORCE;
387 if (!fInstall)
388 dwFlags |= DRIVER_PACKAGE_DELETE_FILES;
389 if (VerInfo.dwMajorVersion < 6 && fInstall)
390 {
391 PrintStr("Using legacy mode for install ...\r\n");
392 dwFlags |= DRIVER_PACKAGE_LEGACY_MODE;
393 }
394 if (fSilent)
395 {
396 /* Don't add DRIVER_PACKAGE_SILENT to dwFlags here, otherwise the
397 installation will fail because we don't have WHQL certified drivers.
398 See CERT_E_WRONG_USAGE on MSDN for more information. */
399 PrintStr("Installation is silent ...\r\n");
400 }
401
402 /* Do the install/uninstall: */
403 BOOL fReboot = FALSE;
404 DWORD dwErr;
405 if (fInstall)
406 dwErr = g_pfnDriverPackageInstall(wszFullDriverInf, dwFlags, &InstInfo, &fReboot);
407 else
408 dwErr = g_pfnDriverPackageUninstall(wszFullDriverInf, dwFlags, &InstInfo, &fReboot);
409
410 /*
411 * Report error
412 */
413 int rcExit = EXIT_FAIL;
414 const char *psz = NULL;
415 switch (dwErr)
416 {
417 case ERROR_SUCCESS:
418 rcExit = EXIT_OK;
419 break;
420
421 case CRYPT_E_FILE_ERROR:
422 psz = "The catalog file for the specified driver package was not found!";
423 break;
424 case ERROR_ACCESS_DENIED:
425 psz = fInstall ? "Caller is not in Administrators group to install this driver package!"
426 : "Caller is not in Administrators group to uninstall this driver package!";
427 break;
428 case ERROR_BAD_ENVIRONMENT:
429 psz = "The current Microsoft Windows version does not support this operation!";
430 break;
431 case ERROR_CANT_ACCESS_FILE:
432 psz = "The driver package files could not be accessed!";
433 break;
434 case ERROR_DEPENDENT_APPLICATIONS_EXIST:
435 psz = "DriverPackageUninstall removed an association between the driver package and the specified application but the function did not uninstall the driver package because other applications are associated with the driver package!";
436 break;
437 case ERROR_DRIVER_PACKAGE_NOT_IN_STORE:
438 psz = fInstall ? "There is no INF file in the DIFx driver store that corresponds to the INF file being installed!"
439 : "There is no INF file in the DIFx driver store that corresponds to the INF file being uninstalled!";
440 break;
441 case ERROR_FILE_NOT_FOUND:
442 psz = "INF-file not found!";
443 break;
444 case ERROR_IN_WOW64:
445 psz = "The calling application is a 32-bit application attempting to execute in a 64-bit environment, which is not allowed!";
446 break;
447 case ERROR_INVALID_FLAGS:
448 psz = "The flags specified are invalid!";
449 break;
450 case ERROR_INSTALL_FAILURE:
451 psz = fInstall ? "The install operation failed! Consult the Setup API logs for more information."
452 : "The uninstall operation failed! Consult the Setup API logs for more information.";
453 break;
454 case ERROR_NO_MORE_ITEMS:
455 psz = "The function found a match for the HardwareId value, but the specified driver was not a better match than the current driver and the caller did not specify the INSTALLFLAG_FORCE flag!";
456 break;
457 case ERROR_NO_DRIVER_SELECTED:
458 psz = "No driver in .INF-file selected!";
459 break;
460 case ERROR_SECTION_NOT_FOUND:
461 psz = "Section in .INF-file was not found!";
462 break;
463 case ERROR_SHARING_VIOLATION:
464 psz = "A component of the driver package in the DIFx driver store is locked by a thread or process!";
465 break;
466
467 /*
468 * ! sig: Verifying file against specific Authenticode(tm) catalog failed! (0x800b0109)
469 * ! sig: Error 0x800b0109: A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.
470 * !!! sto: No error message will be displayed as client is running in non-interactive mode.
471 * !!! ndv: Driver package failed signature validation. Error = 0xE0000247
472 */
473 case ERROR_DRIVER_STORE_ADD_FAILED:
474 psz = "Adding driver to the driver store failed!!";
475 break;
476 case ERROR_UNSUPPORTED_TYPE:
477 psz = "The driver package type is not supported of INF-file!";
478 break;
479 case ERROR_NO_SUCH_DEVINST:
480 psz = "The driver package was installed but no matching devices found in the device tree (ERROR_NO_SUCH_DEVINST).";
481 /* GA installer should ignore this error code and continue */
482 rcExit = EXIT_OK;
483 break;
484
485 default:
486 {
487 /* Try error lookup with GetErrorMsg(). */
488 ErrorMsgSWS(fInstall ? "Installation of '" : "Uninstallation of '", wszFullDriverInf, "' failed!");
489 ErrorMsgBegin("dwErr=");
490 ErrorMsgErrVal(dwErr, false);
491 WCHAR wszErrMsg[1024];
492 if (GetErrorMsg(dwErr, wszErrMsg, RT_ELEMENTS(wszErrMsg)))
493 {
494 ErrorMsgStr(": ");
495 ErrorMsgWStr(wszErrMsg);
496 }
497 ErrorMsgEnd(NULL);
498 break;
499 }
500 }
501 if (psz)
502 {
503 ErrorMsgSWS(fInstall ? "Installation of '" : "Uninstallation of '", wszFullDriverInf, "' failed!");
504 ErrorMsgBegin("dwErr=");
505 ErrorMsgErrVal(dwErr, false);
506 ErrorMsgStr(": ");
507 ErrorMsgEnd(psz);
508 }
509
510 /* Close the log file. */
511 if (pwszLogFile)
512 {
513 g_pfnDIFXAPISetLogCallback(NULL, NULL);
514 if (hLogFile != INVALID_HANDLE_VALUE)
515 CloseHandle(hLogFile);
516 }
517 if (rcExit == EXIT_OK)
518 {
519 PrintStr(fInstall ? "Driver was installed successfully!\r\n"
520 : "Driver was uninstalled successfully!\r\n");
521 if (fReboot)
522 {
523 PrintStr(fInstall ? "A reboot is needed to complete the driver installation!\r\n"
524 : "A reboot is needed to complete the driver uninstallation!\r\n");
525 /** @todo r=bird: We don't set EXIT_REBOOT here for some reason... The
526 * ExecuteInf didn't use EXIT_REBOOT either untill the no-CRT rewrite,
527 * so perhaps the EXIT_REBOOT stuff can be removed? */
528 }
529 }
530
531 return rcExit;
532}
533
534
535/** Handles 'driver install'. */
536static int handleDriverInstall(unsigned cArgs, wchar_t **papwszArgs)
537{
538 return VBoxInstallDriver(true /*fInstall*/, papwszArgs[0], false /*fSilent*/,
539 cArgs > 1 && papwszArgs[1][0] ? papwszArgs[1] : NULL /* pwszLogFile*/);
540}
541
542
543/** Handles 'driver uninstall'. */
544static int handleDriverUninstall(unsigned cArgs, wchar_t **papwszArgs)
545{
546 return VBoxInstallDriver(false /*fInstall*/, papwszArgs[0], false /*fSilent*/,
547 cArgs > 1 && papwszArgs[1][0] ? papwszArgs[1] : NULL /* pwszLogFile*/);
548}
549
550
551/**
552 * Implementes PSP_FILE_CALLBACK_W, used by ExecuteInfFile.
553 */
554static UINT CALLBACK
555vboxDrvInstExecuteInfFileCallback(PVOID pvContext, UINT uNotification, UINT_PTR uParam1, UINT_PTR uParam2) RT_NOTHROW_DEF
556{
557#ifdef DEBUG
558 PrintSXS("Got installation notification ", uNotification, "\r\n");
559#endif
560
561 switch (uNotification)
562 {
563 case SPFILENOTIFY_NEEDMEDIA:
564 PrintStr("Requesting installation media ...\r\n");
565 break;
566
567 case SPFILENOTIFY_STARTCOPY:
568 PrintStr("Copying driver files to destination ...\r\n");
569 break;
570
571 case SPFILENOTIFY_TARGETNEWER:
572 case SPFILENOTIFY_TARGETEXISTS:
573 return TRUE;
574 }
575
576 return SetupDefaultQueueCallbackW(pvContext, uNotification, uParam1, uParam2);
577}
578
579
580/**
581 * Executes a specific .INF section to install/uninstall drivers and/or
582 * services.
583 *
584 * @return Exit code (EXIT_OK, EXIT_FAIL, EXIT_REBOOT)
585 * @param pwszSection Section to execute; usually it's L"DefaultInstall".
586 * @param pwszInf Path of the .INF file to use.
587 */
588static int ExecuteInfFile(const wchar_t *pwszSection, const wchar_t *pwszInf)
589{
590 PrintSWSWS("Installing from INF-File: '", pwszInf, "', Section: '", pwszSection, "' ...\r\n");
591
592 UINT uErrorLine = 0;
593 HINF hInf = SetupOpenInfFileW(pwszInf, NULL, INF_STYLE_WIN4, &uErrorLine);
594 if (hInf == INVALID_HANDLE_VALUE)
595 return ErrorMsgLastErrSWSRSUS("SetupOpenInfFileW failed to open '", pwszInf, "' ", ", error line ", uErrorLine, NULL);
596
597 int rcExit = EXIT_FAIL;
598 PVOID pvQueue = SetupInitDefaultQueueCallback(NULL);
599 if (pvQueue)
600 {
601 if (SetupInstallFromInfSectionW(NULL /*hWndOwner*/, hInf, pwszSection, SPINST_ALL, HKEY_LOCAL_MACHINE,
602 NULL /*pwszSrcRootPath*/, SP_COPY_NEWER_OR_SAME | SP_COPY_NOSKIP,
603 vboxDrvInstExecuteInfFileCallback, pvQueue, NULL /*hDevInfoSet*/, NULL /*pDevInfoData*/))
604 {
605 PrintStr("File installation stage successful\r\n");
606
607 if (SetupInstallServicesFromInfSectionW(hInf, L"DefaultInstall.Services", 0 /* Flags */))
608 {
609 PrintStr("Service installation stage successful. Installation completed.\r\n");
610 rcExit = EXIT_OK;
611 }
612 else if (GetLastError() == ERROR_SUCCESS_REBOOT_REQUIRED)
613 {
614 PrintStr("A reboot is required to complete the installation\r\n");
615 rcExit = EXIT_REBOOT;
616 }
617 else
618 ErrorMsgLastErrSWSWS("SetupInstallServicesFromInfSectionW failed on '", pwszSection, "' in '", pwszInf, "'");
619 }
620 SetupTermDefaultQueueCallback(pvQueue);
621 }
622 else
623 ErrorMsgLastErr("SetupInitDefaultQueueCallback failed");
624 SetupCloseInfFile(hInf);
625 return rcExit;
626}
627
628
629/** Handles 'driver executeinf'. */
630static int handleDriverExecuteInf(unsigned cArgs, wchar_t **papwszArgs)
631{
632 RT_NOREF(cArgs);
633 return ExecuteInfFile(L"DefaultInstall", papwszArgs[0]);
634}
635
636
637/**
638 * Inner NT4 video driver installation function.
639 *
640 * This can normally return immediately on errors as the parent will do the
641 * cleaning up.
642 */
643static int InstallNt4VideoDriverInner(WCHAR const * const pwszDriverDir, HDEVINFO hDevInfo, HINF *phInf)
644{
645 /*
646 * Get the first found driver - our Inf file only contains one so this is ok.
647 *
648 * Note! We must use the V1 structure here as it is the only NT4 recognizes.
649 * There are four versioned structures:
650 * - SP_ALTPLATFORM_INFO
651 * - SP_DRVINFO_DATA_W
652 * - SP_BACKUP_QUEUE_PARAMS_W
653 * - SP_INF_SIGNER_INFO_W,
654 * but we only make use of SP_DRVINFO_DATA_W.
655 */
656 SetLastError(NO_ERROR);
657 SP_DRVINFO_DATA_V1_W drvInfoData = { sizeof(drvInfoData) };
658 if (!SetupDiEnumDriverInfoW(hDevInfo, NULL, SPDIT_CLASSDRIVER, 0, &drvInfoData))
659 return ErrorMsgLastErr("SetupDiEnumDriverInfoW");
660
661 /*
662 * Get necessary driver details
663 */
664 union
665 {
666 SP_DRVINFO_DETAIL_DATA_W s;
667 uint64_t au64Padding[(sizeof(SP_DRVINFO_DETAIL_DATA_W) + 256) / sizeof(uint64_t)];
668 } DriverInfoDetailData = { { sizeof(DriverInfoDetailData.s) } };
669 DWORD cbReqSize = NULL;
670 if ( !SetupDiGetDriverInfoDetailW(hDevInfo, NULL, &drvInfoData,
671 &DriverInfoDetailData.s, sizeof(DriverInfoDetailData), &cbReqSize)
672 && GetLastError() != ERROR_INSUFFICIENT_BUFFER)
673 return ErrorMsgLastErr("SetupDiGetDriverInfoDetailW");
674
675 HINF hInf = *phInf = SetupOpenInfFileW(DriverInfoDetailData.s.InfFileName, NULL, INF_STYLE_WIN4, NULL);
676 if (hInf == INVALID_HANDLE_VALUE)
677 return ErrorMsgLastErr("SetupOpenInfFileW");
678
679 /*
680 * First install the service.
681 */
682 WCHAR wszServiceSection[LINE_LEN];
683 int rc = RTUtf16Copy(wszServiceSection, RT_ELEMENTS(wszServiceSection), DriverInfoDetailData.s.SectionName);
684 if (RT_SUCCESS(rc))
685 rc = RTUtf16CatAscii(wszServiceSection, RT_ELEMENTS(wszServiceSection), ".Services");
686 if (RT_FAILURE(rc))
687 return ErrorMsg("wszServiceSection too small");
688
689 INFCONTEXT SvcCtx;
690 if (!SetupFindFirstLineW(hInf, wszServiceSection, NULL, &SvcCtx))
691 return ErrorMsgLastErr("SetupFindFirstLine"); /* impossible... */
692
693 /*
694 * Get the name
695 */
696 WCHAR wszServiceData[LINE_LEN] = {0};
697 if (!SetupGetStringFieldW(&SvcCtx, 1, wszServiceData, RT_ELEMENTS(wszServiceData), NULL))
698 return ErrorMsgLastErr("SetupGetStringFieldW");
699
700 WCHAR wszDevInstanceId[LINE_LEN];
701 rc = RTUtf16CopyAscii(wszDevInstanceId, RT_ELEMENTS(wszDevInstanceId), "Root\\LEGACY_");
702 if (RT_SUCCESS(rc))
703 rc = RTUtf16Cat(wszDevInstanceId, RT_ELEMENTS(wszDevInstanceId), wszServiceData);
704 if (RT_SUCCESS(rc))
705 rc = RTUtf16CatAscii(wszDevInstanceId, RT_ELEMENTS(wszDevInstanceId), "\\0000");
706 if (RT_FAILURE(rc))
707 return ErrorMsg("wszDevInstanceId too small");
708
709 /*
710 * ...
711 */
712 SP_DEVINFO_DATA deviceInfoData = { sizeof(deviceInfoData) };
713 /* Check for existing first. */
714 BOOL fDevInfoOkay = SetupDiOpenDeviceInfoW(hDevInfo, wszDevInstanceId, NULL, 0, &deviceInfoData);
715 if (!fDevInfoOkay)
716 {
717 /* Okay, try create a new device info element. */
718 if (SetupDiCreateDeviceInfoW(hDevInfo, wszDevInstanceId, (LPGUID)&GUID_DEVCLASS_DISPLAY,
719 NULL, // Do we need a description here?
720 NULL, // No user interface
721 0, &deviceInfoData))
722 {
723 if (SetupDiRegisterDeviceInfo(hDevInfo, &deviceInfoData, 0, NULL, NULL, NULL))
724 fDevInfoOkay = TRUE;
725 else
726 return ErrorMsgLastErr("SetupDiRegisterDeviceInfo"); /** @todo Original code didn't return here. */
727 }
728 else
729 return ErrorMsgLastErr("SetupDiCreateDeviceInfoW"); /** @todo Original code didn't return here. */
730 }
731 if (fDevInfoOkay) /** @todo if not needed if it's okay to fail on failure above */
732 {
733 /* We created a new key in the registry */ /* bogus... */
734
735 /*
736 * Redo the install parameter thing with deviceInfoData.
737 */
738 SP_DEVINSTALL_PARAMS_W DeviceInstallParams = { sizeof(DeviceInstallParams) };
739 if (!SetupDiGetDeviceInstallParamsW(hDevInfo, &deviceInfoData, &DeviceInstallParams))
740 return ErrorMsgLastErr("SetupDiGetDeviceInstallParamsW(#2)"); /** @todo Original code didn't return here. */
741
742 DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
743 DeviceInstallParams.Flags |= DI_NOFILECOPY /* We did our own file copying */
744 | DI_DONOTCALLCONFIGMG
745 | DI_ENUMSINGLEINF; /* .DriverPath specifies an inf file */
746 rc = RTUtf16Copy(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath), pwszDriverDir);
747 if (RT_SUCCESS(rc))
748 rc = RTUtf16CatAscii(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath),
749 VBOXGUEST_NT4_VIDEO_INF_NAME);
750 if (RT_FAILURE(rc))
751 return ErrorMsg("Install dir too deep (long)");
752
753 if (!SetupDiSetDeviceInstallParamsW(hDevInfo, &deviceInfoData, &DeviceInstallParams))
754 return ErrorMsgLastErr("SetupDiSetDeviceInstallParamsW(#2)"); /** @todo Original code didn't return here. */
755
756 if (!SetupDiBuildDriverInfoList(hDevInfo, &deviceInfoData, SPDIT_CLASSDRIVER))
757 return ErrorMsgLastErr("SetupDiBuildDriverInfoList(#2)");
758
759 /*
760 * Repeat the query at the start of the function.
761 */
762 drvInfoData.cbSize = sizeof(drvInfoData);
763 if (!SetupDiEnumDriverInfoW(hDevInfo, &deviceInfoData, SPDIT_CLASSDRIVER, 0, &drvInfoData))
764 return ErrorMsgLastErr("SetupDiEnumDriverInfoW(#2)");
765
766 /*
767 * ...
768 */
769 if (!SetupDiSetSelectedDriverW(hDevInfo, &deviceInfoData, &drvInfoData))
770 return ErrorMsgLastErr("SetupDiSetSelectedDriverW(#2)");
771
772 if (!SetupDiInstallDevice(hDevInfo, &deviceInfoData))
773 return ErrorMsgLastErr("SetupDiInstallDevice(#2)");
774 }
775
776 /*
777 * Make sure the device is enabled.
778 */
779 DWORD fConfig = 0;
780 if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData, SPDRP_CONFIGFLAGS,
781 NULL, (LPBYTE)&fConfig, sizeof(DWORD), NULL))
782 {
783 if (fConfig & CONFIGFLAG_DISABLED)
784 {
785 fConfig &= ~CONFIGFLAG_DISABLED;
786 if (!SetupDiSetDeviceRegistryPropertyW(hDevInfo, &deviceInfoData, SPDRP_CONFIGFLAGS,
787 (LPBYTE)&fConfig, sizeof(fConfig)))
788 ErrorMsg("SetupDiSetDeviceRegistryPropertyW");
789 }
790 }
791 else
792 ErrorMsg("SetupDiGetDeviceRegistryPropertyW");
793
794 /*
795 * Open the service key.
796 */
797 WCHAR wszSvcRegKey[LINE_LEN + 64];
798 rc = RTUtf16CopyAscii(wszSvcRegKey, RT_ELEMENTS(wszSvcRegKey), "System\\CurrentControlSet\\Services\\");
799 if (RT_SUCCESS(rc))
800 rc = RTUtf16Cat(wszSvcRegKey, RT_ELEMENTS(wszSvcRegKey), wszServiceData);
801 if (RT_SUCCESS(rc))
802 rc = RTUtf16CatAscii(wszSvcRegKey, RT_ELEMENTS(wszSvcRegKey), "\\Device0"); /* We only have one device. */
803 if (RT_FAILURE(rc))
804 return ErrorMsg("Service key name too long");
805
806 DWORD dwIgn;
807 HKEY hKey = NULL;
808 LSTATUS lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, wszSvcRegKey, 0, NULL, REG_OPTION_NON_VOLATILE,
809 KEY_READ | KEY_WRITE, NULL, &hKey, &dwIgn);
810 if (lrc == ERROR_SUCCESS)
811 {
812 /*
813 * Insert service description.
814 */
815 lrc = RegSetValueExW(hKey, L"Device Description", 0, REG_SZ, (LPBYTE)DriverInfoDetailData.s.DrvDescription,
816 (DWORD)((RTUtf16Len(DriverInfoDetailData.s.DrvDescription) + 1) * sizeof(WCHAR)));
817 if (lrc != ERROR_SUCCESS)
818 ErrorMsgLStatus("RegSetValueExW", lrc);
819
820 /*
821 * Execute the SoftwareSettings section of the INF-file or something like that.
822 */
823 BOOL fOkay = FALSE;
824 WCHAR wszSoftwareSection[LINE_LEN + 32];
825 rc = RTUtf16Copy(wszSoftwareSection, RT_ELEMENTS(wszSoftwareSection), wszServiceData);
826 if (RT_SUCCESS(rc))
827 rc = RTUtf16CatAscii(wszSoftwareSection, RT_ELEMENTS(wszSoftwareSection), ".SoftwareSettings");
828 if (RT_SUCCESS(rc))
829 {
830 if (SetupInstallFromInfSectionW(NULL, hInf, wszSoftwareSection, SPINST_REGISTRY, hKey,
831 NULL, 0, NULL, NULL, NULL, NULL))
832 fOkay = TRUE;
833 else
834 ErrorMsgLastErr("SetupInstallFromInfSectionW");
835 }
836 else
837 ErrorMsg("Software settings section name too long");
838 RegCloseKey(hKey);
839 if (!fOkay)
840 return EXIT_FAIL;
841 }
842 else
843 ErrorMsgLStatus("RegCreateKeyExW/Service", lrc);
844
845 /*
846 * Install OpenGL stuff.
847 */
848 lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\OpenGLDrivers", 0, NULL,
849 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwIgn);
850 if (lrc == ERROR_SUCCESS)
851 {
852 /* Do installation here if ever necessary. Currently there is no OpenGL stuff */
853 RegCloseKey(hKey);
854 }
855 else
856 ErrorMsgLStatus("RegCreateKeyExW/OpenGLDrivers", lrc);
857
858#if 0
859 /* If this key is inserted into the registry, windows will show the desktop
860 applet on next boot. We decide in the installer if we want that so the code
861 is disabled here. */
862 lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers\\NewDisplay",
863 0, NULL, REG_OPTION_NON_VOLATILE,
864 KEY_READ | KEY_WRITE, NULL, &hHey, &dwIgn)
865 if (lrc == ERROR_SUCCESS)
866 RegCloseKey(hHey);
867 else
868 ErrorMsgLStatus("RegCreateKeyExW/NewDisplay", lrc);
869#endif
870
871 /*
872 * We must reboot at some point
873 */
874 lrc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers\\RebootNecessary", 0, NULL,
875 REG_OPTION_NON_VOLATILE, KEY_READ | KEY_WRITE, NULL, &hKey, &dwIgn);
876 if (lrc == ERROR_SUCCESS)
877 RegCloseKey(hKey);
878 else
879 ErrorMsgLStatus("RegCreateKeyExW/RebootNecessary", lrc);
880
881 return EXIT_OK;
882}
883
884
885
886/**
887 * Install the VBox video driver.
888 *
889 * @param pwszDriverDir The base directory where we find the INF.
890 */
891static int InstallNt4VideoDriver(WCHAR const * const pwszDriverDir)
892{
893 /*
894 * Create an empty list
895 */
896 HDEVINFO hDevInfo = SetupDiCreateDeviceInfoList((LPGUID)&GUID_DEVCLASS_DISPLAY, NULL);
897 if (hDevInfo == INVALID_HANDLE_VALUE)
898 return ErrorMsgLastErr("SetupDiCreateDeviceInfoList");
899
900 /*
901 * Get the default install parameters.
902 */
903 int rcExit = EXIT_FAIL;
904 SP_DEVINSTALL_PARAMS_W DeviceInstallParams = { sizeof(DeviceInstallParams) };
905 if (SetupDiGetDeviceInstallParamsW(hDevInfo, NULL, &DeviceInstallParams))
906 {
907 /*
908 * Insert our install parameters and update hDevInfo with them.
909 */
910 DeviceInstallParams.cbSize = sizeof(DeviceInstallParams);
911 DeviceInstallParams.Flags |= DI_NOFILECOPY /* We did our own file copying */
912 | DI_DONOTCALLCONFIGMG
913 | DI_ENUMSINGLEINF; /* .DriverPath specifies an inf file */
914 int rc = RTUtf16Copy(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath), pwszDriverDir);
915 if (RT_SUCCESS(rc))
916 rc = RTUtf16CatAscii(DeviceInstallParams.DriverPath, RT_ELEMENTS(DeviceInstallParams.DriverPath),
917 VBOXGUEST_NT4_VIDEO_INF_NAME);
918 if (RT_SUCCESS(rc))
919 {
920 if (SetupDiSetDeviceInstallParamsW(hDevInfo, NULL, &DeviceInstallParams))
921 {
922 /*
923 * Read the drivers from the INF-file.
924 */
925 if (SetupDiBuildDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER))
926 {
927 HINF hInf = NULL;
928 rcExit = InstallNt4VideoDriverInner(pwszDriverDir, hDevInfo, &hInf);
929
930 if (hInf)
931 SetupCloseInfFile(hInf);
932 SetupDiDestroyDriverInfoList(hDevInfo, NULL, SPDIT_CLASSDRIVER);
933 }
934 else
935 ErrorMsgLastErr("SetupDiBuildDriverInfoList");
936 }
937 else
938 ErrorMsgLastErr("SetupDiSetDeviceInstallParamsW");
939
940 }
941 else
942 ErrorMsg("Install dir too deep (long)");
943 SetupDiDestroyDeviceInfoList(hDevInfo);
944 }
945 else
946 ErrorMsgLastErr("SetupDiGetDeviceInstallParams"); /** @todo Original code didn't return here. */
947 SetupDiDestroyDeviceInfoList(hDevInfo);
948 return rcExit;
949}
950
951
952/** Handles 'driver nt4-install-video'. */
953static int handleDriverNt4InstallVideo(unsigned cArgs, wchar_t **papwszArgs)
954{
955 /* One optional parameter: installation directory containing INF file. */
956 WCHAR wszInstallDir[MAX_PATH];
957 DWORD cwcInstallDir;
958 if (cArgs < 1)
959 {
960 cwcInstallDir = GetModuleFileNameW(GetModuleHandle(NULL), &wszInstallDir[0], RT_ELEMENTS(wszInstallDir));
961 if (cwcInstallDir > 0)
962 {
963 while (cwcInstallDir > 0 && !RTPATH_IS_SEP(wszInstallDir[cwcInstallDir - 1]))
964 cwcInstallDir--;
965 if (!cwcInstallDir) /* paranoia^3 */
966 {
967 wszInstallDir[cwcInstallDir++] = '.';
968 wszInstallDir[cwcInstallDir++] = '\\';
969 }
970 wszInstallDir[cwcInstallDir] = '\0';
971 }
972 }
973 else
974 {
975 WCHAR *pwszFilenameIgn;
976 cwcInstallDir = GetFullPathNameW(papwszArgs[0], RT_ELEMENTS(wszInstallDir) - 1, wszInstallDir, &pwszFilenameIgn);
977 if (cwcInstallDir == 0 || cwcInstallDir > RT_ELEMENTS(wszInstallDir) - 2)
978 return ErrorMsgLastErrSWS("GetFullPathNameW failed for '", papwszArgs[0], "'!");
979 if (!RTPATH_IS_SEP(wszInstallDir[cwcInstallDir - 1]))
980 {
981 wszInstallDir[cwcInstallDir++] = '\\';
982 wszInstallDir[cwcInstallDir] = '\0';
983 }
984 }
985
986 /* Make sure we're on NT4 before continuing: */
987 OSVERSIONINFO VerInfo = { sizeof(VerInfo) };
988 GetVersionEx(&VerInfo);
989 if ( VerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT
990 || VerInfo.dwMajorVersion != 4)
991 return ErrorMsgSUSUS("This command is only for NT 4. GetVersionEx reports ", VerInfo.dwMajorVersion, ".",
992 VerInfo.dwMinorVersion, ".");
993
994 return (int)InstallNt4VideoDriver(wszInstallDir);
995}
996
997
998
999/*********************************************************************************************************************************
1000* 'service' *
1001*********************************************************************************************************************************/
1002
1003/**
1004 * Worker for the 'service create' handler.
1005 */
1006static int CreateService(const wchar_t *pwszService,
1007 const wchar_t *pwszDisplayName,
1008 uint32_t uServiceType,
1009 uint32_t uStartType,
1010 const wchar_t *pwszBinPath,
1011 const wchar_t *pwszLoadOrderGroup,
1012 const wchar_t *pwszDependencies,
1013 const wchar_t *pwszLogonUser,
1014 const wchar_t *pwszLogonPassword)
1015{
1016 PrintSWSWS("Installing service '", pwszService, "' ('", pwszDisplayName, ") ...\r\n");
1017
1018 /*
1019 * Transform the dependency list to a REG_MULTI_SZ.
1020 */
1021 if (pwszDependencies != NULL)
1022 {
1023 /* Copy it into alloca() buffer so we can modify it. */
1024 size_t cwc = RTUtf16Len(pwszDependencies);
1025 wchar_t *pwszDup = (wchar_t *)alloca((cwc + 2) * sizeof(wchar_t));
1026 memcpy(pwszDup, pwszDependencies, cwc * sizeof(wchar_t));
1027 pwszDup[cwc] = L'\0';
1028 pwszDup[cwc + 1] = L'\0'; /* double termination */
1029
1030 /* Perform: s/,/\0/g */
1031 while (cwc-- > 0 )
1032 if (pwszDup[cwc] == L',')
1033 pwszDup[cwc] = L'\0';
1034
1035 pwszDependencies = pwszDup;
1036 }
1037
1038 SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1039 if (hSCManager == NULL)
1040 return ErrorMsgLastErr("OpenSCManagerW failed");
1041
1042 int rcExit = EXIT_FAIL;
1043 DWORD dwTag = 0xDEADBEAF;
1044 SC_HANDLE hService = CreateServiceW(hSCManager, pwszService, pwszDisplayName, SERVICE_ALL_ACCESS, uServiceType, uStartType,
1045 SERVICE_ERROR_NORMAL, pwszBinPath, pwszLoadOrderGroup, pwszLoadOrderGroup ? &dwTag : NULL,
1046 pwszDependencies, pwszLogonUser, pwszLogonPassword);
1047 if (hService != NULL)
1048 {
1049 CloseServiceHandle(hService);
1050 PrintStr("Installation of service successful!\r\n");
1051 rcExit = EXIT_OK;
1052 }
1053 else
1054 {
1055 DWORD dwErr = GetLastError();
1056 if (dwErr == ERROR_SERVICE_EXISTS)
1057 {
1058 PrintStr("Service already exists. Updating the service config ...\r\n");
1059 hService = OpenServiceW(hSCManager, pwszService, SERVICE_ALL_ACCESS);
1060 if (hService != NULL)
1061 {
1062 if (ChangeServiceConfigW(hService, uServiceType, uStartType, SERVICE_ERROR_NORMAL, pwszBinPath,
1063 pwszLoadOrderGroup, pwszLoadOrderGroup ? &dwTag : NULL, pwszDependencies,
1064 pwszLogonUser, pwszLogonPassword, pwszDisplayName))
1065 {
1066 PrintStr("The service config has been successfully updated.\r\n");
1067 rcExit = EXIT_OK;
1068 }
1069 else
1070 rcExit = ErrorMsgLastErrSWS("ChangeServiceConfigW failed on '", pwszService, "'!");
1071 CloseServiceHandle(hService);
1072 }
1073 else
1074 rcExit = ErrorMsgLastErrSWS("OpenSCManagerW failed on '", pwszService, "'!");
1075
1076 /*
1077 * This branch does not return an error to avoid installations failures,
1078 * if updating service parameters. Better to have a running system with old
1079 * parameters and the failure information in the installation log.
1080 */
1081 rcExit = EXIT_OK;
1082 }
1083 else
1084 rcExit = ErrorMsgLastErrSWS("CreateServiceW for '", pwszService, "'!");
1085 }
1086
1087 CloseServiceHandle(hSCManager);
1088 return rcExit;
1089}
1090
1091
1092/** Handles 'service create'. */
1093static int handleServiceCreate(unsigned cArgs, wchar_t **papwszArgs)
1094{
1095 uint32_t uServiceType;
1096 if (!ArgToUInt32Full(papwszArgs[2], "service-type", &uServiceType))
1097 return EXIT_USAGE;
1098
1099 uint32_t uStartType;
1100 if (!ArgToUInt32Full(papwszArgs[3], "start-type", &uStartType))
1101 return EXIT_USAGE;
1102
1103 return CreateService(papwszArgs[0], papwszArgs[1], uServiceType, uStartType, papwszArgs[4],
1104 cArgs > 5 ? papwszArgs[5] : NULL,
1105 cArgs > 6 ? papwszArgs[6] : NULL,
1106 cArgs > 7 ? papwszArgs[7] : NULL,
1107 cArgs > 8 ? papwszArgs[8] : NULL);
1108}
1109
1110
1111/**
1112 * Worker for the 'service delete' handler.
1113 */
1114static int DelService(const wchar_t *pwszService)
1115{
1116 PrintSWS("Removing service '", pwszService, "' ...\r\n");
1117
1118 SC_HANDLE hSCManager = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
1119 if (hSCManager == NULL)
1120 return ErrorMsgLastErr("OpenSCManagerW failed");
1121
1122 int rcExit = EXIT_FAIL;
1123 SC_HANDLE hService = NULL;
1124 hService = OpenServiceW(hSCManager, pwszService, SERVICE_ALL_ACCESS);
1125 if (hService)
1126 {
1127 SC_LOCK hSCLock = LockServiceDatabase(hSCManager);
1128 if (hSCLock != NULL)
1129 {
1130 if (DeleteService(hService))
1131 {
1132 PrintSWS("Service '", pwszService, "' successfully deleted.\r\n");
1133 rcExit = EXIT_OK;
1134 }
1135 else
1136 {
1137 DWORD dwErr = GetLastError();
1138 if (dwErr == ERROR_SERVICE_MARKED_FOR_DELETE)
1139 {
1140 PrintSWS("Service '", pwszService, "' already marked for deletion.\r\n");
1141 rcExit = EXIT_OK;
1142 }
1143 else
1144 rcExit = ErrorMsgLastErrSWS("Failed to delete service'", pwszService, "'!");
1145 }
1146 UnlockServiceDatabase(hSCLock);
1147 }
1148 else
1149 ErrorMsgLastErr("LockServiceDatabase failed");
1150 CloseServiceHandle(hService);
1151 }
1152 else
1153 rcExit = ErrorMsgLastErrSWS("Failed to open service'", pwszService, "'!");
1154 CloseServiceHandle(hSCManager);
1155 return rcExit;
1156}
1157
1158
1159/** Handles 'service delete' */
1160static int handleServiceDelete(unsigned cArgs, wchar_t **papwszArgs)
1161{
1162 RT_NOREF(cArgs);
1163 return DelService(papwszArgs[0]);
1164}
1165
1166
1167
1168
1169/*********************************************************************************************************************************
1170* 'registry' *
1171*********************************************************************************************************************************/
1172
1173/**
1174 * Translate a registry root specifier into a HKEY_XXX constant.
1175 */
1176static HKEY ArgToRegistryRoot(const wchar_t *pwszRoot)
1177{
1178 HKEY hRootKey = NULL;
1179 if (RTUtf16ICmpAscii(pwszRoot, "hklm") == 0)
1180 hRootKey = HKEY_LOCAL_MACHINE;
1181 else if (RTUtf16ICmpAscii(pwszRoot, "hkcu") == 0)
1182 hRootKey = HKEY_CURRENT_USER;
1183 else if (RTUtf16ICmpAscii(pwszRoot, "hkcr") == 0)
1184 hRootKey = HKEY_CLASSES_ROOT;
1185 else if (RTUtf16ICmpAscii(pwszRoot, "hku") == 0)
1186 hRootKey = HKEY_USERS;
1187 else if (RTUtf16ICmpAscii(pwszRoot, "hkcc") == 0)
1188 hRootKey = HKEY_CURRENT_CONFIG;
1189 else
1190 ErrorBadArg("root", pwszRoot, "hklm, hkcu, hkcr, hku or hkcc");
1191 return hRootKey;
1192}
1193
1194
1195/**
1196 * Reverse of ArgToRegistryRoot.
1197 */
1198static wchar_t const *RegistryRootToWStr(HKEY hRootKey)
1199{
1200 if (hRootKey == HKEY_LOCAL_MACHINE)
1201 return L"HKLM";
1202 if (hRootKey == HKEY_CURRENT_USER)
1203 return L"HKCU";
1204 if (hRootKey == HKEY_CLASSES_ROOT)
1205 return L"HKCR";
1206 if (hRootKey == HKEY_USERS)
1207 return L"HKU";
1208 if (hRootKey == HKEY_CURRENT_CONFIG)
1209 return L"HKCC";
1210 return L"<bad-hkey-root>";
1211}
1212
1213
1214/**
1215 * Checks if a string is a substring of another one.
1216 *
1217 * Used by the RegistryAddStringToMultiSZ & RegistryRemoveStringToMultiSZ
1218 * routines.
1219 */
1220static bool IsSubStringOf(wchar_t volatile const *pwszStr, size_t cwcStr, wchar_t const *pwszSubStr, size_t cwcSubStr)
1221{
1222 if (cwcStr >= cwcSubStr && cwcSubStr > 0)
1223 {
1224 wchar_t const wcFirst = *pwszSubStr;
1225 cwcStr -= cwcSubStr;
1226 do
1227 {
1228 /* Could've used wmemchr here, but it isn't implemented in noCRT yet. */
1229 if ( *pwszStr == wcFirst
1230 && memcmp((void const *)pwszStr, pwszSubStr, cwcSubStr * sizeof(wchar_t)) == 0)
1231 return true;
1232 pwszStr++;
1233 } while (cwcStr-- > 0);
1234 }
1235 return false;
1236}
1237
1238
1239/**
1240 * Adds a string entry to a MULTI_SZ registry list.
1241 *
1242 * @return Exit code (EXIT_OK, EXIT_FAIL)
1243 * @param pwszSubKey Sub key containing the list.
1244 * @param pwszValueName The actual key name of the list.
1245 * @param pwszItemToAdd The item to add to the list.
1246 * @param uPosition Position (zero-based) of where to add the
1247 * value to the list.
1248 */
1249static int RegistryAddStringToMultiSZ(const wchar_t *pwszSubKey, const wchar_t *pwszValueName,
1250 const wchar_t *pwszItemToAdd, uint32_t uPosition)
1251{
1252 size_t const cwcItemToAdd = RTUtf16Len(pwszItemToAdd);
1253 size_t const cbItemToAdd = (cwcItemToAdd + 1) * sizeof(wchar_t);
1254#ifdef DEBUG
1255 PrintSWSWSWSXS("AddStringToMultiSZ: Adding MULTI_SZ item '", pwszItemToAdd,
1256 "' to HKLM/'", pwszSubKey, "'/'", pwszValueName, "' at position ", uPosition, "\r\n");
1257#endif
1258
1259 /*
1260 * Open/create the key.
1261 */
1262 HKEY hKey = NULL;
1263 DWORD dwDisp = 0;
1264 LSTATUS lrc = RegCreateKeyEx(HKEY_LOCAL_MACHINE, pwszSubKey, 0 /*Reserved*/, NULL /*pClass*/, REG_OPTION_NON_VOLATILE,
1265 KEY_READ | KEY_WRITE, NULL /*pSecAttr*/, &hKey, &dwDisp);
1266 if (lrc != ERROR_SUCCESS)
1267 return ErrorMsgLStatusSWSRS("RegistryAddStringToList: RegCreateKeyEx HKLM/'", pwszSubKey, "' failed: ", lrc, NULL);
1268
1269 /*
1270 * Query the current value, first query just gets the buffer size the 2nd does the actual query.
1271 * We make sure the buffer is large enough to contain the new item we're supposed to add.
1272 */
1273 int rcExit = EXIT_FAIL;
1274 PBYTE pbBuf = NULL;
1275 DWORD cbValue = 0;
1276 DWORD dwType = 0;
1277 lrc = RegQueryValueEx(hKey, pwszValueName, NULL, &dwType, NULL, &cbValue);
1278 if (lrc == ERROR_SUCCESS || lrc == ERROR_MORE_DATA)
1279 {
1280 cbValue = cbValue + _1K - sizeof(wchar_t)*2; /* 1KB of paranoia fudge, even if we ASSUME no races. */
1281 pbBuf = (PBYTE)RTMemAllocZ(cbValue + sizeof(wchar_t)*2 /* Two extra wchar_t's for proper zero termination. */
1282 + cbItemToAdd);
1283 if (!pbBuf)
1284 lrc = ERROR_OUTOFMEMORY;
1285 lrc = RegQueryValueEx(hKey, pwszValueName, NULL, &dwType, pbBuf, &cbValue);
1286 }
1287 if (lrc == ERROR_FILE_NOT_FOUND)
1288 {
1289 PrintStr("RegistryAddStringToList: Value not found, creating a new one...\r\n");
1290 pbBuf = (PBYTE)RTMemAllocZ(cbItemToAdd + sizeof(wchar_t)*8);
1291 if (pbBuf)
1292 {
1293 cbValue = sizeof(wchar_t);
1294 dwType = REG_MULTI_SZ;
1295 lrc = ERROR_SUCCESS;
1296 }
1297 else
1298 lrc = ERROR_OUTOFMEMORY;
1299 }
1300 if ( lrc == ERROR_SUCCESS
1301 && dwType == REG_MULTI_SZ)
1302 {
1303#ifdef DEBUG
1304 PrintSXS("RegistryAddStringToList: Current value length: ", cbValue, "\r\n");
1305#endif
1306
1307 /*
1308 * Scan the strings in the buffer, inserting the new item and removing any
1309 * existing duplicates. We do this in place.
1310 *
1311 * We have made sure above that the buffer is both properly zero terminated
1312 * and large enough to contain the new item, so we need do no buffer size
1313 * checking here.
1314 */
1315 wchar_t volatile *pwszSrc = (wchar_t volatile *)pbBuf;
1316 wchar_t volatile *pwszDst = (wchar_t volatile *)pbBuf;
1317 size_t cbLeft = cbValue;
1318 for (uint32_t uCurPos = 0; ; uCurPos++)
1319 {
1320 size_t const cwcSrc = RTUtf16Len((wchar_t const *)pwszSrc);
1321 size_t const cbSrc = (cwcSrc + 1) * sizeof(wchar_t);
1322 bool const fTheEnd = !cwcSrc && cbSrc >= cbLeft;
1323
1324 /* Insert the item if we're in the right position now, or if we're
1325 at the last string and still haven't reached it. */
1326 if (uCurPos == uPosition || (fTheEnd && uCurPos < uPosition))
1327 {
1328 pwszSrc = (wchar_t volatile *)memmove((PBYTE)pwszSrc + cbItemToAdd, (wchar_t const *)pwszSrc, cbLeft);
1329 memcpy((void *)pwszDst, pwszItemToAdd, cbItemToAdd);
1330 pwszDst += cwcItemToAdd + 1;
1331 uCurPos++;
1332 }
1333 if (fTheEnd)
1334 break;
1335
1336 /* We do not add empty strings nor strings matching the one we're adding. */
1337 if (!cwcSrc || IsSubStringOf(pwszSrc, cwcSrc, pwszItemToAdd, cwcItemToAdd))
1338 uCurPos--;
1339 else
1340 {
1341 if (pwszDst != pwszSrc)
1342 memmove((void *)pwszDst, (void const *)pwszSrc, cbSrc);
1343 pwszDst += cwcSrc + 1;
1344 }
1345 pwszSrc += cwcSrc + 1;
1346 cbLeft -= cbSrc;
1347 }
1348 *pwszDst = '\0';
1349 DWORD const cbNewValue = (DWORD)((PBYTE)(pwszDst + 1) - pbBuf);
1350#ifdef DEBUG
1351 PrintSXS("RegistryAddStringToList: New value length: ", cbNewValue, "\r\n");
1352#endif
1353
1354 /*
1355 * Always write the value since we cannot tell whether it changed or
1356 * not without adding a bunch extra code above.
1357 */
1358 lrc = RegSetValueExW(hKey, pwszValueName, 0, REG_MULTI_SZ, pbBuf, cbNewValue);
1359 if (lrc == ERROR_SUCCESS)
1360 {
1361#ifdef DEBUG
1362 PrintSWSWS("RegistryAddStringToList: The item '", pwszItemToAdd, "' was added successfully to '",
1363 pwszValueName, "'.\r\n");
1364#endif
1365 rcExit = EXIT_OK;
1366 }
1367 else
1368 ErrorMsgLStatusSWSWSRS("RegistryAddStringToList: RegSetValueExW HKLM/'",
1369 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1370 }
1371 else if (lrc != ERROR_SUCCESS)
1372 ErrorMsgLStatusSWSWSRS("RemoveStringFromMultiSZ: RegQueryValueEx HKLM/'",
1373 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1374 else
1375 ErrorMsgLStatusSWSWSRS("RemoveStringFromMultiSZ: Unexpected value type for HKLM/'",
1376 pwszSubKey, "'/'", pwszValueName, "': ", (LSTATUS)dwType, ", expected REG_SZ (1)");
1377 return rcExit;
1378}
1379
1380
1381/** Handles 'registry addmultisz'. */
1382static int handleRegistryAddMultiSz(unsigned cArgs, wchar_t **papwszArgs)
1383{
1384 RT_NOREF(cArgs);
1385
1386 uint32_t uPosition;
1387 if (!ArgToUInt32Full(papwszArgs[3], "position", &uPosition))
1388 return EXIT_USAGE;
1389
1390 return RegistryAddStringToMultiSZ(papwszArgs[0], papwszArgs[1], papwszArgs[2], uPosition);
1391}
1392
1393
1394/**
1395 * Removes a item from a MULTI_SZ registry list.
1396 *
1397 * @return Exit code (EXIT_OK, EXIT_FAIL)
1398 * @param pwszSubKey Sub key containing the list.
1399 * @param pwszValueName The actual key name of the list.
1400 * @param pwszItemToRemove The item to remove from the list. Actually, we
1401 * only do a substring match on this, so any item
1402 * containing this string will be removed.
1403 */
1404static int RegistryRemoveStringFromMultiSZ(const wchar_t *pwszSubKey, const wchar_t *pwszValueName,
1405 const wchar_t *pwszItemToRemove)
1406{
1407#ifdef DEBUG
1408 PrintSWSWSWS("RemoveStringFromMultiSZ: Removing MULTI_SZ string '", pwszItemToRemove,
1409 "' from HKLM/'", pwszSubKey, "'/'", pwszValueName, "'\r\n");
1410#endif
1411
1412 /*
1413 * Open the specified key.
1414 */
1415 HKEY hKey = NULL;
1416 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszSubKey, 0 /*dwOptions*/, KEY_READ | KEY_WRITE, &hKey);
1417 if (lrc != ERROR_SUCCESS)
1418 return ErrorMsgLStatusSWSRS("RemoveStringFromMultiSZ: RegOpenKeyExW HKLM/'", pwszSubKey, "' failed: ", lrc, NULL);
1419
1420 /*
1421 * Query the current value, first query just gets the buffer size the 2nd does the actual query.
1422 */
1423 int rcExit = EXIT_FAIL;
1424 PBYTE pbBuf = NULL;
1425 DWORD cbValue = 0;
1426 DWORD dwType = 0;
1427 lrc = RegQueryValueEx(hKey, pwszValueName, NULL, &dwType, NULL, &cbValue);
1428 if (lrc == ERROR_SUCCESS || lrc == ERROR_MORE_DATA)
1429 {
1430 cbValue = cbValue + _1K - sizeof(wchar_t)*2; /* 1KB of paranoia fudge, even if we ASSUME no races. */
1431 pbBuf = (PBYTE)RTMemAllocZ(cbValue + sizeof(wchar_t)*2); /* Two extra wchar_t's for proper zero termination, see docs. */
1432 if (!pbBuf)
1433 lrc = ERROR_OUTOFMEMORY;
1434 lrc = RegQueryValueEx(hKey, pwszValueName, NULL, &dwType, pbBuf, &cbValue);
1435 }
1436 if ( lrc == ERROR_SUCCESS
1437 && dwType == REG_MULTI_SZ)
1438 {
1439#ifdef DEBUG
1440 PrintSXS("RemoveStringFromMultiSZ: Current value length: ", cbValue, "\r\n");
1441#endif
1442 /*
1443 * Scan the buffer and remove all strings containing the pwszItemToRemove
1444 * as a substring.
1445 */
1446 size_t const cwcValueToRemove = RTUtf16Len(pwszItemToRemove);
1447 wchar_t volatile *pwszSrc = (wchar_t volatile *)pbBuf;
1448 wchar_t volatile *pwszDst = (wchar_t volatile *)pbBuf;
1449 size_t cbLeft = cbValue;
1450 for (;;)
1451 {
1452 /* Find the length for the current string. We can safely use RTUtf16Len
1453 here because of a zero terminated buffer with two extra terminator chars. */
1454 size_t const cwcSrc = RTUtf16Len((wchar_t const *)pwszSrc);
1455 size_t const cbSrc = (cwcSrc + 1) * sizeof(wchar_t);
1456 if (!IsSubStringOf(pwszSrc, cwcSrc, pwszItemToRemove, cwcValueToRemove))
1457 {
1458 if (pwszDst != pwszSrc)
1459 memmove((void *)pwszDst, (void const *)pwszSrc, cbSrc);
1460 pwszDst += cwcSrc + 1;
1461 }
1462
1463 /* Advance. */
1464 if (cbLeft < cbSrc)
1465 break;
1466 cbLeft -= cbSrc;
1467 pwszSrc += cwcSrc + 1;
1468 }
1469 *pwszDst = '\0';
1470 DWORD const cbNewValue = (DWORD)((PBYTE)(pwszDst + 1) - pbBuf);
1471#ifdef DEBUG
1472 PrintSXS("RemoveStringFromMultiSZ: New value length: ", cbNewValue, "\r\n");
1473#endif
1474
1475 /*
1476 * Update the value if we made any change.
1477 */
1478 if (cbNewValue == cbValue)
1479 {
1480#ifdef DEBUG
1481 PrintSWSWS("RemoveStringFromMultiSZ: The item '", pwszItemToRemove, "' was not part of '",
1482 pwszValueName, "', so nothing needed doing.\r\n");
1483#endif
1484 rcExit = EXIT_OK;
1485 }
1486 else
1487 {
1488 lrc = RegSetValueExW(hKey, pwszValueName, 0, REG_MULTI_SZ, pbBuf, cbNewValue);
1489 if (lrc == ERROR_SUCCESS)
1490 {
1491#ifdef DEBUG
1492 PrintSWSWS("RemoveStringFromMultiSZ: The item '", pwszItemToRemove, "' was removed successfully from '",
1493 pwszValueName, "'.\r\n");
1494#endif
1495 rcExit = EXIT_OK;
1496 }
1497 else
1498 ErrorMsgLStatusSWSWSRS("RegistryAddStringToList: RegSetValueExW HKLM/'",
1499 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1500 }
1501 }
1502 else if (lrc == ERROR_FILE_NOT_FOUND)
1503 {
1504#ifdef DEBUG
1505 PrintStr("RemoveStringFromMultiSZ: value not present in registry\r\n");
1506#endif
1507 rcExit = EXIT_OK;
1508 }
1509 else if (lrc != ERROR_SUCCESS)
1510 ErrorMsgLStatusSWSWSRS("RemoveStringFromMultiSZ: RegQueryValueEx HKLM/'",
1511 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1512 else
1513 ErrorMsgLStatusSWSWSRS("RemoveStringFromMultiSZ: Unexpected value type for HKLM/'",
1514 pwszSubKey, "'/'", pwszValueName, "': ", (LSTATUS)dwType, ", expected REG_SZ (1)");
1515 RegCloseKey(hKey);
1516 RTMemFree(pbBuf);
1517 return rcExit;
1518}
1519
1520
1521/** Handles 'registry delmultisz'. */
1522static int handleRegistryDelMultiSz(unsigned cArgs, wchar_t **papwszArgs)
1523{
1524 RT_NOREF(cArgs);
1525 return RegistryRemoveStringFromMultiSZ(papwszArgs[0], papwszArgs[1], papwszArgs[2]);
1526}
1527
1528
1529/**
1530 * Compare the current list item with the one to add/remove.
1531 *
1532 * Used by RegistryAddStringToList and RegistryRemoveStringFromList.
1533 */
1534static bool IsStringListItemMatch(wchar_t volatile *pwszItem1, size_t cwcItem1,
1535 wchar_t const *pwszItem2, size_t cwcItem2)
1536{
1537 if (cwcItem1 == cwcItem2)
1538 {
1539#if 0 /* 94720 bytes */
1540 if (RTUtf16NICmp((wchar_t const *)pwszItem1, pwszItem2, cwcItem1) == 0)
1541 return true;
1542#else /* vs 62464 bytes */
1543 /* Temporarily zero termination of item 1 as it's easier, and therefore
1544 safer, to use lstrcmpiW than CompareStringW or CompareStringExW. The
1545 latter is Vista and later, the former has a big fat warning on it. */
1546 wchar_t const wcEnd = pwszItem1[cwcItem1];
1547 pwszItem1[cwcItem1] = '\0';
1548 int const iDiff = lstrcmpiW((wchar_t const *)pwszItem1, pwszItem2);
1549 pwszItem1[cwcItem1] = wcEnd;
1550 return iDiff == 0;
1551#endif
1552 }
1553 return false;
1554}
1555
1556
1557/**
1558 * Adds an item to a comma separated registry string list (REG_SZ).
1559 *
1560 * Only operates in HKLM for now, if needed it can be extended later for use
1561 * with other hives.
1562 *
1563 * @return Exit code (EXIT_OK, EXIT_FAIL)
1564 * @param hRootKey The root key.
1565 * @param pwszSubKey Sub key containing the list value.
1566 * @param pwszValueName The name of the value holding the list.
1567 * @param pwszItemToAdd The value to add to the list.
1568 * @param uPosition Position (zero-based) of where to insert the
1569 * value into the list.
1570 * @param fFlags VBOX_REG_STRINGLIST_ALLOW_DUPLICATES or 0.
1571 */
1572static int RegistryAddStringToList(HKEY hRootKey, const wchar_t *pwszSubKey, const wchar_t *pwszValueName,
1573 const wchar_t *pwszItemToAdd, uint32_t uPosition, uint32_t fFlags)
1574{
1575 /* Overflow precaution - see comment below. */
1576 size_t const cwcItemToAdd = RTUtf16Len(pwszItemToAdd);
1577 if (cwcItemToAdd >= 256 /* see wszNewValue size below */)
1578 return ErrorMsg("RegistryAddStringToList: The value to add is too long! Max 256 chars.");
1579
1580 /*
1581 * Open/create the key.
1582 */
1583 HKEY hKey = NULL;
1584 DWORD dwDisp = 0;
1585 LSTATUS lrc = RegCreateKeyEx(hRootKey, pwszSubKey, 0 /*Reserved*/, NULL /*pClass*/, REG_OPTION_NON_VOLATILE,
1586 KEY_READ | KEY_WRITE, NULL /*pSecAttr*/, &hKey, &dwDisp);
1587 if (lrc != ERROR_SUCCESS)
1588 return ErrorMsgLStatusSWSWSRS("RegistryAddStringToList: RegCreateKeyEx ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey,
1589 "' failed: ", lrc, NULL);
1590
1591 /*
1592 * Query the current value.
1593 */
1594 int rcExit = EXIT_FAIL;
1595 wchar_t wszValue[1024] = { 0 };
1596 DWORD cbValue = sizeof(wszValue) - sizeof(wchar_t);
1597 DWORD dwType = 0;
1598 lrc = RegQueryValueEx(hKey, pwszValueName, NULL /*pReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
1599 if (lrc == ERROR_FILE_NOT_FOUND)
1600 {
1601 PrintStr("RegistryAddStringToList: Value not found, creating a new one...\r\n");
1602 wszValue[0] = '\0';
1603 cbValue = sizeof(wchar_t);
1604 dwType = REG_SZ;
1605 lrc = ERROR_SUCCESS;
1606 }
1607 if (lrc == ERROR_SUCCESS && dwType == REG_SZ)
1608 {
1609#ifdef DEBUG
1610 PrintSWS("RegistryAddStringToList: Value string: '", wszValue, "'\r\n");
1611#endif
1612
1613 /*
1614 * Scan the list and make a new copy of it with the new item added
1615 * in the specified place.
1616 *
1617 * Having checked that what we're adding isn't more than 256 + 1 chars long
1618 * above, we can avoid tedious overflow checking here the simple expedient of
1619 * using an output buffer that's at least 256 + 1 chars bigger than the source.
1620 */
1621 wchar_t wszNewValue[RT_ELEMENTS(wszValue) + 256 + 4] = { 0 };
1622 wchar_t *pwszDst = wszNewValue;
1623 wchar_t *pwszSrc = wszValue;
1624 for (unsigned uCurPos = 0;; uCurPos++)
1625 {
1626 /* Skip leading commas: */
1627 wchar_t wc = *pwszSrc;
1628 bool fLeadingComma = wc == ',';
1629 if (fLeadingComma)
1630 do
1631 wc = *++pwszSrc;
1632 while (wc == ',');
1633
1634 /* Insert the new item if we're at the right position or have reached
1635 the end of the list and have yet done so. */
1636 if (uCurPos == uPosition || (!wc && uCurPos < uPosition))
1637 {
1638 if (fLeadingComma || (wc == '\0' && pwszDst != wszNewValue))
1639 *pwszDst++ = ',';
1640 memcpy(pwszDst, pwszItemToAdd, cwcItemToAdd * sizeof(wchar_t));
1641 pwszDst += cwcItemToAdd;
1642 fLeadingComma = true;
1643 }
1644
1645 /* Get out of the loop if we're at the end of the input. */
1646 if (!wc)
1647 break; /* don't preserve trailing commas? Old code didn't (see strtok_r code). */
1648
1649 /* Start of a new 'value', so, find the end of it. */
1650 wchar_t *pwszSrcEnd = pwszSrc + 1;
1651 do
1652 wc = *++pwszSrcEnd;
1653 while (wc != '\0' && wc != ',');
1654 size_t const cwcItem = (size_t)(pwszSrcEnd - pwszSrc);
1655
1656 /* If it matches pwszItemToRemove and the VBOX_REG_STRINGLIST_ALLOW_DUPLICATES
1657 wasn't specified, we'll skip this value. */
1658 ASMCompilerBarrier(); /* Paranoia ^ 2*/
1659 if ( !(fFlags & VBOX_REG_STRINGLIST_ALLOW_DUPLICATES)
1660 && IsStringListItemMatch(pwszSrc, cwcItem, pwszItemToAdd, cwcItemToAdd))
1661 {
1662 pwszSrc = pwszSrcEnd;
1663 if (!fLeadingComma)
1664 while (*pwszSrc == ',')
1665 pwszSrc++;
1666 uCurPos--;
1667 }
1668 else
1669 {
1670 if (fLeadingComma)
1671 *pwszDst++ = ',';
1672 memmove(pwszDst, pwszSrc, cwcItem * sizeof(*pwszDst));
1673 pwszDst += cwcItem;
1674 pwszSrc = pwszSrcEnd;
1675 ASMCompilerBarrier(); /* Paranoia ^ 3 */
1676 }
1677
1678 /* pwszSrc should not point at a comma or a zero terminator. */
1679 }
1680 *pwszDst = '\0';
1681 DWORD const cbNewValue = (DWORD)((pwszDst + 1 - &wszNewValue[0]) * sizeof(wchar_t));
1682
1683#ifdef DEBUG
1684 PrintSWS("RegistryAddStringToList: New value: '", wszNewValue, "'\r\n");
1685#endif
1686
1687 /*
1688 * Add the value if changed.
1689 */
1690 if ( cbNewValue == cbValue
1691 && memcmp(wszNewValue, wszValue, cbNewValue) == 0)
1692 rcExit = EXIT_OK;
1693 else
1694 {
1695 lrc = RegSetValueExW(hKey, pwszValueName, 0, REG_SZ, (LPBYTE)wszNewValue, cbNewValue);
1696 if (lrc == ERROR_SUCCESS)
1697 rcExit = EXIT_OK;
1698 else
1699 ErrorMsgLStatusSWSWSWSRS("RegistryAddStringToList: RegSetValueExW HKLM/'",
1700 pwszSubKey, "'/'", pwszValueName, "' = '", wszNewValue, "' failed: ", lrc, NULL);
1701 }
1702 }
1703 else if (lrc != ERROR_SUCCESS)
1704 ErrorMsgLStatusSWSWSWSRS("RegistryAddStringToList: RegQueryValueEx ", RegistryRootToWStr(hRootKey), "/'",
1705 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1706 else
1707 ErrorMsgLStatusSWSWSWSRS("RegistryAddStringToList: Unexpected value type for ", RegistryRootToWStr(hRootKey), "/'",
1708 pwszSubKey, "'/'", pwszValueName, "': ", (LSTATUS)dwType, ", expected REG_SZ (1)");
1709
1710 RegCloseKey(hKey);
1711 return rcExit;
1712}
1713
1714
1715/**
1716 * Handles 'netprovider add'.
1717 */
1718static int handleNetProviderAdd(unsigned cArgs, wchar_t **papwszArgs)
1719{
1720 const wchar_t * const pwszProvider = papwszArgs[0];
1721 wchar_t const * const pwszPosition = cArgs > 1 ? papwszArgs[1] : L"0";
1722 uint32_t uPosition = 0;
1723 if (cArgs > 1 && !ArgToUInt32Full(pwszPosition, "position", &uPosition))
1724 return EXIT_USAGE;
1725
1726 PrintSWSWS("Adding network provider '", pwszProvider, "' (Position = ", pwszPosition, ") ...\r\n");
1727 int rcExit = RegistryAddStringToList(HKEY_LOCAL_MACHINE,
1728 L"System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
1729 L"ProviderOrder",
1730 pwszProvider, uPosition, VBOX_REG_STRINGLIST_NONE);
1731 if (rcExit == EXIT_OK)
1732 PrintStr("Network provider successfully added!\r\n");
1733
1734 return rcExit;
1735}
1736
1737
1738/**
1739 * Handles 'registry addlistitem'.
1740 */
1741static int handleRegistryAddListItem(unsigned cArgs, wchar_t **papwszArgs)
1742{
1743 /*
1744 * Parameters.
1745 */
1746 wchar_t const * const pwszRoot = papwszArgs[0];
1747 wchar_t const * const pwszSubKey = papwszArgs[1];
1748 wchar_t const * const pwszValueName = papwszArgs[2];
1749 wchar_t const * const pwszItem = papwszArgs[3];
1750 wchar_t const * const pwszPosition = cArgs > 4 ? papwszArgs[4] : L"0";
1751 wchar_t const * const pwszFlags = cArgs > 5 ? papwszArgs[5] : NULL;
1752
1753 HKEY hRootKey = ArgToRegistryRoot(pwszRoot);
1754 if (hRootKey == NULL)
1755 return EXIT_USAGE;
1756
1757 uint32_t uPosition = 0;
1758 if (!ArgToUInt32Full(pwszPosition, "position", &uPosition))
1759 return EXIT_USAGE;
1760
1761 uint32_t fFlags = 0;
1762 if (pwszFlags)
1763 {
1764 if (RTUtf16ICmpAscii(pwszFlags, "dup") == 0)
1765 fFlags = VBOX_REG_STRINGLIST_ALLOW_DUPLICATES;
1766 else if (RTUtf16ICmpAscii(pwszFlags, "no-dups") == 0)
1767 fFlags = 0;
1768 else
1769 return ErrorBadArg("flags", pwszFlags, "'dup' or 'no-dups'");
1770 }
1771
1772 /*
1773 * Do the work.
1774 */
1775 int rcExit = RegistryAddStringToList(hRootKey, pwszSubKey, pwszValueName, pwszItem, uPosition, fFlags);
1776 if (rcExit == EXIT_OK)
1777 PrintSWSWSWSWS("Successfully added '", pwszItem, "' to ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "'/'",
1778 pwszValueName, "'\r\n");
1779
1780 return rcExit;
1781}
1782
1783
1784/**
1785 * Removes an item from a comma separated registry string (REG_SZ).
1786 *
1787 * Only operates in HKLM for now, if needed it can be extended later for use
1788 * with other hives.
1789 *
1790 * @return Exit code (EXIT_OK, EXIT_FAIL)
1791 * @param hRootKey The root key.
1792 * @param pwszSubKey Subkey containing the list value.
1793 * @param pwszValueName The value name.
1794 * @param pwszItemToRemove The item to remove from the list. Empty values
1795 * are not supported.
1796 */
1797static int RegistryRemoveStringFromList(HKEY hRootKey, const wchar_t *pwszSubKey, const wchar_t *pwszValueName,
1798 const wchar_t *pwszItemToRemove)
1799{
1800 /*
1801 * Open the specified key.
1802 */
1803 HKEY hKey = NULL;
1804 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszSubKey, 0 /*dwOptions*/, KEY_READ | KEY_WRITE, &hKey);
1805 if (lrc != ERROR_SUCCESS)
1806 return ErrorMsgLStatusSWSWSRS("RegistryRemoveStringFromList: RegOpenKeyExW ", RegistryRootToWStr(hRootKey),
1807 "/'", pwszSubKey, "' failed: ", lrc, NULL);
1808
1809 /*
1810 * Query the specified value.
1811 */
1812 int rcExit = EXIT_FAIL;
1813 wchar_t wszValue[1296] = { 0 };
1814 DWORD cbValue = sizeof(wszValue) - sizeof(wchar_t);
1815 DWORD dwType = 0;
1816 lrc = RegQueryValueEx(hKey, pwszValueName, NULL /*pReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
1817 if (lrc == ERROR_SUCCESS && dwType == REG_SZ)
1818 {
1819#ifdef DEBUG
1820 PrintSWS("RegistryRemoveStringFromList: Value string: '", wszValue, "'\r\n");
1821#endif
1822
1823 /*
1824 * Scan for item, shifting the query result as we scan.
1825 */
1826 size_t const cwcItemToRemove = RTUtf16Len(pwszItemToRemove);
1827 wchar_t volatile *pwszSrc = wszValue;
1828 wchar_t volatile *pwszDst = wszValue;
1829 for (;;)
1830 {
1831 /* Skip leading commas: */
1832 wchar_t wc = *pwszSrc;
1833 bool const fLeadingComma = wc == ',';
1834 if (fLeadingComma)
1835 do
1836 wc = *++pwszSrc;
1837 while (wc == ',');
1838 if (!wc)
1839 break; /* don't preserve trailing commas? Old code didn't (see strtok_r code). */
1840
1841 /* Start of a new 'value', so, find the end of it. */
1842 wchar_t volatile *pwszSrcEnd = pwszSrc + 1;
1843 do
1844 wc = *++pwszSrcEnd;
1845 while (wc != '\0' && wc != ',');
1846 size_t const cwcItem = (size_t)(pwszSrcEnd - pwszSrc);
1847
1848 /* If it matches pwszItemToRemove, do not copy it. */
1849 ASMCompilerBarrier(); /* Paranoia ^ 2 */
1850 if (IsStringListItemMatch(pwszSrc, cwcItem, pwszItemToRemove, cwcItemToRemove))
1851 {
1852 pwszSrc = pwszSrcEnd;
1853 if (!fLeadingComma)
1854 while (*pwszSrc == ',')
1855 pwszSrc++;
1856 }
1857 else
1858 {
1859 if (fLeadingComma)
1860 *pwszDst++ = ',';
1861 memmove((void *)pwszDst, (void const *)pwszSrc, cwcItem * sizeof(*pwszDst));
1862 pwszDst += cwcItem;
1863 pwszSrc = pwszSrcEnd;
1864 ASMCompilerBarrier(); /* paranoia ^ 3 */
1865 }
1866
1867 /* pwszSrc should not point at a comma or a zero terminator. */
1868 }
1869 *pwszDst = '\0';
1870#ifdef DEBUG
1871 PrintSWS("RegistryRemoveStringFromList: New value: '", wszValue, "'\r\n");
1872#endif
1873
1874 /*
1875 * Save the new value if we've made any changes.
1876 */
1877 if (pwszDst == pwszSrc)
1878 rcExit = EXIT_OK;
1879 else
1880 {
1881 cbValue = (DWORD)((pwszDst + 1 - &wszValue[0]) * sizeof(wchar_t));
1882 lrc = RegSetValueExW(hKey, pwszValueName, 0, REG_SZ, (LPBYTE)wszValue, cbValue);
1883 if (lrc == ERROR_SUCCESS)
1884 rcExit = EXIT_OK;
1885 else
1886 ErrorMsgLStatusSWSWSWSWSRS("RegistryRemoveStringFromList: RegSetValueExW ", RegistryRootToWStr(hRootKey), "/'",
1887 pwszSubKey, "'/'", pwszValueName, "' = '", wszValue, "' failed: ", lrc, NULL);
1888 }
1889 }
1890 else if (lrc == ERROR_FILE_NOT_FOUND)
1891 {
1892#ifdef DEBUG
1893 PrintStr("RegistryRemoveStringFromList: Value not present in registry\r\n");
1894#endif
1895 rcExit = EXIT_OK;
1896 }
1897 else if (lrc != ERROR_SUCCESS)
1898 ErrorMsgLStatusSWSWSWSRS("RegistryRemoveStringFromList: RegQueryValueEx ", RegistryRootToWStr(hRootKey), "/'",
1899 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1900 else
1901 ErrorMsgLStatusSWSWSWSRS("RegistryRemoveStringFromList: Unexpected value type for ", RegistryRootToWStr(hRootKey), "/'",
1902 pwszSubKey, "'/'", pwszValueName, "': ", (LSTATUS)dwType, ", expected REG_SZ (1)");
1903 RegCloseKey(hKey);
1904 return rcExit;
1905}
1906
1907
1908/**
1909 * Handles 'netprovider remove'.
1910 */
1911static int handleNetProviderRemove(unsigned cArgs, wchar_t **papwszArgs)
1912{
1913 const wchar_t * const pwszProvider = papwszArgs[0];
1914 PrintSWS("Removing network provider '", pwszProvider, "' ...\r\n");
1915
1916 int rcExit = RegistryRemoveStringFromList(HKEY_LOCAL_MACHINE,
1917 L"System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
1918 L"ProviderOrder",
1919 pwszProvider);
1920 if (rcExit == EXIT_OK)
1921 PrintStr("Network provider successfully removed!\r\n");
1922
1923 RT_NOREF(cArgs);
1924 return rcExit;
1925}
1926
1927
1928/**
1929 * Handles 'registry dellistitem'.
1930 */
1931static int handleRegistryDelListItem(unsigned cArgs, wchar_t **papwszArgs)
1932{
1933 /*
1934 * Parameters.
1935 */
1936 RT_NOREF(cArgs);
1937 wchar_t const * const pwszRoot = papwszArgs[0];
1938 wchar_t const * const pwszSubKey = papwszArgs[1];
1939 wchar_t const * const pwszValueName = papwszArgs[2];
1940 wchar_t const * const pwszItem = papwszArgs[3];
1941
1942 HKEY hRootKey = ArgToRegistryRoot(pwszRoot);
1943 if (hRootKey == NULL)
1944 return EXIT_USAGE;
1945
1946 /*
1947 * Do the work.
1948 */
1949 int rcExit = RegistryRemoveStringFromList(hRootKey, pwszSubKey, pwszValueName, pwszItem);
1950 if (rcExit == EXIT_OK)
1951 PrintSWSWSWSWS("Successfully removed '", pwszItem, "' from ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "'/'",
1952 pwszValueName, "'\r\n");
1953
1954 return rcExit;
1955}
1956
1957
1958/**
1959 * Handles 'registry write'.
1960 */
1961static int handleRegistryWrite(unsigned cArgs, wchar_t **papwszArgs)
1962{
1963 /*
1964 * Mandatory parameters.
1965 */
1966 wchar_t const * const pwszRoot = papwszArgs[0];
1967 wchar_t const * const pwszSubKey = papwszArgs[1];
1968 wchar_t const * const pwszValueName = papwszArgs[2];
1969 wchar_t const * const pwszType = papwszArgs[3];
1970 wchar_t const * const pwszValue = papwszArgs[4];
1971
1972 /*
1973 * Root key:
1974 */
1975 HKEY hRootKey = ArgToRegistryRoot(pwszRoot);
1976 if (hRootKey == NULL)
1977 return EXIT_USAGE;
1978
1979 /*
1980 * Type and value with default length.
1981 */
1982 union
1983 {
1984 uint32_t dw;
1985 uint64_t qw;
1986 } uValue;
1987 DWORD dwType;
1988 DWORD cbValue;
1989 BYTE const *pbValue;
1990 if ( RTUtf16ICmpAscii(pwszType, "REG_BINARY") == 0
1991 || RTUtf16ICmpAscii(pwszType, "REG_BIN") == 0
1992 || RTUtf16ICmpAscii(pwszType, "BINARY") == 0)
1993 {
1994 dwType = REG_BINARY;
1995 cbValue = (DWORD)(RTUtf16Len(pwszValue) + 1) * sizeof(wchar_t);
1996 pbValue = (BYTE const *)pwszValue;
1997 }
1998 else if ( RTUtf16ICmpAscii(pwszType, "REG_DWORD") == 0
1999 || RTUtf16ICmpAscii(pwszType, "DWORD") == 0)
2000 {
2001 if (!ArgToUInt32Full(pwszValue, "dword value", &uValue.dw))
2002 return EXIT_USAGE;
2003 dwType = REG_DWORD;
2004 pbValue = (BYTE const *)&uValue.dw;
2005 cbValue = sizeof(uValue.dw);
2006 }
2007 else if ( RTUtf16ICmpAscii(pwszType, "REG_QWORD") == 0
2008 || RTUtf16ICmpAscii(pwszType, "QWORD") == 0)
2009 {
2010 if (!ArgToUInt64Full(pwszValue, "qword value", &uValue.qw))
2011 return EXIT_USAGE;
2012 dwType = REG_QWORD;
2013 pbValue = (BYTE const *)&uValue.qw;
2014 cbValue = sizeof(uValue.qw);
2015 }
2016 else if ( RTUtf16ICmpAscii(pwszType, "REG_SZ") == 0
2017 || RTUtf16ICmpAscii(pwszType, "SZ") == 0)
2018 {
2019 dwType = REG_SZ;
2020 cbValue = (DWORD)((RTUtf16Len(pwszValue) + 1) * sizeof(wchar_t));
2021 pbValue = (BYTE const *)pwszValue;
2022 }
2023 else
2024 return ErrorBadArg("type", pwszType, "");
2025
2026 /*
2027 * Binary only: Reinterpret the input as - optional.
2028 */
2029 if (cArgs > 5)
2030 {
2031 if (dwType != REG_BINARY)
2032 return ErrorMsg("The 'binary-conversion' argument is currently only supported for REG_BINARY type values!");
2033 if (RTUtf16ICmpAscii(papwszArgs[5], "dword") == 0)
2034 {
2035 if (!ArgToUInt32Full(pwszValue, "dword(/binary) value", &uValue.dw))
2036 return EXIT_USAGE;
2037 pbValue = (BYTE const *)&uValue.dw;
2038 cbValue = sizeof(uValue.dw);
2039 }
2040 else if (RTUtf16ICmpAscii(papwszArgs[5], "qword") == 0)
2041 {
2042 if (!ArgToUInt64Full(pwszValue, "qword(/binary) value", &uValue.qw))
2043 return EXIT_USAGE;
2044 pbValue = (BYTE const *)&uValue.qw;
2045 cbValue = sizeof(uValue.qw);
2046 }
2047 else
2048 return ErrorBadArg("binary-conversion", papwszArgs[0], "dword");
2049 }
2050
2051 /*
2052 * Binary only: Max length to write - optional.
2053 */
2054 if (cArgs> 6)
2055 {
2056 if (dwType != REG_BINARY)
2057 return ErrorMsg("The 'max-size' argument is currently only supported for REG_BINARY type values!");
2058 uint32_t cbMaxValue;
2059 if (!ArgToUInt32Full(papwszArgs[6], "max-size", &cbMaxValue))
2060 return EXIT_USAGE;
2061 if (cbValue > cbMaxValue)
2062 cbValue = cbMaxValue;
2063 }
2064
2065 /*
2066 * Do the writing.
2067 */
2068 HKEY hKey = NULL;
2069 LSTATUS lrc = RegCreateKeyExW(hRootKey, pwszSubKey, 0 /*Reserved*/, NULL /*pwszClass*/, 0 /*dwOptions*/,
2070 KEY_WRITE, NULL /*pSecAttr*/, &hKey, NULL /*pdwDisposition*/);
2071 if (lrc != ERROR_SUCCESS)
2072 return ErrorMsgLStatusSWSWSRS("RegCreateKeyExW ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "' failed: ", lrc, NULL);
2073
2074 lrc = RegSetValueExW(hKey, pwszValueName, 0, dwType, pbValue, cbValue);
2075 RegCloseKey(hKey);
2076 if (lrc != ERROR_SUCCESS)
2077 return ErrorMsgLStatusSWSWSWSRS("RegSetValueExW ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "'/'",
2078 pwszValueName, "' failed: ", lrc, NULL);
2079 return EXIT_OK;
2080}
2081
2082
2083/**
2084 * Handles 'registry delete'.
2085 */
2086static int handleRegistryDelete(unsigned cArgs, wchar_t **papwszArgs)
2087{
2088 /*
2089 * Parameters.
2090 */
2091 RT_NOREF(cArgs);
2092 wchar_t const * const pwszRoot = papwszArgs[0];
2093 wchar_t const * const pwszSubKey = papwszArgs[1];
2094 wchar_t const * const pwszValueName = papwszArgs[2];
2095
2096 HKEY const hRootKey = ArgToRegistryRoot(pwszRoot);
2097 if (hRootKey == NULL)
2098 return EXIT_USAGE;
2099
2100 /*
2101 * Do the deleting.
2102 */
2103 HKEY hKey = NULL;
2104 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, papwszArgs[1] /*pwszSubKey*/, 0 /*dwOptions*/, KEY_READ | KEY_WRITE, &hKey);
2105 if (lrc != ERROR_FILE_NOT_FOUND)
2106 {
2107 if (lrc != ERROR_SUCCESS)
2108 return ErrorMsgLStatusSWSWSRS("RegOpenKeyExW ", pwszRoot, "/'", pwszSubKey, "' failed: ", lrc, NULL);
2109
2110 lrc = RegDeleteValueW(hKey, pwszValueName);
2111 RegCloseKey(hKey);
2112 if (lrc != ERROR_SUCCESS && lrc != ERROR_FILE_NOT_FOUND)
2113 return ErrorMsgLStatusSWSWSWSRS("RegDeleteValueW ", pwszRoot, "/'", pwszSubKey, "'/'",
2114 pwszValueName, "' failed: ", lrc, NULL);
2115 }
2116 return EXIT_OK;
2117}
2118
2119
2120/** Handles 'version' and its aliases. */
2121static int handleVersion(unsigned cArgs, wchar_t **papwszArgs)
2122{
2123 PrintStr(RT_XSTR(VBOX_VERSION_MAJOR) "." RT_XSTR(VBOX_VERSION_MINOR) "." RT_XSTR(VBOX_VERSION_BUILD) "r" RT_XSTR(VBOX_SVN_REV) "\r\n");
2124 RT_NOREF(cArgs, papwszArgs);
2125 return EXIT_OK;
2126}
2127
2128
2129/** Handles 'help' and all its aliases. */
2130static int handleHelp(unsigned cArgs, wchar_t **papwszArgs)
2131{
2132 /* "0 1 2 3 4 5 6 7 8 */
2133 /* "012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
2134 PrintStr("VirtualBox Guest Additions Installation Helper for Windows\r\n"
2135 "Version: " RT_XSTR(VBOX_VERSION_MAJOR) "." RT_XSTR(VBOX_VERSION_MINOR) "." RT_XSTR(VBOX_VERSION_BUILD) "r" RT_XSTR(VBOX_SVN_REV) "\r\n"
2136 "\r\n"
2137 "Syntax: VBoxDrvInst <command> <subcommand>\r\n"
2138 "\r\n"
2139 "Drivers:\r\n"
2140 " VBoxDrvInst driver install <inf-file> [log-file]\r\n"
2141 " VBoxDrvInst driver uninstall <inf-file> [log-file]\r\n"
2142 " VBoxDrvInst driver executeinf <inf-file>\r\n"
2143 " VBoxDrvInst driver nt4-install-video [install-dir]\r\n"
2144 "\r\n"
2145 "Service:\r\n"
2146 " VBoxDrvInst service create <name> <display-name> <service-type>\r\n"
2147 " <start-type> <binary-path> [load-order] [deps] [user] [password]\r\n"
2148 " VBoxDrvInst service delete <name>\r\n"
2149 "\r\n"
2150 "Network Provider:\r\n"
2151 " VBoxDrvInst netprovider add <name> <position>\r\n"
2152 " VBoxDrvInst netprovider remove <name>\r\n"
2153 "\r\n"
2154 "Registry:\r\n"
2155 " VBoxDrvInst registry write <root> <sub-key> <value-name> <type> <value>\r\n"
2156 " [binary-conversion] [max-size]\r\n"
2157 " VBoxDrvInst registry delete <root> <sub-key> <value-name>\r\n"
2158 /** @todo Add roots for these two. */
2159 " VBoxDrvInst registry addmultisz <sub-key> <value-name> <to-add> <position>\r\n"
2160 " VBoxDrvInst registry delmultisz <sub-key> <value-name> <to-remove>\r\n"
2161 " VBoxDrvInst registry addlistitem <root> <sub-key> <value-name> <to-add>\r\n"
2162 " [position [dup|no-dup]]\r\n"
2163 " VBoxDrvInst registry dellistitem <root> <sub-key> <value-name> <to-remove>\r\n"
2164 "\r\n"
2165 "Standard options:\r\n"
2166 " VBoxDrvInst [help|--help|/help|-h|/h|-?|/h] [...]\r\n"
2167 " VBoxDrvInst [version|--version|-V]\r\n"
2168 );
2169 RT_NOREF(cArgs, papwszArgs);
2170 return EXIT_OK;
2171}
2172
2173
2174int wmain(int argc, wchar_t **argv)
2175{
2176 /* Not initializing IPRT here, ASSUMING the little bit we use of it does
2177 not need any initialization. Reduces the binary size a little. */
2178
2179 static struct
2180 {
2181 const char *pszCmd;
2182 const char *pszSubCmd;
2183 unsigned cMin, cMax;
2184 int (*pfnHandler)(unsigned cArgs, wchar_t **papwszArgs);
2185 } s_aActions[] =
2186 {
2187 { "driver", "install", 1, 2, handleDriverInstall },
2188 { "driver", "uninstall", 1, 2, handleDriverUninstall },
2189 { "driver", "executeinf", 1, 1, handleDriverExecuteInf },
2190 { "driver", "nt4-install-video", 0, 1, handleDriverNt4InstallVideo },
2191 { "service", "create", 5, 9, handleServiceCreate },
2192 { "service", "delete", 1, 1, handleServiceDelete },
2193 { "netprovider", "add", 1, 2, handleNetProviderAdd },
2194 { "netprovider", "remove", 1, 2, handleNetProviderRemove },
2195 { "registry", "addlistitem", 4, 6, handleRegistryAddListItem },
2196 { "registry", "dellistitem", 4, 4, handleRegistryDelListItem },
2197 { "registry", "addmultisz", 4, 4, handleRegistryAddMultiSz },
2198 { "registry", "delmultisz", 3, 3, handleRegistryDelMultiSz },
2199 { "registry", "write", 5, 7, handleRegistryWrite },
2200 { "registry", "delete", 3, 3, handleRegistryDelete },
2201
2202 { "help", NULL, 0, ~0U, handleHelp },
2203 { "--help", NULL, 0, ~0U, handleHelp },
2204 { "/help", NULL, 0, ~0U, handleHelp },
2205 { "-h", NULL, 0, ~0U, handleHelp },
2206 { "/h", NULL, 0, ~0U, handleHelp },
2207 { "-?", NULL, 0, ~0U, handleHelp },
2208 { "/?", NULL, 0, ~0U, handleHelp },
2209 { "version", NULL, 0, ~0U, handleVersion },
2210 { "--version", NULL, 0, ~0U, handleVersion },
2211 { "-V", NULL, 0, ~0U, handleVersion },
2212 };
2213
2214 /*
2215 * Lookup the action handler.
2216 */
2217 int rcExit = EXIT_USAGE;
2218 if (argc >= 2)
2219 {
2220 const wchar_t * const pwszCmd = argv[1];
2221 const wchar_t * const pwszSubCmd = argc > 2 ? argv[2] : NULL;
2222 unsigned i = 0;
2223 for (i = 0; i < RT_ELEMENTS(s_aActions); i++)
2224 if ( RTUtf16ICmpAscii(pwszCmd, s_aActions[i].pszCmd) == 0
2225 && ( !s_aActions[i].pszSubCmd
2226 || RTUtf16ICmpAscii(pwszSubCmd, s_aActions[i].pszSubCmd) == 0))
2227 {
2228 unsigned const cArgs = (unsigned)argc - (s_aActions[i].pszSubCmd ? 3 : 2);
2229 wchar_t ** const papwszArgs = &argv[s_aActions[i].pszSubCmd ? 3 : 2];
2230 if (cArgs >= s_aActions[i].cMin && cArgs <= s_aActions[i].cMax)
2231 rcExit = s_aActions[i].pfnHandler(cArgs, papwszArgs);
2232 else
2233 {
2234 bool const fTooFew = cArgs < s_aActions[i].cMin;
2235 ErrorMsgBegin(fTooFew ? "Too few parameters for '" : "Too many parameters for '");
2236 ErrorMsgStr(s_aActions[i].pszCmd);
2237 if (s_aActions[i].pszSubCmd)
2238 {
2239 ErrorMsgStr(" ");
2240 ErrorMsgStr(s_aActions[i].pszSubCmd);
2241 }
2242 ErrorMsgStr("'! Got ");
2243 ErrorMsgU64(cArgs);
2244 ErrorMsgStr(fTooFew ? ", expected at least " : ", expected at most ");;
2245 ErrorMsgU64(fTooFew ? s_aActions[i].cMin : s_aActions[i].cMax);
2246 ErrorMsgEnd(".");
2247 }
2248 break;
2249 }
2250 if (i >= RT_ELEMENTS(s_aActions))
2251 {
2252 ErrorMsgBegin("Unknown action '");
2253 ErrorMsgWStr(pwszCmd);
2254 if (pwszSubCmd)
2255 {
2256 ErrorMsgBegin(" ");
2257 ErrorMsgWStr(pwszSubCmd);
2258 }
2259 ErrorMsgEnd("'! Please consult \"--help\" for more information.\r\n");
2260 }
2261 }
2262 else
2263 ErrorMsg("No parameters given. Please consult \"--help\" for more information.\r\n");
2264 return rcExit;
2265}
2266
2267
2268#ifdef IPRT_NO_CRT
2269int main(int argc, char **argv)
2270{
2271 /*
2272 * Convert the arguments to UTF16 and call wmain. We don't bother freeing
2273 * any of these strings as the process is exiting and it's a waste of time.
2274 */
2275 wchar_t **papwszArgs = (wchar_t **)alloca((argc + 1) * sizeof(wchar_t *));
2276 int i = 0;
2277 while (i < argc)
2278 {
2279 papwszArgs[i] = NULL;
2280 int rc = RTStrToUtf16(argv[i], &papwszArgs[i]);
2281 if (RT_SUCCESS(rc))
2282 i++;
2283 else
2284 return ErrorMsg("Failed to convert command line arguments to UTF16!!");
2285 }
2286 papwszArgs[i] = NULL;
2287 return wmain(argc, papwszArgs);
2288}
2289#endif
2290
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