VirtualBox

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

最後變更 在這個檔案從106685是 106529,由 vboxsync 提交於 5 月 前

Add/Nt/Installer: Unused variable. jiraref:VBP-1171

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

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