VirtualBox

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

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

Windows driver installation: Added VBOX_WIN_DRIVERINSTALL_F_DRYRUN to make it possible to run in dry-run mode (no actual installation performed). Required for testcases. bugref:10762

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