VirtualBox

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

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

Add/Nt/Installer: Added a crude hack to suppress the WQHL dialog on windows 2000 and XP. bugref:10261 bugref:8691

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

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