VirtualBox

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

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

Add/Nt/InstallHelper: Don't set the windows version of arm64 and amd64 to nt4. [scm] bugref:10762 VBP-1253

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