VirtualBox

source: vbox/trunk/src/VBox/Additions/WINNT/Installer/InstallHelper/VBoxGuestInstallHelper.cpp@ 106901

Last change on this file since 106901 was 106896, checked in by vboxsync, 3 months ago

Windows Guest Additions: Renamed VBoxDrvInst.exe -> VBoxGuestInstallHelper.exe. Marked the 'driver [...]' commands as being deprecated. bugref:10762

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