VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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

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