VirtualBox

source: vbox/trunk/src/VBox/GuestHost/installation/VBoxWinDrvInst.cpp@ 107457

Last change on this file since 107457 was 107061, checked in by vboxsync, 6 months ago

Windows driver installation: Added VBOX_WIN_DRIVERINSTALL_F_NO_DESTROY and internal VBoxWinDrvInstTestXXX functions, required for testcase(s). bugref:10762

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.8 KB
Line 
1/* $Id: VBoxWinDrvInst.cpp 107061 2024-11-20 16:09:39Z vboxsync $ */
2/** @file
3 * VBoxWinDrvInst - Windows driver installation handling.
4 */
5
6/*
7 * Copyright (C) 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#include <iprt/win/windows.h>
33#include <iprt/win/setupapi.h>
34#include <newdev.h> /* For INSTALLFLAG_XXX. */
35#include <cfgmgr32.h> /* For MAX_DEVICE_ID_LEN. */
36
37#include <iprt/assert.h>
38#include <iprt/buildconfig.h>
39#include <iprt/cdefs.h>
40#include <iprt/dir.h>
41#include <iprt/ldr.h>
42#include <iprt/list.h>
43#include <iprt/mem.h>
44#include <iprt/once.h>
45#include <iprt/path.h>
46#include <iprt/string.h>
47#include <iprt/system.h>
48#include <iprt/utf16.h>
49
50#include <package-generated.h>
51#include "product-generated.h"
52
53#include <VBox/err.h> /* For VERR_PLATFORM_ARCH_NOT_SUPPORTED.*/
54#include <VBox/version.h>
55
56#include <VBox/GuestHost/VBoxWinDrvDefs.h>
57#include <VBox/GuestHost/VBoxWinDrvInst.h>
58#include <VBox/GuestHost/VBoxWinDrvStore.h>
59
60#include "VBoxWinDrvCommon.h"
61#include "VBoxWinDrvInstInternal.h"
62
63
64/*********************************************************************************************************************************
65* Defines *
66*********************************************************************************************************************************/
67
68/* Defines from newdev.h (WINVER >= _WIN32_WINNT_VISTA). */
69#define DIIRFLAG_INF_ALREADY_COPIED 0x00000001
70#define DIIRFLAG_FORCE_INF 0x00000002
71#define DIIRFLAG_HW_USING_THE_INF 0x00000004
72#define DIIRFLAG_HOTPATCH 0x00000008
73#define DIIRFLAG_NOBACKUP 0x00000010
74
75
76/* SetupUninstallOEMInf Flags values. */
77#define SUOI_FORCEDELETE 0x00000001
78
79
80/*********************************************************************************************************************************
81* Defined Constants And Macros *
82*********************************************************************************************************************************/
83/** The magic value for RTFTPSERVERINTERNAL::u32Magic. */
84#define VBOXWINDRVINST_MAGIC UINT32_C(0x20171201)
85
86/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
87#define VBOXWINDRVINST_VALID_RETURN_RC(hDrvInst, a_rc) \
88 do { \
89 AssertPtrReturn((hDrvInst), (a_rc)); \
90 AssertReturn((hDrvInst)->u32Magic == VBOXWINDRVINST_MAGIC, (a_rc)); \
91 } while (0)
92
93/** Validates a handle and returns VERR_INVALID_HANDLE if not valid. */
94#define VBOXWINDRVINST_VALID_RETURN(hDrvInst) VBOXWINDRVINST_VALID_RETURN_RC((hDrvInst), VERR_INVALID_HANDLE)
95
96/** Validates a handle and returns (void) if not valid. */
97#define VBOXWINDRVINST_VALID_RETURN_VOID(hFTPServer) \
98 do { \
99 AssertPtrReturnVoid(hFTPServer); \
100 AssertReturnVoid((hFTPServer)->u32Magic == VBOXWINDRVINST_MAGIC); \
101 } while (0)
102
103
104/*********************************************************************************************************************************
105* Structures and Typedefs *
106*********************************************************************************************************************************/
107/* newdev.dll: */
108typedef BOOL(WINAPI* PFNDIINSTALLDRIVERW) (HWND hwndParent, LPCWSTR InfPath, DWORD Flags, PBOOL NeedReboot);
109typedef BOOL(WINAPI* PFNDIUNINSTALLDRIVERW) (HWND hwndParent, LPCWSTR InfPath, DWORD Flags, PBOOL NeedReboot);
110typedef BOOL(WINAPI* PFNUPDATEDRIVERFORPLUGANDPLAYDEVICESW) (HWND hwndParent, LPCWSTR HardwareId, LPCWSTR FullInfPath, DWORD InstallFlags, PBOOL bRebootRequired);
111/* setupapi.dll: */
112typedef VOID(WINAPI* PFNINSTALLHINFSECTIONW) (HWND Window, HINSTANCE ModuleHandle, PCWSTR CommandLine, INT ShowCommand);
113typedef BOOL(WINAPI* PFNSETUPCOPYOEMINFW) (PCWSTR SourceInfFileName, PCWSTR OEMSourceMediaLocation, DWORD OEMSourceMediaType, DWORD CopyStyle, PWSTR DestinationInfFileName, DWORD DestinationInfFileNameSize, PDWORD RequiredSize, PWSTR DestinationInfFileNameComponent);
114typedef HINF(WINAPI* PFNSETUPOPENINFFILEW) (PCWSTR FileName, PCWSTR InfClass, DWORD InfStyle, PUINT ErrorLine);
115typedef VOID(WINAPI* PFNSETUPCLOSEINFFILE) (HINF InfHandle);
116typedef BOOL(WINAPI* PFNSETUPDIGETINFCLASSW) (PCWSTR, LPGUID, PWSTR, DWORD, PDWORD);
117typedef BOOL(WINAPI* PFNSETUPUNINSTALLOEMINFW) (PCWSTR InfFileName, DWORD Flags, PVOID Reserved);
118typedef BOOL(WINAPI *PFNSETUPSETNONINTERACTIVEMODE) (BOOL NonInteractiveFlag);
119
120/** Function pointer for a general try INF section callback. */
121typedef int (*PFNVBOXWINDRVINST_TRYINFSECTION_CALLBACK)(HINF hInf, PCRTUTF16 pwszSection, void *pvCtx);
122
123
124/*********************************************************************************************************************************
125* Global Variables *
126*********************************************************************************************************************************/
127/** Init once structure for run-as-user functions we need. */
128DECL_HIDDEN_DATA(RTONCE) g_vboxWinDrvInstResolveOnce = RTONCE_INITIALIZER;
129
130/* newdev.dll: */
131DECL_HIDDEN_DATA(PFNDIINSTALLDRIVERW) g_pfnDiInstallDriverW = NULL; /* For Vista+ .*/
132DECL_HIDDEN_DATA(PFNDIUNINSTALLDRIVERW) g_pfnDiUninstallDriverW = NULL; /* Since Win 10 version 1703. */
133DECL_HIDDEN_DATA(PFNUPDATEDRIVERFORPLUGANDPLAYDEVICESW) g_pfnUpdateDriverForPlugAndPlayDevicesW = NULL; /* For < Vista .*/
134/* setupapi.dll: */
135DECL_HIDDEN_DATA(PFNINSTALLHINFSECTIONW) g_pfnInstallHinfSectionW = NULL; /* For W2K+. */
136DECL_HIDDEN_DATA(PFNSETUPCOPYOEMINFW) g_pfnSetupCopyOEMInfW = NULL; /* For W2K+. */
137DECL_HIDDEN_DATA(PFNSETUPOPENINFFILEW) g_pfnSetupOpenInfFileW = NULL; /* For W2K+. */
138DECL_HIDDEN_DATA(PFNSETUPCLOSEINFFILE) g_pfnSetupCloseInfFile = NULL; /* For W2K+. */
139DECL_HIDDEN_DATA(PFNSETUPDIGETINFCLASSW) g_pfnSetupDiGetINFClassW = NULL; /* For W2K+. */
140DECL_HIDDEN_DATA(PFNSETUPUNINSTALLOEMINFW) g_pfnSetupUninstallOEMInfW = NULL; /* For XP+. */
141DECL_HIDDEN_DATA(PFNSETUPSETNONINTERACTIVEMODE) g_pfnSetupSetNonInteractiveMode = NULL; /* For W2K+. */
142
143/**
144 * Structure for keeping the internal Windows driver context.
145 */
146typedef struct VBOXWINDRVINSTINTERNAL
147{
148 /** Magic value. */
149 uint32_t u32Magic;
150 /** Callback function for logging output. Optional and can be NULL. */
151 PFNVBOXWINDRIVERLOGMSG pfnLog;
152 /** User-supplied pointer for \a pfnLog. Optional and can be NULL. */
153 void *pvUser;
154 /** Currently set verbosity level. */
155 unsigned uVerbosity;
156 /** Number of (logged) warnings. */
157 unsigned cWarnings;
158 /** Number of (logged) errors. */
159 unsigned cErrors;
160 /** Whether a reboot is needed in order to perform the current (un)installation. */
161 bool fReboot;
162 /** Parameters for (un)installation. */
163 VBOXWINDRVINSTPARMS Parms;
164 /** Driver store entry to use. */
165 PVBOXWINDRVSTORE pStore;
166} VBOXWINDRVINSTINTERNAL;
167/** Pointer to an internal Windows driver installation context. */
168typedef VBOXWINDRVINSTINTERNAL *PVBOXWINDRVINSTINTERNAL;
169
170/**
171 * Structure for holding a single DLL import symbol.
172 *
173 * Ordinal currently ignored.
174 */
175typedef struct VBOXWINDRVINSTIMPORTSYMBOL
176{
177 /** Symbol name. */
178 const char *pszSymbol;
179 /** Function pointer. */
180 void **pfnFunc;
181} VBOXWINDRVINSTIMPORTSYMBOL;
182
183
184/*********************************************************************************************************************************
185* Prototypes *
186*********************************************************************************************************************************/
187static int vboxWinDrvParmsDetermine(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms, bool fForce);
188
189
190/*********************************************************************************************************************************
191* Import tables *
192*********************************************************************************************************************************/
193
194/* setupapi.dll: */
195static VBOXWINDRVINSTIMPORTSYMBOL s_aSetupApiImports[] =
196{
197 { "InstallHinfSectionW", (void **)&g_pfnInstallHinfSectionW },
198 { "SetupCopyOEMInfW", (void **)&g_pfnSetupCopyOEMInfW },
199 { "SetupUninstallOEMInfW", (void **)&g_pfnSetupUninstallOEMInfW },
200 { "SetupOpenInfFileW", (void **)&g_pfnSetupOpenInfFileW },
201 { "SetupCloseInfFile", (void **)&g_pfnSetupCloseInfFile },
202 { "SetupDiGetINFClassW", (void **)&g_pfnSetupDiGetINFClassW },
203 { "SetupSetNonInteractiveMode", (void **)&g_pfnSetupSetNonInteractiveMode }
204};
205
206/* newdev.dll: */
207static VBOXWINDRVINSTIMPORTSYMBOL s_aNewDevImports[] =
208{
209 /* Only for Vista / 2008 Server and up. */
210 { "DiInstallDriverW", (void **)&g_pfnDiInstallDriverW },
211 { "DiUninstallDriverW", (void **)&g_pfnDiUninstallDriverW },
212 /* Anything older (must support Windows 2000). */
213 { "UpdateDriverForPlugAndPlayDevicesW", (void **)&g_pfnUpdateDriverForPlugAndPlayDevicesW }
214};
215
216
217/*********************************************************************************************************************************
218* Implementation *
219*********************************************************************************************************************************/
220
221/**
222 * Logs message, va_list version.
223 *
224 * @returns VBox status code.
225 * @param pCtx Windows driver installer context.
226 * @param enmType Log type to use.
227 * @param pszFormat Format string to log.
228 * @param args va_list to use.
229 */
230DECLINLINE(void) vboxWinDrvInstLogExV(PVBOXWINDRVINSTINTERNAL pCtx,
231 VBOXWINDRIVERLOGTYPE enmType, const char *pszFormat, va_list args)
232{
233 if (!pCtx->pfnLog)
234 return;
235
236 char *psz = NULL;
237 RTStrAPrintfV(&psz, pszFormat, args);
238 AssertPtrReturnVoid(psz);
239
240 pCtx->pfnLog(enmType, psz, pCtx->pvUser);
241 RTStrFree(psz);
242}
243
244/**
245 * Logs message, extended version.
246 *
247 * @returns VBox status code.
248 * @param pCtx Windows driver installer context.
249 * @param enmType Log type to use.
250 * @param pszFormat Format string to log.
251 */
252DECLINLINE(void) vboxWinDrvInstLogEx(PVBOXWINDRVINSTINTERNAL pCtx,
253 VBOXWINDRIVERLOGTYPE enmType, const char *pszFormat, ...)
254{
255 va_list args;
256 va_start(args, pszFormat);
257 vboxWinDrvInstLogExV(pCtx, enmType, pszFormat, args);
258 va_end(args);
259}
260
261/**
262 * Logs an error message.
263 *
264 * @returns VBox status code.
265 * @param pCtx Windows driver installer context.
266 * @param pszFormat Format string to log.
267 */
268DECLINLINE(void) vboxWinDrvInstLogError(PVBOXWINDRVINSTINTERNAL pCtx, const char *pszFormat, ...)
269{
270 va_list args;
271 va_start(args, pszFormat);
272 vboxWinDrvInstLogExV(pCtx, VBOXWINDRIVERLOGTYPE_ERROR, pszFormat, args);
273 va_end(args);
274
275 pCtx->cErrors++;
276}
277
278/**
279 * Logs a warning message.
280 *
281 * @returns VBox status code.
282 * @param pCtx Windows driver installer context.
283 * @param pszFormat Format string to log.
284 */
285DECLINLINE(void) vboxWinDrvInstLogWarn(PVBOXWINDRVINSTINTERNAL pCtx, const char *pszFormat, ...)
286{
287 va_list args;
288 va_start(args, pszFormat);
289 vboxWinDrvInstLogExV(pCtx, VBOXWINDRIVERLOGTYPE_WARN, pszFormat, args);
290 va_end(args);
291
292 pCtx->cWarnings++;
293}
294
295/**
296 * Logs an information message.
297 *
298 * @returns VBox status code.
299 * @param pCtx Windows driver installer context.
300 * @param pszFormat Format string to log.
301 */
302DECLINLINE(void) vboxWinDrvInstLogInfo(PVBOXWINDRVINSTINTERNAL pCtx, const char *pszFormat, ...)
303{
304 va_list args;
305 va_start(args, pszFormat);
306 vboxWinDrvInstLogExV(pCtx, VBOXWINDRIVERLOGTYPE_INFO, pszFormat, args);
307 va_end(args);
308}
309
310/**
311 * Logs a verbose message.
312 *
313 * @returns VBox status code.
314 * @param pCtx Windows driver installer context.
315 * @param uVerbosity Verbosity level to use for logging.
316 * @param pszFormat Format string to log.
317 */
318DECLINLINE(void) vboxWinDrvInstLogVerbose(PVBOXWINDRVINSTINTERNAL pCtx, unsigned uVerbosity, const char *pszFormat, ...)
319{
320 if (uVerbosity <= pCtx->uVerbosity)
321 {
322 va_list args;
323 va_start(args, pszFormat);
324 vboxWinDrvInstLogExV(pCtx, VBOXWINDRIVERLOGTYPE_VERBOSE, pszFormat, args);
325 va_end(args);
326 }
327}
328
329/**
330 * Logs (and indicates) that a reboot is needed.
331 *
332 * @returns VBox status code.
333 * @param pCtx Windows driver installer context.
334 * @param pszFormat Format string to log.
335 */
336DECLINLINE(void) vboxWinDrvInstLogRebootNeeded(PVBOXWINDRVINSTINTERNAL pCtx, const char *pszFormat, ...)
337{
338 va_list args;
339 va_start(args, pszFormat);
340 vboxWinDrvInstLogExV(pCtx, VBOXWINDRIVERLOGTYPE_REBOOT_NEEDED, pszFormat, args);
341 va_end(args);
342}
343
344/**
345 * Logs the last Windows error given via GetLastError().
346 *
347 * @returns Last Windows error translated into VBox status code.
348 * @retval VERR_INSTALLATION_FAILED if a translation to a VBox status code wasn't possible.
349 * @param pCtx Windows driver installer context.
350 * @param pszFormat Format string to log.
351 */
352DECLINLINE(int) vboxWinDrvInstLogLastError(PVBOXWINDRVINSTINTERNAL pCtx, const char *pszFormat, ...)
353{
354 DWORD const dwErr = GetLastError();
355
356 va_list args;
357 va_start(args, pszFormat);
358
359 char *psz = NULL;
360 RTStrAPrintfV(&psz, pszFormat, args);
361 AssertPtrReturn(psz, VERR_NO_MEMORY);
362
363 int rc = VERR_INSTALLATION_FAILED;
364
365#ifdef DEBUG_andy
366 bool const fAssertMayPanic = RTAssertMayPanic();
367 RTAssertSetMayPanic(false);
368#endif
369
370 /* Try resolving Setup API errors first (we don't handle those in IPRT). */
371 const char *pszErr = VBoxWinDrvSetupApiErrToStr(dwErr);
372 if (!pszErr) /* Also ask for special winerr.h codes we don't handle in IPRT. */
373 pszErr = VBoxWinDrvWinErrToStr(dwErr);
374 if (!pszErr)
375 rc = RTErrConvertFromWin32(dwErr);
376
377#ifdef DEBUG_andy
378 RTAssertSetMayPanic(fAssertMayPanic);
379#endif
380
381 if (pszErr)
382 vboxWinDrvInstLogError(pCtx, "%s: %s (%ld / %#x)", psz, pszErr, dwErr, dwErr);
383 else
384 vboxWinDrvInstLogError(pCtx, "%s: %Rrc (%ld / %#x)", psz, rc, dwErr, dwErr);
385
386 RTStrFree(psz);
387
388 va_end(args);
389
390 return rc;
391}
392
393/**
394 * Resolves a single symbol of a module (DLL).
395 *
396 * @returns VBox status code.
397 * @param pCtx Windows driver installer context.
398 * @param hMod Module handle to use.
399 * @param pszSymbol Name of symbol to resolve.
400 * @param pfnFunc Where to return the function pointer for resolved symbol on success.
401 */
402DECLINLINE(int) vboxWinDrvInstInstModResolveSym(PVBOXWINDRVINSTINTERNAL pCtx, RTLDRMOD hMod, const char *pszSymbol,
403 void **pfnFunc)
404{
405 int rc = RTLdrGetSymbol(hMod, pszSymbol, pfnFunc);
406 if (RT_FAILURE(rc))
407 {
408 vboxWinDrvInstLogVerbose(pCtx, 1, "Warning: Symbol \"%s\" not found (%Rrc)", pszSymbol, rc);
409 *pfnFunc = NULL;
410 }
411
412 return rc;
413}
414
415/**
416 * Resolves symbols of a specific module (DLL).
417 *
418 * @returns VBox status code.
419 * @param pCtx Windows driver installer context.
420 * @param pszFilename Path of module to resolve symbols for.
421 * @param pSymbols Table of symbols to resolve.
422 * @param cSymbols Number of symbols within \a pSymbols to resolve.
423 */
424static DECLCALLBACK(int) vboxWinDrvInstResolveMod(PVBOXWINDRVINSTINTERNAL pCtx,
425 const char *pszFilename, VBOXWINDRVINSTIMPORTSYMBOL *pSymbols, size_t cSymbols)
426{
427 vboxWinDrvInstLogVerbose(pCtx, 1, "Resolving symbols for module \"%s\" ...", pszFilename);
428
429 RTLDRMOD hMod;
430 int rc = RTLdrLoadSystem(pszFilename, true /*fNoUnload*/, &hMod);
431 if (RT_SUCCESS(rc))
432 {
433 for (size_t i = 0; i < cSymbols; i++)
434 {
435 void *pfnFunc;
436 rc = vboxWinDrvInstInstModResolveSym(pCtx, hMod, pSymbols[i].pszSymbol, &pfnFunc);
437 if (RT_SUCCESS(rc))
438 *pSymbols[i].pfnFunc = pfnFunc;
439 }
440
441 RTLdrClose(hMod);
442 }
443 else
444 vboxWinDrvInstLogError(pCtx, "Unabled to load module \"%s\" (%Rrc)", pszFilename, rc);
445
446 return rc;
447}
448
449/**
450 * Initialize the import APIs for run-as-user and special environment support.
451 *
452 * @returns VBox status code.
453 * @param pvUser Pointer to VBOXWINDRVINSTINTERNAL.
454 */
455static DECLCALLBACK(int) vboxWinDrvInstResolveOnce(void *pvUser)
456{
457 PVBOXWINDRVINSTINTERNAL pCtx = (PVBOXWINDRVINSTINTERNAL)pvUser;
458
459 /*
460 * Note: Any use of Difx[app|api].dll imports is forbidden (and also marked as being deprecated since Windows 10)!
461 */
462
463 /* rc ignored, keep going */ vboxWinDrvInstResolveMod(pCtx, "setupapi.dll",
464 s_aSetupApiImports, RT_ELEMENTS(s_aSetupApiImports));
465 /* rc ignored, keep going */ vboxWinDrvInstResolveMod(pCtx, "newdev.dll",
466 s_aNewDevImports, RT_ELEMENTS(s_aNewDevImports));
467
468 return VINF_SUCCESS;
469}
470
471/**
472 * Initializes a driver installation parameters structure.
473 *
474 * @param pParms Installation parameters structure to initialize.
475 */
476static void vboxWinDrvInstParmsInit(PVBOXWINDRVINSTPARMS pParms)
477{
478 RT_BZERO(pParms, sizeof(VBOXWINDRVINSTPARMS));
479}
480
481/**
482 * Destroys a driver installation parameters structure.
483 *
484 * @param pParms Installation parameters structure to destroy.
485 */
486static void vboxWinDrvInstParmsDestroy(PVBOXWINDRVINSTPARMS pParms)
487{
488 switch (pParms->enmMode)
489 {
490 case VBOXWINDRVINSTMODE_INSTALL:
491 case VBOXWINDRVINSTMODE_UNINSTALL:
492 {
493 RTUtf16Free(pParms->u.UnInstall.pwszModel);
494 pParms->u.UnInstall.pwszModel = NULL;
495 RTUtf16Free(pParms->u.UnInstall.pwszPnpId);
496 pParms->u.UnInstall.pwszPnpId = NULL;
497 RTUtf16Free(pParms->u.UnInstall.pwszSection);
498 pParms->u.UnInstall.pwszSection = NULL;
499 break;
500 }
501
502 case VBOXWINDRVINSTMODE_INSTALL_INFSECTION:
503 case VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION:
504 {
505 RTUtf16Free(pParms->u.ExecuteInf.pwszSection);
506 pParms->u.ExecuteInf.pwszSection = NULL;
507 break;
508 }
509
510 case VBOXWINDRVINSTMODE_INVALID:
511 break;
512
513 default:
514 AssertFailed();
515 break;
516 }
517
518 RTUtf16Free(pParms->pwszInfFile);
519 pParms->pwszInfFile = NULL;
520}
521
522/**
523 * Structure for keeping callback data for vboxDrvInstExecuteInfFileCallback().
524 */
525typedef struct VBOXDRVINSTINFCALLBACKCTX
526{
527 /** Pointer to driver installer instance. */
528 PVBOXWINDRVINSTINTERNAL pThis;
529 /** Weak pointer to INF section being handled. */
530 PCRTUTF16 pwszSection;
531 /** User-supplied context pointer. */
532 void *pvSetupContext;
533} VBOXDRVINSTINFCALLBACKCTX;
534/** Pointer to structure for keeping callback data for vboxDrvInstExecuteInfFileCallback(). */
535typedef VBOXDRVINSTINFCALLBACKCTX *PVBOXDRVINSTINFCALLBACKCTX;
536
537/** Callback for SetupInstallFromInfSectionW(). */
538RT_C_DECLS_BEGIN /** @todo r=andy Not sure if we have something else to use. */
539static UINT WINAPI vboxDrvInstExecuteInfFileCallback(PVOID pvCtx,
540 UINT uNotification,
541 UINT_PTR Param1,
542 UINT_PTR Param2)
543{
544 RT_NOREF(Param2);
545
546 PVBOXDRVINSTINFCALLBACKCTX pCtx = (PVBOXDRVINSTINFCALLBACKCTX)pvCtx;
547
548 vboxWinDrvInstLogVerbose(pCtx->pThis, 4, "Got installation notification %#x", uNotification);
549
550 switch (uNotification)
551 {
552 case SPFILENOTIFY_NEEDMEDIA:
553 {
554 PSOURCE_MEDIA_W pSourceMedia = (PSOURCE_MEDIA_W)Param1;
555 vboxWinDrvInstLogInfo(pCtx->pThis, "Requesting installation media \"%ls\\%ls\"...",
556 pSourceMedia->SourcePath, pSourceMedia->SourceFile);
557 break;
558 }
559
560 case SPFILENOTIFY_STARTCOPY:
561 case SPFILENOTIFY_ENDCOPY:
562 {
563 PFILEPATHS_W pFilePaths = (PFILEPATHS_W)Param1;
564 vboxWinDrvInstLogInfo(pCtx->pThis, "%s copying \"%ls\" -> \"%ls\"",
565 uNotification == SPFILENOTIFY_STARTCOPY
566 ? "Started" : "Finished", pFilePaths->Source, pFilePaths->Target);
567 break;
568 }
569
570 case SPFILENOTIFY_RENAMEERROR:
571 case SPFILENOTIFY_DELETEERROR:
572 case SPFILENOTIFY_COPYERROR:
573 {
574 PFILEPATHS_W pFilePaths = (PFILEPATHS_W)Param1;
575 vboxWinDrvInstLogError(pCtx->pThis, "Rename/Delete/Copy error \"%ls\" -> \"%s\" (%#x)",
576 pFilePaths->Source, pFilePaths->Target, pFilePaths->Win32Error);
577 break;
578 }
579
580 case SPFILENOTIFY_TARGETNEWER:
581 vboxWinDrvInstLogInfo(pCtx->pThis, "A newer version of the specified file exists on the target");
582 break;
583
584 case SPFILENOTIFY_TARGETEXISTS:
585 vboxWinDrvInstLogInfo(pCtx->pThis, "A copy of the specified file already exists on the target");
586 break;
587
588 default:
589 break;
590 }
591
592 return SetupDefaultQueueCallbackW(pCtx->pvSetupContext, uNotification, Param1, Param2);;
593}
594RT_C_DECLS_END
595
596/**
597 * Generic function to for probing a list of well-known sections for [un]installation.
598 *
599 * Due to the nature of INF files this function tries different combinations of decorations (e.g. SectionName[.NTAMD64|.X86])
600 * and invokes the given callback for the first found section.
601 *
602 * @returns VBox status code.
603 * @param pCtx Windows driver installer context.
604 * @param hInf Handle of INF file.
605 * @param pwszSection Section to invoke for [un]installation.
606 * If NULL, the "DefaultInstall" / "DefaultUninstall" section will be tried.
607 * @param pfnCallback Callback to invoke for each found section.
608 */
609static int vboxWinDrvTryInfSectionEx(PVBOXWINDRVINSTINTERNAL pCtx, HINF hInf, PCRTUTF16 pwszSection,
610 PFNVBOXWINDRVINST_TRYINFSECTION_CALLBACK pfnCallback)
611{
612 if (pwszSection)
613 vboxWinDrvInstLogVerbose(pCtx, 1, "Trying section \"%ls\"", pwszSection);
614
615 /* Sorted by most likely-ness. */
616 PCRTUTF16 apwszTryInstallSections[] =
617 {
618 /* The more specific (using decorations), the better. Try these first. Might be NULL. */
619 pwszSection,
620 /* The Default[Un]Install sections apply to primitive (and legacy) drivers. */
621 pCtx->Parms.enmMode == VBOXWINDRVINSTMODE_INSTALL
622 ? L"DefaultInstall" : L"DefaultUninstall"
623 };
624
625 PCRTUTF16 apwszTryInstallDecorations[] =
626 {
627 /* No decoration. Try that first. */
628 L"",
629 /* Native architecture. */
630 L"" VBOXWINDRVINF_DOT_NT_NATIVE_ARCH_STR
631 };
632
633 int rc = VERR_NOT_FOUND;
634
635 for (size_t i = 0; i < RT_ELEMENTS(apwszTryInstallSections); i++)
636 {
637 PCRTUTF16 const pwszTrySection = apwszTryInstallSections[i];
638 if (!pwszTrySection)
639 continue;
640
641 for (size_t d = 0; d < RT_ELEMENTS(apwszTryInstallDecorations); d++)
642 {
643 RTUTF16 wszTrySection[64];
644 rc = RTUtf16Copy(wszTrySection, sizeof(wszTrySection), pwszTrySection);
645 AssertRCBreak(rc);
646 rc = RTUtf16Cat(wszTrySection, sizeof(wszTrySection), apwszTryInstallDecorations[d]);
647 AssertRCBreak(rc);
648
649 rc = pfnCallback(hInf, wszTrySection, pCtx /* pvCtx */);
650 if (RT_SUCCESS(rc))
651 break;
652
653 if (rc == VERR_FILE_NOT_FOUND) /* File gone already. */
654 {
655 rc = VINF_SUCCESS;
656 break;
657 }
658
659 if (rc != VERR_NOT_FOUND)
660 vboxWinDrvInstLogError(pCtx, "Trying INF section failed with %Rrc", rc);
661 }
662
663 if (RT_SUCCESS(rc)) /* Bail out if callback above succeeded. */
664 break;
665 }
666
667 if (rc == VERR_NOT_FOUND)
668 {
669 vboxWinDrvInstLogWarn(pCtx, "No matching sections to try found -- buggy driver?");
670 rc = VINF_SUCCESS;
671 }
672
673 return rc;
674}
675
676/**
677 * Generic function to for probing a list of well-known sections for [un]installation.
678 *
679 * Due to the nature of INF files this function tries different combinations of decorations (e.g. SectionName[.NTAMD64|.X86])
680 * and invokes the given callback for the first found section.
681 *
682 * @returns VBox status code.
683 * @param pCtx Windows driver installer context.
684 * @param pwszInfPathAbs Absolute path of INF file to use for [un]installation.
685 * @param pwszSection Section to invoke for [un]installation.
686 * If NULL, the "DefaultInstall" / "DefaultUninstall" section will be tried.
687 * @param pfnCallback Callback to invoke for each found section.
688 */
689static int vboxWinDrvTryInfSection(PVBOXWINDRVINSTINTERNAL pCtx, PCRTUTF16 pwszInfPathAbs, PCRTUTF16 pwszSection,
690 PFNVBOXWINDRVINST_TRYINFSECTION_CALLBACK pfnCallback)
691{
692 HINF hInf;
693 int rc = VBoxWinDrvInfOpen(pwszInfPathAbs, &hInf);
694 if (RT_FAILURE(rc))
695 {
696 vboxWinDrvInstLogError(pCtx, "Unable to open INF file: %Rrc\n", rc);
697 return rc;
698 }
699 vboxWinDrvInstLogVerbose(pCtx, 1, "INF file \"%ls\" opened", pwszInfPathAbs);
700
701 rc = vboxWinDrvTryInfSectionEx(pCtx, hInf, pwszSection, pfnCallback);
702
703 VBoxWinDrvInfClose(hInf);
704 vboxWinDrvInstLogVerbose(pCtx, 1, "INF file \"%ls\" closed", pwszInfPathAbs);
705
706 return rc;
707}
708
709/**
710 * Uninstalls a section of a given INF file.
711 *
712 * @returns VBox status code.
713 * @retval VERR_NOT_FOUND if the given section has not been found.
714 * @param pCtx Windows driver installer context.
715 * @param hInf Handle of INF file.
716 * @param pwszSection Section within INF file to uninstall.
717 * Can have a platform decoration (e.g. "Foobar.NTx86").
718 */
719static int vboxWinDrvUninstallInfSectionEx(PVBOXWINDRVINSTINTERNAL pCtx, HINF hInf, PCRTUTF16 pwszSection)
720{
721 AssertPtrReturn(pwszSection, VERR_INVALID_POINTER);
722
723 int rc = VINF_SUCCESS;
724
725 vboxWinDrvInstLogInfo(pCtx, "Uninstalling INF section \"%ls\" ...", pwszSection);
726
727 /*
728 * Uninstall services (if any).
729 */
730 RTUTF16 wszSection[64];
731 ssize_t const cwchSection = RTUtf16Printf(wszSection, RT_ELEMENTS(wszSection),
732 "%ls%s", pwszSection, ".Services");
733 if (cwchSection > 0)
734 {
735 /* We always want to be the first service tag in the group order list (if any). */
736 DWORD fFlags = SPSVCINST_TAGTOFRONT;
737 BOOL fRc = SetupInstallServicesFromInfSectionW(hInf, wszSection, fFlags);
738 if (!fRc)
739 {
740 DWORD const dwErr = GetLastError();
741 if (dwErr == ERROR_SECTION_NOT_FOUND)
742 {
743 vboxWinDrvInstLogVerbose(pCtx, 1, "INF section \"%ls\" not found", wszSection);
744 rc = VERR_NOT_FOUND;
745 }
746 else
747 {
748 rc = vboxWinDrvInstLogLastError(pCtx, "Could not uninstall INF services section \"%ls\"", wszSection);
749 if (rc == VERR_FILE_NOT_FOUND)
750 {
751 /* Hint: Getting VERR_FILE_NOT_FOUND here might mean that an old service entry still is dangling around.
752 * 'sc query <service name> won't show this, however.
753 * Use 'sc delete <service name>' to delete the leftover. */
754 vboxWinDrvInstLogError(pCtx,
755 "Hint: An old service (SCM) entry might be dangling around.\n"
756 "Try removing it via 'sc delete <service name>' and try again.");
757 }
758 }
759 }
760 else
761 vboxWinDrvInstLogInfo(pCtx, "Uninstalling INF services section \"%ls\" successful", wszSection);
762
763 }
764 else
765 {
766 vboxWinDrvInstLogError(pCtx, "Unable to build uninstallation section string");
767 rc = VERR_BUFFER_OVERFLOW;
768 }
769
770 return rc;
771}
772
773/**
774 * Installs a section of a given INF file.
775 *
776 * @returns VBox status code.
777 * @retval VERR_NOT_FOUND if the given section has not been found.
778 * @param pCtx Windows driver installer context.
779 * @param hInf Handle of INF file.
780 * @param pwszSection Section within INF file to install.
781 * Can have a platform decoration (e.g. "Foobar.NTx86").
782 */
783static int vboxWinDrvInstallInfSectionEx(PVBOXWINDRVINSTINTERNAL pCtx, HINF hInf, PCRTUTF16 pwszSection)
784{
785 AssertPtrReturn(pwszSection, VERR_INVALID_POINTER);
786
787 int rc = VINF_SUCCESS;
788
789 vboxWinDrvInstLogInfo(pCtx, "Installing INF section \"%ls\" ...", pwszSection);
790
791 VBOXDRVINSTINFCALLBACKCTX CallbackCtx;
792 RT_ZERO(CallbackCtx);
793 CallbackCtx.pThis = pCtx;
794 CallbackCtx.pwszSection = pwszSection;
795 CallbackCtx.pvSetupContext = SetupInitDefaultQueueCallback(NULL);
796
797 BOOL fRc = SetupInstallFromInfSectionW(NULL, // hWndOwner
798 hInf,
799 pwszSection,
800 SPINST_ALL, // Flags
801 NULL, // RelativeKeyRoot
802 NULL, // SourceRootPath
803 SP_COPY_NOSKIP
804 | SP_COPY_IN_USE_NEEDS_REBOOT,
805 vboxDrvInstExecuteInfFileCallback,
806 &CallbackCtx,
807 NULL, // DeviceInfoSet
808 NULL); // DeviceInfoData
809 if (fRc)
810 {
811 vboxWinDrvInstLogInfo(pCtx, "Installing INF section \"%ls\" successful", pwszSection);
812 }
813 else
814 {
815 DWORD const dwErr = GetLastError();
816 /* Seems like newer Windows OSes (seen on Win10) don't like undecorated sections with SetupInstallFromInfSectionW().
817 * So ignore this and continue. */
818 if (dwErr == ERROR_BADKEY)
819 {
820 vboxWinDrvInstLogVerbose(pCtx, 1, "Installing INF section \"%ls\" failed with %#x (%d), ignoring",
821 pwszSection, dwErr, dwErr);
822 }
823 else
824 rc = vboxWinDrvInstLogLastError(pCtx, "Installing INF section \"%ls\" failed", pwszSection);
825 }
826
827 /*
828 * Try install services.
829 */
830 RTUTF16 wszSection[64];
831 ssize_t const cwchSection = RTUtf16Printf(wszSection, RT_ELEMENTS(wszSection),
832 "%ls%ls%s", pwszSection, VBOXWINDRVINF_DECORATION_SEP_UTF16_STR, "Services");
833 if (cwchSection > 0)
834 {
835 /* We always want to be the first service tag in the group order list (if any). */
836 DWORD const fFlags = SPSVCINST_TAGTOFRONT;
837 fRc = SetupInstallServicesFromInfSectionW(hInf, wszSection, fFlags);
838 if (!fRc)
839 {
840 DWORD const dwErr = GetLastError();
841 if (dwErr == ERROR_SECTION_NOT_FOUND)
842 {
843 vboxWinDrvInstLogVerbose(pCtx, 1, "INF section \"%ls\" not found, skipping", wszSection);
844 rc = VERR_NOT_FOUND;
845 }
846 else if (dwErr == ERROR_SERVICE_MARKED_FOR_DELETE)
847 vboxWinDrvInstLogWarn(pCtx, "Service in INF section \"%ls\" already marked for deletion, skipping", wszSection);
848 else if (dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
849 vboxWinDrvInstLogWarn(pCtx, "Service in INF section \"%ls\" does not accept any control commands (probably in starting/stopping state), skipping", wszSection);
850 else
851 {
852 rc = vboxWinDrvInstLogLastError(pCtx, "Could not install INF services section \"%ls\"", wszSection);
853 if (rc == VERR_FILE_NOT_FOUND)
854 {
855 /* Hint: Getting VERR_FILE_NOT_FOUND here might mean that an old service entry still is dangling around.
856 * 'sc query <service name> won't show this, however.
857 * Use 'sc delete <service name>' to delete the leftover. */
858 vboxWinDrvInstLogError(pCtx, "An old service (SCM) entry might be dangling around.");
859 vboxWinDrvInstLogInfo (pCtx, "Try removing it via 'sc delete <service name>' and try again.");
860 }
861 }
862 }
863 else
864 vboxWinDrvInstLogInfo(pCtx, "Installing INF services section \"%ls\" successful", wszSection);
865
866 }
867 else
868 {
869 vboxWinDrvInstLogError(pCtx, "Unable to build section string");
870 rc = VERR_BUFFER_OVERFLOW;
871 }
872
873 if (CallbackCtx.pvSetupContext)
874 {
875 SetupTermDefaultQueueCallback(CallbackCtx.pvSetupContext);
876 CallbackCtx.pvSetupContext = NULL;
877 }
878
879 return rc;
880}
881
882/**
883 * Installs a section of a given INF file.
884 *
885 * Only supported for the VBOXWINDRVINSTMODE_INSTALL_INFSECTION + VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION modes.
886 *
887 * @returns VBox status code.
888 * @retval VERR_NOT_FOUND if the given section has not been found.
889 * @param pCtx Windows driver installer context.
890 * @param pParms Windows driver installation parameters to use.
891 */
892static int vboxWinDrvInstallInfSection(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
893{
894 AssertReturn( pParms->enmMode == VBOXWINDRVINSTMODE_INSTALL_INFSECTION
895 || pParms->enmMode == VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION, VERR_INVALID_PARAMETER);
896
897 HINF hInf;
898 int rc = VBoxWinDrvInfOpen(pParms->pwszInfFile, &hInf);
899 if (RT_FAILURE(rc))
900 {
901 vboxWinDrvInstLogError(pCtx, "Unable to open INF file: %Rrc\n", rc);
902 return rc;
903 }
904
905 vboxWinDrvInstLogVerbose(pCtx, 1, "INF file \"%ls\" opened", pParms->pwszInfFile);
906
907 rc = vboxWinDrvInstallInfSectionEx(pCtx, hInf, pParms->u.ExecuteInf.pwszSection);
908
909 VBoxWinDrvInfClose(hInf);
910 vboxWinDrvInstLogVerbose(pCtx, 1, "INF file \"%ls\" closed", pParms->pwszInfFile);
911
912 return rc;
913}
914
915/**
916 * Callback implementation for invoking a section for installation.
917 *
918 * @returns VBox status code.
919 * @param hInf Handle of INF file to use.
920 * @param pwszSection Section to invoke.
921 * @param pvCtx User-supplied pointer. Usually PVBOXWINDRVINSTINTERNAL.
922 */
923DECLCALLBACK(int) vboxWinDrvInstallTryInfSectionCallback(HINF hInf, PCRTUTF16 pwszSection, void *pvCtx)
924{
925 PVBOXWINDRVINSTINTERNAL pCtx = (PVBOXWINDRVINSTINTERNAL)pvCtx;
926
927 return vboxWinDrvInstallInfSectionEx(pCtx, hInf, pwszSection);
928}
929
930/**
931 * Performs the actual driver installation.
932 *
933 * @returns VBox status code.
934 * @param pCtx Windows driver installer context.
935 * @param pParms Windows driver installation parameters to use.
936 */
937static int vboxWinDrvInstallPerform(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
938{
939 int rc = vboxWinDrvParmsDetermine(pCtx, pParms, false /* fForce */);
940 if (RT_FAILURE(rc))
941 return rc;
942
943 switch (pParms->enmMode)
944 {
945 case VBOXWINDRVINSTMODE_INSTALL:
946 {
947 BOOL fRc = FALSE;
948 BOOL fReboot = FALSE;
949
950 uint64_t const uNtVer = RTSystemGetNtVersion();
951
952 /*
953 * Pre-install driver.
954 */
955 DWORD dwInstallFlags = 0;
956 if (uNtVer >= RTSYSTEM_MAKE_NT_VERSION(6, 0, 0)) /* for Vista / 2008 Server and up. */
957 {
958 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_FORCE)
959 dwInstallFlags |= DIIRFLAG_FORCE_INF;
960
961 vboxWinDrvInstLogVerbose(pCtx, 1, "Using g_pfnDiInstallDriverW(), dwInstallFlags=%#x", dwInstallFlags);
962
963 if (!(pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN))
964 fRc = g_pfnDiInstallDriverW(NULL /* hWndParent */, pParms->pwszInfFile, dwInstallFlags, &fReboot);
965 else
966 fRc = TRUE;
967 if (!fRc)
968 {
969 DWORD const dwErr = GetLastError();
970
971 /*
972 * Work around an error code wich only appears on old(er) Windows Server editions (e.g. 2012 R2 or 2016)
973 * where SetupAPI tells "unable to mark devices that match new inf", which ultimately results in
974 * ERROR_LINE_NOT_FOUND. This probably is because of primitive drivers which don't have a PnP ID set in
975 * the INF file.
976 *
977 * pnputil.exe also gives the same error in the SetupAPI log when handling the very same INF file.
978 *
979 * So skip this error and pretend everything is fine. */
980 if (dwErr == ERROR_LINE_NOT_FOUND)
981 fRc = true;
982
983 if (!fRc)
984 rc = vboxWinDrvInstLogLastError(pCtx, "DiInstallDriverW() failed");
985 }
986
987 if (fRc)
988 rc = vboxWinDrvTryInfSection(pCtx,
989 pParms->pwszInfFile, pParms->u.UnInstall.pwszSection,
990 vboxWinDrvInstallTryInfSectionCallback);
991 }
992 else /* For Windows 2000 and below. */
993 {
994 if (pParms->u.UnInstall.pwszPnpId)
995 {
996 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_SILENT)
997 {
998 /* Using INSTALLFLAG_NONINTERACTIVE will trigger an invalid parameter error on Windows 2000. */
999 if (uNtVer >= RTSYSTEM_MAKE_NT_VERSION(5, 1, 0))
1000 dwInstallFlags |= INSTALLFLAG_NONINTERACTIVE;
1001 else
1002 vboxWinDrvInstLogWarn(pCtx, "This version of Windows does not support silent installs");
1003 }
1004
1005 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_FORCE)
1006 dwInstallFlags |= INSTALLFLAG_FORCE;
1007
1008 vboxWinDrvInstLogVerbose(pCtx, 4, "Using g_pfnUpdateDriverForPlugAndPlayDevicesW(), pwszPnpId=%ls, pwszInfFile=%ls, dwInstallFlags=%#x",
1009 pParms->u.UnInstall.pwszPnpId, pParms->pwszInfFile, dwInstallFlags);
1010
1011 if (!(pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN))
1012 fRc = g_pfnUpdateDriverForPlugAndPlayDevicesW(NULL /* hWndParent */,
1013 pParms->u.UnInstall.pwszPnpId,
1014 pParms->pwszInfFile, dwInstallFlags, &fReboot);
1015 else
1016 fRc = TRUE;
1017
1018 if (!fRc)
1019 {
1020 DWORD const dwErr = GetLastError();
1021 switch (dwErr)
1022 {
1023 case ERROR_NO_SUCH_DEVINST:
1024 {
1025 vboxWinDrvInstLogInfo(pCtx, "Device (\"%ls\") not found (yet), pre-installing driver ...",
1026 pParms->u.UnInstall.pwszPnpId);
1027 break;
1028 }
1029
1030 case ERROR_NO_DRIVER_SELECTED:
1031 {
1032 vboxWinDrvInstLogWarn(pCtx, "Not able to select a driver from the given INF, using given model");
1033 break;
1034 }
1035
1036 default:
1037 rc = vboxWinDrvInstLogLastError(pCtx, "Installation(UpdateDriverForPlugAndPlayDevicesW) failed");
1038 break;
1039 }
1040 }
1041 }
1042
1043 if (RT_FAILURE(rc))
1044 break;
1045
1046 /*
1047 * Pre-install driver.
1048 */
1049 RTUTF16 wszInfFileAbs[RTPATH_MAX] = { 0 };
1050 LPWSTR pwszInfFile = NULL;
1051 if ( GetFullPathNameW(pParms->pwszInfFile, RT_ELEMENTS(wszInfFileAbs), wszInfFileAbs, &pwszInfFile)
1052 && pwszInfFile)
1053 {
1054 RTUTF16 wszSrcPathAbs[RTPATH_MAX] = { 0 };
1055 rc = RTUtf16CopyEx(wszSrcPathAbs, RT_ELEMENTS(wszSrcPathAbs), wszInfFileAbs,
1056 RTUtf16Len(wszInfFileAbs) - RTUtf16Len(pwszInfFile));
1057 if (RT_SUCCESS(rc))
1058 {
1059 RTUTF16 wszDstPathAbs[RTPATH_MAX] = { 0 };
1060 if (!(pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN))
1061 fRc = g_pfnSetupCopyOEMInfW(wszInfFileAbs, wszSrcPathAbs, SPOST_PATH, 0,
1062 wszDstPathAbs, RT_ELEMENTS(wszDstPathAbs), NULL, NULL);
1063 else
1064 fRc = TRUE;
1065
1066 vboxWinDrvInstLogVerbose(pCtx, 1, " INF file: %ls", wszInfFileAbs);
1067 vboxWinDrvInstLogVerbose(pCtx, 1, "Source path: %ls", wszSrcPathAbs);
1068 vboxWinDrvInstLogVerbose(pCtx, 1, " Dest path: %ls", wszDstPathAbs);
1069
1070 if (fRc)
1071 vboxWinDrvInstLogInfo(pCtx, "Copying OEM INF successful");
1072 else
1073 rc = vboxWinDrvInstLogLastError(pCtx, "Installation(SetupCopyOEMInfW) failed");
1074 }
1075 }
1076 else
1077 rc = vboxWinDrvInstLogLastError(pCtx, "GetFullPathNameW() failed");
1078
1079 rc = vboxWinDrvTryInfSection(pCtx,
1080 pParms->pwszInfFile, pParms->u.UnInstall.pwszSection,
1081 vboxWinDrvInstallTryInfSectionCallback);
1082 }
1083
1084 if (RT_FAILURE(rc))
1085 break;
1086
1087 pCtx->fReboot = RT_BOOL(fReboot);
1088 break;
1089 }
1090
1091 case VBOXWINDRVINSTMODE_INSTALL_INFSECTION:
1092 {
1093 rc = vboxWinDrvInstallInfSection(pCtx, pParms);
1094 break;
1095 }
1096
1097 default:
1098 break;
1099 }
1100
1101 return rc;
1102}
1103
1104/**
1105 * Returns whether the given (in)installation parameters are valid or not.
1106 *
1107 * @returns \c true if valid, \c false if not.
1108 * @param pCtx Windows driver installer context.
1109 * @param pParms Windows driver (un)installation parameters to validate.
1110 */
1111static bool vboxWinDrvParmsAreValid(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
1112{
1113 if (pParms->u.UnInstall.pwszPnpId)
1114 {
1115 size_t const cchPnpId = RTUtf16Len(pParms->u.UnInstall.pwszPnpId);
1116 if ( !cchPnpId
1117 || cchPnpId > MAX_DEVICE_ID_LEN)
1118 {
1119 vboxWinDrvInstLogVerbose(pCtx, 1, "PnP ID not specified explicitly or invalid");
1120 return false;
1121 }
1122 }
1123
1124 return true;
1125}
1126
1127/**
1128 * Determines (un)installation parameters from a given set of parameters, logged.
1129 *
1130 * @returns VBox status code.
1131 * @retval VERR_INVALID_PARAMETER if no valid parameters could be determined.
1132 * @param pCtx Windows driver installer context.
1133 * @param pParms Windows driver installation parameters to determine for.
1134 * @param fForce Whether to overwrite already set parameters or not.
1135 *
1136 * @note Only can deal with the first model / PnP ID found for now.
1137 */
1138static int vboxWinDrvParmsDetermine(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms, bool fForce)
1139{
1140 int rc;
1141
1142 /* INF file given? */
1143 if (pParms->pwszInfFile)
1144 {
1145 HINF hInf;
1146 rc = VBoxWinDrvInfOpen(pParms->pwszInfFile, &hInf);
1147 if (RT_SUCCESS(rc))
1148 {
1149 /* Get the INF type first. */
1150 PRTUTF16 pwszMainSection;
1151 VBOXWINDRVINFTYPE enmType = VBoxWinDrvInfGetTypeEx(hInf, &pwszMainSection);
1152 if (enmType != VBOXWINDRVINFTYPE_INVALID)
1153 {
1154 vboxWinDrvInstLogVerbose(pCtx, 1, "INF type is: %s",
1155 enmType == VBOXWINDRVINFTYPE_NORMAL
1156 ? "Normal" : "Primitive");
1157 /*
1158 * Determine model.
1159 */
1160 if ( !pParms->u.UnInstall.pwszModel
1161 || fForce)
1162 {
1163 vboxWinDrvInstLogVerbose(pCtx, 1, "Determining model ...");
1164 if (fForce)
1165 {
1166 RTUtf16Free(pParms->u.UnInstall.pwszModel);
1167 pParms->u.UnInstall.pwszModel = NULL;
1168 }
1169 rc = VBoxWinDrvInfQueryFirstModel(hInf, pwszMainSection, &pParms->u.UnInstall.pwszModel);
1170 if (RT_SUCCESS(rc))
1171 {
1172 RTUtf16Free(pParms->u.UnInstall.pwszSection);
1173 pParms->u.UnInstall.pwszSection = NULL;
1174
1175 /* Now that we have determined the model, try if there is a section in the INF file for this model. */
1176 rc = VBoxWinDrvInfQueryInstallSection(hInf, pParms->u.UnInstall.pwszModel,
1177 &pParms->u.UnInstall.pwszSection);
1178 if (RT_FAILURE(rc))
1179 {
1180 switch (enmType)
1181 {
1182 case VBOXWINDRVINFTYPE_NORMAL:
1183 {
1184 vboxWinDrvInstLogError(pCtx, "No section to install found, can't continue");
1185 break;
1186 }
1187
1188 case VBOXWINDRVINFTYPE_PRIMITIVE:
1189 {
1190 /* If for the given model there is no install section, set the section to main section
1191 * we got when we determined the INF type.
1192 *
1193 * This will be mostly the case for primitive drivers. */
1194 if (rc == VERR_NOT_FOUND)
1195 {
1196 pParms->u.UnInstall.pwszSection = RTUtf16Dup(pwszMainSection);
1197 if (pParms->u.UnInstall.pwszSection)
1198 {
1199 rc = VINF_SUCCESS;
1200 }
1201 else
1202 rc = VERR_NO_MEMORY;
1203 }
1204 break;
1205 }
1206
1207 default:
1208 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1209 break;
1210 }
1211 }
1212 }
1213 else
1214 {
1215 switch (rc)
1216 {
1217 case VERR_PLATFORM_ARCH_NOT_SUPPORTED:
1218 {
1219 vboxWinDrvInstLogError(pCtx, "Model found, but platform is not supported");
1220 break;
1221 }
1222
1223 case VERR_NOT_FOUND:
1224 {
1225 vboxWinDrvInstLogError(pCtx, "No model found to install found -- buggy driver?");
1226 break;
1227 }
1228
1229 default:
1230 break;
1231 }
1232 }
1233 }
1234
1235 /*
1236 * Determine PnP ID.
1237 *
1238 * Only available in non-primitive drivers.
1239 */
1240 if ( enmType == VBOXWINDRVINFTYPE_NORMAL
1241 && ( !pParms->u.UnInstall.pwszPnpId
1242 || fForce))
1243 {
1244 if (pParms->u.UnInstall.pwszModel)
1245 {
1246 vboxWinDrvInstLogVerbose(pCtx, 1, "Determining PnP ID ...");
1247 if (fForce)
1248 {
1249 RTUtf16Free(pParms->u.UnInstall.pwszPnpId);
1250 pParms->u.UnInstall.pwszPnpId = NULL;
1251 }
1252 /* ignore rc */ VBoxWinDrvInfQueryFirstPnPId(hInf,
1253 pParms->u.UnInstall.pwszModel, &pParms->u.UnInstall.pwszPnpId);
1254 }
1255 else
1256 vboxWinDrvInstLogVerbose(pCtx, 1, "No first model found/set, skipping determining PnP ID");
1257 }
1258
1259 RTUtf16Free(pwszMainSection);
1260 }
1261 else
1262 {
1263 vboxWinDrvInstLogError(pCtx, "INF file is invalid");
1264 rc = VERR_INVALID_PARAMETER;
1265 }
1266
1267 VBoxWinDrvInfClose(hInf);
1268 }
1269 }
1270 /* No INF file given but either the model or the PnP ID? */
1271 else if ( pParms->u.UnInstall.pwszModel
1272 || pParms->u.UnInstall.pwszPnpId)
1273 {
1274 /* Nothing to do for us here. */
1275 rc = VINF_SUCCESS;
1276 }
1277 else
1278 {
1279 vboxWinDrvInstLogError(pCtx, "Neither INF file nor model/PnP ID given; can't continue");
1280 rc = VERR_INVALID_PARAMETER;
1281 }
1282
1283 if (RT_SUCCESS(rc))
1284 {
1285 vboxWinDrvInstLogVerbose(pCtx, 1, "Determined parameters:");
1286 vboxWinDrvInstLogVerbose(pCtx, 1, "\tINF File: %ls",
1287 pParms->pwszInfFile ? pParms->pwszInfFile : L"<None>");
1288 vboxWinDrvInstLogVerbose(pCtx, 1, "\t Model: %ls",
1289 pParms->u.UnInstall.pwszModel ? pParms->u.UnInstall.pwszModel : L"<None>");
1290 vboxWinDrvInstLogVerbose(pCtx, 1, "\t PnP ID: %ls",
1291 pParms->u.UnInstall.pwszPnpId ? pParms->u.UnInstall.pwszPnpId : L"<None>");
1292 vboxWinDrvInstLogVerbose(pCtx, 1, "\t Section: %ls",
1293 pParms->u.UnInstall.pwszSection ? pParms->u.UnInstall.pwszSection : L"<None>");
1294 }
1295
1296 return rc;
1297}
1298
1299/**
1300 * Queries OEM INF files from the driver store.
1301 *
1302 * @returns VBox status code.
1303 * @param pCtx Windows driver installer context.
1304 * @param pParms Windows driver installation parameters to use.
1305 * @param ppList Where to return the list of found Windows driver store entries on success.
1306 * Needs to be destroyed with VBoxWinDrvStoreListFree().
1307 */
1308static int vboxWinDrvQueryFromDriverStore(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms,
1309 PVBOXWINDRVSTORELIST *ppList)
1310{
1311 int rc = vboxWinDrvParmsDetermine(pCtx, pParms, false /* fForce */);
1312 if (RT_SUCCESS(rc))
1313 {
1314 PVBOXWINDRVSTORELIST pList = NULL;
1315 char *pszNeedle = NULL;
1316
1317 if (pParms->u.UnInstall.pwszPnpId)
1318 {
1319 rc = RTUtf16ToUtf8(pParms->u.UnInstall.pwszPnpId, &pszNeedle);
1320 if (RT_SUCCESS(rc))
1321 rc = VBoxWinDrvStoreQueryByPnpId(pCtx->pStore, pszNeedle, &pList);
1322 }
1323 else if (pParms->u.UnInstall.pwszModel)
1324 {
1325 rc = RTUtf16ToUtf8(pParms->u.UnInstall.pwszModel, &pszNeedle);
1326 if (RT_SUCCESS(rc))
1327 rc = VBoxWinDrvStoreQueryByModelName(pCtx->pStore, pszNeedle, &pList);
1328 }
1329 else if (pParms->pwszInfFile)
1330 {
1331 rc = VERR_NOT_IMPLEMENTED;
1332 }
1333
1334 RTStrFree(pszNeedle);
1335 pszNeedle = NULL;
1336
1337 if (RT_SUCCESS(rc))
1338 {
1339 *ppList = pList;
1340 }
1341 else
1342 VBoxWinDrvStoreListFree(pList);
1343 }
1344
1345 return rc;
1346}
1347
1348/**
1349 * Callback implementation for invoking a section for uninstallation.
1350 *
1351 * @returns VBox status code.
1352 * @param hInf Handle to INF file.
1353 * @param pwszSection Section to invoke.
1354 * @param pvCtx User-supplied pointer. Usually PVBOXWINDRVINSTINTERNAL.
1355 */
1356DECLCALLBACK(int) vboxWinDrvUninstallTryInfSectionCallback(HINF hInf, PCRTUTF16 pwszSection, void *pvCtx)
1357{
1358 PVBOXWINDRVINSTINTERNAL pCtx = (PVBOXWINDRVINSTINTERNAL)pvCtx;
1359
1360 return vboxWinDrvUninstallInfSectionEx(pCtx, hInf, pwszSection);
1361}
1362
1363/**
1364 * Removes OEM INF files from the driver store.
1365 *
1366 * @returns VBox status code.
1367 * @param pCtx Windows driver installer context.
1368 * @param pParms Windows driver uninstallation parameters to use.
1369 * @param pList Driver store list with OEM INF entries to remove.
1370 */
1371static int vboxWinDrvUninstallFromDriverStore(PVBOXWINDRVINSTINTERNAL pCtx,
1372 PVBOXWINDRVINSTPARMS pParms, PVBOXWINDRVSTORELIST pList)
1373{
1374
1375 int rc = VINF_SUCCESS;
1376
1377 const char *pszDrvStorePath = VBoxWinDrvStoreBackendGetLocation(pCtx->pStore);
1378
1379 vboxWinDrvInstLogInfo(pCtx, "Uninstalling %zu matching entr%s", pList->cEntries, pList->cEntries == 1 ? "y" : "ies");
1380 PVBOXWINDRVSTOREENTRY pCur;
1381 RTListForEach(&pList->List, pCur, VBOXWINDRVSTOREENTRY, Node)
1382 {
1383 bool fRc = FALSE;
1384
1385 /*
1386 * Running the uninstalling section(s) first before removing the driver from the driver store below.
1387 */
1388 RTUTF16 wszInfPathAbs[RTPATH_MAX];
1389 ssize_t const cwchInfPathAbs = RTUtf16Printf(wszInfPathAbs, RT_ELEMENTS(wszInfPathAbs),
1390 "%s\\%ls", pszDrvStorePath, pCur->wszInfFile);
1391 AssertBreakStmt(cwchInfPathAbs > 0, rc = VERR_BUFFER_OVERFLOW);
1392
1393 vboxWinDrvInstLogInfo(pCtx, "Uninstalling %ls (%ls)", pCur->wszModel, wszInfPathAbs);
1394
1395 /* Only calltry calling the "DefaultUninstall" section here, as there aren't any other section(s)
1396 * to handle for uninstalling a driver via INF files. */
1397 /* rc ignored, keep going */ vboxWinDrvTryInfSection(pCtx, wszInfPathAbs, NULL /* DefaultUninstall */,
1398 vboxWinDrvUninstallTryInfSectionCallback);
1399
1400 /*
1401 * Remove the driver from the driver store.
1402 */
1403 if (g_pfnDiUninstallDriverW)
1404 {
1405 vboxWinDrvInstLogVerbose(pCtx, 1, "Using DiUninstallDriverW()");
1406 BOOL fReboot = FALSE;
1407 if (!(pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN))
1408 fRc = g_pfnDiUninstallDriverW(NULL /* hWndParent */, pCur->wszInfFile, 0 /* Flags */, &fReboot);
1409 else
1410 fRc = TRUE;
1411 if (fRc)
1412 pCtx->fReboot = RT_BOOL(fReboot);
1413 else
1414 {
1415 /* Not fatal, try next block. */
1416 DWORD const dwErr = GetLastError();
1417 vboxWinDrvInstLogVerbose(pCtx, 1, "DiUninstallDriverW() failed with %#x (%d)", dwErr, dwErr);
1418 }
1419 }
1420
1421 /* Not (yet) successful? Try harder using an older API. */
1422 if ( !fRc
1423 && g_pfnSetupUninstallOEMInfW)
1424 {
1425 vboxWinDrvInstLogVerbose(pCtx, 1, "Using SetupUninstallOEMInfW()");
1426
1427 DWORD dwUninstallFlags = 0;
1428 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_FORCE)
1429 dwUninstallFlags |= SUOI_FORCEDELETE;
1430
1431 if (!(pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN))
1432 fRc = g_pfnSetupUninstallOEMInfW(pCur->wszInfFile, dwUninstallFlags, NULL /* pReserved */);
1433 else
1434 fRc = FALSE;
1435 }
1436
1437 int rc2 = VINF_SUCCESS;
1438
1439 if (fRc)
1440 vboxWinDrvInstLogInfo(pCtx, "Uninstalling OEM INF \"%ls\" successful", wszInfPathAbs);
1441 else
1442 {
1443 DWORD const dwErr = GetLastError();
1444 if (dwErr == ERROR_INF_IN_USE_BY_DEVICES)
1445 vboxWinDrvInstLogError(pCtx, "Unable to uninstall OEM INF \"%ls\": Driver still in use by device", wszInfPathAbs);
1446 else
1447 rc2 = vboxWinDrvInstLogLastError(pCtx, "Uninstalling OEM INF \"%ls\" failed", wszInfPathAbs);
1448 }
1449
1450 /* If anything failed above, try removing stuff ourselves as good as we can. */
1451 if (RT_FAILURE(rc2))
1452 /* rc ignored, keep going */ vboxWinDrvTryInfSection(pCtx, wszInfPathAbs, pCur->wszModel,
1453 vboxWinDrvUninstallTryInfSectionCallback);
1454
1455 if (RT_SUCCESS(rc)) /* Keep first error if already set. */
1456 rc = rc2;
1457
1458 /* Keep going. */
1459 }
1460
1461 return rc;
1462}
1463
1464/**
1465 * Performs the actual driver uninstallation.
1466 *
1467 * @returns VBox status code.
1468 * @param pCtx Windows driver installer context.
1469 * @param pParms Windows driver installation parameters to use.
1470 */
1471static int vboxWinDrvUninstallPerform(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
1472{
1473 int rc;
1474 switch (pParms->enmMode)
1475 {
1476 case VBOXWINDRVINSTMODE_UNINSTALL:
1477 {
1478 PVBOXWINDRVSTORELIST pList = NULL;
1479 rc = vboxWinDrvQueryFromDriverStore(pCtx, pParms, &pList);
1480 if (RT_SUCCESS(rc))
1481 {
1482 rc = vboxWinDrvUninstallFromDriverStore(pCtx, pParms, pList);
1483
1484 VBoxWinDrvStoreListFree(pList);
1485 pList = NULL;
1486 }
1487 break;
1488 }
1489
1490 case VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION:
1491 {
1492 rc = vboxWinDrvInstallInfSection(pCtx, pParms);
1493 break;
1494 }
1495
1496 default:
1497 rc = VINF_SUCCESS;
1498 break;
1499 }
1500
1501 return rc;
1502}
1503
1504/**
1505 * Main function to perform the actual driver [un]installation.
1506 *
1507 * @returns VBox status code.
1508 * @param pCtx Windows driver installer context.
1509 * @param pParms Windows driver installation parameters to use.
1510 */
1511static int vboxWinDrvInstMain(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
1512{
1513 /* Note: Other parameters might be optional, depending on the mode. */
1514 AssertReturn(!(pParms->fFlags & ~VBOX_WIN_DRIVERINSTALL_F_VALID_MASK), VERR_INVALID_PARAMETER);
1515
1516 bool const fInstall = pParms->enmMode == VBOXWINDRVINSTMODE_INSTALL
1517 || pParms->enmMode == VBOXWINDRVINSTMODE_INSTALL_INFSECTION;
1518
1519 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN)
1520 vboxWinDrvInstLogWarn(pCtx, "Dry-run mode active -- no installation performed!");
1521
1522 const char * const pszLogAction = fInstall ? "Installing" : "Uninstalling";
1523 if (pParms->pwszInfFile)
1524 vboxWinDrvInstLogInfo(pCtx, "%s driver \"%ls\" ... ", pszLogAction, pParms->pwszInfFile);
1525 else if (pParms->u.UnInstall.pwszModel)
1526 vboxWinDrvInstLogInfo(pCtx, "%s driver model \"%ls\" ... ", pszLogAction, pParms->u.UnInstall.pwszModel);
1527 else if (pParms->u.UnInstall.pwszPnpId)
1528 vboxWinDrvInstLogInfo(pCtx, "%s PnP ID \"%ls\" ... ", pszLogAction, pParms->u.UnInstall.pwszPnpId);
1529
1530 if ( pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_SILENT
1531 && g_pfnSetupSetNonInteractiveMode)
1532 {
1533 vboxWinDrvInstLogInfo(pCtx, "Setting non-interactive mode ...");
1534 if (!(pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_DRYRUN))
1535 g_pfnSetupSetNonInteractiveMode(TRUE /* fEnable */);
1536 }
1537
1538 if (!vboxWinDrvParmsAreValid(pCtx, pParms))
1539 {
1540 vboxWinDrvInstLogError(pCtx, "%s parameters are invalid, can't continue", fInstall ? "Installation" : "Uninstallation");
1541 return VERR_INVALID_PARAMETER;
1542 }
1543
1544 int rc;
1545 if (fInstall)
1546 rc = vboxWinDrvInstallPerform(pCtx, pParms);
1547 else
1548 rc = vboxWinDrvUninstallPerform(pCtx, pParms);
1549
1550 if (RT_SUCCESS(rc))
1551 {
1552 vboxWinDrvInstLogInfo(pCtx, "Driver was %sinstalled successfully", fInstall ? "" : "un");
1553 if (pCtx->fReboot)
1554 {
1555 vboxWinDrvInstLogRebootNeeded(pCtx, "A reboot is needed in order to complete the driver %sinstallation.",
1556 fInstall ? "" : "un");
1557 rc = VINF_REBOOT_NEEDED;
1558 }
1559 }
1560
1561 /* Note: Call vboxWinDrvInstLogEx() to not increase the error/warn count here. */
1562 if (pCtx->cErrors)
1563 vboxWinDrvInstLogEx(pCtx, VBOXWINDRIVERLOGTYPE_ERROR, "%sstalling driver(s) failed with %u errors, %u warnings (rc=%Rrc)",
1564 fInstall ? "In" : "Unin", pCtx->cErrors, pCtx->cWarnings, rc);
1565 else if (pCtx->cWarnings)
1566 vboxWinDrvInstLogEx(pCtx, VBOXWINDRIVERLOGTYPE_WARN, "%sstalling driver(s) succeeded with %u warnings",
1567 fInstall ? "In" : "Unin", pCtx->cWarnings);
1568
1569 return rc;
1570}
1571
1572/**
1573 * Creates a Windows driver installer instance, extended version.
1574 *
1575 * @returns VBox status code.
1576 * @param phDrvInst where to return the created driver installer handle on success.
1577 * @param uVerbosity Sets the initial verbosity level.
1578 * @param pfnLog Log callback function to set.
1579 * @param pvUser User-supplied pointer to set. Optional and might be NULL.
1580 */
1581int VBoxWinDrvInstCreateEx(PVBOXWINDRVINST phDrvInst, unsigned uVerbosity, PFNVBOXWINDRIVERLOGMSG pfnLog, void *pvUser)
1582{
1583 int rc;
1584
1585 PVBOXWINDRVINSTINTERNAL pCtx = (PVBOXWINDRVINSTINTERNAL)RTMemAllocZ(sizeof(VBOXWINDRVINSTINTERNAL));
1586 if (pCtx)
1587 {
1588 pCtx->u32Magic = VBOXWINDRVINST_MAGIC;
1589 pCtx->uVerbosity = uVerbosity;
1590 pCtx->pfnLog = pfnLog;
1591 pCtx->pvUser = pvUser;
1592
1593 uint64_t const uNtVer = RTSystemGetNtVersion();
1594
1595 vboxWinDrvInstLogInfo(pCtx, VBOX_PRODUCT " Version " VBOX_VERSION_STRING " - r%s", RTBldCfgRevisionStr());
1596 vboxWinDrvInstLogInfo(pCtx, "Detected Windows version %d.%d.%d (%s)", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVer),
1597 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVer),
1598 RTSYSTEM_NT_VERSION_GET_BUILD(uNtVer),
1599 RTBldCfgTargetArch());
1600
1601 rc = RTOnce(&g_vboxWinDrvInstResolveOnce, vboxWinDrvInstResolveOnce, pCtx);
1602 if (RT_SUCCESS(rc))
1603 {
1604 rc = VBoxWinDrvStoreCreate(&pCtx->pStore);
1605 if (RT_SUCCESS(rc))
1606 {
1607 *phDrvInst = (VBOXWINDRVINST)pCtx;
1608 return VINF_SUCCESS;
1609 }
1610 else
1611 vboxWinDrvInstLogError(pCtx, "Creating driver store failed with %Rrc", rc);
1612 }
1613 }
1614 else
1615 rc = VERR_NO_MEMORY;
1616
1617 VBoxWinDrvStoreDestroy(pCtx->pStore);
1618 VBoxWinDrvInstDestroy(pCtx);
1619 return rc;
1620}
1621
1622/**
1623 * Creates a Windows driver installer instance.
1624 *
1625 * @returns VBox status code.
1626 * @param phDrvInst where to return the created driver installer handle on success.
1627 */
1628int VBoxWinDrvInstCreate(PVBOXWINDRVINST phDrvInst)
1629{
1630 return VBoxWinDrvInstCreateEx(phDrvInst, 0 /* uVerbosity */, NULL /* pfnLog */, NULL /* pvUser */);
1631}
1632
1633/**
1634 * Destroys a Windows driver installer instance.
1635 *
1636 * @returns VBox status code.
1637 * @param hDrvInst Windows driver installer handle to destroy.
1638 * The handle will be invalid after calling this function.
1639 */
1640int VBoxWinDrvInstDestroy(VBOXWINDRVINST hDrvInst)
1641{
1642 if (hDrvInst == NIL_VBOXWINDRVINST)
1643 return VINF_SUCCESS;
1644
1645 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1646 VBOXWINDRVINST_VALID_RETURN(pCtx);
1647
1648 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1649
1650 VBoxWinDrvStoreDestroy(pCtx->pStore);
1651 pCtx->pStore = NULL;
1652
1653 RTMemFree(pCtx);
1654
1655 return VINF_SUCCESS;
1656}
1657
1658/**
1659 * Returns the number of (logged) warnings so far.
1660 *
1661 * @returns Number of (logged) warnings so far.
1662 * @param hDrvInst Windows driver installer handle.
1663 */
1664unsigned VBoxWinDrvInstGetWarnings(VBOXWINDRVINST hDrvInst)
1665{
1666 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1667 VBOXWINDRVINST_VALID_RETURN_RC(pCtx, UINT8_MAX);
1668
1669 return pCtx->cWarnings;
1670}
1671
1672/**
1673 * Returns the number of (logged) errors so far.
1674 *
1675 * @returns Number of (logged) errors so far..
1676 * @param hDrvInst Windows driver installer handle.
1677 */
1678unsigned VBoxWinDrvInstGetErrors(VBOXWINDRVINST hDrvInst)
1679{
1680 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1681 VBOXWINDRVINST_VALID_RETURN_RC(pCtx, UINT8_MAX);
1682
1683 return pCtx->cErrors;
1684}
1685
1686/**
1687 * Sets the verbosity of a Windows driver installer instance.
1688 *
1689 * @returns The old verbosity level.
1690 * @param hDrvInst Windows driver installer handle to set verbosity for.
1691 * @param uVerbosity Verbosity level to set.
1692 */
1693unsigned VBoxWinDrvInstSetVerbosity(VBOXWINDRVINST hDrvInst, unsigned uVerbosity)
1694{
1695 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1696 VBOXWINDRVINST_VALID_RETURN_RC(pCtx, UINT8_MAX);
1697
1698 unsigned const uOldVerbosity = hDrvInst->uVerbosity;
1699 hDrvInst->uVerbosity = uVerbosity;
1700 return uOldVerbosity;
1701}
1702
1703/**
1704 * Sets the log callback of a Windows driver installer instance.
1705 *
1706 * @returns VBox status code.
1707 * @param hDrvInst Windows driver installer handle to set log callback for.
1708 * @param pfnLog Log callback function to set.
1709 * @param pvUser User-supplied pointer to set. Optional and might be NULL.
1710 */
1711void VBoxWinDrvInstSetLogCallback(VBOXWINDRVINST hDrvInst, PFNVBOXWINDRIVERLOGMSG pfnLog, void *pvUser)
1712{
1713 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1714 VBOXWINDRVINST_VALID_RETURN_VOID(pCtx);
1715
1716 pCtx->pfnLog = pfnLog;
1717 pCtx->pvUser = pvUser;
1718}
1719
1720/**
1721 * Installs a driver, extended version.
1722 *
1723 * @returns VBox status code.
1724 * @param hDrvInst Windows driver installer handle to use.
1725 * @param pszInfFile INF file to use.
1726 * @param pszModel Model name to use. Optional and can be NULL.
1727 * @param pszPnpId PnP ID to use. NT-style wildcards supported. Optional and can be NULL.
1728 * @param fFlags Installation flags (of type VBOX_WIN_DRIVERINSTALL_F_XXX) to use.
1729 */
1730int VBoxWinDrvInstInstallEx(VBOXWINDRVINST hDrvInst,
1731 const char *pszInfFile, const char *pszModel, const char *pszPnpId, uint32_t fFlags)
1732{
1733 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1734 VBOXWINDRVINST_VALID_RETURN(pCtx);
1735
1736 AssertPtrReturn(pszInfFile, VERR_INVALID_PARAMETER);
1737
1738 vboxWinDrvInstParmsInit(&pCtx->Parms);
1739
1740 /* Resolve the INF file's absolute path, as as Setup API functions tend to need this. */
1741 char szInfPathAbs[RTPATH_MAX];
1742 int rc = RTPathReal(pszInfFile, szInfPathAbs, sizeof(szInfPathAbs));
1743 if (RT_SUCCESS(rc))
1744 {
1745 rc = RTStrToUtf16(szInfPathAbs, &pCtx->Parms.pwszInfFile);
1746 if (RT_FAILURE(rc))
1747 vboxWinDrvInstLogError(pCtx, "Failed to build path for INF file \"%s\", rc=%Rrc", pszInfFile, rc);
1748 }
1749 else
1750 vboxWinDrvInstLogError(pCtx, "Determining real path for INF file \"%s\" failed, rc=%Rrc", pszInfFile, rc);
1751
1752 if (RT_SUCCESS(rc) && pszModel) /* Model is optional. */
1753 rc = RTStrToUtf16(pszModel, &pCtx->Parms.u.UnInstall.pwszModel);
1754 if (RT_SUCCESS(rc) && pszPnpId) /* Ditto. */
1755 rc = RTStrToUtf16(pszPnpId, &pCtx->Parms.u.UnInstall.pwszPnpId);
1756
1757 if (RT_SUCCESS(rc))
1758 {
1759 pCtx->Parms.enmMode = VBOXWINDRVINSTMODE_INSTALL;
1760 pCtx->Parms.fFlags = fFlags;
1761
1762 rc = vboxWinDrvInstMain(pCtx, &pCtx->Parms);
1763 }
1764
1765 if (!(fFlags & VBOX_WIN_DRIVERINSTALL_F_NO_DESTROY))
1766 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1767
1768 if (RT_FAILURE(rc))
1769 vboxWinDrvInstLogError(pCtx, "Driver installation failed with %Rrc", rc);
1770
1771 return rc;
1772}
1773
1774/**
1775 * Installs a driver.
1776 *
1777 * @returns VBox status code.
1778 * @param hDrvInst Windows driver installer handle to use.
1779 * @param pszInfFile INF file to use.
1780 * @param fFlags Installation flags (of type VBOX_WIN_DRIVERINSTALL_F_XXX) to use.
1781 *
1782 * @note This function tries determining the model / PnP ID from the given INF file.
1783 * To control the behavior exactly, use VBoxWinDrvInstInstallEx().
1784 */
1785int VBoxWinDrvInstInstall(VBOXWINDRVINST hDrvInst, const char *pszInfFile, uint32_t fFlags)
1786{
1787 return VBoxWinDrvInstInstallEx(hDrvInst, pszInfFile, NULL /* pszModel */, NULL /* pszPnpId */, fFlags);
1788}
1789
1790/**
1791 * Uninstalls a driver.
1792 *
1793 * @returns VBox status code.
1794 * @param hDrvInst Windows driver installer handle to use.
1795 * @param pszInfFile INF file within driver store to uninstall.
1796 * Optional and can be NULL.
1797 * @param pszModel Model to uninstall (e.g. "VBoxUSB.AMD64"). NT-style wildcards supported.
1798 * Optional and can be NULL.
1799 * @param pszPnpId PnP ID to use (e.g. "USB\\VID_80EE&PID_CAFE"). NT-style wildcards supported.
1800 * Optional and can be NULL.
1801 * @param fFlags Installation flags (of type VBOX_WIN_DRIVERINSTALL_F_XXX) to use.
1802 */
1803int VBoxWinDrvInstUninstall(VBOXWINDRVINST hDrvInst, const char *pszInfFile, const char *pszModel, const char *pszPnpId,
1804 uint32_t fFlags)
1805{
1806 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1807 VBOXWINDRVINST_VALID_RETURN(pCtx);
1808
1809 int rc = VINF_SUCCESS;
1810
1811 vboxWinDrvInstParmsInit(&pCtx->Parms);
1812
1813 /* If given, get the sole INF file name (without path), to make it searchable within the driver store.
1814 * Note: This only will work with "oemXXX.inf" files for the (legcy) driver store. */
1815 if (pszInfFile && *pszInfFile)
1816 {
1817 char *pszInfFileName = RTPathFilename(pszInfFile);
1818 if (pszInfFileName)
1819 rc = RTStrToUtf16(pszInfFileName, &pCtx->Parms.pwszInfFile);
1820 else
1821 rc = VERR_FILE_NOT_FOUND;
1822 }
1823
1824 if (RT_SUCCESS(rc) && pszModel && *pszModel)
1825 rc = RTStrToUtf16(pszModel, &pCtx->Parms.u.UnInstall.pwszModel);
1826 if (RT_SUCCESS(rc) && pszPnpId && *pszPnpId)
1827 rc = RTStrToUtf16(pszPnpId, &pCtx->Parms.u.UnInstall.pwszPnpId);
1828
1829 pCtx->Parms.enmMode = VBOXWINDRVINSTMODE_UNINSTALL;
1830 pCtx->Parms.fFlags = fFlags;
1831
1832 if (RT_SUCCESS(rc))
1833 rc = vboxWinDrvInstMain(pCtx, &pCtx->Parms);
1834
1835 if (!(fFlags & VBOX_WIN_DRIVERINSTALL_F_NO_DESTROY))
1836 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1837
1838 if (RT_FAILURE(rc))
1839 vboxWinDrvInstLogError(pCtx, "Driver uninstallation failed with %Rrc", rc);
1840
1841 return rc;
1842}
1843
1844/**
1845 * Worker function for executing a section of an INF file.
1846 *
1847 * @returns VBox status code.
1848 * @param hDrvInst Windows driver installer handle to use.
1849 * @param fInstall Whether to execute the section to install or uninstall.
1850 * @param pszInfFile INF file to use.
1851 * @param pszSection Section within the INF file to execute.
1852 * @param fFlags Installation flags to use.
1853 */
1854int VBoxWinDrvInstExecuteInfWorker(VBOXWINDRVINST hDrvInst,
1855 bool fInstall, const char *pszInfFile, const char *pszSection, uint32_t fFlags)
1856{
1857 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1858 VBOXWINDRVINST_VALID_RETURN(pCtx);
1859
1860 AssertPtrReturn(pszInfFile, VERR_INVALID_POINTER);
1861
1862 vboxWinDrvInstParmsInit(&pCtx->Parms);
1863
1864 int rc = RTStrToUtf16(pszInfFile, &pCtx->Parms.pwszInfFile);
1865 if (RT_SUCCESS(rc) && pszSection) /* pszSection is optional. */
1866 rc = RTStrToUtf16(pszSection, &pCtx->Parms.u.ExecuteInf.pwszSection);
1867
1868 pCtx->Parms.enmMode = fInstall ? VBOXWINDRVINSTMODE_INSTALL_INFSECTION : VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION;
1869 pCtx->Parms.fFlags = fFlags;
1870
1871 rc = vboxWinDrvInstMain(pCtx, &pCtx->Parms);
1872
1873 if (!(fFlags & VBOX_WIN_DRIVERINSTALL_F_NO_DESTROY))
1874 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1875
1876 return rc;
1877}
1878
1879/**
1880 * Executes a section of an INF file for installation.
1881 *
1882 * @returns VBox status code.
1883 * @param hDrvInst Windows driver installer handle to use.
1884 * @param pszInfFile INF file to use.
1885 * @param pszSection Section within the INF file to execute.
1886 * @param fFlags Installation flags to use.
1887 */
1888int VBoxWinDrvInstInstallExecuteInf(VBOXWINDRVINST hDrvInst, const char *pszInfFile, const char *pszSection, uint32_t fFlags)
1889{
1890 return VBoxWinDrvInstExecuteInfWorker(hDrvInst, true /* fInstall */, pszInfFile, pszSection, fFlags);
1891}
1892
1893/**
1894 * Executes a section of an INF file for uninstallation.
1895 *
1896 * @returns VBox status code.
1897 * @param hDrvInst Windows driver installer handle to use.
1898 * @param pszInfFile INF file to use.
1899 * @param pszSection Section within the INF file to execute.
1900 * @param fFlags Installation flags to use.
1901 */
1902int VBoxWinDrvInstUninstallExecuteInf(VBOXWINDRVINST hDrvInst, const char *pszInfFile, const char *pszSection, uint32_t fFlags)
1903{
1904 return VBoxWinDrvInstExecuteInfWorker(hDrvInst, false /* fInstall */, pszInfFile, pszSection, fFlags);
1905}
1906
1907#ifdef TESTCASE
1908/**
1909 * Returns the internal parameters of an (un)installation.
1910 *
1911 * @returns Internal parameters of an (un)installation.
1912 * @param hDrvInst Windows driver installer handle to use.
1913 */
1914PVBOXWINDRVINSTPARMS VBoxWinDrvInstTestGetParms(VBOXWINDRVINST hDrvInst)
1915{
1916 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1917 VBOXWINDRVINST_VALID_RETURN_RC((hDrvInst), NULL);
1918
1919 return &pCtx->Parms;
1920}
1921
1922/**
1923 * Detroys internal parameters of an (un)installation.
1924 *
1925 * @param pParms Internal parameters of an (un)installation to destroy.
1926 */
1927void VBoxWinDrvInstTestParmsDestroy(PVBOXWINDRVINSTPARMS pParms)
1928{
1929 vboxWinDrvInstParmsDestroy(pParms);
1930}
1931#endif /* TESTCASE */
1932
Note: See TracBrowser for help on using the repository browser.

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