VirtualBox

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

Last change on this file since 106501 was 106347, checked in by vboxsync, 4 months ago

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

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