VirtualBox

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

最後變更 在這個檔案從96451是 96449,由 vboxsync 提交於 3 年 前

Add/NT/Inst: Factored out the no-CRT output and error messaging routines and used them in VBoxWindowsAdditions.cpp as well. bugref:10261

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Id Revision
檔案大小: 72.0 KB
 
1/* $Id: VBoxDrvInst.cpp 96449 2022-08-23 23:55:38Z 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.alldomusa.eu.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[1], 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[1], 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 pwszSubKey Sub key containing the list value.
1197 * @param pwszValueName The name of the value holding the list.
1198 * @param pwszItemToAdd The value to add to the list.
1199 * @param uPosition Position (zero-based) of where to insert the
1200 * value into the list.
1201 * @param fFlags VBOX_REG_STRINGLIST_ALLOW_DUPLICATES or 0.
1202 */
1203static int RegistryAddStringToList(HKEY hRootKey, const wchar_t *pwszSubKey, const wchar_t *pwszValueName,
1204 const wchar_t *pwszItemToAdd, uint32_t uPosition, uint32_t fFlags)
1205{
1206 /* Overflow precaution - see comment below. */
1207 size_t const cwcItemToAdd = RTUtf16Len(pwszItemToAdd);
1208 if (cwcItemToAdd >= 256 /* see wszNewValue size below */)
1209 return ErrorMsg("RegistryAddStringToList: The value to add is too long! Max 256 chars.");
1210
1211 /*
1212 * Open/create the key.
1213 */
1214 HKEY hKey = NULL;
1215 DWORD dwDisp = 0;
1216 LSTATUS lrc = RegCreateKeyEx(hRootKey, pwszSubKey, 0 /*Reserved*/, NULL /*pClass*/, REG_OPTION_NON_VOLATILE,
1217 KEY_READ | KEY_WRITE, NULL /*pSecAttr*/, &hKey, &dwDisp);
1218 if (lrc != ERROR_SUCCESS)
1219 return ErrorMsgLStatusSWSWSRS("RegistryAddStringToList: RegCreateKeyEx ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey,
1220 "' failed: ", lrc, NULL);
1221
1222 /*
1223 * Query the current value.
1224 */
1225 int rcExit = EXIT_FAIL;
1226 wchar_t wszValue[1024] = { 0 };
1227 DWORD cbValue = sizeof(wszValue) - sizeof(wchar_t);
1228 DWORD dwType = 0;
1229 lrc = RegQueryValueEx(hKey, pwszValueName, NULL /*pReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
1230 if (lrc == ERROR_FILE_NOT_FOUND)
1231 {
1232 PrintStr("RegistryAddStringToList: Value not found, creating a new one...\r\n");
1233 wszValue[0] = '\0';
1234 cbValue = sizeof(wchar_t);
1235 dwType = REG_SZ;
1236 lrc = ERROR_SUCCESS;
1237 }
1238 if (lrc == ERROR_SUCCESS && dwType == REG_SZ)
1239 {
1240#ifdef DEBUG
1241 PrintSWS("RegistryAddStringToList: Value string: '", wszValue, "'\r\n");
1242#endif
1243
1244 /*
1245 * Scan the list and make a new copy of it with the new item added
1246 * in the specified place.
1247 *
1248 * Having checked that what we're adding isn't more than 256 + 1 chars long
1249 * above, we can avoid tedious overflow checking here the simple expedient of
1250 * using an output buffer that's at least 256 + 1 chars bigger than the source.
1251 */
1252 wchar_t wszNewValue[RT_ELEMENTS(wszValue) + 256 + 4] = { 0 };
1253 wchar_t *pwszDst = wszNewValue;
1254 wchar_t *pwszSrc = wszValue;
1255 for (unsigned uCurPos = 0;; uCurPos++)
1256 {
1257 /* Skip leading commas: */
1258 wchar_t wc = *pwszSrc;
1259 bool fLeadingComma = wc == ',';
1260 if (fLeadingComma)
1261 do
1262 wc = *++pwszSrc;
1263 while (wc == ',');
1264
1265 /* Insert the new item if we're at the right position or have reached
1266 the end of the list and have yet done so. */
1267 if (uCurPos == uPosition || (!wc && uCurPos < uPosition))
1268 {
1269 if (fLeadingComma || (wc == '\0' && pwszDst != wszNewValue))
1270 *pwszDst++ = ',';
1271 memcpy(pwszDst, pwszItemToAdd, cwcItemToAdd * sizeof(wchar_t));
1272 pwszDst += cwcItemToAdd;
1273 fLeadingComma = true;
1274 }
1275
1276 /* Get out of the loop if we're at the end of the input. */
1277 if (!wc)
1278 break; /* don't preserve trailing commas? Old code didn't (see strtok_r code). */
1279
1280 /* Start of a new 'value', so, find the end of it. */
1281 wchar_t *pwszSrcEnd = pwszSrc + 1;
1282 do
1283 wc = *++pwszSrcEnd;
1284 while (wc != '\0' && wc != ',');
1285 size_t const cwcItem = (size_t)(pwszSrcEnd - pwszSrc);
1286
1287 /* If it matches pwszItemToRemove and the VBOX_REG_STRINGLIST_ALLOW_DUPLICATES
1288 wasn't specified, we'll skip this value. */
1289 ASMCompilerBarrier(); /* Paranoia ^ 2*/
1290 if ( !(fFlags & VBOX_REG_STRINGLIST_ALLOW_DUPLICATES)
1291 && IsStringListItemMatch(pwszSrc, cwcItem, pwszItemToAdd, cwcItemToAdd))
1292 {
1293 pwszSrc = pwszSrcEnd;
1294 if (!fLeadingComma)
1295 while (*pwszSrc == ',')
1296 pwszSrc++;
1297 uCurPos--;
1298 }
1299 else
1300 {
1301 if (fLeadingComma)
1302 *pwszDst++ = ',';
1303 memmove(pwszDst, pwszSrc, cwcItem * sizeof(*pwszDst));
1304 pwszDst += cwcItem;
1305 pwszSrc = pwszSrcEnd;
1306 ASMCompilerBarrier(); /* Paranoia ^ 3 */
1307 }
1308
1309 /* pwszSrc should not point at a comma or a zero terminator. */
1310 }
1311 *pwszDst = '\0';
1312 DWORD const cbNewValue = (DWORD)((pwszDst + 1 - &wszNewValue[0]) * sizeof(wchar_t));
1313
1314#ifdef DEBUG
1315 PrintSWS("RegistryAddStringToList: New value: '", wszNewValue, "'\r\n");
1316#endif
1317
1318 /*
1319 * Add the value if changed.
1320 */
1321 if ( cbNewValue == cbValue
1322 && memcmp(wszNewValue, wszValue, cbNewValue) == 0)
1323 rcExit = EXIT_OK;
1324 else
1325 {
1326 lrc = RegSetValueExW(hKey, pwszValueName, 0, REG_SZ, (LPBYTE)wszNewValue, cbNewValue);
1327 if (lrc == ERROR_SUCCESS)
1328 rcExit = EXIT_OK;
1329 else
1330 ErrorMsgLStatusSWSWSWSRS("RegistryAddStringToList: RegSetValueExW HKLM/'",
1331 pwszSubKey, "'/'", pwszValueName, "' = '", wszNewValue, "' failed: ", lrc, NULL);
1332 }
1333 }
1334 else if (lrc != ERROR_SUCCESS)
1335 ErrorMsgLStatusSWSWSWSRS("RegistryAddStringToList: RegQueryValueEx ", RegistryRootToWStr(hRootKey), "/'",
1336 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1337 else
1338 ErrorMsgLStatusSWSWSWSRS("RegistryAddStringToList: Unexpected value type for ", RegistryRootToWStr(hRootKey), "/'",
1339 pwszSubKey, "'/'", pwszValueName, "': ", (LSTATUS)dwType, ", expected REG_SZ (1)");
1340
1341 RegCloseKey(hKey);
1342 return rcExit;
1343}
1344
1345
1346/**
1347 * Handles 'netprovider add'.
1348 */
1349static int handleNetProviderAdd(unsigned cArgs, wchar_t **papwszArgs)
1350{
1351 const wchar_t * const pwszProvider = papwszArgs[0];
1352 wchar_t const * const pwszPosition = cArgs > 1 ? papwszArgs[1] : L"0";
1353 uint32_t uPosition = 0;
1354 if (cArgs > 1 && !ArgToUInt32Full(pwszPosition, "position", &uPosition))
1355 return EXIT_USAGE;
1356
1357 PrintSWSWS("Adding network provider '", pwszProvider, "' (Position = ", pwszPosition, ") ...\r\n");
1358 int rcExit = RegistryAddStringToList(HKEY_LOCAL_MACHINE,
1359 L"System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
1360 L"ProviderOrder",
1361 pwszProvider, uPosition, VBOX_REG_STRINGLIST_NONE);
1362 if (rcExit == EXIT_OK)
1363 PrintStr("Network provider successfully added!\r\n");
1364
1365 return rcExit;
1366}
1367
1368
1369/**
1370 * Handles 'registry addlistitem'.
1371 */
1372static int handleRegistryAddListItem(unsigned cArgs, wchar_t **papwszArgs)
1373{
1374 /*
1375 * Parameters.
1376 */
1377 wchar_t const * const pwszRoot = papwszArgs[0];
1378 wchar_t const * const pwszSubKey = papwszArgs[1];
1379 wchar_t const * const pwszValueName = papwszArgs[2];
1380 wchar_t const * const pwszItem = papwszArgs[3];
1381 wchar_t const * const pwszPosition = cArgs > 4 ? papwszArgs[4] : L"0";
1382 wchar_t const * const pwszFlags = cArgs > 5 ? papwszArgs[5] : NULL;
1383
1384 HKEY hRootKey = ArgToRegistryRoot(pwszRoot);
1385 if (hRootKey == NULL)
1386 return EXIT_USAGE;
1387
1388 uint32_t uPosition = 0;
1389 if (!ArgToUInt32Full(pwszPosition, "position", &uPosition))
1390 return EXIT_USAGE;
1391
1392 uint32_t fFlags = 0;
1393 if (pwszFlags)
1394 {
1395 if (RTUtf16ICmpAscii(pwszFlags, "dup") == 0)
1396 fFlags = VBOX_REG_STRINGLIST_ALLOW_DUPLICATES;
1397 else if (RTUtf16ICmpAscii(pwszFlags, "no-dups") == 0)
1398 fFlags = 0;
1399 else
1400 return ErrorBadArg("flags", pwszFlags, "'dup' or 'no-dups'");
1401 }
1402
1403 /*
1404 * Do the work.
1405 */
1406 int rcExit = RegistryAddStringToList(hRootKey, pwszSubKey, pwszValueName, pwszItem, uPosition, fFlags);
1407 if (rcExit == EXIT_OK)
1408 PrintSWSWSWSWS("Successfully added '", pwszItem, "' to ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "'/'",
1409 pwszValueName, "'\r\n");
1410
1411 return rcExit;
1412}
1413
1414
1415/**
1416 * Removes an item from a comma separated registry string (REG_SZ).
1417 *
1418 * Only operates in HKLM for now, if needed it can be extended later for use
1419 * with other hives.
1420 *
1421 * @return Exit code (EXIT_OK, EXIT_FAIL)
1422 * @param hRootKey The root key.
1423 * @param pwszSubKey Subkey containing the list value.
1424 * @param pwszValueName The value name.
1425 * @param pwszItemToRemove The item to remove from the list. Empty values
1426 * are not supported.
1427 */
1428static int RegistryRemoveStringFromList(HKEY hRootKey, const wchar_t *pwszSubKey, const wchar_t *pwszValueName,
1429 const wchar_t *pwszItemToRemove)
1430{
1431 /*
1432 * Open the specified key.
1433 */
1434 HKEY hKey = NULL;
1435 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, pwszSubKey, 0 /*dwOptions*/, KEY_READ | KEY_WRITE, &hKey);
1436 if (lrc != ERROR_SUCCESS)
1437 return ErrorMsgLStatusSWSWSRS("RegistryRemoveStringFromList: RegOpenKeyExW ", RegistryRootToWStr(hRootKey),
1438 "/'", pwszSubKey, "' failed: ", lrc, NULL);
1439
1440 /*
1441 * Query the specified value.
1442 */
1443 int rcExit = EXIT_FAIL;
1444 wchar_t wszValue[1296] = { 0 };
1445 DWORD cbValue = sizeof(wszValue) - sizeof(wchar_t);
1446 DWORD dwType = 0;
1447 lrc = RegQueryValueEx(hKey, pwszValueName, NULL /*pReserved*/, &dwType, (LPBYTE)wszValue, &cbValue);
1448 if (lrc == ERROR_SUCCESS && dwType == REG_SZ)
1449 {
1450#ifdef DEBUG
1451 PrintSWS("RegistryRemoveStringFromList: Value string: '", wszValue, "'\r\n");
1452#endif
1453
1454 /*
1455 * Scan for item, shifting the query result as we scan.
1456 */
1457 size_t const cwcItemToRemove = RTUtf16Len(pwszItemToRemove);
1458 wchar_t volatile *pwszSrc = wszValue;
1459 wchar_t volatile *pwszDst = wszValue;
1460 for (;;)
1461 {
1462 /* Skip leading commas: */
1463 wchar_t wc = *pwszSrc;
1464 bool const fLeadingComma = wc == ',';
1465 if (fLeadingComma)
1466 do
1467 wc = *++pwszSrc;
1468 while (wc == ',');
1469 if (!wc)
1470 break; /* don't preserve trailing commas? Old code didn't (see strtok_r code). */
1471
1472 /* Start of a new 'value', so, find the end of it. */
1473 wchar_t volatile *pwszSrcEnd = pwszSrc + 1;
1474 do
1475 wc = *++pwszSrcEnd;
1476 while (wc != '\0' && wc != ',');
1477 size_t const cwcItem = (size_t)(pwszSrcEnd - pwszSrc);
1478
1479 /* If it matches pwszItemToRemove, do not copy it. */
1480 ASMCompilerBarrier(); /* Paranoia ^ 2 */
1481 if (IsStringListItemMatch(pwszSrc, cwcItem, pwszItemToRemove, cwcItemToRemove))
1482 {
1483 pwszSrc = pwszSrcEnd;
1484 if (!fLeadingComma)
1485 while (*pwszSrc == ',')
1486 pwszSrc++;
1487 }
1488 else
1489 {
1490 if (fLeadingComma)
1491 *pwszDst++ = ',';
1492 memmove((void *)pwszDst, (void const *)pwszSrc, cwcItem * sizeof(*pwszDst));
1493 pwszDst += cwcItem;
1494 pwszSrc = pwszSrcEnd;
1495 ASMCompilerBarrier(); /* paranoia ^ 3 */
1496 }
1497
1498 /* pwszSrc should not point at a comma or a zero terminator. */
1499 }
1500 *pwszDst = '\0';
1501#ifdef DEBUG
1502 PrintSWS("RegistryRemoveStringFromList: New value: '", wszValue, "'\r\n");
1503#endif
1504
1505 /*
1506 * Save the new value if we've made any changes.
1507 */
1508 if (pwszDst == pwszSrc)
1509 rcExit = EXIT_OK;
1510 else
1511 {
1512 cbValue = (DWORD)((pwszDst + 1 - &wszValue[0]) * sizeof(wchar_t));
1513 lrc = RegSetValueExW(hKey, pwszValueName, 0, REG_SZ, (LPBYTE)wszValue, cbValue);
1514 if (lrc == ERROR_SUCCESS)
1515 rcExit = EXIT_OK;
1516 else
1517 ErrorMsgLStatusSWSWSWSWSRS("RegistryRemoveStringFromList: RegSetValueExW ", RegistryRootToWStr(hRootKey), "/'",
1518 pwszSubKey, "'/'", pwszValueName, "' = '", wszValue, "' failed: ", lrc, NULL);
1519 }
1520 }
1521 else if (lrc == ERROR_FILE_NOT_FOUND)
1522 {
1523#ifdef DEBUG
1524 PrintStr("RegistryRemoveStringFromList: Value not present in registry\r\n");
1525#endif
1526 rcExit = EXIT_OK;
1527 }
1528 else if (lrc != ERROR_SUCCESS)
1529 ErrorMsgLStatusSWSWSWSRS("RegistryRemoveStringFromList: RegQueryValueEx ", RegistryRootToWStr(hRootKey), "/'",
1530 pwszSubKey, "'/'", pwszValueName, "' failed: ", lrc, NULL);
1531 else
1532 ErrorMsgLStatusSWSWSWSRS("RegistryRemoveStringFromList: Unexpected value type for ", RegistryRootToWStr(hRootKey), "/'",
1533 pwszSubKey, "'/'", pwszValueName, "': ", (LSTATUS)dwType, ", expected REG_SZ (1)");
1534 RegCloseKey(hKey);
1535 return rcExit;
1536}
1537
1538
1539/**
1540 * Handles 'netprovider remove'.
1541 */
1542static int handleNetProviderRemove(unsigned cArgs, wchar_t **papwszArgs)
1543{
1544 const wchar_t * const pwszProvider = papwszArgs[0];
1545 PrintSWS("Removing network provider '", pwszProvider, "' ...\r\n");
1546
1547 int rcExit = RegistryRemoveStringFromList(HKEY_LOCAL_MACHINE,
1548 L"System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
1549 L"ProviderOrder",
1550 pwszProvider);
1551 if (rcExit == EXIT_OK)
1552 PrintStr("Network provider successfully removed!\r\n");
1553
1554 RT_NOREF(cArgs);
1555 return rcExit;
1556}
1557
1558
1559/**
1560 * Handles 'registry dellistitem'.
1561 */
1562static int handleRegistryDelListItem(unsigned cArgs, wchar_t **papwszArgs)
1563{
1564 /*
1565 * Parameters.
1566 */
1567 RT_NOREF(cArgs);
1568 wchar_t const * const pwszRoot = papwszArgs[0];
1569 wchar_t const * const pwszSubKey = papwszArgs[1];
1570 wchar_t const * const pwszValueName = papwszArgs[2];
1571 wchar_t const * const pwszItem = papwszArgs[3];
1572
1573 HKEY hRootKey = ArgToRegistryRoot(pwszRoot);
1574 if (hRootKey == NULL)
1575 return EXIT_USAGE;
1576
1577 /*
1578 * Do the work.
1579 */
1580 int rcExit = RegistryRemoveStringFromList(hRootKey, pwszSubKey, pwszValueName, pwszItem);
1581 if (rcExit == EXIT_OK)
1582 PrintSWSWSWSWS("Successfully removed '", pwszItem, "' from ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "'/'",
1583 pwszValueName, "'\r\n");
1584
1585 return rcExit;
1586}
1587
1588
1589/**
1590 * Handles 'registry write'.
1591 */
1592static int handleRegistryWrite(unsigned cArgs, wchar_t **papwszArgs)
1593{
1594 /*
1595 * Mandatory parameters.
1596 */
1597 wchar_t const * const pwszRoot = papwszArgs[0];
1598 wchar_t const * const pwszSubKey = papwszArgs[1];
1599 wchar_t const * const pwszValueName = papwszArgs[2];
1600 wchar_t const * const pwszType = papwszArgs[3];
1601 wchar_t const * const pwszValue = papwszArgs[4];
1602
1603 /*
1604 * Root key:
1605 */
1606 HKEY hRootKey = ArgToRegistryRoot(pwszRoot);
1607 if (hRootKey == NULL)
1608 return EXIT_USAGE;
1609
1610 /*
1611 * Type and value with default length.
1612 */
1613 union
1614 {
1615 uint32_t dw;
1616 uint64_t qw;
1617 } uValue;
1618 DWORD dwType;
1619 DWORD cbValue;
1620 BYTE const *pbValue;
1621 if ( RTUtf16ICmpAscii(pwszType, "REG_BINARY") == 0
1622 || RTUtf16ICmpAscii(pwszType, "REG_BIN") == 0
1623 || RTUtf16ICmpAscii(pwszType, "BINARY") == 0)
1624 {
1625 dwType = REG_BINARY;
1626 cbValue = (DWORD)(RTUtf16Len(pwszValue) + 1) * sizeof(wchar_t);
1627 pbValue = (BYTE const *)pwszValue;
1628 }
1629 else if ( RTUtf16ICmpAscii(pwszType, "REG_DWORD") == 0
1630 || RTUtf16ICmpAscii(pwszType, "DWORD") == 0)
1631 {
1632 if (!ArgToUInt32Full(pwszValue, "dword value", &uValue.dw))
1633 return EXIT_USAGE;
1634 dwType = REG_DWORD;
1635 pbValue = (BYTE const *)&uValue.dw;
1636 cbValue = sizeof(uValue.dw);
1637 }
1638 else if ( RTUtf16ICmpAscii(pwszType, "REG_QWORD") == 0
1639 || RTUtf16ICmpAscii(pwszType, "QWORD") == 0)
1640 {
1641 if (!ArgToUInt64Full(pwszValue, "qword value", &uValue.qw))
1642 return EXIT_USAGE;
1643 dwType = REG_QWORD;
1644 pbValue = (BYTE const *)&uValue.qw;
1645 cbValue = sizeof(uValue.qw);
1646 }
1647 else if ( RTUtf16ICmpAscii(pwszType, "REG_SZ") == 0
1648 || RTUtf16ICmpAscii(pwszType, "SZ") == 0)
1649 {
1650 dwType = REG_SZ;
1651 cbValue = (DWORD)((RTUtf16Len(pwszValue) + 1) * sizeof(wchar_t));
1652 pbValue = (BYTE const *)pwszValue;
1653 }
1654 else
1655 return ErrorBadArg("type", pwszType, "");
1656
1657 /*
1658 * Binary only: Reinterpret the input as - optional.
1659 */
1660 if (cArgs > 5)
1661 {
1662 if (dwType != REG_BINARY)
1663 return ErrorMsg("The 'binary-conversion' argument is currently only supported for REG_BINARY type values!");
1664 if (RTUtf16ICmpAscii(papwszArgs[5], "dword") == 0)
1665 {
1666 if (!ArgToUInt32Full(pwszValue, "dword(/binary) value", &uValue.dw))
1667 return EXIT_USAGE;
1668 pbValue = (BYTE const *)&uValue.dw;
1669 cbValue = sizeof(uValue.dw);
1670 }
1671 else if (RTUtf16ICmpAscii(papwszArgs[5], "qword") == 0)
1672 {
1673 if (!ArgToUInt64Full(pwszValue, "qword(/binary) value", &uValue.qw))
1674 return EXIT_USAGE;
1675 pbValue = (BYTE const *)&uValue.qw;
1676 cbValue = sizeof(uValue.qw);
1677 }
1678 else
1679 return ErrorBadArg("binary-conversion", papwszArgs[0], "dword");
1680 }
1681
1682 /*
1683 * Binary only: Max length to write - optional.
1684 */
1685 if (cArgs> 6)
1686 {
1687 if (dwType != REG_BINARY)
1688 return ErrorMsg("The 'max-size' argument is currently only supported for REG_BINARY type values!");
1689 uint32_t cbMaxValue;
1690 if (!ArgToUInt32Full(papwszArgs[6], "max-size", &cbMaxValue))
1691 return EXIT_USAGE;
1692 if (cbValue > cbMaxValue)
1693 cbValue = cbMaxValue;
1694 }
1695
1696 /*
1697 * Do the writing.
1698 */
1699 HKEY hKey = NULL;
1700 LSTATUS lrc = RegCreateKeyExW(hRootKey, pwszSubKey, 0 /*Reserved*/, NULL /*pwszClass*/, 0 /*dwOptions*/,
1701 KEY_WRITE, NULL /*pSecAttr*/, &hKey, NULL /*pdwDisposition*/);
1702 if (lrc != ERROR_SUCCESS)
1703 return ErrorMsgLStatusSWSWSRS("RegCreateKeyExW ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "' failed: ", lrc, NULL);
1704
1705 lrc = RegSetValueExW(hKey, pwszValueName, 0, dwType, pbValue, cbValue);
1706 RegCloseKey(hKey);
1707 if (lrc != ERROR_SUCCESS)
1708 return ErrorMsgLStatusSWSWSWSRS("RegSetValueExW ", RegistryRootToWStr(hRootKey), "/'", pwszSubKey, "'/'",
1709 pwszValueName, "' failed: ", lrc, NULL);
1710 return EXIT_OK;
1711}
1712
1713
1714/**
1715 * Handles 'registry delete'.
1716 */
1717static int handleRegistryDelete(unsigned cArgs, wchar_t **papwszArgs)
1718{
1719 /*
1720 * Parameters.
1721 */
1722 RT_NOREF(cArgs);
1723 wchar_t const * const pwszRoot = papwszArgs[0];
1724 wchar_t const * const pwszSubKey = papwszArgs[1];
1725 wchar_t const * const pwszValueName = papwszArgs[2];
1726
1727 HKEY const hRootKey = ArgToRegistryRoot(pwszRoot);
1728 if (hRootKey == NULL)
1729 return EXIT_USAGE;
1730
1731 /*
1732 * Do the deleting.
1733 */
1734 HKEY hKey = NULL;
1735 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, papwszArgs[1] /*pwszSubKey*/, 0 /*dwOptions*/, KEY_READ | KEY_WRITE, &hKey);
1736 if (lrc != ERROR_FILE_NOT_FOUND)
1737 {
1738 if (lrc != ERROR_SUCCESS)
1739 return ErrorMsgLStatusSWSWSRS("RegOpenKeyExW ", pwszRoot, "/'", pwszSubKey, "' failed: ", lrc, NULL);
1740
1741 lrc = RegDeleteValueW(hKey, pwszValueName);
1742 RegCloseKey(hKey);
1743 if (lrc != ERROR_SUCCESS && lrc != ERROR_FILE_NOT_FOUND)
1744 return ErrorMsgLStatusSWSWSWSRS("RegDeleteValueW ", pwszRoot, "/'", pwszSubKey, "'/'",
1745 pwszValueName, "' failed: ", lrc, NULL);
1746 }
1747 return EXIT_OK;
1748}
1749
1750
1751/** Handles 'version' and its aliases. */
1752static int handleVersion(unsigned cArgs, wchar_t **papwszArgs)
1753{
1754 PrintStr(RT_XSTR(VBOX_VERSION_MAJOR) "." RT_XSTR(VBOX_VERSION_MINOR) "." RT_XSTR(VBOX_VERSION_BUILD) "r" RT_XSTR(VBOX_SVN_REV) "\r\n");
1755 RT_NOREF(cArgs, papwszArgs);
1756 return EXIT_OK;
1757}
1758
1759
1760/** Handles 'help' and all its aliases. */
1761static int handleHelp(unsigned cArgs, wchar_t **papwszArgs)
1762{
1763 /* "0 1 2 3 4 5 6 7 8 */
1764 /* "012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
1765 PrintStr("VirtualBox Guest Additions Installation Helper for Windows\r\n"
1766 "Version: " RT_XSTR(VBOX_VERSION_MAJOR) "." RT_XSTR(VBOX_VERSION_MINOR) "." RT_XSTR(VBOX_VERSION_BUILD) "r" RT_XSTR(VBOX_SVN_REV) "\r\n"
1767 "\r\n"
1768 "Syntax: VBoxDrvInst <command> <subcommand>\r\n"
1769 "\r\n"
1770 "Drivers:\r\n"
1771 " VBoxDrvInst driver install <inf-file> [log-file]\r\n"
1772 " VBoxDrvInst driver uninstall <inf-file> [log-file]\r\n"
1773 " VBoxDrvInst driver executeinf <inf-file>\r\n"
1774 "\r\n"
1775 "Service:\r\n"
1776 " VBoxDrvInst service create <name> <display-name> <service-type>\r\n"
1777 " <start-type> <binary-path> [load-order] [deps] [user] [password]\r\n"
1778 " VBoxDrvInst service delete <name>\r\n"
1779 "\r\n"
1780 "Network Provider:\r\n"
1781 " VBoxDrvInst netprovider add <name> <position>\r\n"
1782 " VBoxDrvInst netprovider remove <name>\r\n"
1783 "\r\n"
1784 "Registry:\r\n"
1785 " VBoxDrvInst registry write <root> <sub-key> <value-name> <type> <value>\r\n"
1786 " [binary-conversion] [max-size]\r\n"
1787 " VBoxDrvInst registry delete <root> <sub-key> <value-name>\r\n"
1788 /** @todo Add roots for these two. */
1789 " VBoxDrvInst registry addmultisz <sub-key> <value-name> <to-add> <position>\r\n"
1790 " VBoxDrvInst registry delmultisz <sub-key> <value-name> <to-remove>\r\n"
1791 " VBoxDrvInst registry addlistitem <root> <sub-key> <value-name> <to-add>\r\n"
1792 " [position [dup|no-dup]]\r\n"
1793 " VBoxDrvInst registry dellistitem <root> <sub-key> <value-name> <to-remove>\r\n"
1794 "\r\n"
1795 "Standard options:\r\n"
1796 " VBoxDrvInst [help|--help|/help|-h|/h|-?|/h] [...]\r\n"
1797 " VBoxDrvInst [version|--version|-V]\r\n"
1798 );
1799 RT_NOREF(cArgs, papwszArgs);
1800 return EXIT_OK;
1801}
1802
1803
1804int wmain(int argc, wchar_t **argv)
1805{
1806 /* Not initializing IPRT here, ASSUMING the little bit we use of it does
1807 not need any initialization. Reduces the binary size a little. */
1808
1809 static struct
1810 {
1811 const char *pszCmd;
1812 const char *pszSubCmd;
1813 unsigned cMin, cMax;
1814 int (*pfnHandler)(unsigned cArgs, wchar_t **papwszArgs);
1815 } s_aActions[] =
1816 {
1817 { "driver", "install", 1, 2, handleDriverInstall },
1818 { "driver", "uninstall", 1, 2, handleDriverUninstall },
1819 { "driver", "executeinf", 1, 1, handleDriverExecuteInf },
1820 { "service", "create", 5, 9, handleServiceCreate },
1821 { "service", "delete", 1, 1, handleServiceDelete },
1822 { "netprovider", "add", 1, 2, handleNetProviderAdd },
1823 { "netprovider", "remove", 1, 2, handleNetProviderRemove },
1824 { "registry", "addlistitem", 4, 6, handleRegistryAddListItem },
1825 { "registry", "dellistitem", 4, 4, handleRegistryDelListItem },
1826 { "registry", "addmultisz", 4, 4, handleRegistryAddMultiSz },
1827 { "registry", "delmultisz", 3, 3, handleRegistryDelMultiSz },
1828 { "registry", "write", 5, 7, handleRegistryWrite },
1829 { "registry", "delete", 3, 3, handleRegistryDelete },
1830
1831 { "help", NULL, 0, ~0U, handleHelp },
1832 { "--help", NULL, 0, ~0U, handleHelp },
1833 { "/help", NULL, 0, ~0U, handleHelp },
1834 { "-h", NULL, 0, ~0U, handleHelp },
1835 { "/h", NULL, 0, ~0U, handleHelp },
1836 { "-?", NULL, 0, ~0U, handleHelp },
1837 { "/?", NULL, 0, ~0U, handleHelp },
1838 { "version", NULL, 0, ~0U, handleVersion },
1839 { "--version", NULL, 0, ~0U, handleVersion },
1840 { "-V", NULL, 0, ~0U, handleVersion },
1841 };
1842
1843 /*
1844 * Lookup the action handler.
1845 */
1846 int rcExit = EXIT_USAGE;
1847 if (argc >= 2)
1848 {
1849 const wchar_t * const pwszCmd = argv[1];
1850 const wchar_t * const pwszSubCmd = argc > 2 ? argv[2] : NULL;
1851 unsigned i = 0;
1852 for (i = 0; i < RT_ELEMENTS(s_aActions); i++)
1853 if ( RTUtf16ICmpAscii(pwszCmd, s_aActions[i].pszCmd) == 0
1854 && ( !s_aActions[i].pszSubCmd
1855 || RTUtf16ICmpAscii(pwszSubCmd, s_aActions[i].pszSubCmd) == 0))
1856 {
1857 unsigned const cArgs = (unsigned)argc - (s_aActions[i].pszSubCmd ? 3 : 2);
1858 wchar_t ** const papwszArgs = &argv[s_aActions[i].pszSubCmd ? 3 : 2];
1859 if (cArgs >= s_aActions[i].cMin && cArgs <= s_aActions[i].cMax)
1860 rcExit = s_aActions[i].pfnHandler(cArgs, papwszArgs);
1861 else
1862 {
1863 bool const fTooFew = cArgs < s_aActions[i].cMin;
1864 ErrorMsgBegin(fTooFew ? "Too few parameters for '" : "Too many parameters for '");
1865 ErrorMsgStr(s_aActions[i].pszCmd);
1866 if (s_aActions[i].pszSubCmd)
1867 {
1868 ErrorMsgStr(" ");
1869 ErrorMsgStr(s_aActions[i].pszSubCmd);
1870 }
1871 ErrorMsgStr("'! Got ");
1872 ErrorMsgU64(cArgs);
1873 ErrorMsgStr(fTooFew ? ", expected at least " : ", expected at most ");;
1874 ErrorMsgU64(fTooFew ? s_aActions[i].cMin : s_aActions[i].cMax);
1875 ErrorMsgEnd(".");
1876 }
1877 break;
1878 }
1879 if (i >= RT_ELEMENTS(s_aActions))
1880 {
1881 ErrorMsgBegin("Unknown action '");
1882 ErrorMsgWStr(pwszCmd);
1883 if (pwszSubCmd)
1884 {
1885 ErrorMsgBegin(" ");
1886 ErrorMsgWStr(pwszSubCmd);
1887 }
1888 ErrorMsgEnd("'! Please consult \"--help\" for more information.\r\n");
1889 }
1890 }
1891 else
1892 ErrorMsg("No parameters given. Please consult \"--help\" for more information.\r\n");
1893 return rcExit;
1894}
1895
1896
1897#ifdef IPRT_NO_CRT
1898int main(int argc, char **argv)
1899{
1900 /*
1901 * Convert the arguments to UTF16 and call wmain. We don't bother freeing
1902 * any of these strings as the process is exiting and it's a waste of time.
1903 */
1904 wchar_t **papwszArgs = (wchar_t **)alloca((argc + 1) * sizeof(wchar_t *));
1905 int i = 0;
1906 while (i < argc)
1907 {
1908 papwszArgs[i] = NULL;
1909 int rc = RTStrToUtf16(argv[i], &papwszArgs[i]);
1910 if (RT_SUCCESS(rc))
1911 i++;
1912 else
1913 return ErrorMsg("Failed to convert command line arguments to UTF16!!");
1914 }
1915 papwszArgs[i] = NULL;
1916 return wmain(argc, papwszArgs);
1917}
1918#endif
1919
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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