VirtualBox

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

Last change on this file since 108455 was 108455, checked in by vboxsync, 2 months ago

Windows host installer: Removed the Ndis6" prefix from the exported functions Ndis6CreateHostOnlyInterface + Ndis6UpdateHostOnlyInterfaces -- should we later need to support another (major) NDIS version, simply make configurable thru MSI properties instead of adding another function. bugref:10873

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 102.0 KB
Line 
1/* $Id: VBoxInstallHelper.cpp 108455 2025-03-05 16:33:37Z 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.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#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 NETADP_ID L"sun_VBoxNetAdp" /** @todo Needs to be changed (?). */
1708
1709#define NETLWF_INF_NAME L"VBoxNetLwf.inf"
1710
1711static MSIHANDLE g_hCurrentModule = NULL;
1712
1713static UINT _uninstallNetLwf(MSIHANDLE hModule);
1714
1715static VOID vboxDrvLoggerCallback(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
1716{
1717 RT_NOREF1(pvContext);
1718 switch (enmSeverity)
1719 {
1720 case VBOXDRVCFG_LOG_SEVERITY_FLOW:
1721 case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
1722 break;
1723 case VBOXDRVCFG_LOG_SEVERITY_REL:
1724 if (g_hCurrentModule)
1725 logStringF(g_hCurrentModule, "%s", pszMsg);
1726 break;
1727 default:
1728 break;
1729 }
1730}
1731
1732static DECLCALLBACK(void) netCfgLoggerCallback(const char *pszString)
1733{
1734 if (g_hCurrentModule)
1735 logStringF(g_hCurrentModule, "%s", pszString);
1736}
1737
1738static VOID netCfgLoggerDisable()
1739{
1740 if (g_hCurrentModule)
1741 {
1742 VBoxNetCfgWinSetLogging(NULL);
1743 g_hCurrentModule = NULL;
1744 }
1745}
1746
1747static VOID netCfgLoggerEnable(MSIHANDLE hModule)
1748{
1749 NonStandardAssert(hModule);
1750
1751 if (g_hCurrentModule)
1752 netCfgLoggerDisable();
1753
1754 g_hCurrentModule = hModule;
1755
1756 VBoxNetCfgWinSetLogging(netCfgLoggerCallback);
1757 /* uncomment next line if you want to add logging information from VBoxDrvCfg.cpp */
1758// VBoxDrvCfgLoggerSet(vboxDrvLoggerCallback, NULL);
1759}
1760
1761static UINT errorConvertFromHResult(MSIHANDLE hModule, HRESULT hr)
1762{
1763 UINT uRet;
1764 switch (hr)
1765 {
1766 case S_OK:
1767 uRet = ERROR_SUCCESS;
1768 break;
1769
1770 case NETCFG_S_REBOOT:
1771 {
1772 logStringF(hModule, "Reboot required, setting REBOOT property to \"force\"");
1773 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
1774 if (hr2 != ERROR_SUCCESS)
1775 logStringF(hModule, "Failed to set REBOOT property, error = %#x", hr2);
1776 uRet = ERROR_SUCCESS; /* Never fail here. */
1777 break;
1778 }
1779
1780 default:
1781 logStringF(hModule, "Converting unhandled HRESULT (%#x) to ERROR_GEN_FAILURE", hr);
1782 uRet = ERROR_GEN_FAILURE;
1783 }
1784 return uRet;
1785}
1786
1787static MSIHANDLE createNetCfgLockedMsgRecord(MSIHANDLE hModule)
1788{
1789 MSIHANDLE hRecord = MsiCreateRecord(2);
1790 if (hRecord)
1791 {
1792 UINT uErr = MsiRecordSetInteger(hRecord, 1, 25001);
1793 if (uErr != ERROR_SUCCESS)
1794 {
1795 logStringF(hModule, "createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = %#x", uErr);
1796 MsiCloseHandle(hRecord);
1797 hRecord = NULL;
1798 }
1799 }
1800 else
1801 logStringF(hModule, "createNetCfgLockedMsgRecord: Failed to create a record");
1802
1803 return hRecord;
1804}
1805
1806static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite)
1807{
1808 MSIHANDLE hMsg = NULL;
1809 UINT uErr = ERROR_GEN_FAILURE;
1810 int MsgResult;
1811 int cRetries = 0;
1812
1813 do
1814 {
1815 LPWSTR lpszLockedBy;
1816 HRESULT hr = VBoxNetCfgWinQueryINetCfg(ppnc, bWrite, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy);
1817 if (hr != NETCFG_E_NO_WRITE_LOCK)
1818 {
1819 if (FAILED(hr))
1820 logStringF(hModule, "doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = %#x", hr);
1821 uErr = errorConvertFromHResult(hModule, hr);
1822 break;
1823 }
1824
1825 /* hr == NETCFG_E_NO_WRITE_LOCK */
1826
1827 if (!lpszLockedBy)
1828 {
1829 logStringF(hModule, "doNetCfgInit: lpszLockedBy == NULL, breaking");
1830 break;
1831 }
1832
1833 /* on vista the 6to4svc.dll periodically maintains the lock for some reason,
1834 * if this is the case, increase the wait period by retrying multiple times
1835 * NOTE: we could alternatively increase the wait timeout,
1836 * however it seems unneeded for most cases, e.g. in case some network connection property
1837 * dialog is opened, it would be better to post a notification to the user as soon as possible
1838 * rather than waiting for a longer period of time before displaying it */
1839 if ( cRetries < VBOX_NETCFG_MAX_RETRIES
1840 && RTUtf16ICmpAscii(lpszLockedBy, "6to4svc.dll") == 0)
1841 {
1842 cRetries++;
1843 logStringF(hModule, "doNetCfgInit: lpszLockedBy is 6to4svc.dll, retrying %d out of %d", cRetries, VBOX_NETCFG_MAX_RETRIES);
1844 MsgResult = IDRETRY;
1845 }
1846 else
1847 {
1848 if (!hMsg)
1849 {
1850 hMsg = createNetCfgLockedMsgRecord(hModule);
1851 if (!hMsg)
1852 {
1853 logStringF(hModule, "doNetCfgInit: Failed to create a message record, breaking");
1854 CoTaskMemFree(lpszLockedBy);
1855 break;
1856 }
1857 }
1858
1859 UINT rTmp = MsiRecordSetStringW(hMsg, 2, lpszLockedBy);
1860 NonStandardAssert(rTmp == ERROR_SUCCESS);
1861 if (rTmp != ERROR_SUCCESS)
1862 {
1863 logStringF(hModule, "doNetCfgInit: MsiRecordSetStringW failed, error = #%x", rTmp);
1864 CoTaskMemFree(lpszLockedBy);
1865 break;
1866 }
1867
1868 MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg);
1869 NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL);
1870 logStringF(hModule, "doNetCfgInit: MsiProcessMessage returned (%#x)", MsgResult);
1871 }
1872 CoTaskMemFree(lpszLockedBy);
1873 } while(MsgResult == IDRETRY);
1874
1875 if (hMsg)
1876 MsiCloseHandle(hMsg);
1877
1878 return uErr;
1879}
1880#endif /* defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) */
1881
1882#ifdef VBOX_WITH_NETFLT
1883static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf,
1884 OUT LPWSTR pwszMpInf, DWORD cwcMpInf)
1885{
1886 DWORD cwcEffBuf = cwcPtInf - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)) / sizeof(WCHAR);
1887 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &cwcEffBuf);
1888 if ( uErr == ERROR_SUCCESS
1889 && cwcEffBuf > 0)
1890 {
1891 int vrc = RTUtf16Copy(pwszMpInf, cwcMpInf, pwszPtInf);
1892 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1893
1894 vrc = RTUtf16Cat(pwszPtInf, cwcPtInf, NETFLT_PT_INF_REL_PATH);
1895 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1896 logStringF(hModule, "vboxNetFltQueryInfArray: INF 1: %ls", pwszPtInf);
1897
1898 vrc = RTUtf16Cat(pwszMpInf, cwcMpInf, NETFLT_MP_INF_REL_PATH);
1899 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1900 logStringF(hModule, "vboxNetFltQueryInfArray: INF 2: %ls", pwszMpInf);
1901 }
1902 else if (uErr != ERROR_SUCCESS)
1903 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1904 else
1905 {
1906 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1907 uErr = ERROR_GEN_FAILURE;
1908 }
1909
1910 return uErr;
1911}
1912
1913static UINT _uninstallNetLwf(MSIHANDLE hModule)
1914{
1915 INetCfg *pNetCfg;
1916 UINT uErr;
1917
1918 netCfgLoggerEnable(hModule);
1919
1920 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1921
1922 __try
1923 {
1924 logStringF(hModule, "Uninstalling NetLwf");
1925
1926 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1927 if (uErr == ERROR_SUCCESS)
1928 {
1929 HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg);
1930 if (hr != S_OK)
1931 logStringF(hModule, "UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1932
1933 uErr = errorConvertFromHResult(hModule, hr);
1934
1935 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1936
1937 logStringF(hModule, "Uninstalling NetLwf done, error = %#x", uErr);
1938 }
1939 else
1940 logStringF(hModule, "UninstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
1941 }
1942 __finally
1943 {
1944 if (bOldIntMode)
1945 {
1946 /* The prev mode != FALSE, i.e. non-interactive. */
1947 SetupSetNonInteractiveMode(bOldIntMode);
1948 }
1949 netCfgLoggerDisable();
1950 }
1951
1952 /* Never fail the uninstall even if we did not succeed. */
1953 return ERROR_SUCCESS;
1954}
1955#endif /* VBOX_WITH_NETFLT */
1956
1957UINT __stdcall UninstallNetLwf(MSIHANDLE hModule)
1958{
1959#ifdef VBOX_WITH_NETFLT
1960 return _uninstallNetLwf(hModule);
1961#else
1962 RT_NOREF(hModule);
1963 return ERROR_SUCCESS;
1964#endif
1965}
1966
1967#ifdef VBOX_WITH_NETFLT
1968static UINT _installNetLwf(MSIHANDLE hModule)
1969{
1970 UINT uErr;
1971 INetCfg *pNetCfg;
1972
1973 netCfgLoggerEnable(hModule);
1974
1975 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1976
1977 __try
1978 {
1979
1980 logStringF(hModule, "InstallNetLwf: Installing NetLwf");
1981
1982 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1983 if (uErr == ERROR_SUCCESS)
1984 {
1985 WCHAR wszInf[MAX_PATH];
1986 DWORD cwcInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1;
1987 uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cwcInf);
1988 if (uErr == ERROR_SUCCESS)
1989 {
1990 if (cwcInf)
1991 {
1992 if (wszInf[cwcInf - 1] != L'\\')
1993 {
1994 wszInf[cwcInf++] = L'\\';
1995 wszInf[cwcInf] = L'\0';
1996 }
1997
1998 int vrc = RTUtf16Cat(wszInf, RT_ELEMENTS(wszInf), NETLWF_INF_NAME);
1999 AssertRC(vrc);
2000
2001 HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf);
2002 if (FAILED(hr))
2003 logStringF(hModule, "InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = %#x", hr);
2004
2005 uErr = errorConvertFromHResult(hModule, hr);
2006 }
2007 else
2008 {
2009 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
2010 uErr = ERROR_GEN_FAILURE;
2011 }
2012 }
2013 else
2014 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
2015
2016 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2017
2018 logStringF(hModule, "InstallNetLwf: Done");
2019 }
2020 else
2021 logStringF(hModule, "InstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
2022 }
2023 __finally
2024 {
2025 if (bOldIntMode)
2026 {
2027 /* The prev mode != FALSE, i.e. non-interactive. */
2028 SetupSetNonInteractiveMode(bOldIntMode);
2029 }
2030 netCfgLoggerDisable();
2031 }
2032
2033 /* Never fail the install even if we did not succeed. */
2034 return ERROR_SUCCESS;
2035}
2036#endif /* VBOX_WITH_NETFLT */
2037
2038UINT __stdcall InstallNetLwf(MSIHANDLE hModule)
2039{
2040#ifdef VBOX_WITH_NETFLT
2041 return _installNetLwf(hModule);
2042#else
2043 RT_NOREF(hModule);
2044 return ERROR_SUCCESS;
2045#endif
2046}
2047
2048#ifdef VBOX_WITH_NETADP
2049static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName)
2050{
2051 netCfgLoggerEnable(hModule);
2052
2053 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2054
2055 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface");
2056
2057 HRESULT hr = E_FAIL;
2058 GUID guid;
2059 WCHAR wszMpInf[MAX_PATH];
2060 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2061 LPCWSTR pwszInfPath = NULL;
2062 bool fIsFile = false;
2063 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2064 if (uErr == ERROR_SUCCESS)
2065 {
2066 if (cwcMpInf)
2067 {
2068 logStringF(hModule, "CreateHostOnlyInterface: NetAdpDir property = %ls", wszMpInf);
2069 if (wszMpInf[cwcMpInf - 1] != L'\\')
2070 {
2071 wszMpInf[cwcMpInf++] = L'\\';
2072 wszMpInf[cwcMpInf] = L'\0';
2073 }
2074
2075 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2076 AssertRC(vrc);
2077
2078 pwszInfPath = wszMpInf;
2079 fIsFile = true;
2080
2081 logStringF(hModule, "CreateHostOnlyInterface: Resulting INF path = %ls", pwszInfPath);
2082 }
2083 else
2084 logStringF(hModule, "CreateHostOnlyInterface: VBox installation path is empty");
2085 }
2086 else
2087 logStringF(hModule, "CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = %#x", uErr);
2088
2089 /* Make sure the inf file is installed. */
2090 if (pwszInfPath != NULL && fIsFile)
2091 {
2092 logStringF(hModule, "CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%ls)", pwszInfPath);
2093 hr = VBoxDrvCfgInfInstall(pwszInfPath);
2094 logStringF(hModule, "CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns %#x", hr);
2095 if (FAILED(hr))
2096 logStringF(hModule, "CreateHostOnlyInterface: Failed to install INF file, error = %#x", hr);
2097 }
2098
2099 if (SUCCEEDED(hr))
2100 {
2101 //first, try to update Host Only Network Interface
2102 BOOL fRebootRequired = FALSE;
2103 hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2104 if (SUCCEEDED(hr))
2105 {
2106 if (fRebootRequired)
2107 {
2108 logStringF(hModule, "CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force");
2109 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2110 if (hr2 != ERROR_SUCCESS)
2111 logStringF(hModule, "CreateHostOnlyInterface: Failed to set REBOOT property for update, error = %#x", hr2);
2112 }
2113 }
2114 else
2115 {
2116 //in fail case call CreateHostOnlyInterface
2117 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2118 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface");
2119# ifdef VBOXNETCFG_DELAYEDRENAME
2120 BSTR devId;
2121 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL);
2122# else /* !VBOXNETCFG_DELAYEDRENAME */
2123 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL);
2124# endif /* !VBOXNETCFG_DELAYEDRENAME */
2125 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr);
2126 if (SUCCEEDED(hr))
2127 {
2128 ULONG ip = inet_addr("192.168.56.1");
2129 ULONG mask = inet_addr("255.255.255.0");
2130 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig");
2131 hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
2132 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr);
2133 if (FAILED(hr))
2134 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr);
2135# ifdef VBOXNETCFG_DELAYEDRENAME
2136 hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL);
2137 if (FAILED(hr))
2138 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr);
2139 SysFreeString(devId);
2140# endif /* VBOXNETCFG_DELAYEDRENAME */
2141 }
2142 else
2143 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr);
2144 }
2145 }
2146
2147 if (SUCCEEDED(hr))
2148 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface done");
2149
2150 /* Restore original setup mode. */
2151 logStringF(hModule, "CreateHostOnlyInterface: Almost done...");
2152 if (fSetupModeInteractive)
2153 SetupSetNonInteractiveMode(fSetupModeInteractive);
2154
2155 netCfgLoggerDisable();
2156
2157 logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)");
2158 /* Never fail the install even if we did not succeed. */
2159 return ERROR_SUCCESS;
2160}
2161#endif /* VBOX_WITH_NETADP */
2162
2163UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule)
2164{
2165#ifdef VBOX_WITH_NETADP
2166# if 0 /* Trick for allowing the debugger to be attached. */
2167 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
2168 {
2169 logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId());
2170 Sleep(1001);
2171 }
2172 Sleep(1002);
2173 __debugbreak();
2174# endif
2175 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf");
2176#else /* !VBOX_WITH_NETADP */
2177 RT_NOREF(hModule);
2178 return ERROR_SUCCESS;
2179#endif
2180}
2181
2182#ifdef VBOX_WITH_NETADP
2183static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2184{
2185 netCfgLoggerEnable(hModule);
2186
2187 logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces");
2188
2189 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2190
2191 HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(pwszId);
2192 if (SUCCEEDED(hr))
2193 {
2194 hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", pwszId, SUOI_FORCEDELETE/* could be SUOI_FORCEDELETE */);
2195 if (FAILED(hr))
2196 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files");
2197 else
2198 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully");
2199 }
2200 else
2201 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = %#x", hr);
2202
2203 /* Restore original setup mode. */
2204 if (fSetupModeInteractive)
2205 SetupSetNonInteractiveMode(fSetupModeInteractive);
2206
2207 netCfgLoggerDisable();
2208
2209 /* Never fail the uninstall even if we did not succeed. */
2210 return ERROR_SUCCESS;
2211}
2212#endif /* VBOX_WITH_NETADP */
2213
2214UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule)
2215{
2216#ifdef VBOX_WITH_NETADP
2217 return _removeHostOnlyInterfaces(hModule, NETADP_ID);
2218#else
2219 RT_NOREF(hModule);
2220 return ERROR_SUCCESS;
2221#endif
2222}
2223
2224#ifdef VBOX_WITH_NETADP
2225static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2226{
2227 netCfgLoggerEnable(hModule);
2228
2229 logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces");
2230
2231 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2232
2233 HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
2234 if (SUCCEEDED(hr))
2235 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = %#x", hr);
2236 else
2237 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces failed, hr = %#x", hr);
2238
2239 /* Restore original setup mode. */
2240 if (fSetupModeInteractive)
2241 SetupSetNonInteractiveMode(fSetupModeInteractive);
2242
2243 netCfgLoggerDisable();
2244
2245 /* Never fail the uninstall even if we did not succeed. */
2246 return ERROR_SUCCESS;
2247}
2248#endif /* VBOX_WITH_NETADP */
2249
2250UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule)
2251{
2252#ifdef VBOX_WITH_NETADP
2253 return _stopHostOnlyInterfaces(hModule, NETADP_ID);
2254#else
2255 RT_NOREF(hModule);
2256 return ERROR_SUCCESS;
2257#endif
2258}
2259
2260#ifdef VBOX_WITH_NETADP
2261static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId)
2262{
2263 netCfgLoggerEnable(hModule);
2264
2265 logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces");
2266
2267 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2268
2269 WCHAR wszMpInf[MAX_PATH];
2270 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2271 LPCWSTR pwszInfPath = NULL;
2272 bool fIsFile = false;
2273 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2274 if (uErr == ERROR_SUCCESS)
2275 {
2276 if (cwcMpInf)
2277 {
2278 logStringF(hModule, "UpdateHostOnlyInterfaces: NetAdpDir property = %ls", wszMpInf);
2279 if (wszMpInf[cwcMpInf - 1] != L'\\')
2280 {
2281 wszMpInf[cwcMpInf++] = L'\\';
2282 wszMpInf[cwcMpInf] = L'\0';
2283 }
2284
2285 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2286 AssertRC(vrc);
2287 pwszInfPath = wszMpInf;
2288 fIsFile = true;
2289
2290 logStringF(hModule, "UpdateHostOnlyInterfaces: Resulting INF path = %ls", pwszInfPath);
2291
2292 DWORD attrFile = GetFileAttributesW(pwszInfPath);
2293 if (attrFile == INVALID_FILE_ATTRIBUTES)
2294 {
2295 DWORD dwErr = GetLastError();
2296 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" not found, dwErr=%ld", pwszInfPath, dwErr);
2297 }
2298 else
2299 {
2300 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" exists", pwszInfPath);
2301
2302 BOOL fRebootRequired = FALSE;
2303 HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2304 if (SUCCEEDED(hr))
2305 {
2306 if (fRebootRequired)
2307 {
2308 logStringF(hModule, "UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force");
2309 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2310 if (hr2 != ERROR_SUCCESS)
2311 logStringF(hModule, "UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = %#x", hr2);
2312 }
2313 }
2314 else
2315 logStringF(hModule, "UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2316 }
2317 }
2318 else
2319 logStringF(hModule, "UpdateHostOnlyInterfaces: VBox installation path is empty");
2320 }
2321 else
2322 logStringF(hModule, "UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = %#x", uErr);
2323
2324 /* Restore original setup mode. */
2325 if (fSetupModeInteractive)
2326 SetupSetNonInteractiveMode(fSetupModeInteractive);
2327
2328 netCfgLoggerDisable();
2329
2330 /* Never fail the update even if we did not succeed. */
2331 return ERROR_SUCCESS;
2332}
2333#endif /* VBOX_WITH_NETADP */
2334
2335UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2336{
2337#ifdef VBOX_WITH_NETADP
2338 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID);
2339#else
2340 RT_NOREF(hModule);
2341 return ERROR_SUCCESS;
2342#endif
2343}
2344
2345#ifdef VBOX_WITH_NETADP
2346static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId)
2347{
2348 INetCfg *pNetCfg;
2349 UINT uErr;
2350
2351 netCfgLoggerEnable(hModule);
2352
2353 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2354
2355 __try
2356 {
2357 logStringF(hModule, "Uninstalling NetAdp");
2358
2359 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2360 if (uErr == ERROR_SUCCESS)
2361 {
2362 HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId);
2363 if (hr != S_OK)
2364 logStringF(hModule, "UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
2365
2366 uErr = errorConvertFromHResult(hModule, hr);
2367
2368 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2369
2370 logStringF(hModule, "Uninstalling NetAdp done, error = %#x", uErr);
2371 }
2372 else
2373 logStringF(hModule, "UninstallNetAdp: doNetCfgInit failed, error = %#x", uErr);
2374 }
2375 __finally
2376 {
2377 if (bOldIntMode)
2378 {
2379 /* The prev mode != FALSE, i.e. non-interactive. */
2380 SetupSetNonInteractiveMode(bOldIntMode);
2381 }
2382 netCfgLoggerDisable();
2383 }
2384
2385 /* Never fail the uninstall even if we did not succeed. */
2386 return ERROR_SUCCESS;
2387}
2388#endif /* VBOX_WITH_NETADP */
2389
2390UINT __stdcall UninstallNetAdp(MSIHANDLE hModule)
2391{
2392#ifdef VBOX_WITH_NETADP
2393 return _uninstallNetAdp(hModule, NETADP_ID);
2394#else
2395 RT_NOREF(hModule);
2396 return ERROR_SUCCESS;
2397#endif
2398}
2399
2400static bool isTAPDevice(const WCHAR *pwszGUID)
2401{
2402 HKEY hNetcard;
2403 bool bIsTapDevice = false;
2404 LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2405 L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
2406 0, KEY_READ, &hNetcard);
2407 if (lStatus != ERROR_SUCCESS)
2408 return false;
2409
2410 int i = 0;
2411 for (;;)
2412 {
2413 WCHAR wszEnumName[256];
2414 WCHAR wszNetCfgInstanceId[256];
2415 DWORD dwKeyType;
2416 HKEY hNetCardGUID;
2417
2418 DWORD dwLen = sizeof(wszEnumName);
2419 lStatus = RegEnumKeyExW(hNetcard, i, wszEnumName, &dwLen, NULL, NULL, NULL, NULL);
2420 if (lStatus != ERROR_SUCCESS)
2421 break;
2422
2423 lStatus = RegOpenKeyExW(hNetcard, wszEnumName, 0, KEY_READ, &hNetCardGUID);
2424 if (lStatus == ERROR_SUCCESS)
2425 {
2426 dwLen = sizeof(wszNetCfgInstanceId);
2427 lStatus = RegQueryValueExW(hNetCardGUID, L"NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)wszNetCfgInstanceId, &dwLen);
2428 if ( lStatus == ERROR_SUCCESS
2429 && dwKeyType == REG_SZ)
2430 {
2431 WCHAR wszNetProductName[256];
2432 WCHAR wszNetProviderName[256];
2433
2434 wszNetProductName[0] = 0;
2435 dwLen = sizeof(wszNetProductName);
2436 lStatus = RegQueryValueExW(hNetCardGUID, L"ProductName", NULL, &dwKeyType, (LPBYTE)wszNetProductName, &dwLen);
2437
2438 wszNetProviderName[0] = 0;
2439 dwLen = sizeof(wszNetProviderName);
2440 lStatus = RegQueryValueExW(hNetCardGUID, L"ProviderName", NULL, &dwKeyType, (LPBYTE)wszNetProviderName, &dwLen);
2441
2442 if ( !RTUtf16Cmp(wszNetCfgInstanceId, pwszGUID)
2443 && !RTUtf16Cmp(wszNetProductName, L"VirtualBox TAP Adapter")
2444 && ( (!RTUtf16Cmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */
2445 || (!RTUtf16Cmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */
2446 || (!RTUtf16Cmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */
2447 )
2448 )
2449 {
2450 bIsTapDevice = true;
2451 RegCloseKey(hNetCardGUID);
2452 break;
2453 }
2454 }
2455 RegCloseKey(hNetCardGUID);
2456 }
2457 ++i;
2458 }
2459
2460 RegCloseKey(hNetcard);
2461 return bIsTapDevice;
2462}
2463
2464/** @todo r=andy BUGBUG WTF! Why do we a) set the rc to 0 (success), and b) need this macro at all!?
2465 *
2466 * @todo r=bird: Because it's returning a bool, not int? The return code is
2467 * ignored anyway, both internally in removeNetworkInterface and in it's caller.
2468 * There is similar code in VBoxNetCfg.cpp, which is probably where it was copied from. */
2469#define SetErrBreak(args) \
2470 if (1) { \
2471 rc = 0; \
2472 logStringF args; \
2473 break; \
2474 } else do {} while (0)
2475
2476int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID)
2477{
2478 int rc = 1;
2479 do /* break-loop */
2480 {
2481 WCHAR wszPnPInstanceId[512] = {0};
2482
2483 /* We have to find the device instance ID through a registry search */
2484
2485 HKEY hkeyNetwork = 0;
2486 HKEY hkeyConnection = 0;
2487
2488 do /* break-loop */
2489 {
2490 WCHAR wszRegLocation[256];
2491 RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
2492 "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", pwszGUID);
2493 LONG lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
2494 if (lrc != ERROR_SUCCESS || !hkeyNetwork)
2495 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [1]",
2496 wszRegLocation, lrc));
2497
2498 lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
2499 if (lrc != ERROR_SUCCESS || !hkeyConnection)
2500 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [2]",
2501 wszRegLocation, lrc));
2502
2503 DWORD len = sizeof(wszPnPInstanceId);
2504 DWORD dwKeyType;
2505 lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len);
2506 if (lrc != ERROR_SUCCESS || dwKeyType != REG_SZ)
2507 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [3]",
2508 wszRegLocation, lrc));
2509 }
2510 while (0);
2511
2512 if (hkeyConnection)
2513 RegCloseKey(hkeyConnection);
2514 if (hkeyNetwork)
2515 RegCloseKey(hkeyNetwork);
2516
2517 /*
2518 * Now we are going to enumerate all network devices and
2519 * wait until we encounter the right device instance ID
2520 */
2521
2522 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2523 BOOL fResult;
2524
2525 do /* break-loop */
2526 {
2527 /* initialize the structure size */
2528 SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
2529
2530 /* copy the net class GUID */
2531 GUID netGuid;
2532 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2533
2534 /* return a device info set contains all installed devices of the Net class */
2535 hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
2536 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2537 {
2538 logStringF(hModule, "VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError());
2539 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2540 }
2541
2542 /* enumerate the driver info list */
2543 BOOL fFoundDevice = FALSE;
2544 for (DWORD index = 0;; index++)
2545 {
2546 fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
2547 if (!fResult)
2548 {
2549 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2550 break;
2551 continue;
2552 }
2553
2554 /* try to get the hardware ID registry property */
2555 WCHAR *pwszDeviceHwid;
2556 DWORD size = 0;
2557 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2558 &DeviceInfoData,
2559 SPDRP_HARDWAREID,
2560 NULL,
2561 NULL,
2562 0,
2563 &size);
2564 if (!fResult)
2565 {
2566 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2567 continue;
2568
2569 pwszDeviceHwid = (WCHAR *)RTMemAllocZ(size);
2570 if (!pwszDeviceHwid)
2571 continue;
2572
2573 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2574 &DeviceInfoData,
2575 SPDRP_HARDWAREID,
2576 NULL,
2577 (PBYTE)pwszDeviceHwid,
2578 size,
2579 &size);
2580 if (!fResult)
2581 {
2582 RTMemFree(pwszDeviceHwid);
2583 continue;
2584 }
2585 }
2586 else
2587 {
2588 /* something is wrong. This shouldn't have worked with a NULL buffer */
2589 continue;
2590 }
2591
2592 for (WCHAR *t = pwszDeviceHwid;
2593 *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)];
2594 t += RTUtf16Len(t) + 1)
2595 {
2596 if (RTUtf16ICmpAscii(t, "vboxtap") == 0)
2597 {
2598 /* get the device instance ID */
2599 WCHAR wszDevID[MAX_DEVICE_ID_LEN];
2600 if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2601 {
2602 /* compare to what we determined before */
2603 if (RTUtf16Cmp(wszDevID, wszPnPInstanceId) == 0)
2604 {
2605 fFoundDevice = TRUE;
2606 break;
2607 }
2608 }
2609 }
2610 }
2611
2612 RTMemFree(pwszDeviceHwid);
2613
2614 if (fFoundDevice)
2615 break;
2616 }
2617
2618 if (fFoundDevice)
2619 {
2620 fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
2621 if (!fResult)
2622 {
2623 logStringF(hModule, "VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError());
2624 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2625 }
2626
2627 fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2628 if (!fResult)
2629 {
2630 logStringF(hModule, "VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError());
2631 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2632 }
2633 }
2634 else
2635 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network device not found!"));
2636 } while (0);
2637
2638 /* clean up the device info set */
2639 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2640 SetupDiDestroyDeviceInfoList(hDeviceInfo);
2641 } while (0);
2642 return rc;
2643}
2644
2645UINT __stdcall UninstallTAPInstances(MSIHANDLE hModule)
2646{
2647 static const wchar_t s_wszNetworkKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
2648 HKEY hCtrlNet;
2649
2650 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet);
2651 if (lrc == ERROR_SUCCESS)
2652 {
2653 logStringF(hModule, "VBox HostInterfaces: Enumerating interfaces ...");
2654 for (int i = 0; ; ++i)
2655 {
2656 WCHAR wszNetworkGUID[256] = { 0 };
2657 DWORD dwLen = (DWORD)sizeof(wszNetworkGUID);
2658 lrc = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL);
2659 if (lrc != ERROR_SUCCESS)
2660 {
2661 switch (lrc)
2662 {
2663 case ERROR_NO_MORE_ITEMS:
2664 logStringF(hModule, "VBox HostInterfaces: No interfaces found.");
2665 break;
2666 default:
2667 logStringF(hModule, "VBox HostInterfaces: Enumeration failed: %ld", lrc);
2668 break;
2669 }
2670 break;
2671 }
2672
2673 if (isTAPDevice(wszNetworkGUID))
2674 {
2675 logStringF(hModule, "VBox HostInterfaces: Removing interface \"%ls\" ...", wszNetworkGUID);
2676 removeNetworkInterface(hModule, wszNetworkGUID);
2677 lrc = RegDeleteKeyW(hCtrlNet, wszNetworkGUID);
2678 }
2679 }
2680 RegCloseKey(hCtrlNet);
2681 logStringF(hModule, "VBox HostInterfaces: Removing interfaces done.");
2682 }
2683 return ERROR_SUCCESS;
2684}
2685
2686
2687/**
2688 * This is used to remove the old VBoxDrv service before installation.
2689 *
2690 * The current service name is VBoxSup but the INF file won't remove the old
2691 * one, so we do it manually to try prevent trouble as the device nodes are the
2692 * same and we would fail starting VBoxSup.sys if VBoxDrv.sys is still loading.
2693 *
2694 * Status code is ignored for now as a reboot should fix most potential trouble
2695 * here (and I don't want to break stuff too badly).
2696 *
2697 * @sa @bugref{10162}
2698 */
2699UINT __stdcall UninstallVBoxDrv(MSIHANDLE hModule)
2700{
2701 /*
2702 * Try open the service.
2703 */
2704 SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG | SERVICE_STOP | SERVICE_QUERY_STATUS);
2705 if (hSMgr)
2706 {
2707 SC_HANDLE hService = OpenServiceW(hSMgr, L"VBoxDrv", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
2708 if (hService)
2709 {
2710 /*
2711 * Try stop it before we delete it.
2712 */
2713 SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 };
2714 QueryServiceStatus(hService, &Status);
2715 if (Status.dwCurrentState == SERVICE_STOPPED)
2716 logStringF(hModule, "VBoxDrv: The service old service was already stopped");
2717 else
2718 {
2719 logStringF(hModule, "VBoxDrv: Stopping the service (state %u)", Status.dwCurrentState);
2720 if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
2721 {
2722 /* waiting for it to stop: */
2723 int iWait = 100;
2724 while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
2725 {
2726 Sleep(100);
2727 QueryServiceStatus(hService, &Status);
2728 }
2729
2730 if (Status.dwCurrentState == SERVICE_STOPPED)
2731 logStringF(hModule, "VBoxDrv: Stopped service");
2732 else
2733 logStringF(hModule, "VBoxDrv: Failed to stop the service, status: %u", Status.dwCurrentState);
2734 }
2735 else
2736 {
2737 DWORD const dwErr = GetLastError();
2738 if ( Status.dwCurrentState == SERVICE_STOP_PENDING
2739 && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
2740 logStringF(hModule, "VBoxDrv: Failed to stop the service: stop pending, not accepting control messages");
2741 else
2742 logStringF(hModule, "VBoxDrv: Failed to stop the service: dwErr=%u status=%u", dwErr, Status.dwCurrentState);
2743 }
2744 }
2745
2746 /*
2747 * Delete the service, or at least mark it for deletion.
2748 */
2749 if (DeleteService(hService))
2750 logStringF(hModule, "VBoxDrv: Successfully delete service");
2751 else
2752 logStringF(hModule, "VBoxDrv: Failed to delete the service: %u", GetLastError());
2753
2754 CloseServiceHandle(hService);
2755 }
2756 else
2757 {
2758 DWORD const dwErr = GetLastError();
2759 if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
2760 logStringF(hModule, "VBoxDrv: Nothing to do, the old service does not exist");
2761 else
2762 logStringF(hModule, "VBoxDrv: Failed to open the service: %u", dwErr);
2763 }
2764
2765 CloseServiceHandle(hSMgr);
2766 }
2767 else
2768 logStringF(hModule, "VBoxDrv: Failed to open service manager (%u).", GetLastError());
2769
2770 return ERROR_SUCCESS;
2771}
Note: See TracBrowser for help on using the repository browser.

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