VirtualBox

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

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

Add/NT/Inst: Doxygen fix. bugref:10261

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