VirtualBox

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

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

Windows driver installation: More cleanup, a few detection fixes. bugref:10762

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 74.5 KB
Line 
1/* $Id: VBoxWinDrvInst.cpp 106890 2024-11-08 15:15:20Z 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
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 fRc = g_pfnDiInstallDriverW(NULL /* hWndParent */, pParms->pwszInfFile, dwInstallFlags, &fReboot);
1014 if (!fRc)
1015 {
1016 DWORD const dwErr = GetLastError();
1017
1018 /*
1019 * Work around an error code wich only appears on old(er) Windows Server editions (e.g. 2012 R2 or 2016)
1020 * where SetupAPI tells "unable to mark devices that match new inf", which ultimately results in
1021 * ERROR_LINE_NOT_FOUND. This probably is because of primitive drivers which don't have a PnP ID set in
1022 * the INF file.
1023 *
1024 * pnputil.exe also gives the same error in the SetupAPI log when handling the very same INF file.
1025 *
1026 * So skip this error and pretend everything is fine. */
1027 if (dwErr == ERROR_LINE_NOT_FOUND)
1028 fRc = true;
1029
1030 if (!fRc)
1031 rc = vboxWinDrvInstLogLastError(pCtx, "DiInstallDriverW() failed");
1032 }
1033
1034 if (fRc)
1035 rc = vboxWinDrvTryInfSection(pCtx,
1036 pParms->pwszInfFile, pParms->u.UnInstall.pwszSection,
1037 vboxWinDrvInstallTryInfSectionCallback);
1038 }
1039 else /* For Windows 2000 and below. */
1040 {
1041 if (pParms->u.UnInstall.pwszPnpId)
1042 {
1043 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_SILENT)
1044 {
1045 /* Using INSTALLFLAG_NONINTERACTIVE will trigger an invalid parameter error on Windows 2000. */
1046 if (uNtVer >= RTSYSTEM_MAKE_NT_VERSION(5, 1, 0))
1047 dwInstallFlags |= INSTALLFLAG_NONINTERACTIVE;
1048 else
1049 vboxWinDrvInstLogWarn(pCtx, "This version of Windows does not support silent installs");
1050 }
1051
1052 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_FORCE)
1053 dwInstallFlags |= INSTALLFLAG_FORCE;
1054
1055 vboxWinDrvInstLogVerbose(pCtx, 4, "Using g_pfnUpdateDriverForPlugAndPlayDevicesW(), pwszPnpId=%ls, pwszInfFile=%ls, dwInstallFlags=%#x",
1056 pParms->u.UnInstall.pwszPnpId, pParms->pwszInfFile, dwInstallFlags);
1057
1058 fRc = g_pfnUpdateDriverForPlugAndPlayDevicesW(NULL /* hWndParent */,
1059 pParms->u.UnInstall.pwszPnpId,
1060 pParms->pwszInfFile, dwInstallFlags, &fReboot);
1061 if (!fRc)
1062 {
1063 DWORD const dwErr = GetLastError();
1064 switch (dwErr)
1065 {
1066 case ERROR_NO_SUCH_DEVINST:
1067 {
1068 vboxWinDrvInstLogInfo(pCtx, "Device (\"%ls\") not found (yet), pre-installing driver ...",
1069 pParms->u.UnInstall.pwszPnpId);
1070 break;
1071 }
1072
1073 case ERROR_NO_DRIVER_SELECTED:
1074 {
1075 vboxWinDrvInstLogWarn(pCtx, "Not able to select a driver from the given INF, using given model");
1076 break;
1077 }
1078
1079 default:
1080 rc = vboxWinDrvInstLogLastError(pCtx, "Installation(UpdateDriverForPlugAndPlayDevicesW) failed");
1081 break;
1082 }
1083 }
1084 }
1085
1086 if (RT_FAILURE(rc))
1087 break;
1088
1089 /*
1090 * Pre-install driver.
1091 */
1092 RTUTF16 wszInfFileAbs[RTPATH_MAX] = { 0 };
1093 LPWSTR pwszInfFile = NULL;
1094 if ( GetFullPathNameW(pParms->pwszInfFile, RT_ELEMENTS(wszInfFileAbs), wszInfFileAbs, &pwszInfFile)
1095 && pwszInfFile)
1096 {
1097 RTUTF16 wszSrcPathAbs[RTPATH_MAX] = { 0 };
1098 rc = RTUtf16CopyEx(wszSrcPathAbs, RT_ELEMENTS(wszSrcPathAbs), wszInfFileAbs,
1099 RTUtf16Len(wszInfFileAbs) - RTUtf16Len(pwszInfFile));
1100 if (RT_SUCCESS(rc))
1101 {
1102 RTUTF16 wszDstPathAbs[RTPATH_MAX] = { 0 };
1103 fRc = g_pfnSetupCopyOEMInfW(wszInfFileAbs, wszSrcPathAbs, SPOST_PATH, 0,
1104 wszDstPathAbs, RT_ELEMENTS(wszDstPathAbs), NULL, NULL);
1105
1106 vboxWinDrvInstLogVerbose(pCtx, 1, " INF file: %ls", wszInfFileAbs);
1107 vboxWinDrvInstLogVerbose(pCtx, 1, "Source path: %ls", wszSrcPathAbs);
1108 vboxWinDrvInstLogVerbose(pCtx, 1, " Dest path: %ls", wszDstPathAbs);
1109
1110 if (fRc)
1111 vboxWinDrvInstLogInfo(pCtx, "Copying OEM INF successful");
1112 else
1113 rc = vboxWinDrvInstLogLastError(pCtx, "Installation(SetupCopyOEMInfW) failed");
1114 }
1115 }
1116 else
1117 rc = vboxWinDrvInstLogLastError(pCtx, "GetFullPathNameW() failed");
1118
1119 rc = vboxWinDrvTryInfSection(pCtx,
1120 pParms->pwszInfFile, pParms->u.UnInstall.pwszSection,
1121 vboxWinDrvInstallTryInfSectionCallback);
1122 }
1123
1124 if (RT_FAILURE(rc))
1125 break;
1126
1127 pCtx->fReboot = RT_BOOL(fReboot);
1128 break;
1129 }
1130
1131 case VBOXWINDRVINSTMODE_INSTALL_INFSECTION:
1132 {
1133 rc = vboxWinDrvInstallInfSection(pCtx, pParms);
1134 break;
1135 }
1136
1137 default:
1138 break;
1139 }
1140
1141 return rc;
1142}
1143
1144/**
1145 * Returns whether the given (in)installation parameters are valid or not.
1146 *
1147 * @returns \c true if valid, \c false if not.
1148 * @param pCtx Windows driver installer context.
1149 * @param pParms Windows driver (un)installation parameters to validate.
1150 */
1151static bool vboxWinDrvParmsAreValid(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
1152{
1153 if (pParms->u.UnInstall.pwszPnpId)
1154 {
1155 size_t const cchPnpId = RTUtf16Len(pParms->u.UnInstall.pwszPnpId);
1156 if ( !cchPnpId
1157 || cchPnpId > MAX_DEVICE_ID_LEN)
1158 {
1159 vboxWinDrvInstLogVerbose(pCtx, 1, "PnP ID not specified explicitly or invalid");
1160 return false;
1161 }
1162 }
1163
1164 return true;
1165}
1166
1167/**
1168 * Determines (un)installation parameters from a given set of parameters, logged.
1169 *
1170 * @returns VBox status code.
1171 * @retval VERR_INVALID_PARAMETER if no valid parameters could be determined.
1172 * @param pCtx Windows driver installer context.
1173 * @param pParms Windows driver installation parameters to determine for.
1174 * @param fForce Whether to overwrite already set parameters or not.
1175 *
1176 * @note Only can deal with the first model / PnP ID found for now.
1177 */
1178static int vboxWinDrvParmsDetermine(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms, bool fForce)
1179{
1180 int rc;
1181
1182 /* INF file given? */
1183 if (pParms->pwszInfFile)
1184 {
1185 HINF hInf;
1186 rc = VBoxWinDrvInfOpen(pParms->pwszInfFile, &hInf);
1187 if (RT_SUCCESS(rc))
1188 {
1189 /* Get the INF type first. */
1190 PRTUTF16 pwszMainSection;
1191 VBOXWINDRVINFTYPE enmType = VBoxWinDrvInfGetTypeEx(hInf, &pwszMainSection);
1192 if (enmType != VBOXWINDRVINFTYPE_INVALID)
1193 {
1194 vboxWinDrvInstLogVerbose(pCtx, 1, "INF type is: %s",
1195 enmType == VBOXWINDRVINFTYPE_NORMAL
1196 ? "Normal" : "Primitive");
1197 /*
1198 * Determine model.
1199 */
1200 if ( !pParms->u.UnInstall.pwszModel
1201 || fForce)
1202 {
1203 vboxWinDrvInstLogVerbose(pCtx, 1, "Determining model ...");
1204 if (fForce)
1205 {
1206 RTUtf16Free(pParms->u.UnInstall.pwszModel);
1207 pParms->u.UnInstall.pwszModel = NULL;
1208 }
1209 rc = VBoxWinDrvInfQueryFirstModel(hInf, pwszMainSection, &pParms->u.UnInstall.pwszModel);
1210 if (RT_SUCCESS(rc))
1211 {
1212 RTUtf16Free(pParms->u.UnInstall.pwszSection);
1213 pParms->u.UnInstall.pwszSection = NULL;
1214
1215 /* Now that we have determined the model, try if there is a section in the INF file for this model. */
1216 rc = VBoxWinDrvInfQueryInstallSection(hInf, pParms->u.UnInstall.pwszModel,
1217 &pParms->u.UnInstall.pwszSection);
1218 if (RT_FAILURE(rc))
1219 {
1220 switch (enmType)
1221 {
1222 case VBOXWINDRVINFTYPE_NORMAL:
1223 {
1224 vboxWinDrvInstLogError(pCtx, "No section to install found, can't continue");
1225 break;
1226 }
1227
1228 case VBOXWINDRVINFTYPE_PRIMITIVE:
1229 {
1230 /* If for the given model there is no install section, set the section to main section
1231 * we got when we determined the INF type.
1232 *
1233 * This will be mostly the case for primitive drivers. */
1234 if (rc == VERR_NOT_FOUND)
1235 {
1236 pParms->u.UnInstall.pwszSection = RTUtf16Dup(pwszMainSection);
1237 if (pParms->u.UnInstall.pwszSection)
1238 {
1239 rc = VINF_SUCCESS;
1240 }
1241 else
1242 rc = VERR_NO_MEMORY;
1243 }
1244 break;
1245 }
1246
1247 default:
1248 AssertFailedStmt(rc = VERR_NOT_IMPLEMENTED);
1249 break;
1250 }
1251 }
1252 }
1253 else
1254 {
1255 switch (rc)
1256 {
1257 case VERR_PLATFORM_ARCH_NOT_SUPPORTED:
1258 {
1259 vboxWinDrvInstLogError(pCtx, "Model found, but platform is not supported");
1260 break;
1261 }
1262
1263 case VERR_NOT_FOUND:
1264 {
1265 vboxWinDrvInstLogError(pCtx, "No model found to install found -- buggy driver?");
1266 break;
1267 }
1268
1269 default:
1270 break;
1271 }
1272 }
1273 }
1274
1275 /*
1276 * Determine PnP ID.
1277 *
1278 * Only available in non-primitive drivers.
1279 */
1280 if ( enmType == VBOXWINDRVINFTYPE_NORMAL
1281 && ( !pParms->u.UnInstall.pwszPnpId
1282 || fForce))
1283 {
1284 if (pParms->u.UnInstall.pwszModel)
1285 {
1286 vboxWinDrvInstLogVerbose(pCtx, 1, "Determining PnP ID ...");
1287 if (fForce)
1288 {
1289 RTUtf16Free(pParms->u.UnInstall.pwszPnpId);
1290 pParms->u.UnInstall.pwszPnpId = NULL;
1291 }
1292 /* ignore rc */ VBoxWinDrvInfQueryFirstPnPId(hInf,
1293 pParms->u.UnInstall.pwszModel, &pParms->u.UnInstall.pwszPnpId);
1294 }
1295 else
1296 vboxWinDrvInstLogVerbose(pCtx, 1, "No first model found/set, skipping determining PnP ID");
1297 }
1298
1299 RTUtf16Free(pwszMainSection);
1300 }
1301 else
1302 {
1303 vboxWinDrvInstLogError(pCtx, "INF file is invalid");
1304 rc = VERR_INVALID_PARAMETER;
1305 }
1306
1307 VBoxWinDrvInfClose(hInf);
1308 }
1309 }
1310 /* No INF file given but either the model or the PnP ID? */
1311 else if ( pParms->u.UnInstall.pwszModel
1312 || pParms->u.UnInstall.pwszPnpId)
1313 {
1314 /* Nothing to do for us here. */
1315 rc = VINF_SUCCESS;
1316 }
1317 else
1318 {
1319 vboxWinDrvInstLogError(pCtx, "Neither INF file nor model/PnP ID given; can't continue");
1320 rc = VERR_INVALID_PARAMETER;
1321 }
1322
1323 if (RT_SUCCESS(rc))
1324 {
1325 vboxWinDrvInstLogVerbose(pCtx, 1, "Determined parameters:");
1326 vboxWinDrvInstLogVerbose(pCtx, 1, "\tINF File: %ls",
1327 pParms->pwszInfFile ? pParms->pwszInfFile : L"<None>");
1328 vboxWinDrvInstLogVerbose(pCtx, 1, "\t Model: %ls",
1329 pParms->u.UnInstall.pwszModel ? pParms->u.UnInstall.pwszModel : L"<None>");
1330 vboxWinDrvInstLogVerbose(pCtx, 1, "\t PnP ID: %ls",
1331 pParms->u.UnInstall.pwszPnpId ? pParms->u.UnInstall.pwszPnpId : L"<None>");
1332 vboxWinDrvInstLogVerbose(pCtx, 1, "\t Section: %ls",
1333 pParms->u.UnInstall.pwszSection ? pParms->u.UnInstall.pwszSection : L"<None>");
1334 }
1335
1336 return rc;
1337}
1338
1339/**
1340 * Queries OEM INF files from the driver store.
1341 *
1342 * @returns VBox status code.
1343 * @param pCtx Windows driver installer context.
1344 * @param pParms Windows driver installation parameters to use.
1345 * @param ppList Where to return the list of found Windows driver store entries on success.
1346 * Needs to be destroyed with VBoxWinDrvStoreListFree().
1347 */
1348static int vboxWinDrvQueryFromDriverStore(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms,
1349 PVBOXWINDRVSTORELIST *ppList)
1350{
1351 int rc = vboxWinDrvParmsDetermine(pCtx, pParms, false /* fForce */);
1352 if (RT_SUCCESS(rc))
1353 {
1354 PVBOXWINDRVSTORELIST pList = NULL;
1355 char *pszNeedle = NULL;
1356
1357 if (pParms->u.UnInstall.pwszPnpId)
1358 {
1359 rc = RTUtf16ToUtf8(pParms->u.UnInstall.pwszPnpId, &pszNeedle);
1360 if (RT_SUCCESS(rc))
1361 rc = VBoxWinDrvStoreQueryByPnpId(pCtx->pStore, pszNeedle, &pList);
1362 }
1363 else if (pParms->u.UnInstall.pwszModel)
1364 {
1365 rc = RTUtf16ToUtf8(pParms->u.UnInstall.pwszModel, &pszNeedle);
1366 if (RT_SUCCESS(rc))
1367 rc = VBoxWinDrvStoreQueryByModelName(pCtx->pStore, pszNeedle, &pList);
1368 }
1369 else if (pParms->pwszInfFile)
1370 {
1371 rc = VERR_NOT_IMPLEMENTED;
1372 }
1373
1374 RTStrFree(pszNeedle);
1375 pszNeedle = NULL;
1376
1377 if (RT_SUCCESS(rc))
1378 {
1379 *ppList = pList;
1380 }
1381 else
1382 VBoxWinDrvStoreListFree(pList);
1383 }
1384
1385 return rc;
1386}
1387
1388/**
1389 * Callback implementation for invoking a section for uninstallation.
1390 *
1391 * @returns VBox status code.
1392 * @param hInf Handle to INF file.
1393 * @param pwszSection Section to invoke.
1394 * @param pvCtx User-supplied pointer. Usually PVBOXWINDRVINSTINTERNAL.
1395 */
1396DECLCALLBACK(int) vboxWinDrvUninstallTryInfSectionCallback(HINF hInf, PCRTUTF16 pwszSection, void *pvCtx)
1397{
1398 PVBOXWINDRVINSTINTERNAL pCtx = (PVBOXWINDRVINSTINTERNAL)pvCtx;
1399
1400 return vboxWinDrvUninstallInfSectionEx(pCtx, hInf, pwszSection);
1401}
1402
1403/**
1404 * Removes OEM INF files from the driver store.
1405 *
1406 * @returns VBox status code.
1407 * @param pCtx Windows driver installer context.
1408 * @param pParms Windows driver uninstallation parameters to use.
1409 * @param pList Driver store list with OEM INF entries to remove.
1410 */
1411static int vboxWinDrvUninstallFromDriverStore(PVBOXWINDRVINSTINTERNAL pCtx,
1412 PVBOXWINDRVINSTPARMS pParms, PVBOXWINDRVSTORELIST pList)
1413{
1414
1415 int rc = VINF_SUCCESS;
1416
1417 const char *pszDrvStorePath = VBoxWinDrvStoreBackendGetLocation(pCtx->pStore);
1418
1419 vboxWinDrvInstLogInfo(pCtx, "Uninstalling %zu matching entr%s", pList->cEntries, pList->cEntries == 1 ? "y" : "ies");
1420 PVBOXWINDRVSTOREENTRY pCur;
1421 RTListForEach(&pList->List, pCur, VBOXWINDRVSTOREENTRY, Node)
1422 {
1423 bool fRc = FALSE;
1424
1425 /*
1426 * Running the uninstalling section(s) first before removing the driver from the driver store below.
1427 */
1428 RTUTF16 wszInfPathAbs[RTPATH_MAX];
1429 ssize_t const cwchInfPathAbs = RTUtf16Printf(wszInfPathAbs, RT_ELEMENTS(wszInfPathAbs),
1430 "%s\\%ls", pszDrvStorePath, pCur->wszInfFile);
1431 AssertBreakStmt(cwchInfPathAbs > 0, rc = VERR_BUFFER_OVERFLOW);
1432
1433 vboxWinDrvInstLogInfo(pCtx, "Uninstalling %ls (%ls)", pCur->wszModel, wszInfPathAbs);
1434
1435 /* Only calltry calling the "DefaultUninstall" section here, as there aren't any other section(s)
1436 * to handle for uninstalling a driver via INF files. */
1437 /* rc ignored, keep going */ vboxWinDrvTryInfSection(pCtx, wszInfPathAbs, NULL /* DefaultUninstall */,
1438 vboxWinDrvUninstallTryInfSectionCallback);
1439
1440 /*
1441 * Remove the driver from the driver store.
1442 */
1443 if (g_pfnDiUninstallDriverW)
1444 {
1445 vboxWinDrvInstLogVerbose(pCtx, 1, "Using DiUninstallDriverW()");
1446 BOOL fReboot = FALSE;
1447 fRc = g_pfnDiUninstallDriverW(NULL /* hWndParent */, pCur->wszInfFile, 0 /* Flags */, &fReboot);
1448 if (fRc)
1449 pCtx->fReboot = RT_BOOL(fReboot);
1450 else
1451 {
1452 /* Not fatal, try next block. */
1453 DWORD const dwErr = GetLastError();
1454 vboxWinDrvInstLogVerbose(pCtx, 1, "DiUninstallDriverW() failed with %#x (%d)", dwErr, dwErr);
1455 }
1456 }
1457
1458 /* Not (yet) successful? Try harder using an older API. */
1459 if ( !fRc
1460 && g_pfnSetupUninstallOEMInfW)
1461 {
1462 vboxWinDrvInstLogVerbose(pCtx, 1, "Using SetupUninstallOEMInfW()");
1463
1464 DWORD dwUninstallFlags = 0;
1465 if (pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_FORCE)
1466 dwUninstallFlags |= SUOI_FORCEDELETE;
1467
1468 fRc = g_pfnSetupUninstallOEMInfW(pCur->wszInfFile, dwUninstallFlags, NULL /* pReserved */);
1469 }
1470
1471 int rc2 = VINF_SUCCESS;
1472
1473 if (fRc)
1474 vboxWinDrvInstLogInfo(pCtx, "Uninstalling OEM INF \"%ls\" successful", wszInfPathAbs);
1475 else
1476 {
1477 DWORD const dwErr = GetLastError();
1478 if (dwErr == ERROR_INF_IN_USE_BY_DEVICES)
1479 vboxWinDrvInstLogError(pCtx, "Unable to uninstall OEM INF \"%ls\": Driver still in use by device", wszInfPathAbs);
1480 else
1481 rc2 = vboxWinDrvInstLogLastError(pCtx, "Uninstalling OEM INF \"%ls\" failed", wszInfPathAbs);
1482 }
1483
1484 /* If anything failed above, try removing stuff ourselves as good as we can. */
1485 if (RT_FAILURE(rc2))
1486 /* rc ignored, keep going */ vboxWinDrvTryInfSection(pCtx, wszInfPathAbs, pCur->wszModel,
1487 vboxWinDrvUninstallTryInfSectionCallback);
1488
1489 if (RT_SUCCESS(rc)) /* Keep first error if already set. */
1490 rc = rc2;
1491
1492 /* Keep going. */
1493 }
1494
1495 return rc;
1496}
1497
1498/**
1499 * Performs the actual driver uninstallation.
1500 *
1501 * @returns VBox status code.
1502 * @param pCtx Windows driver installer context.
1503 * @param pParms Windows driver installation parameters to use.
1504 */
1505static int vboxWinDrvUninstallPerform(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
1506{
1507 int rc;
1508 switch (pParms->enmMode)
1509 {
1510 case VBOXWINDRVINSTMODE_UNINSTALL:
1511 {
1512 PVBOXWINDRVSTORELIST pList = NULL;
1513 rc = vboxWinDrvQueryFromDriverStore(pCtx, pParms, &pList);
1514 if (RT_SUCCESS(rc))
1515 {
1516 rc = vboxWinDrvUninstallFromDriverStore(pCtx, pParms, pList);
1517
1518 VBoxWinDrvStoreListFree(pList);
1519 pList = NULL;
1520 }
1521 break;
1522 }
1523
1524 case VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION:
1525 {
1526 rc = vboxWinDrvInstallInfSection(pCtx, pParms);
1527 break;
1528 }
1529
1530 default:
1531 rc = VINF_SUCCESS;
1532 break;
1533 }
1534
1535 return rc;
1536}
1537
1538/**
1539 * Main function to perform the actual driver [un]installation.
1540 *
1541 * @returns VBox status code.
1542 * @param pCtx Windows driver installer context.
1543 * @param pParms Windows driver installation parameters to use.
1544 */
1545static int vboxWinDrvInstMain(PVBOXWINDRVINSTINTERNAL pCtx, PVBOXWINDRVINSTPARMS pParms)
1546{
1547 /* Note: Other parameters might be optional, depending on the mode. */
1548 AssertReturn(!(pParms->fFlags & ~VBOX_WIN_DRIVERINSTALL_F_VALID_MASK), VERR_INVALID_PARAMETER);
1549
1550 bool const fInstall = pParms->enmMode == VBOXWINDRVINSTMODE_INSTALL
1551 || pParms->enmMode == VBOXWINDRVINSTMODE_INSTALL_INFSECTION;
1552
1553 const char * const pszLogAction = fInstall ? "Installing" : "Uninstalling";
1554 if (pParms->pwszInfFile)
1555 vboxWinDrvInstLogInfo(pCtx, "%s driver \"%ls\" ... ", pszLogAction, pParms->pwszInfFile);
1556 else if (pParms->u.UnInstall.pwszModel)
1557 vboxWinDrvInstLogInfo(pCtx, "%s driver model \"%ls\" ... ", pszLogAction, pParms->u.UnInstall.pwszModel);
1558 else if (pParms->u.UnInstall.pwszPnpId)
1559 vboxWinDrvInstLogInfo(pCtx, "%s PnP ID \"%ls\" ... ", pszLogAction, pParms->u.UnInstall.pwszPnpId);
1560
1561 if ( pParms->fFlags & VBOX_WIN_DRIVERINSTALL_F_SILENT
1562 && g_pfnSetupSetNonInteractiveMode)
1563 {
1564 vboxWinDrvInstLogInfo(pCtx, "Setting non-interactive mode ...");
1565 g_pfnSetupSetNonInteractiveMode(TRUE /* fEnable */);
1566 }
1567
1568 if (!vboxWinDrvParmsAreValid(pCtx, pParms))
1569 {
1570 vboxWinDrvInstLogError(pCtx, "%s parameters are invalid, can't continue", fInstall ? "Installation" : "Uninstallation");
1571 return VERR_INVALID_PARAMETER;
1572 }
1573
1574 int rc;
1575 if (fInstall)
1576 rc = vboxWinDrvInstallPerform(pCtx, pParms);
1577 else
1578 rc = vboxWinDrvUninstallPerform(pCtx, pParms);
1579
1580 if (RT_SUCCESS(rc))
1581 {
1582 vboxWinDrvInstLogInfo(pCtx, "Driver was %sinstalled successfully", fInstall ? "" : "un");
1583 if (pCtx->fReboot)
1584 {
1585 vboxWinDrvInstLogRebootNeeded(pCtx, "A reboot is needed in order to complete the driver %sinstallation.",
1586 fInstall ? "" : "un");
1587 rc = VINF_REBOOT_NEEDED;
1588 }
1589 }
1590
1591 /* Note: Call vboxWinDrvInstLogEx() to not increase the error/warn count here. */
1592 if (pCtx->cErrors)
1593 vboxWinDrvInstLogEx(pCtx, VBOXWINDRIVERLOGTYPE_ERROR, "%sstalling driver(s) failed with %u errors, %u warnings (rc=%Rrc)",
1594 fInstall ? "In" : "Unin", pCtx->cErrors, pCtx->cWarnings, rc);
1595 else if (pCtx->cWarnings)
1596 vboxWinDrvInstLogEx(pCtx, VBOXWINDRIVERLOGTYPE_WARN, "%sstalling driver(s) succeeded with %u warnings",
1597 fInstall ? "In" : "Unin", pCtx->cWarnings);
1598
1599 return rc;
1600}
1601
1602/**
1603 * Creates a Windows driver installer instance, extended version.
1604 *
1605 * @returns VBox status code.
1606 * @param phDrvInst where to return the created driver installer handle on success.
1607 * @param uVerbosity Sets the initial verbosity level.
1608 * @param pfnLog Log callback function to set.
1609 * @param pvUser User-supplied pointer to set. Optional and might be NULL.
1610 */
1611int VBoxWinDrvInstCreateEx(PVBOXWINDRVINST phDrvInst, unsigned uVerbosity, PFNVBOXWINDRIVERLOGMSG pfnLog, void *pvUser)
1612{
1613 int rc;
1614
1615 PVBOXWINDRVINSTINTERNAL pCtx = (PVBOXWINDRVINSTINTERNAL)RTMemAllocZ(sizeof(VBOXWINDRVINSTINTERNAL));
1616 if (pCtx)
1617 {
1618 pCtx->u32Magic = VBOXWINDRVINST_MAGIC;
1619 pCtx->uVerbosity = uVerbosity;
1620 pCtx->pfnLog = pfnLog;
1621 pCtx->pvUser = pvUser;
1622
1623 uint64_t const uNtVer = RTSystemGetNtVersion();
1624
1625 vboxWinDrvInstLogInfo(pCtx, VBOX_PRODUCT " Version " VBOX_VERSION_STRING " - r%s", RTBldCfgRevisionStr());
1626 vboxWinDrvInstLogInfo(pCtx, "Detected Windows version %d.%d.%d (%s)", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVer),
1627 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVer),
1628 RTSYSTEM_NT_VERSION_GET_BUILD(uNtVer),
1629 RTBldCfgTargetArch());
1630
1631 rc = RTOnce(&g_vboxWinDrvInstResolveOnce, vboxWinDrvInstResolveOnce, pCtx);
1632 if (RT_SUCCESS(rc))
1633 {
1634 rc = VBoxWinDrvStoreCreate(&pCtx->pStore);
1635 if (RT_SUCCESS(rc))
1636 {
1637 *phDrvInst = (VBOXWINDRVINST)pCtx;
1638 return VINF_SUCCESS;
1639 }
1640 else
1641 vboxWinDrvInstLogError(pCtx, "Creating driver store failed with %Rrc", rc);
1642 }
1643 }
1644 else
1645 rc = VERR_NO_MEMORY;
1646
1647 VBoxWinDrvStoreDestroy(pCtx->pStore);
1648 VBoxWinDrvInstDestroy(pCtx);
1649 return rc;
1650}
1651
1652/**
1653 * Creates a Windows driver installer instance.
1654 *
1655 * @returns VBox status code.
1656 * @param phDrvInst where to return the created driver installer handle on success.
1657 */
1658int VBoxWinDrvInstCreate(PVBOXWINDRVINST phDrvInst)
1659{
1660 return VBoxWinDrvInstCreateEx(phDrvInst, 0 /* uVerbosity */, NULL /* pfnLog */, NULL /* pvUser */);
1661}
1662
1663/**
1664 * Destroys a Windows driver installer instance.
1665 *
1666 * @returns VBox status code.
1667 * @param hDrvInst Windows driver installer handle to destroy.
1668 * The handle will be invalid after calling this function.
1669 */
1670int VBoxWinDrvInstDestroy(VBOXWINDRVINST hDrvInst)
1671{
1672 if (hDrvInst == NIL_VBOXWINDRVINST)
1673 return VINF_SUCCESS;
1674
1675 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1676 VBOXWINDRVINST_VALID_RETURN(pCtx);
1677
1678 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1679
1680 VBoxWinDrvStoreDestroy(pCtx->pStore);
1681 pCtx->pStore = NULL;
1682
1683 RTMemFree(pCtx);
1684
1685 return VINF_SUCCESS;
1686}
1687
1688/**
1689 * Returns the number of (logged) warnings so far.
1690 *
1691 * @returns Number of (logged) warnings so far.
1692 * @param hDrvInst Windows driver installer handle.
1693 */
1694unsigned VBoxWinDrvInstGetWarnings(VBOXWINDRVINST hDrvInst)
1695{
1696 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1697 VBOXWINDRVINST_VALID_RETURN_RC(pCtx, UINT8_MAX);
1698
1699 return pCtx->cWarnings;
1700}
1701
1702/**
1703 * Returns the number of (logged) errors so far.
1704 *
1705 * @returns Number of (logged) errors so far..
1706 * @param hDrvInst Windows driver installer handle.
1707 */
1708unsigned VBoxWinDrvInstGetErrors(VBOXWINDRVINST hDrvInst)
1709{
1710 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1711 VBOXWINDRVINST_VALID_RETURN_RC(pCtx, UINT8_MAX);
1712
1713 return pCtx->cErrors;
1714}
1715
1716/**
1717 * Sets the verbosity of a Windows driver installer instance.
1718 *
1719 * @returns The old verbosity level.
1720 * @param hDrvInst Windows driver installer handle to set verbosity for.
1721 * @param uVerbosity Verbosity level to set.
1722 */
1723unsigned VBoxWinDrvInstSetVerbosity(VBOXWINDRVINST hDrvInst, unsigned uVerbosity)
1724{
1725 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1726 VBOXWINDRVINST_VALID_RETURN_RC(pCtx, UINT8_MAX);
1727
1728 unsigned const uOldVerbosity = hDrvInst->uVerbosity;
1729 hDrvInst->uVerbosity = uVerbosity;
1730 return uOldVerbosity;
1731}
1732
1733/**
1734 * Sets the log callback of a Windows driver installer instance.
1735 *
1736 * @returns VBox status code.
1737 * @param hDrvInst Windows driver installer handle to set log callback for.
1738 * @param pfnLog Log callback function to set.
1739 * @param pvUser User-supplied pointer to set. Optional and might be NULL.
1740 */
1741void VBoxWinDrvInstSetLogCallback(VBOXWINDRVINST hDrvInst, PFNVBOXWINDRIVERLOGMSG pfnLog, void *pvUser)
1742{
1743 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1744 VBOXWINDRVINST_VALID_RETURN_VOID(pCtx);
1745
1746 pCtx->pfnLog = pfnLog;
1747 pCtx->pvUser = pvUser;
1748}
1749
1750/**
1751 * Installs a driver, extended version.
1752 *
1753 * @returns VBox status code.
1754 * @param hDrvInst Windows driver installer handle to use.
1755 * @param pszInfFile INF file to use.
1756 * @param pszModel Model name to use. Optional and can be NULL.
1757 * @param pszPnpId PnP ID to use. NT-style wildcards supported. Optional and can be NULL.
1758 * @param fFlags Installation flags (of type VBOX_WIN_DRIVERINSTALL_F_XXX) to use.
1759 */
1760int VBoxWinDrvInstInstallEx(VBOXWINDRVINST hDrvInst,
1761 const char *pszInfFile, const char *pszModel, const char *pszPnpId, uint32_t fFlags)
1762{
1763 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1764 VBOXWINDRVINST_VALID_RETURN(pCtx);
1765
1766 AssertPtrReturn(pszInfFile, VERR_INVALID_PARAMETER);
1767
1768 vboxWinDrvInstParmsInit(&pCtx->Parms);
1769
1770 /* Resolve the INF file's absolute path, as as Setup API functions tend to need this. */
1771 char szInfPathAbs[RTPATH_MAX];
1772 int rc = RTPathReal(pszInfFile, szInfPathAbs, sizeof(szInfPathAbs));
1773 if (RT_SUCCESS(rc))
1774 rc = RTStrToUtf16(szInfPathAbs, &pCtx->Parms.pwszInfFile);
1775
1776 if (RT_FAILURE(rc))
1777 vboxWinDrvInstLogError(pCtx, "Failed to build path for INF file \"%s\", rc=%Rrc", pszInfFile, rc);
1778
1779 if (RT_SUCCESS(rc) && pszModel) /* Model is optional. */
1780 rc = RTStrToUtf16(pszModel, &pCtx->Parms.u.UnInstall.pwszModel);
1781 if (RT_SUCCESS(rc) && pszPnpId) /* Ditto. */
1782 rc = RTStrToUtf16(pszPnpId, &pCtx->Parms.u.UnInstall.pwszPnpId);
1783
1784 if (RT_SUCCESS(rc))
1785 {
1786 pCtx->Parms.enmMode = VBOXWINDRVINSTMODE_INSTALL;
1787 pCtx->Parms.fFlags = fFlags;
1788
1789 rc = vboxWinDrvInstMain(pCtx, &pCtx->Parms);
1790 }
1791
1792 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1793
1794 if (RT_FAILURE(rc))
1795 vboxWinDrvInstLogError(pCtx, "Driver installation failed with %Rrc", rc);
1796
1797 return rc;
1798}
1799
1800/**
1801 * Installs a driver.
1802 *
1803 * @returns VBox status code.
1804 * @param hDrvInst Windows driver installer handle to use.
1805 * @param pszInfFile INF file to use.
1806 * @param fFlags Installation flags (of type VBOX_WIN_DRIVERINSTALL_F_XXX) to use.
1807 *
1808 * @note This function tries determining the model / PnP ID from the given INF file.
1809 * To control the behavior exactly, use VBoxWinDrvInstInstallEx().
1810 */
1811int VBoxWinDrvInstInstall(VBOXWINDRVINST hDrvInst, const char *pszInfFile, uint32_t fFlags)
1812{
1813 return VBoxWinDrvInstInstallEx(hDrvInst, pszInfFile, NULL /* pszModel */, NULL /* pszPnpId */, fFlags);
1814}
1815
1816/**
1817 * Uninstalls a driver.
1818 *
1819 * @returns VBox status code.
1820 * @param hDrvInst Windows driver installer handle to use.
1821 * @param pszInfFile INF file within driver store to uninstall.
1822 * Optional and can be NULL.
1823 * @param pszModel Model to uninstall (e.g. "VBoxUSB.AMD64"). NT-style wildcards supported.
1824 * Optional and can be NULL.
1825 * @param pszPnpId PnP ID to use (e.g. "USB\\VID_80EE&PID_CAFE"). NT-style wildcards supported.
1826 * Optional and can be NULL.
1827 * @param fFlags Installation flags (of type VBOX_WIN_DRIVERINSTALL_F_XXX) to use.
1828 */
1829int VBoxWinDrvInstUninstall(VBOXWINDRVINST hDrvInst, const char *pszInfFile, const char *pszModel, const char *pszPnpId,
1830 uint32_t fFlags)
1831{
1832 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1833 VBOXWINDRVINST_VALID_RETURN(pCtx);
1834
1835 int rc = VINF_SUCCESS;
1836
1837 vboxWinDrvInstParmsInit(&pCtx->Parms);
1838
1839 /* If given, get the sole INF file name (without path), to make it searchable within the driver store.
1840 * Note: This only will work with "oemXXX.inf" files for the (legcy) driver store. */
1841 if (pszInfFile && *pszInfFile)
1842 {
1843 char *pszInfFileName = RTPathFilename(pszInfFile);
1844 if (pszInfFileName)
1845 rc = RTStrToUtf16(pszInfFileName, &pCtx->Parms.pwszInfFile);
1846 else
1847 rc = VERR_FILE_NOT_FOUND;
1848 }
1849
1850 if (RT_SUCCESS(rc) && pszModel && *pszModel)
1851 rc = RTStrToUtf16(pszModel, &pCtx->Parms.u.UnInstall.pwszModel);
1852 if (RT_SUCCESS(rc) && pszPnpId && *pszPnpId)
1853 rc = RTStrToUtf16(pszPnpId, &pCtx->Parms.u.UnInstall.pwszPnpId);
1854
1855 pCtx->Parms.enmMode = VBOXWINDRVINSTMODE_UNINSTALL;
1856 pCtx->Parms.fFlags = fFlags;
1857
1858 if (RT_SUCCESS(rc))
1859 rc = vboxWinDrvInstMain(pCtx, &pCtx->Parms);
1860
1861 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1862
1863 if (RT_FAILURE(rc))
1864 vboxWinDrvInstLogError(pCtx, "Driver uninstallation failed with %Rrc", rc);
1865
1866 return rc;
1867}
1868
1869/**
1870 * Worker function for executing a section of an INF file.
1871 *
1872 * @returns VBox status code.
1873 * @param hDrvInst Windows driver installer handle to use.
1874 * @param fInstall Whether to execute the section to install or uninstall.
1875 * @param pszInfFile INF file to use.
1876 * @param pszSection Section within the INF file to execute.
1877 * @param fFlags Installation flags to use.
1878 */
1879int VBoxWinDrvInstExecuteInfWorker(VBOXWINDRVINST hDrvInst,
1880 bool fInstall, const char *pszInfFile, const char *pszSection, uint32_t fFlags)
1881{
1882 PVBOXWINDRVINSTINTERNAL pCtx = hDrvInst;
1883 VBOXWINDRVINST_VALID_RETURN(pCtx);
1884
1885 AssertPtrReturn(pszInfFile, VERR_INVALID_POINTER);
1886
1887 vboxWinDrvInstParmsInit(&pCtx->Parms);
1888
1889 int rc = RTStrToUtf16(pszInfFile, &pCtx->Parms.pwszInfFile);
1890 if (RT_SUCCESS(rc) && pszSection) /* pszSection is optional. */
1891 rc = RTStrToUtf16(pszSection, &pCtx->Parms.u.ExecuteInf.pwszSection);
1892
1893 pCtx->Parms.enmMode = fInstall ? VBOXWINDRVINSTMODE_INSTALL_INFSECTION : VBOXWINDRVINSTMODE_UNINSTALL_INFSECTION;
1894 pCtx->Parms.fFlags = fFlags;
1895
1896 rc = vboxWinDrvInstMain(pCtx, &pCtx->Parms);
1897
1898 vboxWinDrvInstParmsDestroy(&pCtx->Parms);
1899
1900 return rc;
1901}
1902
1903/**
1904 * Executes a section of an INF file for installation.
1905 *
1906 * @returns VBox status code.
1907 * @param hDrvInst Windows driver installer handle to use.
1908 * @param pszInfFile INF file to use.
1909 * @param pszSection Section within the INF file to execute.
1910 * @param fFlags Installation flags to use.
1911 */
1912int VBoxWinDrvInstInstallExecuteInf(VBOXWINDRVINST hDrvInst, const char *pszInfFile, const char *pszSection, uint32_t fFlags)
1913{
1914 return VBoxWinDrvInstExecuteInfWorker(hDrvInst, true /* fInstall */, pszInfFile, pszSection, fFlags);
1915}
1916
1917/**
1918 * Executes a section of an INF file for uninstallation.
1919 *
1920 * @returns VBox status code.
1921 * @param hDrvInst Windows driver installer handle to use.
1922 * @param pszInfFile INF file to use.
1923 * @param pszSection Section within the INF file to execute.
1924 * @param fFlags Installation flags to use.
1925 */
1926int VBoxWinDrvInstUninstallExecuteInf(VBOXWINDRVINST hDrvInst, const char *pszInfFile, const char *pszSection, uint32_t fFlags)
1927{
1928 return VBoxWinDrvInstExecuteInfWorker(hDrvInst, false /* fInstall */, pszInfFile, pszSection, fFlags);
1929}
1930
Note: See TracBrowser for help on using the repository browser.

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