VirtualBox

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

Last change on this file since 106321 was 106321, checked in by vboxsync, 6 weeks ago

Windows installers: Big revamp for removing all DIFxApp-related / DIFxApi-related code and build dependencies for the host and guest installers. bugref:10762

This implements an own framework (VBoxWinDrvInst and VBoxWinDrvStore) for installing Windows drivers and querying / handling the Windows driver store,
along with testcases for the Windows guest and host installers.

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