VirtualBox

source: vbox/trunk/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp@ 108453

最後變更 在這個檔案從108453是 108302,由 vboxsync 提交於 3 週 前

Host installer/win: Run our custom driver installation code in deferred custom actions rather in immediate ones, as this otherwise can make trouble on Windows 11 and/or 10 if the UAC prompt is configured differently. This makes use of the code committed in r167628. bugref:10762

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 108.0 KB
 
1/* $Id: VBoxInstallHelper.cpp 108302 2025-02-19 16:01:47Z vboxsync $ */
2/** @file
3 * VBoxInstallHelper - Various helper routines for Windows host installer.
4 */
5
6/*
7 * Copyright (C) 2008-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.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#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP)
33# include "VBox/VBoxNetCfg-win.h"
34# include "VBox/VBoxDrvCfg-win.h"
35#endif
36
37#define _WIN32_DCOM
38#include <iprt/win/windows.h>
39
40#include <aclapi.h>
41#include <msi.h>
42#include <msiquery.h>
43
44#include <shellapi.h>
45#define INITGUID
46#include <guiddef.h>
47#include <cfgmgr32.h>
48#include <devguid.h>
49#include <sddl.h> /* For ConvertSidToStringSidW. */
50
51#include <iprt/win/objbase.h>
52#include <iprt/win/setupapi.h>
53#include <iprt/win/shlobj.h>
54
55#include <VBox/version.h>
56
57#include <iprt/assert.h>
58#include <iprt/alloca.h>
59#include <iprt/dir.h>
60#include <iprt/err.h>
61#include <iprt/file.h>
62#include <iprt/initterm.h>
63#include <iprt/mem.h>
64#include <iprt/path.h> /* RTPATH_MAX, RTPATH_IS_SLASH */
65#include <iprt/string.h> /* RT_ZERO */
66#include <iprt/stream.h>
67#include <iprt/system.h>
68#include <iprt/thread.h>
69#include <iprt/utf16.h>
70
71#include <VBox/GuestHost/VBoxWinDrvInst.h>
72
73#include "VBoxCommon.h"
74#ifndef VBOX_OSE
75# include "internal/VBoxSerial.h"
76#endif
77
78
79/*********************************************************************************************************************************
80* Defined Constants And Macros *
81*********************************************************************************************************************************/
82#ifdef DEBUG
83# define NonStandardAssert(_expr) Assert(_expr)
84#else
85# define NonStandardAssert(_expr) do{ }while(0)
86#endif
87
88#define MY_WTEXT_HLP(a_str) L##a_str
89#define MY_WTEXT(a_str) MY_WTEXT_HLP(a_str)
90
91
92/*********************************************************************************************************************************
93* Internal structures *
94*********************************************************************************************************************************/
95/**
96 * Structure for keeping a target's directory security context.
97 */
98typedef struct TGTDIRSECCTX
99{
100 /** Initialized status. */
101 bool fInitialized;
102 /** Handle of the target's parent directory.
103 *
104 * Kept open while the context is around and initialized. */
105 RTDIR hParentDir;
106 /** Absolute (resolved) path of the target directory. */
107 char szTargetDirAbs[RTPATH_MAX];
108 /** Access mask which is forbidden for an ACE of type ACCESS_ALLOWED_ACE_TYPE. */
109 uint32_t fAccessMaskForbidden;
110 /** Array of well-known SIDs which are forbidden. */
111 PSID *paWellKnownSidsForbidden;
112 /** Number of entries in \a paWellKnownSidsForbidden. */
113 size_t cWellKnownSidsForbidden;
114} TGTDIRSECCTX;
115/** Pointer to a target's directory security context. */
116typedef TGTDIRSECCTX *PTGTDIRSECCTX;
117
118
119/*********************************************************************************************************************************
120* Prototypes *
121*********************************************************************************************************************************/
122static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx);
123
124
125/*********************************************************************************************************************************
126* Globals *
127*********************************************************************************************************************************/
128static uint32_t g_cRef = 0;
129/** Our target directory security context.
130 *
131 * Has to be global in order to keep it around as long as the DLL is being loaded. */
132static TGTDIRSECCTX g_TargetDirSecCtx = { 0 };
133
134
135/**
136 * DLL entry point.
137 */
138BOOL WINAPI DllMain(HANDLE hInst, ULONG uReason, LPVOID pReserved)
139{
140 RT_NOREF(hInst, pReserved);
141
142#ifdef DEBUG
143 WCHAR wszMsg[128];
144 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "DllMain: hInst=%#x, uReason=%u (PID %u), g_cRef=%RU32\n",
145 hInst, uReason, GetCurrentProcessId(), g_cRef);
146 OutputDebugStringW(wszMsg);
147#endif
148
149 switch (uReason)
150 {
151 case DLL_PROCESS_ATTACH:
152 {
153 int rc = RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE);
154 if (RT_FAILURE(rc))
155 return FALSE;
156
157 g_cRef++;
158#if 0
159 /*
160 * This is a trick for allowing the debugger to be attached, don't know if
161 * there is an official way to do that, but this is a pretty efficient.
162 *
163 * Monitor the debug output in DbgView and be ready to start windbg when
164 * the message below appear. This will happen 3-4 times during install,
165 * and 2-3 times during uninstall.
166 *
167 */
168 RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId());
169 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
170 {
171 OutputDebugStringW(wszMsg);
172 Sleep(1001);
173 }
174 Sleep(1002);
175 __debugbreak();
176#endif
177 break;
178 }
179
180 case DLL_PROCESS_DETACH:
181 {
182 /** @todo RTR3Term(); */
183
184 g_cRef--;
185 break;
186 }
187
188 default:
189 break;
190 }
191
192 return TRUE;
193}
194
195/**
196 * Format a log message and print it to whatever is there (i.e. to the MSI log).
197 *
198 * UTF-16 strings are formatted using '%ls' (lowercase).
199 * ANSI strings are formatted using '%s' (uppercase).
200 *
201 * @returns VBox status code.
202 * @param hInstall MSI installer handle. Optional and can be NULL.
203 * @param pszFmt Format string.
204 * @param ... Variable arguments for format string.
205 */
206static int logStringF(MSIHANDLE hInstall, const char *pszFmt, ...)
207{
208 RTUTF16 wszVa[RTPATH_MAX + 256];
209 va_list va;
210 va_start(va, pszFmt);
211 ssize_t cwc = RTUtf16PrintfV(wszVa, RT_ELEMENTS(wszVa), pszFmt, va);
212 va_end(va);
213
214 RTUTF16 wszMsg[RTPATH_MAX + 256];
215 cwc = RTUtf16Printf(wszMsg, sizeof(wszMsg), "VBoxInstallHelper: %ls", wszVa);
216 if (cwc <= 0)
217 return VERR_BUFFER_OVERFLOW;
218
219#ifdef DEBUG
220 OutputDebugStringW(wszMsg);
221#endif
222#ifdef TESTCASE
223 RTPrintf("%ls\n", wszMsg);
224#endif
225 PMSIHANDLE hMSI = MsiCreateRecord(2 /* cParms */);
226 if (hMSI)
227 {
228 MsiRecordSetStringW(hMSI, 0, wszMsg);
229 MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hMSI);
230 MsiCloseHandle(hMSI);
231 }
232
233 return cwc < RT_ELEMENTS(wszVa) ? VINF_SUCCESS : VERR_BUFFER_OVERFLOW;
234}
235
236UINT __stdcall IsSerialCheckNeeded(MSIHANDLE hModule)
237{
238#ifndef VBOX_OSE
239 /*BOOL fRet =*/ serialCheckNeeded(hModule);
240#else
241 RT_NOREF(hModule);
242#endif
243 return ERROR_SUCCESS;
244}
245
246UINT __stdcall CheckSerial(MSIHANDLE hModule)
247{
248#ifndef VBOX_OSE
249 /*BOOL bRet =*/ serialIsValid(hModule);
250#else
251 RT_NOREF(hModule);
252#endif
253 return ERROR_SUCCESS;
254}
255
256/**
257 * Initializes a target security context.
258 *
259 * @returns VBox status code.
260 * @param pCtx Target directory security context to initialize.
261 * @param hModule Windows installer module handle.
262 * @param pszPath Target directory path to use.
263 */
264static int initTargetDirSecurityCtx(PTGTDIRSECCTX pCtx, MSIHANDLE hModule, const char *pszPath)
265{
266 if (pCtx->fInitialized)
267 return VINF_SUCCESS;
268
269#ifdef DEBUG
270 logStringF(hModule, "initTargetDirSecurityCtx: pszPath=%s\n", pszPath);
271#endif
272
273 char szPathTemp[RTPATH_MAX];
274 int vrc = RTStrCopy(szPathTemp, sizeof(szPathTemp), pszPath);
275 if (RT_FAILURE(vrc))
276 return vrc;
277
278 /* Try to find a parent path which exists. */
279 char szPathParentAbs[RTPATH_MAX] = { 0 };
280 for (int i = 0; i < 256; i++) /* Failsafe counter. */
281 {
282 RTPathStripTrailingSlash(szPathTemp);
283 RTPathStripFilename(szPathTemp);
284 vrc = RTPathReal(szPathTemp, szPathParentAbs, sizeof(szPathParentAbs));
285 if (RT_SUCCESS(vrc))
286 break;
287 }
288
289 if (RT_FAILURE(vrc))
290 {
291 logStringF(hModule, "initTargetDirSecurityCtx: No existing / valid parent directory found (%Rrc), giving up\n", vrc);
292 return vrc;
293 }
294
295 RTDIR hParentDir;
296 vrc = RTDirOpen(&hParentDir, szPathParentAbs);
297 if (RT_FAILURE(vrc))
298 {
299 logStringF(hModule, "initTargetDirSecurityCtx: Locking parent directory '%s' failed with %Rrc\n", szPathParentAbs, vrc);
300 return vrc;
301 }
302
303#ifdef DEBUG
304 logStringF(hModule, "initTargetDirSecurityCtx: Locked parent directory '%s'\n", szPathParentAbs);
305#endif
306
307 char szPathTargetAbs[RTPATH_MAX];
308 vrc = RTPathReal(pszPath, szPathTargetAbs, sizeof(szPathTargetAbs));
309 if (RT_FAILURE(vrc))
310 vrc = RTStrCopy(szPathTargetAbs, sizeof(szPathTargetAbs), pszPath);
311 if (RT_FAILURE(vrc))
312 {
313 logStringF(hModule, "initTargetDirSecurityCtx: Failed to resolve absolute target path (%Rrc)\n", vrc);
314 return vrc;
315 }
316
317#ifdef DEBUG
318 logStringF(hModule, "initTargetDirSecurityCtx: szPathTargetAbs=%s, szPathParentAbs=%s\n", szPathTargetAbs, szPathParentAbs);
319#endif
320
321 /* Target directory validation. */
322 if ( !RTStrCmp(szPathTargetAbs, szPathParentAbs) /* Don't allow installation into root directories. */
323 || RTStrStr(szPathTargetAbs, ".."))
324 {
325 logStringF(hModule, "initTargetDirSecurityCtx: Directory '%s' invalid", szPathTargetAbs);
326 vrc = VERR_INVALID_NAME;
327 }
328
329 if (RT_SUCCESS(vrc))
330 {
331 RTFSOBJINFO fsObjInfo;
332 vrc = RTPathQueryInfo(szPathParentAbs, &fsObjInfo, RTFSOBJATTRADD_NOTHING);
333 if (RT_SUCCESS(vrc))
334 {
335 if (RTFS_IS_DIRECTORY(fsObjInfo.Attr.fMode)) /* No symlinks or other fun stuff. */
336 {
337 static WELL_KNOWN_SID_TYPE aForbiddenWellKnownSids[] =
338 {
339 WinNullSid,
340 WinWorldSid,
341 WinAuthenticatedUserSid,
342 WinBuiltinUsersSid,
343 WinBuiltinGuestsSid,
344 WinBuiltinPowerUsersSid
345 };
346
347 pCtx->paWellKnownSidsForbidden = (PSID *)RTMemAlloc(sizeof(PSID) * RT_ELEMENTS(aForbiddenWellKnownSids));
348 AssertPtrReturn(pCtx->paWellKnownSidsForbidden, VERR_NO_MEMORY);
349
350 size_t i = 0;
351 for(; i < RT_ELEMENTS(aForbiddenWellKnownSids); i++)
352 {
353 pCtx->paWellKnownSidsForbidden[i] = RTMemAlloc(SECURITY_MAX_SID_SIZE);
354 AssertPtrBreakStmt(pCtx->paWellKnownSidsForbidden, vrc = VERR_NO_MEMORY);
355 DWORD cbSid = SECURITY_MAX_SID_SIZE;
356 if (!CreateWellKnownSid(aForbiddenWellKnownSids[i], NULL, pCtx->paWellKnownSidsForbidden[i], &cbSid))
357 {
358 vrc = RTErrConvertFromWin32(GetLastError());
359 logStringF(hModule, "initTargetDirSecurityCtx: Creating SID (index %zu) failed with %Rrc\n", i, vrc);
360 break;
361 }
362 }
363
364 if (RT_SUCCESS(vrc))
365 {
366 vrc = RTStrCopy(pCtx->szTargetDirAbs, sizeof(pCtx->szTargetDirAbs), szPathTargetAbs);
367 if (RT_SUCCESS(vrc))
368 {
369 pCtx->fInitialized = true;
370 pCtx->hParentDir = hParentDir;
371 pCtx->cWellKnownSidsForbidden = i;
372 pCtx->fAccessMaskForbidden = FILE_WRITE_DATA
373 | FILE_APPEND_DATA
374 | FILE_WRITE_ATTRIBUTES
375 | FILE_WRITE_EA;
376
377 RTFILE fh;
378 RTFileOpen(&fh, "c:\\temp\\targetdir.ctx", RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE | RTFILE_O_WRITE);
379 RTFileClose(fh);
380
381 return VINF_SUCCESS;
382 }
383 }
384 }
385 else
386 vrc = VERR_INVALID_NAME;
387 }
388 }
389
390 RTDirClose(hParentDir);
391
392 while (pCtx->cWellKnownSidsForbidden--)
393 {
394 RTMemFree(pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden]);
395 pCtx->paWellKnownSidsForbidden[pCtx->cWellKnownSidsForbidden] = NULL;
396 }
397
398 logStringF(hModule, "initTargetDirSecurityCtx: Initialization failed failed with %Rrc\n", vrc);
399 return vrc;
400}
401
402/**
403 * Destroys a target security context.
404 *
405 * @returns VBox status code.
406 * @param pCtx Target directory security context to destroy.
407 */
408static void destroyTargetDirSecurityCtx(PTGTDIRSECCTX pCtx)
409{
410 if ( !pCtx
411 || !pCtx->fInitialized)
412 return;
413
414 if (pCtx->hParentDir != NIL_RTDIR)
415 {
416 RTDirClose(pCtx->hParentDir);
417 pCtx->hParentDir = NIL_RTDIR;
418 }
419 RT_ZERO(pCtx->szTargetDirAbs);
420
421 for (size_t i = 0; i < pCtx->cWellKnownSidsForbidden; i++)
422 RTMemFree(pCtx->paWellKnownSidsForbidden[i]);
423 pCtx->cWellKnownSidsForbidden = 0;
424
425 RTMemFree(pCtx->paWellKnownSidsForbidden);
426 pCtx->paWellKnownSidsForbidden = NULL;
427
428 RTFileDelete("c:\\temp\\targetdir.ctx");
429
430 logStringF(NULL, "destroyTargetDirSecurityCtx\n");
431}
432
433#ifdef DEBUG
434/**
435 * Returns a stingified version of an ACE type.
436 *
437 * @returns Stingified version of an ACE type.
438 * @param uType ACE type.
439 */
440inline const char *dbgAceTypeToString(uint8_t uType)
441{
442 switch (uType)
443 {
444 RT_CASE_RET_STR(ACCESS_ALLOWED_ACE_TYPE);
445 RT_CASE_RET_STR(ACCESS_DENIED_ACE_TYPE);
446 RT_CASE_RET_STR(SYSTEM_AUDIT_ACE_TYPE);
447 RT_CASE_RET_STR(SYSTEM_ALARM_ACE_TYPE);
448 default: break;
449 }
450
451 return "<Invalid>";
452}
453
454/**
455 * Returns an allocated string for a SID containing the user/domain name.
456 *
457 * @returns Allocated string (UTF-8). Must be free'd using RTStrFree().
458 * @param pSid SID to return allocated string for.
459 */
460inline char *dbgSidToNameA(const PSID pSid)
461{
462 char *pszName = NULL;
463 int vrc = VINF_SUCCESS;
464
465 LPWSTR pwszSid = NULL;
466 if (ConvertSidToStringSid(pSid, &pwszSid))
467 {
468 SID_NAME_USE SidNameUse;
469
470 WCHAR wszUser[MAX_PATH];
471 DWORD cbUser = sizeof(wszUser);
472 WCHAR wszDomain[MAX_PATH];
473 DWORD cbDomain = sizeof(wszDomain);
474 if (LookupAccountSid(NULL, pSid, wszUser, &cbUser, wszDomain, &cbDomain, &SidNameUse))
475 {
476 RTUTF16 wszName[RTPATH_MAX];
477 if (RTUtf16Printf(wszName, RT_ELEMENTS(wszName), "%ls%s%ls (%ls)",
478 wszUser, wszDomain[0] == L'\0' ? "" : "\\", wszDomain, pwszSid))
479 {
480 vrc = RTUtf16ToUtf8(wszName, &pszName);
481 }
482 else
483 vrc = VERR_NO_MEMORY;
484 }
485 else
486 vrc = RTStrAPrintf(&pszName, "<Lookup Error>");
487
488 LocalFree(pwszSid);
489 }
490 else
491 vrc = VERR_NOT_FOUND;
492
493 return RT_SUCCESS(vrc) ? pszName : "<Invalid>";
494}
495#endif /* DEBUG */
496
497/**
498 * Checks a single target path whether it's safe to use or not.
499 *
500 * We check if the given path is owned by "NT Service\TrustedInstaller" and therefore assume that it's safe to use.
501 *
502 * @returns VBox status code. On error the path should be considered unsafe.
503 * @retval VERR_INVALID_NAME if the given path is considered unsafe.
504 * @retval VINF_SUCCESS if the given path is found to be safe to use.
505 * @param hModule Windows installer module handle.
506 * @param pszPath Path to check.
507 */
508static int checkTargetDirOne(MSIHANDLE hModule, PTGTDIRSECCTX pCtx, const char *pszPath)
509{
510 logStringF(hModule, "checkTargetDirOne: Checking '%s' ...", pszPath);
511
512 PRTUTF16 pwszPath;
513 int vrc = RTStrToUtf16(pszPath, &pwszPath);
514 if (RT_FAILURE(vrc))
515 return vrc;
516
517 PACL pDacl = NULL;
518 PSECURITY_DESCRIPTOR pSecurityDescriptor = { 0 };
519 DWORD dwErr = GetNamedSecurityInfo(pwszPath, SE_FILE_OBJECT, GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
520 NULL, NULL, NULL, NULL, &pSecurityDescriptor);
521 if (dwErr == ERROR_SUCCESS)
522 {
523 BOOL fDaclPresent = FALSE;
524 BOOL fDaclDefaultedIgnored = FALSE;
525 if (GetSecurityDescriptorDacl(pSecurityDescriptor, &fDaclPresent,
526 &pDacl, &fDaclDefaultedIgnored))
527 {
528 if ( !fDaclPresent
529 || !pDacl)
530 {
531 /* Bail out early if the DACL isn't provided or is missing. */
532 vrc = VERR_INVALID_NAME;
533 }
534 else
535 {
536 ACL_SIZE_INFORMATION aclSizeInfo;
537 RT_ZERO(aclSizeInfo);
538 if (GetAclInformation(pDacl, &aclSizeInfo, sizeof(aclSizeInfo), AclSizeInformation))
539 {
540 for(DWORD idxACE = 0; idxACE < aclSizeInfo.AceCount; idxACE++)
541 {
542 ACE_HEADER *pAceHdr = NULL;
543 if (GetAce(pDacl, idxACE, (LPVOID *)&pAceHdr))
544 {
545#ifdef DEBUG
546 logStringF(hModule, "checkTargetDirOne: ACE type=%s, flags=%#x, size=%#x",
547 dbgAceTypeToString(pAceHdr->AceType), pAceHdr->AceFlags, pAceHdr->AceSize);
548#endif
549 /* Note: We print the ACEs in canonoical order. */
550 switch (pAceHdr->AceType)
551 {
552 case ACCESS_ALLOWED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */
553 {
554 ACCESS_ALLOWED_ACE const *pAce = (ACCESS_ALLOWED_ACE *)pAceHdr;
555 PSID const pSid = (PSID)&pAce->SidStart;
556#ifdef DEBUG
557 char *pszSid = dbgSidToNameA(pSid);
558 logStringF(hModule, "checkTargetDirOne:\t%s fMask=%#x", pszSid, pAce->Mask);
559 RTStrFree(pszSid);
560#endif
561 /* We check the flags here first for performance reasons. */
562 if ((pAce->Mask & pCtx->fAccessMaskForbidden) == pCtx->fAccessMaskForbidden)
563 {
564 for (size_t idxSID = 0; idxSID < pCtx->cWellKnownSidsForbidden; idxSID++)
565 {
566 PSID const pSidForbidden = pCtx->paWellKnownSidsForbidden[idxSID];
567 bool const fForbidden = EqualSid(pSid, pSidForbidden);
568#ifdef DEBUG
569 char *pszName = dbgSidToNameA(pSidForbidden);
570 logStringF(hModule, "checkTargetDirOne:\t%s : %s",
571 fForbidden ? "** FORBIDDEN **" : "ALLOWED ", pszName);
572 RTStrFree(pszName);
573#endif /* DEBUG */
574 if (fForbidden)
575 {
576 vrc = VERR_INVALID_NAME;
577 break;
578 }
579 }
580 }
581
582 break;
583 }
584#ifdef DEBUG
585 case ACCESS_DENIED_ACE_TYPE: /* We're only interested in the ALLOW ACE. */
586 {
587 ACCESS_DENIED_ACE const *pAce = (ACCESS_DENIED_ACE *)pAceHdr;
588
589 LPWSTR pwszSid = NULL;
590 ConvertSidToStringSid((PSID)&pAce->SidStart, &pwszSid);
591
592 logStringF(hModule, "checkTargetDirOne:\t%ls fMask=%#x (generic %#x specific %#x)",
593 pwszSid ? pwszSid : L"<Allocation Error>", pAce->Mask);
594
595 LocalFree(pwszSid);
596 break;
597 }
598#endif /* DEBUG */
599 default:
600 /* Ignore everything else. */
601 break;
602 }
603 }
604 else
605 dwErr = GetLastError();
606
607 /* No point in checking further if we failed somewhere above. */
608 if (RT_FAILURE(vrc))
609 break;
610
611 } /* for ACE */
612 }
613 else
614 dwErr = GetLastError();
615 }
616 }
617 else
618 dwErr = GetLastError();
619
620 LocalFree(pSecurityDescriptor);
621 }
622 else
623 dwErr = GetLastError();
624
625 if (RT_SUCCESS(vrc))
626 vrc = RTErrConvertFromWin32(dwErr);
627
628#ifdef DEBUG
629 logStringF(hModule, "checkTargetDirOne: Returning %Rrc", vrc);
630#endif
631
632 if ( RT_FAILURE(vrc)
633 && vrc != VERR_INVALID_NAME)
634 logStringF(hModule, "checkTargetDirOne: Failed with %Rrc (%#x)", vrc, dwErr);
635
636 return vrc;
637}
638
639/**
640 * Checks whether the path in the public property INSTALLDIR has the correct ACL permissions and returns whether
641 * it's valid or not.
642 *
643 * Called from the MSI installer as a custom action.
644 *
645 * @returns Success status (acccording to MSI custom actions).
646 * @retval ERROR_SUCCESS if checking the target directory turned out to be valid.
647 * @retval ERROR_NO_NET_OR_BAD_PATH is the target directory is invalid.
648 * @param hModule Windows installer module handle.
649 *
650 * @note Sets private property VBox_Target_Dir_Is_Valid to "1" (true) if the given target path is valid,
651 * or "0" (false) if it is not. An empty target directory is considered to be valid (i.e. INSTALLDIR not set yet).
652 *
653 * @sa @bugref{10616}
654 */
655UINT __stdcall CheckTargetDir(MSIHANDLE hModule)
656{
657 char *pszTargetDir;
658
659 int vrc = VBoxMsiQueryPropUtf8(hModule, "INSTALLDIR", &pszTargetDir);
660 if (vrc == VERR_NOT_FOUND) /* Target directory might not set yet. */
661 {
662 logStringF(hModule, "CheckTargetDir: No INSTALLDIR set (yet), skipping ...");
663
664 VBoxMsiSetProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
665 vrc = VINF_SUCCESS;
666 }
667 else if (RT_SUCCESS(vrc))
668 {
669 logStringF(hModule, "CheckTargetDir: Checking target directory '%s' ...", pszTargetDir);
670
671 union
672 {
673 RTPATHPARSED Parsed;
674 uint8_t ab[RTPATH_MAX];
675 } u;
676
677 vrc = RTPathParse(pszTargetDir, &u.Parsed, sizeof(u), RTPATH_STR_F_STYLE_DOS);
678 if (RT_SUCCESS(vrc))
679 {
680 if (u.Parsed.fProps & RTPATH_PROP_DOTDOT_REFS)
681 vrc = VERR_INVALID_PARAMETER;
682 if (RT_SUCCESS(vrc))
683 {
684 vrc = initTargetDirSecurityCtx(&g_TargetDirSecCtx, hModule, pszTargetDir);
685 if (RT_SUCCESS(vrc))
686 {
687 uint16_t idxComp = u.Parsed.cComps;
688 char szPathToCheck[RTPATH_MAX];
689 while (idxComp > 1) /* We traverse backwards from INSTALLDIR and leave out the root (e.g. C:\"). */
690 {
691 u.Parsed.cComps = idxComp;
692 vrc = RTPathParsedReassemble(pszTargetDir, &u.Parsed, RTPATH_STR_F_STYLE_DOS,
693 szPathToCheck, sizeof(szPathToCheck));
694 if (RT_FAILURE(vrc))
695 break;
696 if (RTDirExists(szPathToCheck))
697 {
698 vrc = checkTargetDirOne(hModule, &g_TargetDirSecCtx, szPathToCheck);
699 if (RT_FAILURE(vrc))
700 break;
701 }
702 else
703 logStringF(hModule, "CheckTargetDir: Path '%s' does not exist (yet)", szPathToCheck);
704 idxComp--;
705 }
706
707 destroyTargetDirSecurityCtx(&g_TargetDirSecCtx);
708 }
709 else
710 logStringF(hModule, "CheckTargetDir: initTargetDirSecurityCtx failed with %Rrc\n", vrc);
711
712 if (RT_SUCCESS(vrc))
713 VBoxMsiSetProp(hModule, L"VBox_Target_Dir_Is_Valid", L"1");
714 }
715 }
716 else
717 logStringF(hModule, "CheckTargetDir: Parsing path failed with %Rrc", vrc);
718
719 RTStrFree(pszTargetDir);
720 }
721
722 if (RT_FAILURE(vrc)) /* On failure (or when in doubt), mark the installation directory as invalid. */
723 {
724 logStringF(hModule, "CheckTargetDir: Checking failed with %Rrc", vrc);
725 VBoxMsiSetProp(hModule, L"VBox_Target_Dir_Is_Valid", L"0");
726 }
727
728 /* Return back outcome to the MSI engine. */
729 return RT_SUCCESS(vrc) ? ERROR_SUCCESS : ERROR_NO_NET_OR_BAD_PATH;
730}
731
732/**
733 * Runs an executable on the OS.
734 *
735 * @returns Windows error code.
736 * @param hModule Windows installer module handle.
737 * @param pwszImage The executable to run.
738 * @param pwszArgs The arguments (command line w/o executable).
739 */
740static UINT procRun(MSIHANDLE hModule, const wchar_t *pwszImage, wchar_t const *pwszArgs)
741{
742 /*
743 * Construct a full command line.
744 */
745 size_t const cwcImage = RTUtf16Len(pwszImage);
746 size_t const cwcArgs = RTUtf16Len(pwszArgs);
747
748 wchar_t *pwszCmdLine = (wchar_t *)alloca((1 + cwcImage + 1 + 1 + cwcArgs + 1) * sizeof(wchar_t));
749 pwszCmdLine[0] = '"';
750 memcpy(&pwszCmdLine[1], pwszImage, cwcImage * sizeof(wchar_t));
751 pwszCmdLine[1 + cwcImage] = '"';
752 pwszCmdLine[1 + cwcImage + 1] = ' ';
753 memcpy(&pwszCmdLine[1 + cwcImage + 1 + 1], pwszArgs, (cwcArgs + 1) * sizeof(wchar_t));
754
755 /*
756 * Construct startup info.
757 */
758 STARTUPINFOW StartupInfo;
759 RT_ZERO(StartupInfo);
760 StartupInfo.cb = sizeof(StartupInfo);
761 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
762 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
763 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
764 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
765#ifndef DEBUG
766 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
767 StartupInfo.wShowWindow = SW_HIDE;
768#endif
769
770 /*
771 * Start it.
772 */
773 UINT rcWin;
774 PROCESS_INFORMATION ChildInfo = { NULL, NULL, 0, 0 };
775 if (CreateProcessW(pwszImage, pwszCmdLine, NULL /*pProcessAttribs*/, NULL /*pThreadAttribs*/, TRUE /*fInheritHandles*/,
776 0 /*fFlags*/, NULL /*pwszEnv*/, NULL /*pwszCwd*/, &StartupInfo, &ChildInfo))
777 {
778 logStringF(hModule, "procRun: Info: Started process %u: %ls", ChildInfo.dwProcessId, pwszCmdLine);
779 CloseHandle(ChildInfo.hThread);
780 DWORD const dwWait = WaitForSingleObject(ChildInfo.hProcess, RT_MS_30SEC);
781 DWORD dwExitCode = 0xf00dface;
782 if (GetExitCodeProcess(ChildInfo.hProcess, &dwExitCode))
783 {
784 if (dwExitCode == 0)
785 {
786 logStringF(hModule, "procRun: Info: Process '%ls' terminated exit code zero", pwszCmdLine);
787 rcWin = ERROR_SUCCESS;
788 }
789 else
790 {
791 logStringF(hModule, "procRun: Process '%ls' terminated with non-zero exit code: %u (%#x)",
792 pwszCmdLine, dwExitCode, dwExitCode);
793 rcWin = ERROR_GEN_FAILURE;
794 }
795 }
796 else
797 {
798 rcWin = GetLastError();
799 logStringF(hModule, "procRun: Process '%ls' is probably still running: rcWin=%u dwWait=%u (%#x)",
800 pwszCmdLine, rcWin, dwWait, dwWait);
801 }
802 }
803 else
804 {
805 rcWin = GetLastError();
806 logStringF(hModule, "procRun: Creating process '%ls' failed: rcWin=%u\n", pwszCmdLine, rcWin);
807 }
808 return rcWin;
809}
810
811/**
812 * Tries to retrieve the Python installation path on the system, extended version.
813 *
814 * @returns Windows error code.
815 * @param hModule Windows installer module handle.
816 * @param hKeyRoot Registry root key to use, e.g. HKEY_LOCAL_MACHINE.
817 * @param pwszPythonPath Buffer to return the path for python.exe in.
818 * @param cwcPythonPath Buffer size in UTF-16 units.
819 * @param fReturnExe Return the path to python.exe if true, otherwise
820 * just the python install directory.
821 */
822static UINT getPythonPathEx(MSIHANDLE hModule, HKEY hKeyRoot, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe)
823{
824 *pwszPythonPath = '\0';
825
826 /*
827 * Enumerate the subkeys of python core installation key.
828 *
829 * Note: The loop ASSUMES that later found versions are higher, e.g. newer
830 * Python versions. For now we always go by the newest version.
831 */
832 HKEY hKeyPythonCore = NULL;
833 LSTATUS dwErr = RegOpenKeyExW(hKeyRoot, L"SOFTWARE\\Python\\PythonCore", 0, KEY_READ, &hKeyPythonCore);
834 if (dwErr != ERROR_SUCCESS)
835 return dwErr;
836
837 UINT rcWinRet = ERROR_PATH_NOT_FOUND;
838 for (DWORD i = 0; i < 16384; ++i)
839 {
840 static wchar_t const s_wszInstallPath[] = L"\\InstallPath";
841 static wchar_t const s_wszPythonExe[] = L"python.exe";
842
843 /* Get key name: */
844 wchar_t wszBuf[RTPATH_MAX + RT_MAX(RT_ELEMENTS(s_wszInstallPath), RT_ELEMENTS(s_wszPythonExe)) + 2];
845 DWORD cwcKeyNm = RTPATH_MAX;
846 DWORD dwKeyType = REG_SZ;
847 dwErr = RegEnumKeyExW(hKeyPythonCore, i, wszBuf, &cwcKeyNm, NULL, NULL, NULL, NULL);
848 if (dwErr == ERROR_NO_MORE_ITEMS)
849 break;
850 if (dwErr != ERROR_SUCCESS)
851 continue;
852 if (dwKeyType != REG_SZ)
853 continue;
854 if (cwcKeyNm == 0)
855 continue;
856 NonStandardAssert(cwcKeyNm <= sizeof(wszBuf));
857
858 /* Try Open the InstallPath subkey: */
859 memcpy(&wszBuf[cwcKeyNm], s_wszInstallPath, sizeof(s_wszInstallPath));
860
861 HKEY hKeyInstallPath = NULL;
862 dwErr = RegOpenKeyExW(hKeyPythonCore, wszBuf, 0, KEY_READ, &hKeyInstallPath);
863 if (dwErr != ERROR_SUCCESS)
864 continue;
865
866 /* Query the value. We double buffer this so we don't overwrite an okay
867 return value with this. Use the smaller of cwcPythonPath and wszValue
868 so RegQueryValueExW can do all the buffer overflow checking for us.
869 For paranoid reasons, we reserve a space for a terminator as well as
870 a slash. (ASSUMES reasonably sized output buffer.) */
871 NonStandardAssert(cwcPythonPath > RT_ELEMENTS(s_wszPythonExe) + 16);
872 DWORD cbValue = (DWORD)RT_MIN( cwcPythonPath * sizeof(wchar_t)
873 - (fReturnExe ? sizeof(s_wszInstallPath) - sizeof(wchar_t) * 2 : sizeof(wchar_t) * 2),
874 RTPATH_MAX * sizeof(wchar_t));
875 DWORD dwValueType = REG_SZ;
876 dwErr = RegQueryValueExW(hKeyInstallPath, L"", NULL, &dwValueType, (LPBYTE)wszBuf, &cbValue);
877 RegCloseKey(hKeyInstallPath);
878 if ( dwErr == ERROR_SUCCESS
879 && dwValueType == REG_SZ
880 && cbValue >= sizeof(L"C:\\") - sizeof(L""))
881 {
882 /* Find length in wchar_t unit w/o terminator: */
883 DWORD cwc = cbValue / sizeof(wchar_t);
884 while (cwc > 0 && wszBuf[cwc - 1] == '\0')
885 cwc--;
886 wszBuf[cwc] = '\0';
887 if (cwc > 2)
888 {
889 /* Check if the path leads to a directory with a python.exe file in it. */
890 if (!RTPATH_IS_SLASH(wszBuf[cwc - 1]))
891 wszBuf[cwc++] = '\\';
892 memcpy(&wszBuf[cwc], s_wszPythonExe, sizeof(s_wszPythonExe));
893 DWORD const fAttribs = GetFileAttributesW(wszBuf);
894 if (fAttribs != INVALID_FILE_ATTRIBUTES)
895 {
896 if (!(fAttribs & FILE_ATTRIBUTE_DIRECTORY))
897 {
898 /* Okay, we found something that can be returned. */
899 if (fReturnExe)
900 cwc += RT_ELEMENTS(s_wszPythonExe) - 1;
901 wszBuf[cwc] = '\0';
902 logStringF(hModule, "getPythonPath: Found: \"%ls\"", wszBuf);
903
904 NonStandardAssert(cwcPythonPath > cwc);
905 memcpy(pwszPythonPath, wszBuf, cwc * sizeof(wchar_t));
906 pwszPythonPath[cwc] = '\0';
907 rcWinRet = ERROR_SUCCESS;
908 }
909 else
910 logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": is a directory (%#x)", wszBuf, fAttribs);
911 }
912 else
913 logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": Does not exist (%u)", wszBuf, GetLastError());
914 }
915 }
916 }
917
918 RegCloseKey(hKeyPythonCore);
919 if (rcWinRet != ERROR_SUCCESS)
920 logStringF(hModule, "getPythonPath: Unable to find python");
921 return rcWinRet;
922}
923
924/**
925 * Retrieves the absolute path of the Python installation.
926 *
927 * @returns Windows error code.
928 * @param hModule Windows installer module handle.
929 * @param pwszPythonPath Buffer to return the path for python.exe in.
930 * @param cwcPythonPath Buffer size in UTF-16 units.
931 * @param fReturnExe Return the path to python.exe if true, otherwise
932 * just the python install directory.
933 */
934static UINT getPythonPath(MSIHANDLE hModule, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe = false)
935{
936 UINT rcWin = getPythonPathEx(hModule, HKEY_LOCAL_MACHINE, pwszPythonPath, cwcPythonPath, fReturnExe);
937 if (rcWin != ERROR_SUCCESS)
938 rcWin = getPythonPathEx(hModule, HKEY_CURRENT_USER, pwszPythonPath, cwcPythonPath, fReturnExe);
939 return rcWin;
940}
941
942/**
943 * Retrieves the absolute path of the Python executable.
944 *
945 * @returns Windows error code.
946 * @param hModule Windows installer module handle.
947 * @param pwszPythonExe Buffer to return the path for python.exe in.
948 * @param cwcPythonExe Buffer size in UTF-16 units.
949 */
950static UINT getPythonExe(MSIHANDLE hModule, wchar_t *pwszPythonExe, size_t cwcPythonExe)
951{
952 return getPythonPath(hModule, pwszPythonExe, cwcPythonExe, true /*fReturnExe*/);
953}
954
955/**
956 * Checks if all dependencies for running the VBox Python API bindings are met.
957 *
958 * @returns VBox status code, or error if depedencies are not met.
959 * @param hModule Windows installer module handle.
960 * @param pwszPythonExe Path to Python interpreter image (.exe).
961 */
962static int checkPythonDependencies(MSIHANDLE hModule, const wchar_t *pwszPythonExe)
963{
964 /*
965 * Check if importing the win32api module works.
966 * This is a prerequisite for setting up the VBox API.
967 */
968 logStringF(hModule, "checkPythonDependencies: Checking for win32api extensions ...");
969
970 UINT rcWin = procRun(hModule, pwszPythonExe, L"-c \"import win32api\"");
971 if (rcWin == ERROR_SUCCESS)
972 logStringF(hModule, "checkPythonDependencies: win32api found\n");
973 else
974 logStringF(hModule, "checkPythonDependencies: Importing win32api failed with %u (%#x)\n", rcWin, rcWin);
975
976 return rcWin;
977}
978
979/**
980 * Checks for a valid Python installation on the system.
981 *
982 * Called from the MSI installer as custom action.
983 *
984 * @returns Always ERROR_SUCCESS.
985 * Sets public property VBOX_PYTHON_INSTALLED to "0" (false) or "1" (success).
986 * Sets public property VBOX_PYTHON_PATH to the Python installation path (if found).
987 *
988 * @param hModule Windows installer module handle.
989 */
990UINT __stdcall IsPythonInstalled(MSIHANDLE hModule)
991{
992 wchar_t wszPythonPath[RTPATH_MAX];
993 UINT rcWin = getPythonPath(hModule, wszPythonPath, RTPATH_MAX);
994 if (rcWin == ERROR_SUCCESS)
995 {
996 logStringF(hModule, "IsPythonInstalled: Python installation found at \"%ls\"", wszPythonPath);
997 VBoxMsiSetProp(hModule, L"VBOX_PYTHON_PATH", wszPythonPath);
998 VBoxMsiSetProp(hModule, L"VBOX_PYTHON_INSTALLED", L"1");
999 }
1000 else
1001 {
1002 logStringF(hModule, "IsPythonInstalled: Error: No suitable Python installation found (%u), skipping installation.", rcWin);
1003 logStringF(hModule, "IsPythonInstalled: Python seems not to be installed; please download + install the Python Core package.");
1004 VBoxMsiSetProp(hModule, L"VBOX_PYTHON_INSTALLED", L"0");
1005 }
1006
1007 return ERROR_SUCCESS; /* Never return failure. */
1008}
1009
1010/**
1011 * Checks if all dependencies for running the VBox Python API bindings are met.
1012 *
1013 * Called from the MSI installer as custom action.
1014 *
1015 * @returns Always ERROR_SUCCESS.
1016 * Sets public property VBOX_PYTHON_DEPS_INSTALLED to "0" (false) or "1" (success).
1017 *
1018 * @param hModule Windows installer module handle.
1019 */
1020UINT __stdcall ArePythonAPIDepsInstalled(MSIHANDLE hModule)
1021{
1022 wchar_t wszPythonExe[RTPATH_MAX];
1023 UINT dwErr = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1024 if (dwErr == ERROR_SUCCESS)
1025 {
1026 dwErr = checkPythonDependencies(hModule, wszPythonExe);
1027 if (dwErr == ERROR_SUCCESS)
1028 logStringF(hModule, "ArePythonAPIDepsInstalled: Dependencies look good.");
1029 }
1030
1031 if (dwErr != ERROR_SUCCESS)
1032 logStringF(hModule, "ArePythonAPIDepsInstalled: Failed with dwErr=%u", dwErr);
1033
1034 VBoxMsiSetProp(hModule, L"VBOX_PYTHON_DEPS_INSTALLED", dwErr == ERROR_SUCCESS ? L"1" : L"0");
1035 return ERROR_SUCCESS; /* Never return failure. */
1036}
1037
1038/**
1039 * Checks if all required MS CRTs (Visual Studio Redistributable Package) are installed on the system.
1040 *
1041 * Called from the MSI installer as custom action.
1042 *
1043 * @returns Always ERROR_SUCCESS.
1044 * Sets public property VBOX_MSCRT_INSTALLED to "" (false, to use "NOT" in WiX) or "1" (success).
1045 *
1046 * Also exposes public properties VBOX_MSCRT_VER_MIN + VBOX_MSCRT_VER_MAJ strings
1047 * with the most recent MSCRT version detected.
1048 *
1049 * @param hModule Windows installer module handle.
1050 *
1051 * @sa https://docs.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files?view=msvc-170
1052 */
1053UINT __stdcall IsMSCRTInstalled(MSIHANDLE hModule)
1054{
1055 HKEY hKey;
1056 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1057 L"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
1058 0, KEY_READ, &hKey);
1059 if (lrc == ERROR_SUCCESS)
1060 {
1061 DWORD dwVal;
1062 int rc = VBoxMsiRegQueryDWORD(hModule, hKey, "Installed", &dwVal);
1063 if (RT_SUCCESS(rc))
1064 {
1065 if (dwVal >= 1)
1066 {
1067 DWORD dwMaj;
1068 rc = VBoxMsiRegQueryDWORD(hModule, hKey, "Major", &dwMaj);
1069 if (RT_SUCCESS(rc))
1070 {
1071 VBoxMsiSetPropDWORD(hModule, L"VBOX_MSCRT_VER_MAJ", dwMaj);
1072
1073 DWORD dwMin;
1074 lrc = VBoxMsiRegQueryDWORD(hModule, hKey, "Minor", &dwMin);
1075 if (RT_SUCCESS(rc))
1076 {
1077 VBoxMsiSetPropDWORD(hModule, L"VBOX_MSCRT_VER_MIN", dwMin);
1078
1079 logStringF(hModule, "IsMSCRTInstalled: Found v%u.%u\n", dwMaj, dwMin);
1080
1081 /* Check for at least 2019. */
1082 if (dwMaj > 14 || (dwMaj == 14 && dwMin >= 20))
1083 VBoxMsiSetProp(hModule, L"VBOX_MSCRT_INSTALLED", L"1");
1084 }
1085 else
1086 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Minor' key not present (lrc=%d)", lrc);
1087 }
1088 else
1089 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Major' key not present (lrc=%d)", lrc);
1090 }
1091 else
1092 {
1093 logStringF(hModule, "IsMSCRTInstalled: Found, but not marked as installed");
1094 lrc = ERROR_NOT_INSTALLED;
1095 }
1096 }
1097 else
1098 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Installed' key not present (lrc=%d)", lrc);
1099
1100 RegCloseKey(hKey);
1101 }
1102
1103 if (lrc != ERROR_SUCCESS)
1104 logStringF(hModule, "IsMSCRTInstalled: Failed with lrc=%ld", lrc);
1105
1106 return ERROR_SUCCESS; /* Never return failure. */
1107}
1108
1109/**
1110 * Checks if the running OS is supported for installing (at least Windows 10 (e.g. >= build 10000)).
1111 *
1112 * Called from the MSI installer as custom action.
1113 *
1114 * @returns Always ERROR_SUCCESS.
1115 * Sets public property VBOX_IS_WINDOWS_SUPPORTED to "" (empty / false) or "1" (success).
1116 * Sets public property VBOX_WIN_VER_MAJOR to the Windows major version.
1117 * Sets public property VBOX_WIN_VER_MINOR to the Windows minor version.
1118 *
1119 * @param hModule Windows installer module handle.
1120 */
1121UINT __stdcall IsWindowsSupported(MSIHANDLE hModule)
1122{
1123 /*
1124 * Note: We cannot use RtlGetVersion() / GetVersionExW() here, as the Windows Installer service
1125 * all shims this, unfortunately. So we have to go another route by querying the major version
1126 * number from the registry.
1127 */
1128 HKEY hKey;
1129 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKey);
1130 if (lrc == ERROR_SUCCESS)
1131 {
1132 DWORD dwMaj;
1133 int rc = VBoxMsiRegQueryDWORD(hModule, hKey, "CurrentMajorVersionNumber", &dwMaj);
1134 if (RT_SUCCESS(rc))
1135 {
1136 /* We support installing on Windows 10 or newer. */
1137 VBoxMsiSetProp(hModule, L"VBOX_IS_WINDOWS_SUPPORTED", dwMaj >= 10 ? L"1" : L"");
1138 VBoxMsiSetPropDWORD(hModule, L"VBOX_WIN_VER_MAJOR", dwMaj);
1139
1140 DWORD dwMin;
1141 rc = VBoxMsiRegQueryDWORD(hModule, hKey, "CurrentMinorVersionNumber", &dwMin);
1142 if (RT_SUCCESS(rc))
1143 {
1144 VBoxMsiSetPropDWORD(hModule, L"VBOX_WIN_VER_MINOR", dwMin);
1145
1146 logStringF(hModule, "IsWindowsSupported: Detected Windows %u.%u", dwMaj, dwMin);
1147 }
1148 else
1149 logStringF(hModule, "IsWindowsSupported: Error reading CurrentMinorVersionNumber (%Rrc)", rc);
1150 }
1151 else
1152 logStringF(hModule, "IsWindowsSupported: Error reading CurrentMajorVersionNumber (%Rrc)", rc);
1153
1154 RegCloseKey(hKey);
1155 }
1156 else
1157 logStringF(hModule, "IsWindowsSupported/RegOpenKeyExW: Error opening CurrentVersion key (%ld)", lrc);
1158
1159 return ERROR_SUCCESS; /* Never return failure. */
1160}
1161
1162/**
1163 * Installs and compiles the VBox Python bindings.
1164 *
1165 * Called from the MSI installer as custom action.
1166 *
1167 * @returns Always ERROR_SUCCESS.
1168 * Sets public property VBOX_API_INSTALLED to "0" (false) or "1" (success).
1169 *
1170 * @param hModule Windows installer module handle.
1171 */
1172UINT __stdcall InstallPythonAPI(MSIHANDLE hModule)
1173{
1174 logStringF(hModule, "InstallPythonAPI: Checking for installed Python environment(s) ...");
1175
1176 /** @todo r=bird: Can't we get the VBOX_PYTHON_PATH property here? */
1177 wchar_t wszPythonExe[RTPATH_MAX];
1178 UINT rcWin = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1179 if (rcWin != ERROR_SUCCESS)
1180 {
1181 VBoxMsiSetProp(hModule, L"VBOX_API_INSTALLED", L"0");
1182 return ERROR_SUCCESS;
1183 }
1184
1185 /*
1186 * Set up the VBox API.
1187 */
1188 /* Get the VBox API setup string. */
1189 WCHAR wszVBoxPythonInstallerPath[RTPATH_MAX];
1190 int rc = VBoxMsiQueryProp(hModule, L"CustomActionData", wszVBoxPythonInstallerPath, RT_ELEMENTS(wszVBoxPythonInstallerPath));
1191 if (RT_SUCCESS(rc))
1192 {
1193 /* Make sure our current working directory is the VBox installation path. */
1194 if (SetCurrentDirectoryW(wszVBoxPythonInstallerPath))
1195 {
1196 /* Set required environment variables. */
1197 /** @todo r=andy: That can't be right!
1198 *
1199 * r=bird: The variable probably isn't used because VBOX_MSI_INSTALL_PATH is
1200 * set by VBoxMergeApp.wxi. */
1201 if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxPythonInstallerPath))
1202 {
1203 logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxPythonInstallerPath);
1204
1205 rcWin = procRun(hModule, wszPythonExe, L"vboxapisetup.py install");
1206 if (rcWin == ERROR_SUCCESS)
1207 {
1208 logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful");
1209
1210 /*
1211 * Do some sanity checking if the VBox API works.
1212 */
1213 logStringF(hModule, "InstallPythonAPI: Validating VBox API ...");
1214
1215 rcWin = procRun(hModule, wszPythonExe, L"-c \"from vboxapi import VirtualBoxManager\"");
1216 if (rcWin == ERROR_SUCCESS)
1217 {
1218 logStringF(hModule, "InstallPythonAPI: VBox API looks good.");
1219 VBoxMsiSetProp(hModule, L"VBOX_API_INSTALLED", L"1");
1220 return ERROR_SUCCESS;
1221 }
1222
1223 /* failed */
1224 logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %u (%#x)", rcWin, rcWin);
1225 }
1226 else
1227 logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %u (%#x)", rcWin, rcWin);
1228 }
1229 else
1230 logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH: LastError=%u",
1231 GetLastError());
1232 }
1233 else
1234 logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%ls\": LastError=%u",
1235 wszVBoxPythonInstallerPath, GetLastError());
1236 }
1237 else
1238 logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory: rc=%Rrc", rc);
1239
1240 VBoxMsiSetProp(hModule, L"VBOX_API_INSTALLED", L"0");
1241 logStringF(hModule, "InstallPythonAPI: Installation failed");
1242 return ERROR_SUCCESS; /* Do not fail here. */
1243}
1244
1245static LONG installBrandingValue(MSIHANDLE hModule,
1246 const WCHAR *pwszFileName,
1247 const WCHAR *pwszSection,
1248 const WCHAR *pwszValue)
1249{
1250 LONG rc;
1251 WCHAR wszValue[MAX_PATH];
1252 if (GetPrivateProfileStringW(pwszSection, pwszValue, NULL, wszValue, sizeof(wszValue), pwszFileName) > 0)
1253 {
1254 WCHAR wszKey[MAX_PATH + 64];
1255 if (RTUtf16ICmpAscii(pwszSection, "General") != 0)
1256 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding\\%ls", VBOX_VENDOR_SHORT, pwszSection);
1257 else
1258 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding", VBOX_VENDOR_SHORT);
1259
1260 HKEY hkBranding = NULL;
1261 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKey, 0, KEY_WRITE, &hkBranding);
1262 if (rc == ERROR_SUCCESS)
1263 {
1264 rc = RegSetValueExW(hkBranding,
1265 pwszValue,
1266 NULL,
1267 REG_SZ,
1268 (BYTE *)wszValue,
1269 (DWORD)RTUtf16Len(wszValue));
1270 if (rc != ERROR_SUCCESS)
1271 logStringF(hModule, "InstallBranding: Could not write value %s! Error %d", pwszValue, rc);
1272 RegCloseKey(hkBranding);
1273 }
1274 }
1275 else
1276 rc = ERROR_NOT_FOUND;
1277 return rc;
1278}
1279
1280/**
1281 * @note Both paths strings must have an extra terminator.
1282 */
1283static UINT CopyDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1284{
1285 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1286 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1287
1288 SHFILEOPSTRUCTW s = {0};
1289 s.hwnd = NULL;
1290 s.wFunc = FO_COPY;
1291 s.pTo = pwszzDstDir;
1292 s.pFrom = pwszzSrcDir;
1293 s.fFlags = FOF_SILENT
1294 | FOF_NOCONFIRMATION
1295 | FOF_NOCONFIRMMKDIR
1296 | FOF_NOERRORUI;
1297
1298 logStringF(hModule, "CopyDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1299 int r = SHFileOperationW(&s);
1300 if (r == 0)
1301 return ERROR_SUCCESS;
1302 logStringF(hModule, "CopyDir: Copy operation returned status %#x", r);
1303 return ERROR_GEN_FAILURE;
1304}
1305
1306/**
1307 * @note The directory string must have two zero terminators!
1308 */
1309static UINT RemoveDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir)
1310{
1311 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1312
1313 SHFILEOPSTRUCTW s = {0};
1314 s.hwnd = NULL;
1315 s.wFunc = FO_DELETE;
1316 s.pFrom = pwszzDstDir;
1317 s.fFlags = FOF_SILENT
1318 | FOF_NOCONFIRMATION
1319 | FOF_NOCONFIRMMKDIR
1320 | FOF_NOERRORUI;
1321
1322 logStringF(hModule, "RemoveDir: pwszzDstDir=%ls", pwszzDstDir);
1323 int r = SHFileOperationW(&s);
1324 if (r == 0)
1325 return ERROR_SUCCESS;
1326 logStringF(hModule, "RemoveDir: Remove operation returned status %#x", r);
1327 return ERROR_GEN_FAILURE;
1328}
1329
1330/**
1331 * @note Both paths strings must have an extra terminator.
1332 */
1333static UINT RenameDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1334{
1335 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1336 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1337
1338 SHFILEOPSTRUCTW s = {0};
1339 s.hwnd = NULL;
1340 s.wFunc = FO_RENAME;
1341 s.pTo = pwszzDstDir;
1342 s.pFrom = pwszzSrcDir;
1343 s.fFlags = FOF_SILENT
1344 | FOF_NOCONFIRMATION
1345 | FOF_NOCONFIRMMKDIR
1346 | FOF_NOERRORUI;
1347
1348 logStringF(hModule, "RenameDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1349 int r = SHFileOperationW(&s);
1350 if (r == 0)
1351 return ERROR_SUCCESS;
1352 logStringF(hModule, "RenameDir: Rename operation returned status %#x", r);
1353 return ERROR_GEN_FAILURE;
1354}
1355
1356/** RTPathAppend-like function. */
1357static UINT AppendToPath(wchar_t *pwszPath, size_t cwcPath, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1358{
1359 size_t cwcCurPath = RTUtf16Len(pwszPath);
1360 size_t cwcSlash = cwcCurPath > 1 && RTPATH_IS_SLASH(pwszPath[cwcCurPath - 1]) ? 0 : 1;
1361 while (RTPATH_IS_SLASH(*pwszAppend))
1362 pwszAppend++;
1363 size_t cwcAppend = RTUtf16Len(pwszAppend);
1364 if (cwcCurPath + cwcCurPath + cwcAppend + fDoubleTerm < cwcPath)
1365 {
1366 if (cwcSlash)
1367 pwszPath[cwcCurPath++] = '\\';
1368 memcpy(&pwszPath[cwcCurPath], pwszAppend, (cwcAppend + 1) * sizeof(wchar_t));
1369 if (fDoubleTerm)
1370 pwszPath[cwcCurPath + cwcAppend + 1] = '\0';
1371 return ERROR_SUCCESS;
1372 }
1373 return ERROR_BUFFER_OVERFLOW;
1374}
1375
1376/** RTPathJoin-like function. */
1377static UINT JoinPaths(wchar_t *pwszPath, size_t cwcPath, wchar_t *pwszPath1, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1378{
1379 size_t cwcCurPath = RTUtf16Len(pwszPath1);
1380 if (cwcCurPath < cwcPath)
1381 {
1382 memcpy(pwszPath, pwszPath1, (cwcCurPath + 1) * sizeof(wchar_t));
1383 return AppendToPath(pwszPath, cwcPath, pwszAppend, fDoubleTerm);
1384 }
1385 return ERROR_BUFFER_OVERFLOW;
1386}
1387
1388UINT __stdcall UninstallBranding(MSIHANDLE hModule)
1389{
1390 logStringF(hModule, "UninstallBranding: Handling branding file ...");
1391
1392 WCHAR wszPath[RTPATH_MAX];
1393 int rc = VBoxMsiQueryProp(hModule, L"CustomActionData", wszPath, RT_ELEMENTS(wszPath));
1394 if (RT_SUCCESS(rc))
1395 {
1396 size_t const cwcPath = RTUtf16Len(wszPath);
1397 rc = AppendToPath(wszPath, RTPATH_MAX, L"custom", true /*fDoubleTerm*/);
1398 if (rc == ERROR_SUCCESS)
1399 rc = RemoveDir(hModule, wszPath);
1400
1401 /* Check for .custom directory from a failed install and remove it. */
1402 wszPath[cwcPath] = '\0';
1403 rc = AppendToPath(wszPath, RTPATH_MAX, L".custom", true /*fDoubleTerm*/);
1404 if (rc == ERROR_SUCCESS)
1405 rc = RemoveDir(hModule, wszPath);
1406 }
1407
1408 logStringF(hModule, "UninstallBranding: Handling done. (rc=%Rrc (ignored))", rc);
1409 return ERROR_SUCCESS; /* Do not fail here. */
1410}
1411
1412UINT __stdcall InstallBranding(MSIHANDLE hModule)
1413{
1414 logStringF(hModule, "InstallBranding: Handling branding file ...");
1415
1416 /*
1417 * Get the paths.
1418 */
1419 wchar_t wszSrcPath[RTPATH_MAX];
1420 int rc = VBoxMsiQueryProp(hModule, L"SOURCEDIR", wszSrcPath, RT_ELEMENTS(wszSrcPath));
1421 if (RT_SUCCESS(rc))
1422 {
1423 wchar_t wszDstPath[RTPATH_MAX];
1424 rc = VBoxMsiQueryProp(hModule, L"CustomActionData", wszDstPath, RT_ELEMENTS(wszDstPath));
1425 if (RT_SUCCESS(rc))
1426 {
1427 /*
1428 * First we copy the src\.custom dir to the target.
1429 */
1430 UINT rcWin = AppendToPath(wszSrcPath, RT_ELEMENTS(wszSrcPath) - 1, L".custom", true /*fDoubleTerm*/);
1431 if (rcWin == ERROR_SUCCESS)
1432 {
1433 rcWin = CopyDir(hModule, wszDstPath, wszSrcPath);
1434 if (rcWin == ERROR_SUCCESS)
1435 {
1436 /*
1437 * The rename the '.custom' directory we now got in the target area to 'custom'.
1438 */
1439 rcWin = JoinPaths(wszSrcPath, RT_ELEMENTS(wszSrcPath), wszDstPath, L".custom", true /*fDoubleTerm*/);
1440 if (rc == ERROR_SUCCESS)
1441 {
1442 rcWin = AppendToPath(wszDstPath, RT_ELEMENTS(wszDstPath), L"custom", true /*fDoubleTerm*/);
1443 if (rc == ERROR_SUCCESS)
1444 rcWin = RenameDir(hModule, wszDstPath, wszSrcPath);
1445 }
1446 }
1447 }
1448
1449 logStringF(hModule, "InstallBranding: Handling done. (rc=%Rrc (ignored))", rc);
1450 }
1451 }
1452
1453 logStringF(hModule, "InstallBranding: Handling done. (rc=%Rrc (ignored))", rc);
1454 return ERROR_SUCCESS; /* Do not fail here. */
1455}
1456
1457static DECLCALLBACK(void) vboxWinDrvInstLogCallback(VBOXWINDRIVERLOGTYPE enmType, const char *pszMsg, void *pvUser)
1458{
1459 MSIHANDLE *phModule = (MSIHANDLE *)pvUser;
1460
1461 switch (enmType)
1462 {
1463 case VBOXWINDRIVERLOGTYPE_ERROR:
1464 logStringF(*phModule, "*** Error: %s", pszMsg);
1465 break;
1466
1467 default:
1468 logStringF(*phModule, "%s", pszMsg);
1469 break;
1470 }
1471}
1472
1473/**
1474 * Returns a custom action data value.
1475 *
1476 * @returns Value of \a pszName if found, or NULL if not found.
1477 * @param hModule Windows installer module handle.
1478 * @param pData Custom action data to search in.
1479 * @param pszName Name of the custom action data value to search for.
1480 * @param fOptional Whether the custom action data value is optional or not.
1481 * If @c true, \a pData will be destroyed automatically,
1482 * so that the caller can skip cleaning up.
1483 * That implies that \a pData will be invalid when returning
1484 * a failure, so use with care.
1485 */
1486static const char *getCustomActionDataValue(MSIHANDLE hModule, PVBOXMSICUSTOMACTIONDATA pData, const char *pszName, bool fOptional)
1487{
1488 const char *pszVal = VBoxMsiCustomActionDataFind(pData, pszName);
1489 if ( !pszVal
1490 && !fOptional)
1491 {
1492 logStringF(hModule, "Error: Value '%s' not specified in CustomActionData!", pszName);
1493
1494#ifdef DEBUG
1495 for (size_t i = 0; i < pData->cEntries; i++)
1496 logStringF(hModule, "CustomActionData: %s = %s", pData->paEntries[i].pszKey, pData->paEntries[i].pszVal);
1497#endif
1498 VBoxMsiCustomActionDataFree(pData);
1499 pData = NULL;
1500 }
1501
1502 return pszVal;
1503}
1504
1505UINT __stdcall DriverInstall(MSIHANDLE hModule)
1506{
1507 logStringF(hModule, "Installing driver ...");
1508
1509 PVBOXMSICUSTOMACTIONDATA pData;
1510 int rc = VBoxMsiCustomActionDataQuery(hModule, &pData);
1511 if (RT_FAILURE(rc))
1512 {
1513 logStringF(hModule, "DriverInstall: No CustomActionData specified!");
1514 return ERROR_INVALID_PARAMETER;
1515 }
1516
1517 const char *pszInfFile = getCustomActionDataValue(hModule, pData, "VBoxDrvInstInfFile", false /* fOptional */);
1518 if (!pszInfFile)
1519 return ERROR_INVALID_PARAMETER;
1520
1521 /* VBoxDrvInstInfSection is optional. */
1522 const char *pszInfSection = getCustomActionDataValue(hModule, pData, "VBoxDrvInstInfSection", true /* fOptional */);
1523 /* VBoxDrvInstModel is optional. */
1524 const char *pszModel = getCustomActionDataValue(hModule, pData, "VBoxDrvInstModel", true /* fOptional */);
1525 /* VBoxDrvInstPnpId is optional. */
1526 const char *pszPnpId = getCustomActionDataValue(hModule, pData, "VBoxDrvInstPnpId", true /* fOptional */);
1527
1528 uint32_t fFlags = VBOX_WIN_DRIVERINSTALL_F_NONE;
1529 if (getCustomActionDataValue(hModule, pData, "VBoxDrvInstFlagForce", true /* fOptional */))
1530 fFlags |= VBOX_WIN_DRIVERINSTALL_F_FORCE;
1531 if (getCustomActionDataValue(hModule, pData, "VBoxDrvInstFlagSilent", true /* fOptional */))
1532 fFlags |= VBOX_WIN_DRIVERINSTALL_F_SILENT;
1533
1534 VBOXWINDRVINST hWinDrvInst;
1535 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, 1 /* uVerbostiy */, &vboxWinDrvInstLogCallback, &hModule /* pvUser */);
1536 if (RT_SUCCESS(rc))
1537 {
1538 if (pszInfSection && *pszInfSection)
1539 rc = VBoxWinDrvInstInstallExecuteInf(hWinDrvInst, pszInfFile, pszInfSection, fFlags);
1540 else
1541 rc = VBoxWinDrvInstInstallEx(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fFlags);
1542
1543 VBoxWinDrvInstDestroy(hWinDrvInst);
1544 }
1545
1546 VBoxMsiCustomActionDataFree(pData);
1547 pData = NULL;
1548
1549 logStringF(hModule, "DriverInstall: Handling done (rc=%Rrc)", rc);
1550 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_DRIVER_INSTALL_BLOCKED /* Close enough */;
1551}
1552
1553UINT __stdcall DriverUninstall(MSIHANDLE hModule)
1554{
1555 logStringF(hModule, "Uninstalling driver ...");
1556
1557 PVBOXMSICUSTOMACTIONDATA pData;
1558 int rc = VBoxMsiCustomActionDataQuery(hModule, &pData);
1559 if (RT_FAILURE(rc))
1560 {
1561 logStringF(hModule, "DriverUninstall: No CustomActionData specified!");
1562 return ERROR_INVALID_PARAMETER;
1563 }
1564
1565#ifdef DEBUG
1566 for (size_t i = 0; i < pData->cEntries; i++)
1567 logStringF(hModule, "CustomActionData: %s = %s", pData->paEntries[i].pszKey, pData->paEntries[i].pszVal);
1568#endif
1569
1570 /* VBoxDrvUninstInfFile is optional. */
1571 const char *pszInfFile = VBoxMsiCustomActionDataFind(pData, "VBoxDrvUninstInfFile");
1572 /* VBoxDrvUninstInfSection is optional. */
1573 const char *pszInfSection = VBoxMsiCustomActionDataFind(pData, "VBoxDrvUninstInfSection");
1574 /* VBoxDrvUninstModel is optional. */
1575 const char *pszModel = VBoxMsiCustomActionDataFind(pData, "VBoxDrvUninstModel");
1576 /* VBoxDrvUninstPnpId is optional. */
1577 const char *pszPnpId = VBoxMsiCustomActionDataFind(pData, "VBoxDrvUninstPnpId");
1578
1579 VBOXWINDRVINST hWinDrvInst;
1580 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, 1 /* uVerbostiy */,
1581 &vboxWinDrvInstLogCallback, &hModule /* pvUser */);
1582 if (RT_SUCCESS(rc))
1583 {
1584 if (pszInfSection && *pszInfSection)
1585 rc = VBoxWinDrvInstUninstallExecuteInf(hWinDrvInst, pszInfFile, pszInfSection,
1586 VBOX_WIN_DRIVERINSTALL_F_NONE);
1587 else
1588 rc = VBoxWinDrvInstUninstall(hWinDrvInst, pszInfFile, pszModel, pszPnpId,
1589 VBOX_WIN_DRIVERINSTALL_F_NONE);
1590
1591 VBoxWinDrvInstDestroy(hWinDrvInst);
1592 }
1593
1594 VBoxMsiCustomActionDataFree(pData);
1595 pData = NULL;
1596
1597 logStringF(hModule, "DriverUninstall: Handling done (rc=%Rrc)", rc);
1598 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_DRIVER_STORE_DELETE_FAILED /* Close enough */;
1599}
1600
1601/**
1602 * Returns the host's platform architecture as a string.
1603 *
1604 * Sets public property VBOX_PLATFORM_ARCH to "x86", "amd64" or "arm64" on success.
1605 * Called from the MSI installer as custom action.
1606 *
1607 * We need this in order to distinguish the installer's build
1608 * architecture from the current host architecture. Also,
1609 * this deliberately is kept as a public property, so that it
1610 * can be overriden for testing purposes.
1611 *
1612 * @returns UINT as Windows error code.
1613 * @retval ERROR_INSTALL_PLATFORM_UNSUPPORTED if the platform is invalid or unsupported.
1614 * @param hModule Windows installer module handle.
1615 *
1616 * @note We don't use WIX' util.QueryNativeMachine, as it's apparently not available on Windows 10 >= 1709.
1617 */
1618UINT __stdcall GetPlatformArchitecture(MSIHANDLE hModule)
1619{
1620 const char *pszArch;
1621
1622 /* Only add supported platforms here.
1623 * Also, keep the string the same as kBuild's targets for easier comparrsion. */
1624 uint32_t const uNativeArch = RTSystemGetNativeArch();
1625 switch (uNativeArch)
1626 {
1627 case RT_ARCH_VAL_X86: pszArch = "x86"; break;
1628 case RT_ARCH_VAL_AMD64: pszArch = "amd64"; break;
1629 case RT_ARCH_VAL_ARM64: pszArch = "arm64"; break;
1630 default: pszArch = NULL; break;
1631 }
1632
1633 int rc;
1634 if (pszArch)
1635 rc = VBoxMsiSetPropUtf8(hModule, "VBOX_PLATFORM_ARCH", pszArch);
1636 else
1637 rc = VERR_NOT_SUPPORTED;
1638
1639 if (RT_SUCCESS(rc))
1640 logStringF(hModule, "GetPlatformArchitecture: Detected host architecture '%s'", pszArch);
1641 else
1642 logStringF(hModule, "GetPlatformArchitecture: Error detecting host architecture: %Rrc", rc);
1643
1644 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_INSTALL_PLATFORM_UNSUPPORTED;
1645}
1646
1647UINT __stdcall ServiceControl(MSIHANDLE hModule)
1648{
1649 PVBOXMSICUSTOMACTIONDATA pData;
1650 int rc = VBoxMsiCustomActionDataQuery(hModule, &pData);
1651 if (RT_FAILURE(rc))
1652 {
1653 logStringF(hModule, "ServiceControl: No CustomActionData specified!");
1654 return ERROR_INVALID_PARAMETER;
1655 }
1656
1657 const char *pszSvcCtlName = getCustomActionDataValue(hModule, pData, "VBoxSvcCtlName", false /* fOptional */);
1658 if (!pszSvcCtlName)
1659 return ERROR_INVALID_PARAMETER;
1660 const char *pszSvcCtlFn = getCustomActionDataValue(hModule, pData, "VBoxSvcCtlFn", false /* fOptional */);
1661 if (!pszSvcCtlFn)
1662 return ERROR_INVALID_PARAMETER;
1663
1664 VBOXWINDRVSVCFN enmFn = VBOXWINDRVSVCFN_INVALID; /* Shut up MSVC. */
1665 if (!RTStrICmp(pszSvcCtlFn, "start"))
1666 enmFn = VBOXWINDRVSVCFN_START;
1667 else if (!RTStrICmp(pszSvcCtlFn, "stop"))
1668 enmFn = VBOXWINDRVSVCFN_STOP;
1669 else if (!RTStrICmp(pszSvcCtlFn, "restart"))
1670 enmFn = VBOXWINDRVSVCFN_RESTART;
1671 else
1672 rc = VERR_INVALID_PARAMETER;
1673
1674 if (RT_SUCCESS(rc))
1675 {
1676 RTMSINTERVAL msTimeout = 0; /* Don't wait by default. */
1677 const char *pszTmp = getCustomActionDataValue(hModule, pData, "VBoxSvcCtlWaitMs", true /* fOptional */);
1678 if (pszTmp)
1679 msTimeout = RTStrToUInt32(pszTmp);
1680
1681 VBOXWINDRVINST hWinDrvInst;
1682 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, 1 /* uVerbostiy */,
1683 &vboxWinDrvInstLogCallback, &hModule /* pvUser */);
1684 if (RT_SUCCESS(rc))
1685 {
1686 rc = VBoxWinDrvInstControlServiceEx(hWinDrvInst, pszSvcCtlName, enmFn,
1687 msTimeout == 0 ? VBOXWINDRVSVCFN_F_NONE : VBOXWINDRVSVCFN_F_WAIT,
1688 msTimeout);
1689 VBoxWinDrvInstDestroy(hWinDrvInst);
1690 }
1691 }
1692
1693 VBoxMsiCustomActionDataFree(pData);
1694 pData = NULL;
1695
1696 logStringF(hModule, "ServiceControl: Handling done (rc=%Rrc)", rc);
1697 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_INVALID_SERVICE_CONTROL;
1698}
1699
1700#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP)
1701
1702/** @todo should use some real VBox app name */
1703#define VBOX_NETCFG_APP_NAME L"VirtualBox Installer"
1704#define VBOX_NETCFG_MAX_RETRIES 10
1705#define NETFLT_PT_INF_REL_PATH L"VBoxNetFlt.inf"
1706#define NETFLT_MP_INF_REL_PATH L"VBoxNetFltM.inf"
1707#define NETFLT_ID L"sun_VBoxNetFlt" /** @todo Needs to be changed (?). */
1708#define NETADP_ID L"sun_VBoxNetAdp" /** @todo Needs to be changed (?). */
1709
1710#define NETLWF_INF_NAME L"VBoxNetLwf.inf"
1711
1712static MSIHANDLE g_hCurrentModule = NULL;
1713
1714static UINT _uninstallNetFlt(MSIHANDLE hModule);
1715static UINT _uninstallNetLwf(MSIHANDLE hModule);
1716
1717static VOID vboxDrvLoggerCallback(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
1718{
1719 RT_NOREF1(pvContext);
1720 switch (enmSeverity)
1721 {
1722 case VBOXDRVCFG_LOG_SEVERITY_FLOW:
1723 case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
1724 break;
1725 case VBOXDRVCFG_LOG_SEVERITY_REL:
1726 if (g_hCurrentModule)
1727 logStringF(g_hCurrentModule, "%s", pszMsg);
1728 break;
1729 default:
1730 break;
1731 }
1732}
1733
1734static DECLCALLBACK(void) netCfgLoggerCallback(const char *pszString)
1735{
1736 if (g_hCurrentModule)
1737 logStringF(g_hCurrentModule, "%s", pszString);
1738}
1739
1740static VOID netCfgLoggerDisable()
1741{
1742 if (g_hCurrentModule)
1743 {
1744 VBoxNetCfgWinSetLogging(NULL);
1745 g_hCurrentModule = NULL;
1746 }
1747}
1748
1749static VOID netCfgLoggerEnable(MSIHANDLE hModule)
1750{
1751 NonStandardAssert(hModule);
1752
1753 if (g_hCurrentModule)
1754 netCfgLoggerDisable();
1755
1756 g_hCurrentModule = hModule;
1757
1758 VBoxNetCfgWinSetLogging(netCfgLoggerCallback);
1759 /* uncomment next line if you want to add logging information from VBoxDrvCfg.cpp */
1760// VBoxDrvCfgLoggerSet(vboxDrvLoggerCallback, NULL);
1761}
1762
1763static UINT errorConvertFromHResult(MSIHANDLE hModule, HRESULT hr)
1764{
1765 UINT uRet;
1766 switch (hr)
1767 {
1768 case S_OK:
1769 uRet = ERROR_SUCCESS;
1770 break;
1771
1772 case NETCFG_S_REBOOT:
1773 {
1774 logStringF(hModule, "Reboot required, setting REBOOT property to \"force\"");
1775 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
1776 if (hr2 != ERROR_SUCCESS)
1777 logStringF(hModule, "Failed to set REBOOT property, error = %#x", hr2);
1778 uRet = ERROR_SUCCESS; /* Never fail here. */
1779 break;
1780 }
1781
1782 default:
1783 logStringF(hModule, "Converting unhandled HRESULT (%#x) to ERROR_GEN_FAILURE", hr);
1784 uRet = ERROR_GEN_FAILURE;
1785 }
1786 return uRet;
1787}
1788
1789static MSIHANDLE createNetCfgLockedMsgRecord(MSIHANDLE hModule)
1790{
1791 MSIHANDLE hRecord = MsiCreateRecord(2);
1792 if (hRecord)
1793 {
1794 UINT uErr = MsiRecordSetInteger(hRecord, 1, 25001);
1795 if (uErr != ERROR_SUCCESS)
1796 {
1797 logStringF(hModule, "createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = %#x", uErr);
1798 MsiCloseHandle(hRecord);
1799 hRecord = NULL;
1800 }
1801 }
1802 else
1803 logStringF(hModule, "createNetCfgLockedMsgRecord: Failed to create a record");
1804
1805 return hRecord;
1806}
1807
1808static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite)
1809{
1810 MSIHANDLE hMsg = NULL;
1811 UINT uErr = ERROR_GEN_FAILURE;
1812 int MsgResult;
1813 int cRetries = 0;
1814
1815 do
1816 {
1817 LPWSTR lpszLockedBy;
1818 HRESULT hr = VBoxNetCfgWinQueryINetCfg(ppnc, bWrite, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy);
1819 if (hr != NETCFG_E_NO_WRITE_LOCK)
1820 {
1821 if (FAILED(hr))
1822 logStringF(hModule, "doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = %#x", hr);
1823 uErr = errorConvertFromHResult(hModule, hr);
1824 break;
1825 }
1826
1827 /* hr == NETCFG_E_NO_WRITE_LOCK */
1828
1829 if (!lpszLockedBy)
1830 {
1831 logStringF(hModule, "doNetCfgInit: lpszLockedBy == NULL, breaking");
1832 break;
1833 }
1834
1835 /* on vista the 6to4svc.dll periodically maintains the lock for some reason,
1836 * if this is the case, increase the wait period by retrying multiple times
1837 * NOTE: we could alternatively increase the wait timeout,
1838 * however it seems unneeded for most cases, e.g. in case some network connection property
1839 * dialog is opened, it would be better to post a notification to the user as soon as possible
1840 * rather than waiting for a longer period of time before displaying it */
1841 if ( cRetries < VBOX_NETCFG_MAX_RETRIES
1842 && RTUtf16ICmpAscii(lpszLockedBy, "6to4svc.dll") == 0)
1843 {
1844 cRetries++;
1845 logStringF(hModule, "doNetCfgInit: lpszLockedBy is 6to4svc.dll, retrying %d out of %d", cRetries, VBOX_NETCFG_MAX_RETRIES);
1846 MsgResult = IDRETRY;
1847 }
1848 else
1849 {
1850 if (!hMsg)
1851 {
1852 hMsg = createNetCfgLockedMsgRecord(hModule);
1853 if (!hMsg)
1854 {
1855 logStringF(hModule, "doNetCfgInit: Failed to create a message record, breaking");
1856 CoTaskMemFree(lpszLockedBy);
1857 break;
1858 }
1859 }
1860
1861 UINT rTmp = MsiRecordSetStringW(hMsg, 2, lpszLockedBy);
1862 NonStandardAssert(rTmp == ERROR_SUCCESS);
1863 if (rTmp != ERROR_SUCCESS)
1864 {
1865 logStringF(hModule, "doNetCfgInit: MsiRecordSetStringW failed, error = #%x", rTmp);
1866 CoTaskMemFree(lpszLockedBy);
1867 break;
1868 }
1869
1870 MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg);
1871 NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL);
1872 logStringF(hModule, "doNetCfgInit: MsiProcessMessage returned (%#x)", MsgResult);
1873 }
1874 CoTaskMemFree(lpszLockedBy);
1875 } while(MsgResult == IDRETRY);
1876
1877 if (hMsg)
1878 MsiCloseHandle(hMsg);
1879
1880 return uErr;
1881}
1882#endif /* defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) */
1883
1884#ifdef VBOX_WITH_NETFLT
1885static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf,
1886 OUT LPWSTR pwszMpInf, DWORD cwcMpInf)
1887{
1888 DWORD cwcEffBuf = cwcPtInf - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)) / sizeof(WCHAR);
1889 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &cwcEffBuf);
1890 if ( uErr == ERROR_SUCCESS
1891 && cwcEffBuf > 0)
1892 {
1893 int vrc = RTUtf16Copy(pwszMpInf, cwcMpInf, pwszPtInf);
1894 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1895
1896 vrc = RTUtf16Cat(pwszPtInf, cwcPtInf, NETFLT_PT_INF_REL_PATH);
1897 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1898 logStringF(hModule, "vboxNetFltQueryInfArray: INF 1: %ls", pwszPtInf);
1899
1900 vrc = RTUtf16Cat(pwszMpInf, cwcMpInf, NETFLT_MP_INF_REL_PATH);
1901 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1902 logStringF(hModule, "vboxNetFltQueryInfArray: INF 2: %ls", pwszMpInf);
1903 }
1904 else if (uErr != ERROR_SUCCESS)
1905 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1906 else
1907 {
1908 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1909 uErr = ERROR_GEN_FAILURE;
1910 }
1911
1912 return uErr;
1913}
1914
1915static UINT _uninstallNetFlt(MSIHANDLE hModule)
1916{
1917 INetCfg *pNetCfg;
1918 UINT uErr;
1919
1920 netCfgLoggerEnable(hModule);
1921
1922 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1923
1924 __try
1925 {
1926 logStringF(hModule, "Uninstalling NetFlt");
1927
1928 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1929 if (uErr == ERROR_SUCCESS)
1930 {
1931 HRESULT hr = VBoxNetCfgWinNetFltUninstall(pNetCfg);
1932 if (hr != S_OK)
1933 logStringF(hModule, "UninstallNetFlt: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1934
1935 uErr = errorConvertFromHResult(hModule, hr);
1936
1937 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1938
1939 logStringF(hModule, "Uninstalling NetFlt done, error = %#x", uErr);
1940 }
1941 else
1942 logStringF(hModule, "UninstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1943 }
1944 __finally
1945 {
1946 if (bOldIntMode)
1947 {
1948 /* The prev mode != FALSE, i.e. non-interactive. */
1949 SetupSetNonInteractiveMode(bOldIntMode);
1950 }
1951 netCfgLoggerDisable();
1952 }
1953
1954 /* Never fail the uninstall even if we did not succeed. */
1955 return ERROR_SUCCESS;
1956}
1957#endif /* VBOX_WITH_NETFLT */
1958
1959UINT __stdcall UninstallNetFlt(MSIHANDLE hModule)
1960{
1961#ifdef VBOX_WITH_NETFLT
1962 _uninstallNetLwf(hModule);
1963 return _uninstallNetFlt(hModule);
1964#else
1965 RT_NOREF(hModule);
1966 return ERROR_SUCCESS;
1967#endif
1968}
1969
1970#ifdef VBOX_WITH_NETFLT
1971static UINT _installNetFlt(MSIHANDLE hModule)
1972{
1973 UINT uErr;
1974 INetCfg *pNetCfg;
1975
1976 netCfgLoggerEnable(hModule);
1977
1978 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1979
1980 __try
1981 {
1982
1983 logStringF(hModule, "InstallNetFlt: Installing NetFlt");
1984
1985 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1986 if (uErr == ERROR_SUCCESS)
1987 {
1988 WCHAR wszPtInf[MAX_PATH];
1989 WCHAR wszMpInf[MAX_PATH];
1990 uErr = vboxNetFltQueryInfArray(hModule, wszPtInf, RT_ELEMENTS(wszPtInf), wszMpInf, RT_ELEMENTS(wszMpInf));
1991 if (uErr == ERROR_SUCCESS)
1992 {
1993 LPCWSTR const apwszInfs[] = { wszPtInf, wszMpInf };
1994 HRESULT hr = VBoxNetCfgWinNetFltInstall(pNetCfg, &apwszInfs[0], RT_ELEMENTS(apwszInfs));
1995 if (FAILED(hr))
1996 logStringF(hModule, "InstallNetFlt: VBoxNetCfgWinNetFltInstall failed, error = %#x", hr);
1997
1998 uErr = errorConvertFromHResult(hModule, hr);
1999 }
2000 else
2001 logStringF(hModule, "InstallNetFlt: vboxNetFltQueryInfArray failed, error = %#x", uErr);
2002
2003 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2004
2005 logStringF(hModule, "InstallNetFlt: Done");
2006 }
2007 else
2008 logStringF(hModule, "InstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
2009 }
2010 __finally
2011 {
2012 if (bOldIntMode)
2013 {
2014 /* The prev mode != FALSE, i.e. non-interactive. */
2015 SetupSetNonInteractiveMode(bOldIntMode);
2016 }
2017 netCfgLoggerDisable();
2018 }
2019
2020 /* Never fail the install even if we did not succeed. */
2021 return ERROR_SUCCESS;
2022}
2023#endif /* VBOX_WITH_NETFLT */
2024
2025UINT __stdcall InstallNetFlt(MSIHANDLE hModule)
2026{
2027#ifdef VBOX_WITH_NETFLT
2028 _uninstallNetLwf(hModule);
2029 return _installNetFlt(hModule);
2030#else
2031 RT_NOREF(hModule);
2032 return ERROR_SUCCESS;
2033#endif
2034}
2035
2036#ifdef VBOX_WITH_NETFLT
2037static UINT _uninstallNetLwf(MSIHANDLE hModule)
2038{
2039 INetCfg *pNetCfg;
2040 UINT uErr;
2041
2042 netCfgLoggerEnable(hModule);
2043
2044 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2045
2046 __try
2047 {
2048 logStringF(hModule, "Uninstalling NetLwf");
2049
2050 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2051 if (uErr == ERROR_SUCCESS)
2052 {
2053 HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg);
2054 if (hr != S_OK)
2055 logStringF(hModule, "UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
2056
2057 uErr = errorConvertFromHResult(hModule, hr);
2058
2059 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2060
2061 logStringF(hModule, "Uninstalling NetLwf done, error = %#x", uErr);
2062 }
2063 else
2064 logStringF(hModule, "UninstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
2065 }
2066 __finally
2067 {
2068 if (bOldIntMode)
2069 {
2070 /* The prev mode != FALSE, i.e. non-interactive. */
2071 SetupSetNonInteractiveMode(bOldIntMode);
2072 }
2073 netCfgLoggerDisable();
2074 }
2075
2076 /* Never fail the uninstall even if we did not succeed. */
2077 return ERROR_SUCCESS;
2078}
2079#endif /* VBOX_WITH_NETFLT */
2080
2081UINT __stdcall UninstallNetLwf(MSIHANDLE hModule)
2082{
2083#ifdef VBOX_WITH_NETFLT
2084 _uninstallNetFlt(hModule);
2085 return _uninstallNetLwf(hModule);
2086#else
2087 RT_NOREF(hModule);
2088 return ERROR_SUCCESS;
2089#endif
2090}
2091
2092#ifdef VBOX_WITH_NETFLT
2093static UINT _installNetLwf(MSIHANDLE hModule)
2094{
2095 UINT uErr;
2096 INetCfg *pNetCfg;
2097
2098 netCfgLoggerEnable(hModule);
2099
2100 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2101
2102 __try
2103 {
2104
2105 logStringF(hModule, "InstallNetLwf: Installing NetLwf");
2106
2107 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2108 if (uErr == ERROR_SUCCESS)
2109 {
2110 WCHAR wszInf[MAX_PATH];
2111 DWORD cwcInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1;
2112 uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cwcInf);
2113 if (uErr == ERROR_SUCCESS)
2114 {
2115 if (cwcInf)
2116 {
2117 if (wszInf[cwcInf - 1] != L'\\')
2118 {
2119 wszInf[cwcInf++] = L'\\';
2120 wszInf[cwcInf] = L'\0';
2121 }
2122
2123 int vrc = RTUtf16Cat(wszInf, RT_ELEMENTS(wszInf), NETLWF_INF_NAME);
2124 AssertRC(vrc);
2125
2126 HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf);
2127 if (FAILED(hr))
2128 logStringF(hModule, "InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = %#x", hr);
2129
2130 uErr = errorConvertFromHResult(hModule, hr);
2131 }
2132 else
2133 {
2134 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
2135 uErr = ERROR_GEN_FAILURE;
2136 }
2137 }
2138 else
2139 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
2140
2141 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2142
2143 logStringF(hModule, "InstallNetLwf: Done");
2144 }
2145 else
2146 logStringF(hModule, "InstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
2147 }
2148 __finally
2149 {
2150 if (bOldIntMode)
2151 {
2152 /* The prev mode != FALSE, i.e. non-interactive. */
2153 SetupSetNonInteractiveMode(bOldIntMode);
2154 }
2155 netCfgLoggerDisable();
2156 }
2157
2158 /* Never fail the install even if we did not succeed. */
2159 return ERROR_SUCCESS;
2160}
2161#endif /* VBOX_WITH_NETFLT */
2162
2163UINT __stdcall InstallNetLwf(MSIHANDLE hModule)
2164{
2165#ifdef VBOX_WITH_NETFLT
2166 _uninstallNetFlt(hModule);
2167 return _installNetLwf(hModule);
2168#else
2169 RT_NOREF(hModule);
2170 return ERROR_SUCCESS;
2171#endif
2172}
2173
2174
2175#if 0 /** @todo r=andy Remove this? */
2176static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
2177{
2178 WCHAR DevName[256];
2179 DWORD winEr;
2180
2181 if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev,
2182 SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
2183 NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
2184 (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/
2185 sizeof(DevName), /* IN DWORD PropertyBufferSize,*/
2186 NULL /*OUT PDWORD RequiredSize OPTIONAL*/
2187 ))
2188 {
2189 HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, pDev,
2190 DICS_FLAG_GLOBAL, /* IN DWORD Scope,*/
2191 0, /*IN DWORD HwProfile, */
2192 DIREG_DRV, /* IN DWORD KeyType, */
2193 KEY_READ /*IN REGSAM samDesired*/
2194 );
2195 NonStandardAssert(hKey != INVALID_HANDLE_VALUE);
2196 if (hKey != INVALID_HANDLE_VALUE)
2197 {
2198 WCHAR guid[50];
2199 DWORD cbGuid=sizeof(guid);
2200 winEr = RegQueryValueExW(hKey,
2201 L"NetCfgInstanceId", /*__in_opt LPCTSTR lpValueName,*/
2202 NULL, /*__reserved LPDWORD lpReserved,*/
2203 NULL, /*__out_opt LPDWORD lpType,*/
2204 (LPBYTE)guid, /*__out_opt LPBYTE lpData,*/
2205 &cbGuid /*guid__inout_opt LPDWORD lpcbData*/
2206 );
2207 NonStandardAssert(winEr == ERROR_SUCCESS);
2208 if (winEr == ERROR_SUCCESS)
2209 {
2210 WCHAR ConnectoinName[128];
2211 ULONG cbName = sizeof(ConnectoinName);
2212
2213 HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(DevName, ConnectoinName, &cbName);
2214 NonStandardAssert(hr == S_OK);
2215 if (SUCCEEDED(hr))
2216 {
2217 hr = VBoxNetCfgWinRenameConnection(guid, ConnectoinName);
2218 NonStandardAssert(hr == S_OK);
2219 }
2220 }
2221 }
2222 RegCloseKey(hKey);
2223 }
2224 else
2225 {
2226 NonStandardAssert(0);
2227 }
2228
2229 return TRUE;
2230}
2231#endif /* 0 */
2232
2233#ifdef VBOX_WITH_NETADP
2234static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName)
2235{
2236 netCfgLoggerEnable(hModule);
2237
2238 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2239
2240 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface");
2241
2242 HRESULT hr = E_FAIL;
2243 GUID guid;
2244 WCHAR wszMpInf[MAX_PATH];
2245 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2246 LPCWSTR pwszInfPath = NULL;
2247 bool fIsFile = false;
2248 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2249 if (uErr == ERROR_SUCCESS)
2250 {
2251 if (cwcMpInf)
2252 {
2253 logStringF(hModule, "CreateHostOnlyInterface: NetAdpDir property = %ls", wszMpInf);
2254 if (wszMpInf[cwcMpInf - 1] != L'\\')
2255 {
2256 wszMpInf[cwcMpInf++] = L'\\';
2257 wszMpInf[cwcMpInf] = L'\0';
2258 }
2259
2260 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2261 AssertRC(vrc);
2262
2263 pwszInfPath = wszMpInf;
2264 fIsFile = true;
2265
2266 logStringF(hModule, "CreateHostOnlyInterface: Resulting INF path = %ls", pwszInfPath);
2267 }
2268 else
2269 logStringF(hModule, "CreateHostOnlyInterface: VBox installation path is empty");
2270 }
2271 else
2272 logStringF(hModule, "CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = %#x", uErr);
2273
2274 /* Make sure the inf file is installed. */
2275 if (pwszInfPath != NULL && fIsFile)
2276 {
2277 logStringF(hModule, "CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%ls)", pwszInfPath);
2278 hr = VBoxDrvCfgInfInstall(pwszInfPath);
2279 logStringF(hModule, "CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns %#x", hr);
2280 if (FAILED(hr))
2281 logStringF(hModule, "CreateHostOnlyInterface: Failed to install INF file, error = %#x", hr);
2282 }
2283
2284 if (SUCCEEDED(hr))
2285 {
2286 //first, try to update Host Only Network Interface
2287 BOOL fRebootRequired = FALSE;
2288 hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2289 if (SUCCEEDED(hr))
2290 {
2291 if (fRebootRequired)
2292 {
2293 logStringF(hModule, "CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force");
2294 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2295 if (hr2 != ERROR_SUCCESS)
2296 logStringF(hModule, "CreateHostOnlyInterface: Failed to set REBOOT property for update, error = %#x", hr2);
2297 }
2298 }
2299 else
2300 {
2301 //in fail case call CreateHostOnlyInterface
2302 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2303 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface");
2304# ifdef VBOXNETCFG_DELAYEDRENAME
2305 BSTR devId;
2306 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL);
2307# else /* !VBOXNETCFG_DELAYEDRENAME */
2308 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL);
2309# endif /* !VBOXNETCFG_DELAYEDRENAME */
2310 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr);
2311 if (SUCCEEDED(hr))
2312 {
2313 ULONG ip = inet_addr("192.168.56.1");
2314 ULONG mask = inet_addr("255.255.255.0");
2315 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig");
2316 hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
2317 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr);
2318 if (FAILED(hr))
2319 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr);
2320# ifdef VBOXNETCFG_DELAYEDRENAME
2321 hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL);
2322 if (FAILED(hr))
2323 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr);
2324 SysFreeString(devId);
2325# endif /* VBOXNETCFG_DELAYEDRENAME */
2326 }
2327 else
2328 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr);
2329 }
2330 }
2331
2332 if (SUCCEEDED(hr))
2333 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface done");
2334
2335 /* Restore original setup mode. */
2336 logStringF(hModule, "CreateHostOnlyInterface: Almost done...");
2337 if (fSetupModeInteractive)
2338 SetupSetNonInteractiveMode(fSetupModeInteractive);
2339
2340 netCfgLoggerDisable();
2341
2342 logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)");
2343 /* Never fail the install even if we did not succeed. */
2344 return ERROR_SUCCESS;
2345}
2346#endif /* VBOX_WITH_NETADP */
2347
2348UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule)
2349{
2350#ifdef VBOX_WITH_NETADP
2351 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp.inf");
2352#else
2353 RT_NOREF(hModule);
2354 return ERROR_SUCCESS;
2355#endif
2356}
2357
2358UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule)
2359{
2360#ifdef VBOX_WITH_NETADP
2361# if 0 /* Trick for allowing the debugger to be attached. */
2362 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
2363 {
2364 logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId());
2365 Sleep(1001);
2366 }
2367 Sleep(1002);
2368 __debugbreak();
2369# endif
2370 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf");
2371#else /* !VBOX_WITH_NETADP */
2372 RT_NOREF(hModule);
2373 return ERROR_SUCCESS;
2374#endif
2375}
2376
2377#ifdef VBOX_WITH_NETADP
2378static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2379{
2380 netCfgLoggerEnable(hModule);
2381
2382 logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces");
2383
2384 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2385
2386 HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(pwszId);
2387 if (SUCCEEDED(hr))
2388 {
2389 hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", pwszId, SUOI_FORCEDELETE/* could be SUOI_FORCEDELETE */);
2390 if (FAILED(hr))
2391 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files");
2392 else
2393 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully");
2394 }
2395 else
2396 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = %#x", hr);
2397
2398 /* Restore original setup mode. */
2399 if (fSetupModeInteractive)
2400 SetupSetNonInteractiveMode(fSetupModeInteractive);
2401
2402 netCfgLoggerDisable();
2403
2404 /* Never fail the uninstall even if we did not succeed. */
2405 return ERROR_SUCCESS;
2406}
2407#endif /* VBOX_WITH_NETADP */
2408
2409UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule)
2410{
2411#ifdef VBOX_WITH_NETADP
2412 return _removeHostOnlyInterfaces(hModule, NETADP_ID);
2413#else
2414 RT_NOREF(hModule);
2415 return ERROR_SUCCESS;
2416#endif
2417}
2418
2419#ifdef VBOX_WITH_NETADP
2420static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2421{
2422 netCfgLoggerEnable(hModule);
2423
2424 logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces");
2425
2426 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2427
2428 HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
2429 if (SUCCEEDED(hr))
2430 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = %#x", hr);
2431 else
2432 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces failed, hr = %#x", hr);
2433
2434 /* Restore original setup mode. */
2435 if (fSetupModeInteractive)
2436 SetupSetNonInteractiveMode(fSetupModeInteractive);
2437
2438 netCfgLoggerDisable();
2439
2440 /* Never fail the uninstall even if we did not succeed. */
2441 return ERROR_SUCCESS;
2442}
2443#endif /* VBOX_WITH_NETADP */
2444
2445UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule)
2446{
2447#ifdef VBOX_WITH_NETADP
2448 return _stopHostOnlyInterfaces(hModule, NETADP_ID);
2449#else
2450 RT_NOREF(hModule);
2451 return ERROR_SUCCESS;
2452#endif
2453}
2454
2455#ifdef VBOX_WITH_NETADP
2456static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId)
2457{
2458 netCfgLoggerEnable(hModule);
2459
2460 logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces");
2461
2462 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2463
2464 WCHAR wszMpInf[MAX_PATH];
2465 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2466 LPCWSTR pwszInfPath = NULL;
2467 bool fIsFile = false;
2468 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2469 if (uErr == ERROR_SUCCESS)
2470 {
2471 if (cwcMpInf)
2472 {
2473 logStringF(hModule, "UpdateHostOnlyInterfaces: NetAdpDir property = %ls", wszMpInf);
2474 if (wszMpInf[cwcMpInf - 1] != L'\\')
2475 {
2476 wszMpInf[cwcMpInf++] = L'\\';
2477 wszMpInf[cwcMpInf] = L'\0';
2478 }
2479
2480 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2481 AssertRC(vrc);
2482 pwszInfPath = wszMpInf;
2483 fIsFile = true;
2484
2485 logStringF(hModule, "UpdateHostOnlyInterfaces: Resulting INF path = %ls", pwszInfPath);
2486
2487 DWORD attrFile = GetFileAttributesW(pwszInfPath);
2488 if (attrFile == INVALID_FILE_ATTRIBUTES)
2489 {
2490 DWORD dwErr = GetLastError();
2491 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" not found, dwErr=%ld", pwszInfPath, dwErr);
2492 }
2493 else
2494 {
2495 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" exists", pwszInfPath);
2496
2497 BOOL fRebootRequired = FALSE;
2498 HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2499 if (SUCCEEDED(hr))
2500 {
2501 if (fRebootRequired)
2502 {
2503 logStringF(hModule, "UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force");
2504 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2505 if (hr2 != ERROR_SUCCESS)
2506 logStringF(hModule, "UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = %#x", hr2);
2507 }
2508 }
2509 else
2510 logStringF(hModule, "UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2511 }
2512 }
2513 else
2514 logStringF(hModule, "UpdateHostOnlyInterfaces: VBox installation path is empty");
2515 }
2516 else
2517 logStringF(hModule, "UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = %#x", uErr);
2518
2519 /* Restore original setup mode. */
2520 if (fSetupModeInteractive)
2521 SetupSetNonInteractiveMode(fSetupModeInteractive);
2522
2523 netCfgLoggerDisable();
2524
2525 /* Never fail the update even if we did not succeed. */
2526 return ERROR_SUCCESS;
2527}
2528#endif /* VBOX_WITH_NETADP */
2529
2530UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2531{
2532#ifdef VBOX_WITH_NETADP
2533 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp.inf", NETADP_ID);
2534#else
2535 RT_NOREF(hModule);
2536 return ERROR_SUCCESS;
2537#endif
2538}
2539
2540UINT __stdcall Ndis6UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2541{
2542#ifdef VBOX_WITH_NETADP
2543 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID);
2544#else
2545 RT_NOREF(hModule);
2546 return ERROR_SUCCESS;
2547#endif
2548}
2549
2550#ifdef VBOX_WITH_NETADP
2551static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId)
2552{
2553 INetCfg *pNetCfg;
2554 UINT uErr;
2555
2556 netCfgLoggerEnable(hModule);
2557
2558 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2559
2560 __try
2561 {
2562 logStringF(hModule, "Uninstalling NetAdp");
2563
2564 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2565 if (uErr == ERROR_SUCCESS)
2566 {
2567 HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId);
2568 if (hr != S_OK)
2569 logStringF(hModule, "UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
2570
2571 uErr = errorConvertFromHResult(hModule, hr);
2572
2573 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2574
2575 logStringF(hModule, "Uninstalling NetAdp done, error = %#x", uErr);
2576 }
2577 else
2578 logStringF(hModule, "UninstallNetAdp: doNetCfgInit failed, error = %#x", uErr);
2579 }
2580 __finally
2581 {
2582 if (bOldIntMode)
2583 {
2584 /* The prev mode != FALSE, i.e. non-interactive. */
2585 SetupSetNonInteractiveMode(bOldIntMode);
2586 }
2587 netCfgLoggerDisable();
2588 }
2589
2590 /* Never fail the uninstall even if we did not succeed. */
2591 return ERROR_SUCCESS;
2592}
2593#endif /* VBOX_WITH_NETADP */
2594
2595UINT __stdcall UninstallNetAdp(MSIHANDLE hModule)
2596{
2597#ifdef VBOX_WITH_NETADP
2598 return _uninstallNetAdp(hModule, NETADP_ID);
2599#else
2600 RT_NOREF(hModule);
2601 return ERROR_SUCCESS;
2602#endif
2603}
2604
2605static bool isTAPDevice(const WCHAR *pwszGUID)
2606{
2607 HKEY hNetcard;
2608 bool bIsTapDevice = false;
2609 LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2610 L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
2611 0, KEY_READ, &hNetcard);
2612 if (lStatus != ERROR_SUCCESS)
2613 return false;
2614
2615 int i = 0;
2616 for (;;)
2617 {
2618 WCHAR wszEnumName[256];
2619 WCHAR wszNetCfgInstanceId[256];
2620 DWORD dwKeyType;
2621 HKEY hNetCardGUID;
2622
2623 DWORD dwLen = sizeof(wszEnumName);
2624 lStatus = RegEnumKeyExW(hNetcard, i, wszEnumName, &dwLen, NULL, NULL, NULL, NULL);
2625 if (lStatus != ERROR_SUCCESS)
2626 break;
2627
2628 lStatus = RegOpenKeyExW(hNetcard, wszEnumName, 0, KEY_READ, &hNetCardGUID);
2629 if (lStatus == ERROR_SUCCESS)
2630 {
2631 dwLen = sizeof(wszNetCfgInstanceId);
2632 lStatus = RegQueryValueExW(hNetCardGUID, L"NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)wszNetCfgInstanceId, &dwLen);
2633 if ( lStatus == ERROR_SUCCESS
2634 && dwKeyType == REG_SZ)
2635 {
2636 WCHAR wszNetProductName[256];
2637 WCHAR wszNetProviderName[256];
2638
2639 wszNetProductName[0] = 0;
2640 dwLen = sizeof(wszNetProductName);
2641 lStatus = RegQueryValueExW(hNetCardGUID, L"ProductName", NULL, &dwKeyType, (LPBYTE)wszNetProductName, &dwLen);
2642
2643 wszNetProviderName[0] = 0;
2644 dwLen = sizeof(wszNetProviderName);
2645 lStatus = RegQueryValueExW(hNetCardGUID, L"ProviderName", NULL, &dwKeyType, (LPBYTE)wszNetProviderName, &dwLen);
2646
2647 if ( !RTUtf16Cmp(wszNetCfgInstanceId, pwszGUID)
2648 && !RTUtf16Cmp(wszNetProductName, L"VirtualBox TAP Adapter")
2649 && ( (!RTUtf16Cmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */
2650 || (!RTUtf16Cmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */
2651 || (!RTUtf16Cmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */
2652 )
2653 )
2654 {
2655 bIsTapDevice = true;
2656 RegCloseKey(hNetCardGUID);
2657 break;
2658 }
2659 }
2660 RegCloseKey(hNetCardGUID);
2661 }
2662 ++i;
2663 }
2664
2665 RegCloseKey(hNetcard);
2666 return bIsTapDevice;
2667}
2668
2669/** @todo r=andy BUGBUG WTF! Why do we a) set the rc to 0 (success), and b) need this macro at all!?
2670 *
2671 * @todo r=bird: Because it's returning a bool, not int? The return code is
2672 * ignored anyway, both internally in removeNetworkInterface and in it's caller.
2673 * There is similar code in VBoxNetCfg.cpp, which is probably where it was copied from. */
2674#define SetErrBreak(args) \
2675 if (1) { \
2676 rc = 0; \
2677 logStringF args; \
2678 break; \
2679 } else do {} while (0)
2680
2681int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID)
2682{
2683 int rc = 1;
2684 do /* break-loop */
2685 {
2686 WCHAR wszPnPInstanceId[512] = {0};
2687
2688 /* We have to find the device instance ID through a registry search */
2689
2690 HKEY hkeyNetwork = 0;
2691 HKEY hkeyConnection = 0;
2692
2693 do /* break-loop */
2694 {
2695 WCHAR wszRegLocation[256];
2696 RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
2697 "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", pwszGUID);
2698 LONG lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
2699 if (lrc != ERROR_SUCCESS || !hkeyNetwork)
2700 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [1]",
2701 wszRegLocation, lrc));
2702
2703 lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
2704 if (lrc != ERROR_SUCCESS || !hkeyConnection)
2705 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [2]",
2706 wszRegLocation, lrc));
2707
2708 DWORD len = sizeof(wszPnPInstanceId);
2709 DWORD dwKeyType;
2710 lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len);
2711 if (lrc != ERROR_SUCCESS || dwKeyType != REG_SZ)
2712 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [3]",
2713 wszRegLocation, lrc));
2714 }
2715 while (0);
2716
2717 if (hkeyConnection)
2718 RegCloseKey(hkeyConnection);
2719 if (hkeyNetwork)
2720 RegCloseKey(hkeyNetwork);
2721
2722 /*
2723 * Now we are going to enumerate all network devices and
2724 * wait until we encounter the right device instance ID
2725 */
2726
2727 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2728 BOOL fResult;
2729
2730 do /* break-loop */
2731 {
2732 /* initialize the structure size */
2733 SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
2734
2735 /* copy the net class GUID */
2736 GUID netGuid;
2737 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2738
2739 /* return a device info set contains all installed devices of the Net class */
2740 hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
2741 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2742 {
2743 logStringF(hModule, "VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError());
2744 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2745 }
2746
2747 /* enumerate the driver info list */
2748 BOOL fFoundDevice = FALSE;
2749 for (DWORD index = 0;; index++)
2750 {
2751 fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
2752 if (!fResult)
2753 {
2754 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2755 break;
2756 continue;
2757 }
2758
2759 /* try to get the hardware ID registry property */
2760 WCHAR *pwszDeviceHwid;
2761 DWORD size = 0;
2762 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2763 &DeviceInfoData,
2764 SPDRP_HARDWAREID,
2765 NULL,
2766 NULL,
2767 0,
2768 &size);
2769 if (!fResult)
2770 {
2771 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2772 continue;
2773
2774 pwszDeviceHwid = (WCHAR *)RTMemAllocZ(size);
2775 if (!pwszDeviceHwid)
2776 continue;
2777
2778 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2779 &DeviceInfoData,
2780 SPDRP_HARDWAREID,
2781 NULL,
2782 (PBYTE)pwszDeviceHwid,
2783 size,
2784 &size);
2785 if (!fResult)
2786 {
2787 RTMemFree(pwszDeviceHwid);
2788 continue;
2789 }
2790 }
2791 else
2792 {
2793 /* something is wrong. This shouldn't have worked with a NULL buffer */
2794 continue;
2795 }
2796
2797 for (WCHAR *t = pwszDeviceHwid;
2798 *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)];
2799 t += RTUtf16Len(t) + 1)
2800 {
2801 if (RTUtf16ICmpAscii(t, "vboxtap") == 0)
2802 {
2803 /* get the device instance ID */
2804 WCHAR wszDevID[MAX_DEVICE_ID_LEN];
2805 if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2806 {
2807 /* compare to what we determined before */
2808 if (RTUtf16Cmp(wszDevID, wszPnPInstanceId) == 0)
2809 {
2810 fFoundDevice = TRUE;
2811 break;
2812 }
2813 }
2814 }
2815 }
2816
2817 RTMemFree(pwszDeviceHwid);
2818
2819 if (fFoundDevice)
2820 break;
2821 }
2822
2823 if (fFoundDevice)
2824 {
2825 fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
2826 if (!fResult)
2827 {
2828 logStringF(hModule, "VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError());
2829 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2830 }
2831
2832 fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2833 if (!fResult)
2834 {
2835 logStringF(hModule, "VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError());
2836 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2837 }
2838 }
2839 else
2840 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network device not found!"));
2841 } while (0);
2842
2843 /* clean up the device info set */
2844 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2845 SetupDiDestroyDeviceInfoList(hDeviceInfo);
2846 } while (0);
2847 return rc;
2848}
2849
2850UINT __stdcall UninstallTAPInstances(MSIHANDLE hModule)
2851{
2852 static const wchar_t s_wszNetworkKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
2853 HKEY hCtrlNet;
2854
2855 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet);
2856 if (lrc == ERROR_SUCCESS)
2857 {
2858 logStringF(hModule, "VBox HostInterfaces: Enumerating interfaces ...");
2859 for (int i = 0; ; ++i)
2860 {
2861 WCHAR wszNetworkGUID[256] = { 0 };
2862 DWORD dwLen = (DWORD)sizeof(wszNetworkGUID);
2863 lrc = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL);
2864 if (lrc != ERROR_SUCCESS)
2865 {
2866 switch (lrc)
2867 {
2868 case ERROR_NO_MORE_ITEMS:
2869 logStringF(hModule, "VBox HostInterfaces: No interfaces found.");
2870 break;
2871 default:
2872 logStringF(hModule, "VBox HostInterfaces: Enumeration failed: %ld", lrc);
2873 break;
2874 }
2875 break;
2876 }
2877
2878 if (isTAPDevice(wszNetworkGUID))
2879 {
2880 logStringF(hModule, "VBox HostInterfaces: Removing interface \"%ls\" ...", wszNetworkGUID);
2881 removeNetworkInterface(hModule, wszNetworkGUID);
2882 lrc = RegDeleteKeyW(hCtrlNet, wszNetworkGUID);
2883 }
2884 }
2885 RegCloseKey(hCtrlNet);
2886 logStringF(hModule, "VBox HostInterfaces: Removing interfaces done.");
2887 }
2888 return ERROR_SUCCESS;
2889}
2890
2891
2892/**
2893 * This is used to remove the old VBoxDrv service before installation.
2894 *
2895 * The current service name is VBoxSup but the INF file won't remove the old
2896 * one, so we do it manually to try prevent trouble as the device nodes are the
2897 * same and we would fail starting VBoxSup.sys if VBoxDrv.sys is still loading.
2898 *
2899 * Status code is ignored for now as a reboot should fix most potential trouble
2900 * here (and I don't want to break stuff too badly).
2901 *
2902 * @sa @bugref{10162}
2903 */
2904UINT __stdcall UninstallVBoxDrv(MSIHANDLE hModule)
2905{
2906 /*
2907 * Try open the service.
2908 */
2909 SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG | SERVICE_STOP | SERVICE_QUERY_STATUS);
2910 if (hSMgr)
2911 {
2912 SC_HANDLE hService = OpenServiceW(hSMgr, L"VBoxDrv", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
2913 if (hService)
2914 {
2915 /*
2916 * Try stop it before we delete it.
2917 */
2918 SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 };
2919 QueryServiceStatus(hService, &Status);
2920 if (Status.dwCurrentState == SERVICE_STOPPED)
2921 logStringF(hModule, "VBoxDrv: The service old service was already stopped");
2922 else
2923 {
2924 logStringF(hModule, "VBoxDrv: Stopping the service (state %u)", Status.dwCurrentState);
2925 if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
2926 {
2927 /* waiting for it to stop: */
2928 int iWait = 100;
2929 while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
2930 {
2931 Sleep(100);
2932 QueryServiceStatus(hService, &Status);
2933 }
2934
2935 if (Status.dwCurrentState == SERVICE_STOPPED)
2936 logStringF(hModule, "VBoxDrv: Stopped service");
2937 else
2938 logStringF(hModule, "VBoxDrv: Failed to stop the service, status: %u", Status.dwCurrentState);
2939 }
2940 else
2941 {
2942 DWORD const dwErr = GetLastError();
2943 if ( Status.dwCurrentState == SERVICE_STOP_PENDING
2944 && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
2945 logStringF(hModule, "VBoxDrv: Failed to stop the service: stop pending, not accepting control messages");
2946 else
2947 logStringF(hModule, "VBoxDrv: Failed to stop the service: dwErr=%u status=%u", dwErr, Status.dwCurrentState);
2948 }
2949 }
2950
2951 /*
2952 * Delete the service, or at least mark it for deletion.
2953 */
2954 if (DeleteService(hService))
2955 logStringF(hModule, "VBoxDrv: Successfully delete service");
2956 else
2957 logStringF(hModule, "VBoxDrv: Failed to delete the service: %u", GetLastError());
2958
2959 CloseServiceHandle(hService);
2960 }
2961 else
2962 {
2963 DWORD const dwErr = GetLastError();
2964 if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
2965 logStringF(hModule, "VBoxDrv: Nothing to do, the old service does not exist");
2966 else
2967 logStringF(hModule, "VBoxDrv: Failed to open the service: %u", dwErr);
2968 }
2969
2970 CloseServiceHandle(hSMgr);
2971 }
2972 else
2973 logStringF(hModule, "VBoxDrv: Failed to open service manager (%u).", GetLastError());
2974
2975 return ERROR_SUCCESS;
2976}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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