VirtualBox

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

Last change on this file since 107950 was 107949, checked in by vboxsync, 3 months ago

Host installer/win: Added GetPlatformArchitecture() to the VBoxInstallHelper.dll [forgot to remove hardcoded breakpoint]. bugref:10849

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 104.9 KB
Line 
1/* $Id: VBoxInstallHelper.cpp 107949 2025-01-27 16:58:42Z 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 hKeyVS = NULL;
1056 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
1057 L"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
1058 0, KEY_READ, &hKeyVS);
1059 if (lrc == ERROR_SUCCESS)
1060 {
1061 DWORD dwVal = 0;
1062 DWORD cbVal = sizeof(dwVal);
1063 DWORD dwValueType = REG_DWORD; /** @todo r=bird: output only parameter, optional, so pointless. */
1064 lrc = RegQueryValueExW(hKeyVS, L"Installed", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
1065 if (lrc == ERROR_SUCCESS)
1066 {
1067 if (dwVal >= 1)
1068 {
1069 DWORD dwMaj = 0; /** @todo r=bird: It's purdent to initialize values if you don't bother to check the type and size! */
1070 lrc = RegQueryValueExW(hKeyVS, L"Major", NULL, &dwValueType, (LPBYTE)&dwMaj, &cbVal);
1071 if (lrc == ERROR_SUCCESS)
1072 {
1073 VBoxMsiSetPropDWORD(hModule, L"VBOX_MSCRT_VER_MAJ", dwMaj);
1074
1075 DWORD dwMin = 0;
1076 lrc = RegQueryValueExW(hKeyVS, L"Minor", NULL, &dwValueType, (LPBYTE)&dwMin, &cbVal);
1077 if (lrc == ERROR_SUCCESS)
1078 {
1079 VBoxMsiSetPropDWORD(hModule, L"VBOX_MSCRT_VER_MIN", dwMin);
1080
1081 logStringF(hModule, "IsMSCRTInstalled: Found v%u.%u\n", dwMaj, dwMin);
1082
1083 /* Check for at least 2019. */
1084 if (dwMaj > 14 || (dwMaj == 14 && dwMin >= 20))
1085 VBoxMsiSetProp(hModule, L"VBOX_MSCRT_INSTALLED", L"1");
1086 }
1087 else
1088 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Minor' key not present (lrc=%d)", lrc);
1089 }
1090 else
1091 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Major' key not present (lrc=%d)", lrc);
1092 }
1093 else
1094 {
1095 logStringF(hModule, "IsMSCRTInstalled: Found, but not marked as installed");
1096 lrc = ERROR_NOT_INSTALLED;
1097 }
1098 }
1099 else
1100 logStringF(hModule, "IsMSCRTInstalled: Found, but 'Installed' key not present (lrc=%d)", lrc);
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 (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_10 to "" (empty / false) or "1" (success).
1116 *
1117 * @param hModule Windows installer module handle.
1118 */
1119UINT __stdcall IsWindows10(MSIHANDLE hModule)
1120{
1121 /*
1122 * Note: We cannot use RtlGetVersion() / GetVersionExW() here, as the Windows Installer service
1123 * all shims this, unfortunately. So we have to go another route by querying the major version
1124 * number from the registry.
1125 */
1126 HKEY hKeyCurVer = NULL;
1127 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKeyCurVer);
1128 if (lrc == ERROR_SUCCESS)
1129 {
1130 DWORD dwVal = 0;
1131 DWORD cbVal = sizeof(dwVal);
1132 DWORD dwValueType = REG_DWORD; /** @todo r=bird: Again, the type is an optional output parameter. pointless to init or pass it unless you check. */
1133 lrc = RegQueryValueExW(hKeyCurVer, L"CurrentMajorVersionNumber", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
1134 if (lrc == ERROR_SUCCESS)
1135 {
1136 logStringF(hModule, "IsWindows10/CurrentMajorVersionNumber: %u", dwVal);
1137
1138 VBoxMsiSetProp(hModule, L"VBOX_IS_WINDOWS_10", dwVal >= 10 ? L"1" : L"");
1139 }
1140 else
1141 logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error reading CurrentMajorVersionNumber (%ld)", lrc);
1142
1143 RegCloseKey(hKeyCurVer);
1144 }
1145 else
1146 logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error opening CurrentVersion key (%ld)", lrc);
1147
1148 return ERROR_SUCCESS; /* Never return failure. */
1149}
1150
1151/**
1152 * Installs and compiles the VBox Python bindings.
1153 *
1154 * Called from the MSI installer as custom action.
1155 *
1156 * @returns Always ERROR_SUCCESS.
1157 * Sets public property VBOX_API_INSTALLED to "0" (false) or "1" (success).
1158 *
1159 * @param hModule Windows installer module handle.
1160 */
1161UINT __stdcall InstallPythonAPI(MSIHANDLE hModule)
1162{
1163 logStringF(hModule, "InstallPythonAPI: Checking for installed Python environment(s) ...");
1164
1165 /** @todo r=bird: Can't we get the VBOX_PYTHON_PATH property here? */
1166 wchar_t wszPythonExe[RTPATH_MAX];
1167 UINT rcWin = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
1168 if (rcWin != ERROR_SUCCESS)
1169 {
1170 VBoxMsiSetProp(hModule, L"VBOX_API_INSTALLED", L"0");
1171 return ERROR_SUCCESS;
1172 }
1173
1174 /*
1175 * Set up the VBox API.
1176 */
1177 /* Get the VBox API setup string. */
1178 WCHAR wszVBoxPythonInstallerPath[RTPATH_MAX];
1179 int rc = VBoxMsiQueryProp(hModule, L"CustomActionData", wszVBoxPythonInstallerPath, RT_ELEMENTS(wszVBoxPythonInstallerPath));
1180 if (RT_SUCCESS(rc))
1181 {
1182 /* Make sure our current working directory is the VBox installation path. */
1183 if (SetCurrentDirectoryW(wszVBoxPythonInstallerPath))
1184 {
1185 /* Set required environment variables. */
1186 /** @todo r=andy: That can't be right!
1187 *
1188 * r=bird: The variable probably isn't used because VBOX_MSI_INSTALL_PATH is
1189 * set by VBoxMergeApp.wxi. */
1190 if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxPythonInstallerPath))
1191 {
1192 logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxPythonInstallerPath);
1193
1194 rcWin = procRun(hModule, wszPythonExe, L"vboxapisetup.py install");
1195 if (rcWin == ERROR_SUCCESS)
1196 {
1197 logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful");
1198
1199 /*
1200 * Do some sanity checking if the VBox API works.
1201 */
1202 logStringF(hModule, "InstallPythonAPI: Validating VBox API ...");
1203
1204 rcWin = procRun(hModule, wszPythonExe, L"-c \"from vboxapi import VirtualBoxManager\"");
1205 if (rcWin == ERROR_SUCCESS)
1206 {
1207 logStringF(hModule, "InstallPythonAPI: VBox API looks good.");
1208 VBoxMsiSetProp(hModule, L"VBOX_API_INSTALLED", L"1");
1209 return ERROR_SUCCESS;
1210 }
1211
1212 /* failed */
1213 logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %u (%#x)", rcWin, rcWin);
1214 }
1215 else
1216 logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %u (%#x)", rcWin, rcWin);
1217 }
1218 else
1219 logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH: LastError=%u",
1220 GetLastError());
1221 }
1222 else
1223 logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%ls\": LastError=%u",
1224 wszVBoxPythonInstallerPath, GetLastError());
1225 }
1226 else
1227 logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory: rc=%Rrc", rc);
1228
1229 VBoxMsiSetProp(hModule, L"VBOX_API_INSTALLED", L"0");
1230 logStringF(hModule, "InstallPythonAPI: Installation failed");
1231 return ERROR_SUCCESS; /* Do not fail here. */
1232}
1233
1234static LONG installBrandingValue(MSIHANDLE hModule,
1235 const WCHAR *pwszFileName,
1236 const WCHAR *pwszSection,
1237 const WCHAR *pwszValue)
1238{
1239 LONG rc;
1240 WCHAR wszValue[MAX_PATH];
1241 if (GetPrivateProfileStringW(pwszSection, pwszValue, NULL, wszValue, sizeof(wszValue), pwszFileName) > 0)
1242 {
1243 WCHAR wszKey[MAX_PATH + 64];
1244 if (RTUtf16ICmpAscii(pwszSection, "General") != 0)
1245 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding\\%ls", VBOX_VENDOR_SHORT, pwszSection);
1246 else
1247 RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding", VBOX_VENDOR_SHORT);
1248
1249 HKEY hkBranding = NULL;
1250 rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKey, 0, KEY_WRITE, &hkBranding);
1251 if (rc == ERROR_SUCCESS)
1252 {
1253 rc = RegSetValueExW(hkBranding,
1254 pwszValue,
1255 NULL,
1256 REG_SZ,
1257 (BYTE *)wszValue,
1258 (DWORD)RTUtf16Len(wszValue));
1259 if (rc != ERROR_SUCCESS)
1260 logStringF(hModule, "InstallBranding: Could not write value %s! Error %d", pwszValue, rc);
1261 RegCloseKey(hkBranding);
1262 }
1263 }
1264 else
1265 rc = ERROR_NOT_FOUND;
1266 return rc;
1267}
1268
1269/**
1270 * @note Both paths strings must have an extra terminator.
1271 */
1272static UINT CopyDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1273{
1274 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1275 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1276
1277 SHFILEOPSTRUCTW s = {0};
1278 s.hwnd = NULL;
1279 s.wFunc = FO_COPY;
1280 s.pTo = pwszzDstDir;
1281 s.pFrom = pwszzSrcDir;
1282 s.fFlags = FOF_SILENT
1283 | FOF_NOCONFIRMATION
1284 | FOF_NOCONFIRMMKDIR
1285 | FOF_NOERRORUI;
1286
1287 logStringF(hModule, "CopyDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1288 int r = SHFileOperationW(&s);
1289 if (r == 0)
1290 return ERROR_SUCCESS;
1291 logStringF(hModule, "CopyDir: Copy operation returned status %#x", r);
1292 return ERROR_GEN_FAILURE;
1293}
1294
1295/**
1296 * @note The directory string must have two zero terminators!
1297 */
1298static UINT RemoveDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir)
1299{
1300 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1301
1302 SHFILEOPSTRUCTW s = {0};
1303 s.hwnd = NULL;
1304 s.wFunc = FO_DELETE;
1305 s.pFrom = pwszzDstDir;
1306 s.fFlags = FOF_SILENT
1307 | FOF_NOCONFIRMATION
1308 | FOF_NOCONFIRMMKDIR
1309 | FOF_NOERRORUI;
1310
1311 logStringF(hModule, "RemoveDir: pwszzDstDir=%ls", pwszzDstDir);
1312 int r = SHFileOperationW(&s);
1313 if (r == 0)
1314 return ERROR_SUCCESS;
1315 logStringF(hModule, "RemoveDir: Remove operation returned status %#x", r);
1316 return ERROR_GEN_FAILURE;
1317}
1318
1319/**
1320 * @note Both paths strings must have an extra terminator.
1321 */
1322static UINT RenameDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
1323{
1324 NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
1325 NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
1326
1327 SHFILEOPSTRUCTW s = {0};
1328 s.hwnd = NULL;
1329 s.wFunc = FO_RENAME;
1330 s.pTo = pwszzDstDir;
1331 s.pFrom = pwszzSrcDir;
1332 s.fFlags = FOF_SILENT
1333 | FOF_NOCONFIRMATION
1334 | FOF_NOCONFIRMMKDIR
1335 | FOF_NOERRORUI;
1336
1337 logStringF(hModule, "RenameDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
1338 int r = SHFileOperationW(&s);
1339 if (r == 0)
1340 return ERROR_SUCCESS;
1341 logStringF(hModule, "RenameDir: Rename operation returned status %#x", r);
1342 return ERROR_GEN_FAILURE;
1343}
1344
1345/** RTPathAppend-like function. */
1346static UINT AppendToPath(wchar_t *pwszPath, size_t cwcPath, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1347{
1348 size_t cwcCurPath = RTUtf16Len(pwszPath);
1349 size_t cwcSlash = cwcCurPath > 1 && RTPATH_IS_SLASH(pwszPath[cwcCurPath - 1]) ? 0 : 1;
1350 while (RTPATH_IS_SLASH(*pwszAppend))
1351 pwszAppend++;
1352 size_t cwcAppend = RTUtf16Len(pwszAppend);
1353 if (cwcCurPath + cwcCurPath + cwcAppend + fDoubleTerm < cwcPath)
1354 {
1355 if (cwcSlash)
1356 pwszPath[cwcCurPath++] = '\\';
1357 memcpy(&pwszPath[cwcCurPath], pwszAppend, (cwcAppend + 1) * sizeof(wchar_t));
1358 if (fDoubleTerm)
1359 pwszPath[cwcCurPath + cwcAppend + 1] = '\0';
1360 return ERROR_SUCCESS;
1361 }
1362 return ERROR_BUFFER_OVERFLOW;
1363}
1364
1365/** RTPathJoin-like function. */
1366static UINT JoinPaths(wchar_t *pwszPath, size_t cwcPath, wchar_t *pwszPath1, const wchar_t *pwszAppend, bool fDoubleTerm = false)
1367{
1368 size_t cwcCurPath = RTUtf16Len(pwszPath1);
1369 if (cwcCurPath < cwcPath)
1370 {
1371 memcpy(pwszPath, pwszPath1, (cwcCurPath + 1) * sizeof(wchar_t));
1372 return AppendToPath(pwszPath, cwcPath, pwszAppend, fDoubleTerm);
1373 }
1374 return ERROR_BUFFER_OVERFLOW;
1375}
1376
1377UINT __stdcall UninstallBranding(MSIHANDLE hModule)
1378{
1379 logStringF(hModule, "UninstallBranding: Handling branding file ...");
1380
1381 WCHAR wszPath[RTPATH_MAX];
1382 int rc = VBoxMsiQueryProp(hModule, L"CustomActionData", wszPath, RT_ELEMENTS(wszPath));
1383 if (RT_SUCCESS(rc))
1384 {
1385 size_t const cwcPath = RTUtf16Len(wszPath);
1386 rc = AppendToPath(wszPath, RTPATH_MAX, L"custom", true /*fDoubleTerm*/);
1387 if (rc == ERROR_SUCCESS)
1388 rc = RemoveDir(hModule, wszPath);
1389
1390 /* Check for .custom directory from a failed install and remove it. */
1391 wszPath[cwcPath] = '\0';
1392 rc = AppendToPath(wszPath, RTPATH_MAX, L".custom", true /*fDoubleTerm*/);
1393 if (rc == ERROR_SUCCESS)
1394 rc = RemoveDir(hModule, wszPath);
1395 }
1396
1397 logStringF(hModule, "UninstallBranding: Handling done. (rc=%Rrc (ignored))", rc);
1398 return ERROR_SUCCESS; /* Do not fail here. */
1399}
1400
1401UINT __stdcall InstallBranding(MSIHANDLE hModule)
1402{
1403 logStringF(hModule, "InstallBranding: Handling branding file ...");
1404
1405 /*
1406 * Get the paths.
1407 */
1408 wchar_t wszSrcPath[RTPATH_MAX];
1409 int rc = VBoxMsiQueryProp(hModule, L"SOURCEDIR", wszSrcPath, RT_ELEMENTS(wszSrcPath));
1410 if (RT_SUCCESS(rc))
1411 {
1412 wchar_t wszDstPath[RTPATH_MAX];
1413 rc = VBoxMsiQueryProp(hModule, L"CustomActionData", wszDstPath, RT_ELEMENTS(wszDstPath));
1414 if (RT_SUCCESS(rc))
1415 {
1416 /*
1417 * First we copy the src\.custom dir to the target.
1418 */
1419 UINT rcWin = AppendToPath(wszSrcPath, RT_ELEMENTS(wszSrcPath) - 1, L".custom", true /*fDoubleTerm*/);
1420 if (rcWin == ERROR_SUCCESS)
1421 {
1422 rcWin = CopyDir(hModule, wszDstPath, wszSrcPath);
1423 if (rcWin == ERROR_SUCCESS)
1424 {
1425 /*
1426 * The rename the '.custom' directory we now got in the target area to 'custom'.
1427 */
1428 rcWin = JoinPaths(wszSrcPath, RT_ELEMENTS(wszSrcPath), wszDstPath, L".custom", true /*fDoubleTerm*/);
1429 if (rc == ERROR_SUCCESS)
1430 {
1431 rcWin = AppendToPath(wszDstPath, RT_ELEMENTS(wszDstPath), L"custom", true /*fDoubleTerm*/);
1432 if (rc == ERROR_SUCCESS)
1433 rcWin = RenameDir(hModule, wszDstPath, wszSrcPath);
1434 }
1435 }
1436 }
1437
1438 logStringF(hModule, "InstallBranding: Handling done. (rc=%Rrc (ignored))", rc);
1439 }
1440 }
1441
1442 logStringF(hModule, "InstallBranding: Handling done. (rc=%Rrc (ignored))", rc);
1443 return ERROR_SUCCESS; /* Do not fail here. */
1444}
1445
1446static DECLCALLBACK(void) vboxWinDrvInstLogCallback(VBOXWINDRIVERLOGTYPE enmType, const char *pszMsg, void *pvUser)
1447{
1448 MSIHANDLE *phModule = (MSIHANDLE *)pvUser;
1449
1450 switch (enmType)
1451 {
1452 case VBOXWINDRIVERLOGTYPE_ERROR:
1453 logStringF(*phModule, "*** Error: %s", pszMsg);
1454 break;
1455
1456 default:
1457 logStringF(*phModule, "%s", pszMsg);
1458 break;
1459 }
1460}
1461
1462UINT __stdcall DriverInstall(MSIHANDLE hModule)
1463{
1464 logStringF(hModule, "Installing driver ...");
1465
1466 char *pszInfFile = NULL;
1467 int rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvInstInfFile", &pszInfFile);
1468 if (RT_SUCCESS(rc))
1469 {
1470 char *pszInfSection = NULL;
1471 rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvInstInfSection", &pszInfSection);
1472 if ( RT_SUCCESS(rc)
1473 || rc == VERR_NOT_FOUND) /* VBoxDrvInstInfSection is optional. */
1474 {
1475 char *pszModel = NULL;
1476 rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvInstModel", &pszModel);
1477 if ( RT_SUCCESS(rc)
1478 || rc == VERR_NOT_FOUND) /* VBoxDrvInstModel is optional. */
1479 {
1480 char *pszPnpId = NULL;
1481 rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvInstPnpId", &pszPnpId);
1482 if ( RT_SUCCESS(rc)
1483 || rc == VERR_NOT_FOUND) /* VBoxDrvInstPnpId is optional. */
1484 {
1485 uint32_t fFlags = VBOX_WIN_DRIVERINSTALL_F_NONE;
1486
1487 DWORD dwVal;
1488 rc = VBoxMsiQueryPropInt32(hModule, "VBoxDrvInstFlagForce", &dwVal);
1489 if (RT_SUCCESS(rc))
1490 fFlags |= VBOX_WIN_DRIVERINSTALL_F_FORCE;
1491 rc = VBoxMsiQueryPropInt32(hModule, "VBoxDrvInstFlagSilent", &dwVal);
1492 if (RT_SUCCESS(rc))
1493 fFlags |= VBOX_WIN_DRIVERINSTALL_F_SILENT;
1494
1495 VBOXWINDRVINST hWinDrvInst;
1496 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, 1 /* uVerbostiy */, &vboxWinDrvInstLogCallback, &hModule /* pvUser */);
1497 if (RT_SUCCESS(rc))
1498 {
1499 if (pszInfSection && *pszInfSection)
1500 rc = VBoxWinDrvInstInstallExecuteInf(hWinDrvInst, pszInfFile, pszInfSection, fFlags);
1501 else
1502 rc = VBoxWinDrvInstInstallEx(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fFlags);
1503
1504 VBoxWinDrvInstDestroy(hWinDrvInst);
1505 }
1506
1507 RTStrFree(pszPnpId);
1508 }
1509
1510 RTStrFree(pszModel);
1511 }
1512
1513 RTStrFree(pszInfSection);
1514 }
1515
1516 RTStrFree(pszInfFile);
1517 }
1518 else
1519 {
1520 logStringF(hModule, "DriverInstall: No INF or invalid file to install specified!");
1521 if (rc == VERR_NOT_FOUND) /* Give a better clue. */
1522 rc = VERR_INVALID_PARAMETER;
1523 }
1524
1525 logStringF(hModule, "DriverInstall: Handling done (rc=%Rrc)", rc);
1526 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_DRIVER_INSTALL_BLOCKED /* Close enough */;
1527}
1528
1529UINT __stdcall DriverUninstall(MSIHANDLE hModule)
1530{
1531 char *pszInfFile = NULL;
1532 int rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvUninstInfFile", &pszInfFile);
1533 if ( RT_SUCCESS(rc)
1534 || rc == VERR_NOT_FOUND) /* VBoxDrvUninstInfFile is optional. */
1535 {
1536 char *pszInfSection = NULL;
1537 rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvUninstInfSection", &pszInfSection);
1538 if ( RT_SUCCESS(rc)
1539 || rc == VERR_NOT_FOUND) /* VBoxDrvUninstInfSection is optional. */
1540 {
1541 char *pszModel = NULL;
1542 rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvUninstModel", &pszModel);
1543 if ( RT_SUCCESS(rc)
1544 || rc == VERR_NOT_FOUND) /* VBoxDrvUninstModel is optional. */
1545 {
1546 char *pszPnpId = NULL;
1547 rc = VBoxMsiQueryPropUtf8(hModule, "VBoxDrvUninstPnpId", &pszPnpId);
1548 if ( RT_SUCCESS(rc)
1549 || rc == VERR_NOT_FOUND) /* VBoxDrvUninstPnpId is optional. */
1550 {
1551 VBOXWINDRVINST hWinDrvInst;
1552 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, 1 /* uVerbostiy */,
1553 &vboxWinDrvInstLogCallback, &hModule /* pvUser */);
1554 if (RT_SUCCESS(rc))
1555 {
1556 if (pszInfSection && *pszInfSection)
1557 rc = VBoxWinDrvInstUninstallExecuteInf(hWinDrvInst, pszInfFile, pszInfSection,
1558 VBOX_WIN_DRIVERINSTALL_F_NONE);
1559 else
1560 rc = VBoxWinDrvInstUninstall(hWinDrvInst, pszInfFile, pszModel, pszPnpId,
1561 VBOX_WIN_DRIVERINSTALL_F_NONE);
1562
1563 VBoxWinDrvInstDestroy(hWinDrvInst);
1564 }
1565
1566 RTStrFree(pszPnpId);
1567 }
1568
1569 RTStrFree(pszModel);
1570 }
1571
1572 RTStrFree(pszInfSection);
1573 }
1574
1575 RTStrFree(pszInfFile);
1576 }
1577
1578 logStringF(hModule, "DriverUninstall: Handling done (rc=%Rrc)", rc);
1579 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_DRIVER_STORE_DELETE_FAILED /* Close enough */;
1580}
1581
1582/**
1583 * Returns the platform architecture as a string.
1584 *
1585 * Sets public property VBOX_PLATFORM_ARCH to "x86", "amd64" or "arm64" on success.
1586 * Called from the MSI installer as custom action.
1587 *
1588 * @returns UINT as Windows error code.
1589 * @retval ERROR_INSTALL_PLATFORM_UNSUPPORTED if the platform is invalid or unsupported.
1590 * @param hModule Windows installer module handle.
1591 *
1592 * @note We don't use WIX' util.QueryNativeMachine, as it's apparently on available on Windows 10 >= 1709.
1593 */
1594UINT __stdcall GetPlatformArchitecture(MSIHANDLE hModule)
1595{
1596 const char *pszArch;
1597
1598 /* Only add supported platforms here. */
1599 uint32_t const uNativeArch = RTSystemGetNativeArch();
1600 switch (uNativeArch)
1601 {
1602 case RT_ARCH_VAL_X86: pszArch = "x86"; break;
1603 case RT_ARCH_VAL_AMD64: pszArch = "amd64"; break;
1604 case RT_ARCH_VAL_ARM64: pszArch = "arm64"; break;
1605 default: pszArch = NULL; break;
1606 }
1607
1608 int rc;
1609 if (pszArch)
1610 rc = VBoxMsiSetPropUtf8(hModule, "VBOX_PLATFORM_ARCH", pszArch);
1611 else
1612 rc = VERR_NOT_SUPPORTED;
1613
1614 logStringF(hModule, "GetPlatformArchitecture: Detected '%s' (%Rrc)", pszArch ? pszArch : "<Unknown>", rc);
1615
1616 return RT_SUCCESS(rc) ? ERROR_SUCCESS : ERROR_INSTALL_PLATFORM_UNSUPPORTED;
1617}
1618
1619#if defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP)
1620
1621/** @todo should use some real VBox app name */
1622#define VBOX_NETCFG_APP_NAME L"VirtualBox Installer"
1623#define VBOX_NETCFG_MAX_RETRIES 10
1624#define NETFLT_PT_INF_REL_PATH L"VBoxNetFlt.inf"
1625#define NETFLT_MP_INF_REL_PATH L"VBoxNetFltM.inf"
1626#define NETFLT_ID L"sun_VBoxNetFlt" /** @todo Needs to be changed (?). */
1627#define NETADP_ID L"sun_VBoxNetAdp" /** @todo Needs to be changed (?). */
1628
1629#define NETLWF_INF_NAME L"VBoxNetLwf.inf"
1630
1631static MSIHANDLE g_hCurrentModule = NULL;
1632
1633static UINT _uninstallNetFlt(MSIHANDLE hModule);
1634static UINT _uninstallNetLwf(MSIHANDLE hModule);
1635
1636static VOID vboxDrvLoggerCallback(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
1637{
1638 RT_NOREF1(pvContext);
1639 switch (enmSeverity)
1640 {
1641 case VBOXDRVCFG_LOG_SEVERITY_FLOW:
1642 case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
1643 break;
1644 case VBOXDRVCFG_LOG_SEVERITY_REL:
1645 if (g_hCurrentModule)
1646 logStringF(g_hCurrentModule, "%s", pszMsg);
1647 break;
1648 default:
1649 break;
1650 }
1651}
1652
1653static DECLCALLBACK(void) netCfgLoggerCallback(const char *pszString)
1654{
1655 if (g_hCurrentModule)
1656 logStringF(g_hCurrentModule, "%s", pszString);
1657}
1658
1659static VOID netCfgLoggerDisable()
1660{
1661 if (g_hCurrentModule)
1662 {
1663 VBoxNetCfgWinSetLogging(NULL);
1664 g_hCurrentModule = NULL;
1665 }
1666}
1667
1668static VOID netCfgLoggerEnable(MSIHANDLE hModule)
1669{
1670 NonStandardAssert(hModule);
1671
1672 if (g_hCurrentModule)
1673 netCfgLoggerDisable();
1674
1675 g_hCurrentModule = hModule;
1676
1677 VBoxNetCfgWinSetLogging(netCfgLoggerCallback);
1678 /* uncomment next line if you want to add logging information from VBoxDrvCfg.cpp */
1679// VBoxDrvCfgLoggerSet(vboxDrvLoggerCallback, NULL);
1680}
1681
1682static UINT errorConvertFromHResult(MSIHANDLE hModule, HRESULT hr)
1683{
1684 UINT uRet;
1685 switch (hr)
1686 {
1687 case S_OK:
1688 uRet = ERROR_SUCCESS;
1689 break;
1690
1691 case NETCFG_S_REBOOT:
1692 {
1693 logStringF(hModule, "Reboot required, setting REBOOT property to \"force\"");
1694 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
1695 if (hr2 != ERROR_SUCCESS)
1696 logStringF(hModule, "Failed to set REBOOT property, error = %#x", hr2);
1697 uRet = ERROR_SUCCESS; /* Never fail here. */
1698 break;
1699 }
1700
1701 default:
1702 logStringF(hModule, "Converting unhandled HRESULT (%#x) to ERROR_GEN_FAILURE", hr);
1703 uRet = ERROR_GEN_FAILURE;
1704 }
1705 return uRet;
1706}
1707
1708static MSIHANDLE createNetCfgLockedMsgRecord(MSIHANDLE hModule)
1709{
1710 MSIHANDLE hRecord = MsiCreateRecord(2);
1711 if (hRecord)
1712 {
1713 UINT uErr = MsiRecordSetInteger(hRecord, 1, 25001);
1714 if (uErr != ERROR_SUCCESS)
1715 {
1716 logStringF(hModule, "createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = %#x", uErr);
1717 MsiCloseHandle(hRecord);
1718 hRecord = NULL;
1719 }
1720 }
1721 else
1722 logStringF(hModule, "createNetCfgLockedMsgRecord: Failed to create a record");
1723
1724 return hRecord;
1725}
1726
1727static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite)
1728{
1729 MSIHANDLE hMsg = NULL;
1730 UINT uErr = ERROR_GEN_FAILURE;
1731 int MsgResult;
1732 int cRetries = 0;
1733
1734 do
1735 {
1736 LPWSTR lpszLockedBy;
1737 HRESULT hr = VBoxNetCfgWinQueryINetCfg(ppnc, bWrite, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy);
1738 if (hr != NETCFG_E_NO_WRITE_LOCK)
1739 {
1740 if (FAILED(hr))
1741 logStringF(hModule, "doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = %#x", hr);
1742 uErr = errorConvertFromHResult(hModule, hr);
1743 break;
1744 }
1745
1746 /* hr == NETCFG_E_NO_WRITE_LOCK */
1747
1748 if (!lpszLockedBy)
1749 {
1750 logStringF(hModule, "doNetCfgInit: lpszLockedBy == NULL, breaking");
1751 break;
1752 }
1753
1754 /* on vista the 6to4svc.dll periodically maintains the lock for some reason,
1755 * if this is the case, increase the wait period by retrying multiple times
1756 * NOTE: we could alternatively increase the wait timeout,
1757 * however it seems unneeded for most cases, e.g. in case some network connection property
1758 * dialog is opened, it would be better to post a notification to the user as soon as possible
1759 * rather than waiting for a longer period of time before displaying it */
1760 if ( cRetries < VBOX_NETCFG_MAX_RETRIES
1761 && RTUtf16ICmpAscii(lpszLockedBy, "6to4svc.dll") == 0)
1762 {
1763 cRetries++;
1764 logStringF(hModule, "doNetCfgInit: lpszLockedBy is 6to4svc.dll, retrying %d out of %d", cRetries, VBOX_NETCFG_MAX_RETRIES);
1765 MsgResult = IDRETRY;
1766 }
1767 else
1768 {
1769 if (!hMsg)
1770 {
1771 hMsg = createNetCfgLockedMsgRecord(hModule);
1772 if (!hMsg)
1773 {
1774 logStringF(hModule, "doNetCfgInit: Failed to create a message record, breaking");
1775 CoTaskMemFree(lpszLockedBy);
1776 break;
1777 }
1778 }
1779
1780 UINT rTmp = MsiRecordSetStringW(hMsg, 2, lpszLockedBy);
1781 NonStandardAssert(rTmp == ERROR_SUCCESS);
1782 if (rTmp != ERROR_SUCCESS)
1783 {
1784 logStringF(hModule, "doNetCfgInit: MsiRecordSetStringW failed, error = #%x", rTmp);
1785 CoTaskMemFree(lpszLockedBy);
1786 break;
1787 }
1788
1789 MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg);
1790 NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL);
1791 logStringF(hModule, "doNetCfgInit: MsiProcessMessage returned (%#x)", MsgResult);
1792 }
1793 CoTaskMemFree(lpszLockedBy);
1794 } while(MsgResult == IDRETRY);
1795
1796 if (hMsg)
1797 MsiCloseHandle(hMsg);
1798
1799 return uErr;
1800}
1801#endif /* defined(VBOX_WITH_NETFLT) || defined(VBOX_WITH_NETADP) */
1802
1803#ifdef VBOX_WITH_NETFLT
1804static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf,
1805 OUT LPWSTR pwszMpInf, DWORD cwcMpInf)
1806{
1807 DWORD cwcEffBuf = cwcPtInf - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)) / sizeof(WCHAR);
1808 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &cwcEffBuf);
1809 if ( uErr == ERROR_SUCCESS
1810 && cwcEffBuf > 0)
1811 {
1812 int vrc = RTUtf16Copy(pwszMpInf, cwcMpInf, pwszPtInf);
1813 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1814
1815 vrc = RTUtf16Cat(pwszPtInf, cwcPtInf, NETFLT_PT_INF_REL_PATH);
1816 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1817 logStringF(hModule, "vboxNetFltQueryInfArray: INF 1: %ls", pwszPtInf);
1818
1819 vrc = RTUtf16Cat(pwszMpInf, cwcMpInf, NETFLT_MP_INF_REL_PATH);
1820 AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
1821 logStringF(hModule, "vboxNetFltQueryInfArray: INF 2: %ls", pwszMpInf);
1822 }
1823 else if (uErr != ERROR_SUCCESS)
1824 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
1825 else
1826 {
1827 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
1828 uErr = ERROR_GEN_FAILURE;
1829 }
1830
1831 return uErr;
1832}
1833
1834static UINT _uninstallNetFlt(MSIHANDLE hModule)
1835{
1836 INetCfg *pNetCfg;
1837 UINT uErr;
1838
1839 netCfgLoggerEnable(hModule);
1840
1841 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1842
1843 __try
1844 {
1845 logStringF(hModule, "Uninstalling NetFlt");
1846
1847 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1848 if (uErr == ERROR_SUCCESS)
1849 {
1850 HRESULT hr = VBoxNetCfgWinNetFltUninstall(pNetCfg);
1851 if (hr != S_OK)
1852 logStringF(hModule, "UninstallNetFlt: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1853
1854 uErr = errorConvertFromHResult(hModule, hr);
1855
1856 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1857
1858 logStringF(hModule, "Uninstalling NetFlt done, error = %#x", uErr);
1859 }
1860 else
1861 logStringF(hModule, "UninstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1862 }
1863 __finally
1864 {
1865 if (bOldIntMode)
1866 {
1867 /* The prev mode != FALSE, i.e. non-interactive. */
1868 SetupSetNonInteractiveMode(bOldIntMode);
1869 }
1870 netCfgLoggerDisable();
1871 }
1872
1873 /* Never fail the uninstall even if we did not succeed. */
1874 return ERROR_SUCCESS;
1875}
1876#endif /* VBOX_WITH_NETFLT */
1877
1878UINT __stdcall UninstallNetFlt(MSIHANDLE hModule)
1879{
1880#ifdef VBOX_WITH_NETFLT
1881 _uninstallNetLwf(hModule);
1882 return _uninstallNetFlt(hModule);
1883#else
1884 RT_NOREF(hModule);
1885 return ERROR_SUCCESS;
1886#endif
1887}
1888
1889#ifdef VBOX_WITH_NETFLT
1890static UINT _installNetFlt(MSIHANDLE hModule)
1891{
1892 UINT uErr;
1893 INetCfg *pNetCfg;
1894
1895 netCfgLoggerEnable(hModule);
1896
1897 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1898
1899 __try
1900 {
1901
1902 logStringF(hModule, "InstallNetFlt: Installing NetFlt");
1903
1904 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1905 if (uErr == ERROR_SUCCESS)
1906 {
1907 WCHAR wszPtInf[MAX_PATH];
1908 WCHAR wszMpInf[MAX_PATH];
1909 uErr = vboxNetFltQueryInfArray(hModule, wszPtInf, RT_ELEMENTS(wszPtInf), wszMpInf, RT_ELEMENTS(wszMpInf));
1910 if (uErr == ERROR_SUCCESS)
1911 {
1912 LPCWSTR const apwszInfs[] = { wszPtInf, wszMpInf };
1913 HRESULT hr = VBoxNetCfgWinNetFltInstall(pNetCfg, &apwszInfs[0], RT_ELEMENTS(apwszInfs));
1914 if (FAILED(hr))
1915 logStringF(hModule, "InstallNetFlt: VBoxNetCfgWinNetFltInstall failed, error = %#x", hr);
1916
1917 uErr = errorConvertFromHResult(hModule, hr);
1918 }
1919 else
1920 logStringF(hModule, "InstallNetFlt: vboxNetFltQueryInfArray failed, error = %#x", uErr);
1921
1922 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1923
1924 logStringF(hModule, "InstallNetFlt: Done");
1925 }
1926 else
1927 logStringF(hModule, "InstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
1928 }
1929 __finally
1930 {
1931 if (bOldIntMode)
1932 {
1933 /* The prev mode != FALSE, i.e. non-interactive. */
1934 SetupSetNonInteractiveMode(bOldIntMode);
1935 }
1936 netCfgLoggerDisable();
1937 }
1938
1939 /* Never fail the install even if we did not succeed. */
1940 return ERROR_SUCCESS;
1941}
1942#endif /* VBOX_WITH_NETFLT */
1943
1944UINT __stdcall InstallNetFlt(MSIHANDLE hModule)
1945{
1946#ifdef VBOX_WITH_NETFLT
1947 _uninstallNetLwf(hModule);
1948 return _installNetFlt(hModule);
1949#else
1950 RT_NOREF(hModule);
1951 return ERROR_SUCCESS;
1952#endif
1953}
1954
1955#ifdef VBOX_WITH_NETFLT
1956static UINT _uninstallNetLwf(MSIHANDLE hModule)
1957{
1958 INetCfg *pNetCfg;
1959 UINT uErr;
1960
1961 netCfgLoggerEnable(hModule);
1962
1963 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
1964
1965 __try
1966 {
1967 logStringF(hModule, "Uninstalling NetLwf");
1968
1969 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
1970 if (uErr == ERROR_SUCCESS)
1971 {
1972 HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg);
1973 if (hr != S_OK)
1974 logStringF(hModule, "UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
1975
1976 uErr = errorConvertFromHResult(hModule, hr);
1977
1978 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
1979
1980 logStringF(hModule, "Uninstalling NetLwf done, error = %#x", uErr);
1981 }
1982 else
1983 logStringF(hModule, "UninstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
1984 }
1985 __finally
1986 {
1987 if (bOldIntMode)
1988 {
1989 /* The prev mode != FALSE, i.e. non-interactive. */
1990 SetupSetNonInteractiveMode(bOldIntMode);
1991 }
1992 netCfgLoggerDisable();
1993 }
1994
1995 /* Never fail the uninstall even if we did not succeed. */
1996 return ERROR_SUCCESS;
1997}
1998#endif /* VBOX_WITH_NETFLT */
1999
2000UINT __stdcall UninstallNetLwf(MSIHANDLE hModule)
2001{
2002#ifdef VBOX_WITH_NETFLT
2003 _uninstallNetFlt(hModule);
2004 return _uninstallNetLwf(hModule);
2005#else
2006 RT_NOREF(hModule);
2007 return ERROR_SUCCESS;
2008#endif
2009}
2010
2011#ifdef VBOX_WITH_NETFLT
2012static UINT _installNetLwf(MSIHANDLE hModule)
2013{
2014 UINT uErr;
2015 INetCfg *pNetCfg;
2016
2017 netCfgLoggerEnable(hModule);
2018
2019 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2020
2021 __try
2022 {
2023
2024 logStringF(hModule, "InstallNetLwf: Installing NetLwf");
2025
2026 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2027 if (uErr == ERROR_SUCCESS)
2028 {
2029 WCHAR wszInf[MAX_PATH];
2030 DWORD cwcInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1;
2031 uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cwcInf);
2032 if (uErr == ERROR_SUCCESS)
2033 {
2034 if (cwcInf)
2035 {
2036 if (wszInf[cwcInf - 1] != L'\\')
2037 {
2038 wszInf[cwcInf++] = L'\\';
2039 wszInf[cwcInf] = L'\0';
2040 }
2041
2042 int vrc = RTUtf16Cat(wszInf, RT_ELEMENTS(wszInf), NETLWF_INF_NAME);
2043 AssertRC(vrc);
2044
2045 HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf);
2046 if (FAILED(hr))
2047 logStringF(hModule, "InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = %#x", hr);
2048
2049 uErr = errorConvertFromHResult(hModule, hr);
2050 }
2051 else
2052 {
2053 logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
2054 uErr = ERROR_GEN_FAILURE;
2055 }
2056 }
2057 else
2058 logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
2059
2060 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2061
2062 logStringF(hModule, "InstallNetLwf: Done");
2063 }
2064 else
2065 logStringF(hModule, "InstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
2066 }
2067 __finally
2068 {
2069 if (bOldIntMode)
2070 {
2071 /* The prev mode != FALSE, i.e. non-interactive. */
2072 SetupSetNonInteractiveMode(bOldIntMode);
2073 }
2074 netCfgLoggerDisable();
2075 }
2076
2077 /* Never fail the install even if we did not succeed. */
2078 return ERROR_SUCCESS;
2079}
2080#endif /* VBOX_WITH_NETFLT */
2081
2082UINT __stdcall InstallNetLwf(MSIHANDLE hModule)
2083{
2084#ifdef VBOX_WITH_NETFLT
2085 _uninstallNetFlt(hModule);
2086 return _installNetLwf(hModule);
2087#else
2088 RT_NOREF(hModule);
2089 return ERROR_SUCCESS;
2090#endif
2091}
2092
2093
2094#if 0 /** @todo r=andy Remove this? */
2095static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
2096{
2097 WCHAR DevName[256];
2098 DWORD winEr;
2099
2100 if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev,
2101 SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
2102 NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
2103 (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/
2104 sizeof(DevName), /* IN DWORD PropertyBufferSize,*/
2105 NULL /*OUT PDWORD RequiredSize OPTIONAL*/
2106 ))
2107 {
2108 HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, pDev,
2109 DICS_FLAG_GLOBAL, /* IN DWORD Scope,*/
2110 0, /*IN DWORD HwProfile, */
2111 DIREG_DRV, /* IN DWORD KeyType, */
2112 KEY_READ /*IN REGSAM samDesired*/
2113 );
2114 NonStandardAssert(hKey != INVALID_HANDLE_VALUE);
2115 if (hKey != INVALID_HANDLE_VALUE)
2116 {
2117 WCHAR guid[50];
2118 DWORD cbGuid=sizeof(guid);
2119 winEr = RegQueryValueExW(hKey,
2120 L"NetCfgInstanceId", /*__in_opt LPCTSTR lpValueName,*/
2121 NULL, /*__reserved LPDWORD lpReserved,*/
2122 NULL, /*__out_opt LPDWORD lpType,*/
2123 (LPBYTE)guid, /*__out_opt LPBYTE lpData,*/
2124 &cbGuid /*guid__inout_opt LPDWORD lpcbData*/
2125 );
2126 NonStandardAssert(winEr == ERROR_SUCCESS);
2127 if (winEr == ERROR_SUCCESS)
2128 {
2129 WCHAR ConnectoinName[128];
2130 ULONG cbName = sizeof(ConnectoinName);
2131
2132 HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(DevName, ConnectoinName, &cbName);
2133 NonStandardAssert(hr == S_OK);
2134 if (SUCCEEDED(hr))
2135 {
2136 hr = VBoxNetCfgWinRenameConnection(guid, ConnectoinName);
2137 NonStandardAssert(hr == S_OK);
2138 }
2139 }
2140 }
2141 RegCloseKey(hKey);
2142 }
2143 else
2144 {
2145 NonStandardAssert(0);
2146 }
2147
2148 return TRUE;
2149}
2150#endif /* 0 */
2151
2152#ifdef VBOX_WITH_NETADP
2153static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName)
2154{
2155 netCfgLoggerEnable(hModule);
2156
2157 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2158
2159 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface");
2160
2161 HRESULT hr = E_FAIL;
2162 GUID guid;
2163 WCHAR wszMpInf[MAX_PATH];
2164 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2165 LPCWSTR pwszInfPath = NULL;
2166 bool fIsFile = false;
2167 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2168 if (uErr == ERROR_SUCCESS)
2169 {
2170 if (cwcMpInf)
2171 {
2172 logStringF(hModule, "CreateHostOnlyInterface: NetAdpDir property = %ls", wszMpInf);
2173 if (wszMpInf[cwcMpInf - 1] != L'\\')
2174 {
2175 wszMpInf[cwcMpInf++] = L'\\';
2176 wszMpInf[cwcMpInf] = L'\0';
2177 }
2178
2179 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2180 AssertRC(vrc);
2181
2182 pwszInfPath = wszMpInf;
2183 fIsFile = true;
2184
2185 logStringF(hModule, "CreateHostOnlyInterface: Resulting INF path = %ls", pwszInfPath);
2186 }
2187 else
2188 logStringF(hModule, "CreateHostOnlyInterface: VBox installation path is empty");
2189 }
2190 else
2191 logStringF(hModule, "CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = %#x", uErr);
2192
2193 /* Make sure the inf file is installed. */
2194 if (pwszInfPath != NULL && fIsFile)
2195 {
2196 logStringF(hModule, "CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%ls)", pwszInfPath);
2197 hr = VBoxDrvCfgInfInstall(pwszInfPath);
2198 logStringF(hModule, "CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns %#x", hr);
2199 if (FAILED(hr))
2200 logStringF(hModule, "CreateHostOnlyInterface: Failed to install INF file, error = %#x", hr);
2201 }
2202
2203 if (SUCCEEDED(hr))
2204 {
2205 //first, try to update Host Only Network Interface
2206 BOOL fRebootRequired = FALSE;
2207 hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2208 if (SUCCEEDED(hr))
2209 {
2210 if (fRebootRequired)
2211 {
2212 logStringF(hModule, "CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force");
2213 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2214 if (hr2 != ERROR_SUCCESS)
2215 logStringF(hModule, "CreateHostOnlyInterface: Failed to set REBOOT property for update, error = %#x", hr2);
2216 }
2217 }
2218 else
2219 {
2220 //in fail case call CreateHostOnlyInterface
2221 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2222 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface");
2223# ifdef VBOXNETCFG_DELAYEDRENAME
2224 BSTR devId;
2225 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL);
2226# else /* !VBOXNETCFG_DELAYEDRENAME */
2227 hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL);
2228# endif /* !VBOXNETCFG_DELAYEDRENAME */
2229 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr);
2230 if (SUCCEEDED(hr))
2231 {
2232 ULONG ip = inet_addr("192.168.56.1");
2233 ULONG mask = inet_addr("255.255.255.0");
2234 logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig");
2235 hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
2236 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr);
2237 if (FAILED(hr))
2238 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr);
2239# ifdef VBOXNETCFG_DELAYEDRENAME
2240 hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL);
2241 if (FAILED(hr))
2242 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr);
2243 SysFreeString(devId);
2244# endif /* VBOXNETCFG_DELAYEDRENAME */
2245 }
2246 else
2247 logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr);
2248 }
2249 }
2250
2251 if (SUCCEEDED(hr))
2252 logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface done");
2253
2254 /* Restore original setup mode. */
2255 logStringF(hModule, "CreateHostOnlyInterface: Almost done...");
2256 if (fSetupModeInteractive)
2257 SetupSetNonInteractiveMode(fSetupModeInteractive);
2258
2259 netCfgLoggerDisable();
2260
2261 logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)");
2262 /* Never fail the install even if we did not succeed. */
2263 return ERROR_SUCCESS;
2264}
2265#endif /* VBOX_WITH_NETADP */
2266
2267UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule)
2268{
2269#ifdef VBOX_WITH_NETADP
2270 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp.inf");
2271#else
2272 RT_NOREF(hModule);
2273 return ERROR_SUCCESS;
2274#endif
2275}
2276
2277UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule)
2278{
2279#ifdef VBOX_WITH_NETADP
2280# if 0 /* Trick for allowing the debugger to be attached. */
2281 for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
2282 {
2283 logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId());
2284 Sleep(1001);
2285 }
2286 Sleep(1002);
2287 __debugbreak();
2288# endif
2289 return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf");
2290#else /* !VBOX_WITH_NETADP */
2291 RT_NOREF(hModule);
2292 return ERROR_SUCCESS;
2293#endif
2294}
2295
2296#ifdef VBOX_WITH_NETADP
2297static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2298{
2299 netCfgLoggerEnable(hModule);
2300
2301 logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces");
2302
2303 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2304
2305 HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(pwszId);
2306 if (SUCCEEDED(hr))
2307 {
2308 hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", pwszId, SUOI_FORCEDELETE/* could be SUOI_FORCEDELETE */);
2309 if (FAILED(hr))
2310 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files");
2311 else
2312 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully");
2313 }
2314 else
2315 logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = %#x", hr);
2316
2317 /* Restore original setup mode. */
2318 if (fSetupModeInteractive)
2319 SetupSetNonInteractiveMode(fSetupModeInteractive);
2320
2321 netCfgLoggerDisable();
2322
2323 /* Never fail the uninstall even if we did not succeed. */
2324 return ERROR_SUCCESS;
2325}
2326#endif /* VBOX_WITH_NETADP */
2327
2328UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule)
2329{
2330#ifdef VBOX_WITH_NETADP
2331 return _removeHostOnlyInterfaces(hModule, NETADP_ID);
2332#else
2333 RT_NOREF(hModule);
2334 return ERROR_SUCCESS;
2335#endif
2336}
2337
2338#ifdef VBOX_WITH_NETADP
2339static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
2340{
2341 netCfgLoggerEnable(hModule);
2342
2343 logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces");
2344
2345 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2346
2347 HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
2348 if (SUCCEEDED(hr))
2349 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = %#x", hr);
2350 else
2351 logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces failed, hr = %#x", hr);
2352
2353 /* Restore original setup mode. */
2354 if (fSetupModeInteractive)
2355 SetupSetNonInteractiveMode(fSetupModeInteractive);
2356
2357 netCfgLoggerDisable();
2358
2359 /* Never fail the uninstall even if we did not succeed. */
2360 return ERROR_SUCCESS;
2361}
2362#endif /* VBOX_WITH_NETADP */
2363
2364UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule)
2365{
2366#ifdef VBOX_WITH_NETADP
2367 return _stopHostOnlyInterfaces(hModule, NETADP_ID);
2368#else
2369 RT_NOREF(hModule);
2370 return ERROR_SUCCESS;
2371#endif
2372}
2373
2374#ifdef VBOX_WITH_NETADP
2375static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId)
2376{
2377 netCfgLoggerEnable(hModule);
2378
2379 logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces");
2380
2381 BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
2382
2383 WCHAR wszMpInf[MAX_PATH];
2384 DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
2385 LPCWSTR pwszInfPath = NULL;
2386 bool fIsFile = false;
2387 UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
2388 if (uErr == ERROR_SUCCESS)
2389 {
2390 if (cwcMpInf)
2391 {
2392 logStringF(hModule, "UpdateHostOnlyInterfaces: NetAdpDir property = %ls", wszMpInf);
2393 if (wszMpInf[cwcMpInf - 1] != L'\\')
2394 {
2395 wszMpInf[cwcMpInf++] = L'\\';
2396 wszMpInf[cwcMpInf] = L'\0';
2397 }
2398
2399 int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
2400 AssertRC(vrc);
2401 pwszInfPath = wszMpInf;
2402 fIsFile = true;
2403
2404 logStringF(hModule, "UpdateHostOnlyInterfaces: Resulting INF path = %ls", pwszInfPath);
2405
2406 DWORD attrFile = GetFileAttributesW(pwszInfPath);
2407 if (attrFile == INVALID_FILE_ATTRIBUTES)
2408 {
2409 DWORD dwErr = GetLastError();
2410 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" not found, dwErr=%ld", pwszInfPath, dwErr);
2411 }
2412 else
2413 {
2414 logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" exists", pwszInfPath);
2415
2416 BOOL fRebootRequired = FALSE;
2417 HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
2418 if (SUCCEEDED(hr))
2419 {
2420 if (fRebootRequired)
2421 {
2422 logStringF(hModule, "UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force");
2423 HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
2424 if (hr2 != ERROR_SUCCESS)
2425 logStringF(hModule, "UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = %#x", hr2);
2426 }
2427 }
2428 else
2429 logStringF(hModule, "UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
2430 }
2431 }
2432 else
2433 logStringF(hModule, "UpdateHostOnlyInterfaces: VBox installation path is empty");
2434 }
2435 else
2436 logStringF(hModule, "UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = %#x", uErr);
2437
2438 /* Restore original setup mode. */
2439 if (fSetupModeInteractive)
2440 SetupSetNonInteractiveMode(fSetupModeInteractive);
2441
2442 netCfgLoggerDisable();
2443
2444 /* Never fail the update even if we did not succeed. */
2445 return ERROR_SUCCESS;
2446}
2447#endif /* VBOX_WITH_NETADP */
2448
2449UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2450{
2451#ifdef VBOX_WITH_NETADP
2452 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp.inf", NETADP_ID);
2453#else
2454 RT_NOREF(hModule);
2455 return ERROR_SUCCESS;
2456#endif
2457}
2458
2459UINT __stdcall Ndis6UpdateHostOnlyInterfaces(MSIHANDLE hModule)
2460{
2461#ifdef VBOX_WITH_NETADP
2462 return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID);
2463#else
2464 RT_NOREF(hModule);
2465 return ERROR_SUCCESS;
2466#endif
2467}
2468
2469#ifdef VBOX_WITH_NETADP
2470static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId)
2471{
2472 INetCfg *pNetCfg;
2473 UINT uErr;
2474
2475 netCfgLoggerEnable(hModule);
2476
2477 BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
2478
2479 __try
2480 {
2481 logStringF(hModule, "Uninstalling NetAdp");
2482
2483 uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
2484 if (uErr == ERROR_SUCCESS)
2485 {
2486 HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId);
2487 if (hr != S_OK)
2488 logStringF(hModule, "UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
2489
2490 uErr = errorConvertFromHResult(hModule, hr);
2491
2492 VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
2493
2494 logStringF(hModule, "Uninstalling NetAdp done, error = %#x", uErr);
2495 }
2496 else
2497 logStringF(hModule, "UninstallNetAdp: doNetCfgInit failed, error = %#x", uErr);
2498 }
2499 __finally
2500 {
2501 if (bOldIntMode)
2502 {
2503 /* The prev mode != FALSE, i.e. non-interactive. */
2504 SetupSetNonInteractiveMode(bOldIntMode);
2505 }
2506 netCfgLoggerDisable();
2507 }
2508
2509 /* Never fail the uninstall even if we did not succeed. */
2510 return ERROR_SUCCESS;
2511}
2512#endif /* VBOX_WITH_NETADP */
2513
2514UINT __stdcall UninstallNetAdp(MSIHANDLE hModule)
2515{
2516#ifdef VBOX_WITH_NETADP
2517 return _uninstallNetAdp(hModule, NETADP_ID);
2518#else
2519 RT_NOREF(hModule);
2520 return ERROR_SUCCESS;
2521#endif
2522}
2523
2524static bool isTAPDevice(const WCHAR *pwszGUID)
2525{
2526 HKEY hNetcard;
2527 bool bIsTapDevice = false;
2528 LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
2529 L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
2530 0, KEY_READ, &hNetcard);
2531 if (lStatus != ERROR_SUCCESS)
2532 return false;
2533
2534 int i = 0;
2535 for (;;)
2536 {
2537 WCHAR wszEnumName[256];
2538 WCHAR wszNetCfgInstanceId[256];
2539 DWORD dwKeyType;
2540 HKEY hNetCardGUID;
2541
2542 DWORD dwLen = sizeof(wszEnumName);
2543 lStatus = RegEnumKeyExW(hNetcard, i, wszEnumName, &dwLen, NULL, NULL, NULL, NULL);
2544 if (lStatus != ERROR_SUCCESS)
2545 break;
2546
2547 lStatus = RegOpenKeyExW(hNetcard, wszEnumName, 0, KEY_READ, &hNetCardGUID);
2548 if (lStatus == ERROR_SUCCESS)
2549 {
2550 dwLen = sizeof(wszNetCfgInstanceId);
2551 lStatus = RegQueryValueExW(hNetCardGUID, L"NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)wszNetCfgInstanceId, &dwLen);
2552 if ( lStatus == ERROR_SUCCESS
2553 && dwKeyType == REG_SZ)
2554 {
2555 WCHAR wszNetProductName[256];
2556 WCHAR wszNetProviderName[256];
2557
2558 wszNetProductName[0] = 0;
2559 dwLen = sizeof(wszNetProductName);
2560 lStatus = RegQueryValueExW(hNetCardGUID, L"ProductName", NULL, &dwKeyType, (LPBYTE)wszNetProductName, &dwLen);
2561
2562 wszNetProviderName[0] = 0;
2563 dwLen = sizeof(wszNetProviderName);
2564 lStatus = RegQueryValueExW(hNetCardGUID, L"ProviderName", NULL, &dwKeyType, (LPBYTE)wszNetProviderName, &dwLen);
2565
2566 if ( !RTUtf16Cmp(wszNetCfgInstanceId, pwszGUID)
2567 && !RTUtf16Cmp(wszNetProductName, L"VirtualBox TAP Adapter")
2568 && ( (!RTUtf16Cmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */
2569 || (!RTUtf16Cmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */
2570 || (!RTUtf16Cmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */
2571 )
2572 )
2573 {
2574 bIsTapDevice = true;
2575 RegCloseKey(hNetCardGUID);
2576 break;
2577 }
2578 }
2579 RegCloseKey(hNetCardGUID);
2580 }
2581 ++i;
2582 }
2583
2584 RegCloseKey(hNetcard);
2585 return bIsTapDevice;
2586}
2587
2588/** @todo r=andy BUGBUG WTF! Why do we a) set the rc to 0 (success), and b) need this macro at all!?
2589 *
2590 * @todo r=bird: Because it's returning a bool, not int? The return code is
2591 * ignored anyway, both internally in removeNetworkInterface and in it's caller.
2592 * There is similar code in VBoxNetCfg.cpp, which is probably where it was copied from. */
2593#define SetErrBreak(args) \
2594 if (1) { \
2595 rc = 0; \
2596 logStringF args; \
2597 break; \
2598 } else do {} while (0)
2599
2600int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID)
2601{
2602 int rc = 1;
2603 do /* break-loop */
2604 {
2605 WCHAR wszPnPInstanceId[512] = {0};
2606
2607 /* We have to find the device instance ID through a registry search */
2608
2609 HKEY hkeyNetwork = 0;
2610 HKEY hkeyConnection = 0;
2611
2612 do /* break-loop */
2613 {
2614 WCHAR wszRegLocation[256];
2615 RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
2616 "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", pwszGUID);
2617 LONG lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
2618 if (lrc != ERROR_SUCCESS || !hkeyNetwork)
2619 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [1]",
2620 wszRegLocation, lrc));
2621
2622 lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
2623 if (lrc != ERROR_SUCCESS || !hkeyConnection)
2624 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [2]",
2625 wszRegLocation, lrc));
2626
2627 DWORD len = sizeof(wszPnPInstanceId);
2628 DWORD dwKeyType;
2629 lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len);
2630 if (lrc != ERROR_SUCCESS || dwKeyType != REG_SZ)
2631 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [3]",
2632 wszRegLocation, lrc));
2633 }
2634 while (0);
2635
2636 if (hkeyConnection)
2637 RegCloseKey(hkeyConnection);
2638 if (hkeyNetwork)
2639 RegCloseKey(hkeyNetwork);
2640
2641 /*
2642 * Now we are going to enumerate all network devices and
2643 * wait until we encounter the right device instance ID
2644 */
2645
2646 HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
2647 BOOL fResult;
2648
2649 do /* break-loop */
2650 {
2651 /* initialize the structure size */
2652 SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
2653
2654 /* copy the net class GUID */
2655 GUID netGuid;
2656 memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
2657
2658 /* return a device info set contains all installed devices of the Net class */
2659 hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
2660 if (hDeviceInfo == INVALID_HANDLE_VALUE)
2661 {
2662 logStringF(hModule, "VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError());
2663 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2664 }
2665
2666 /* enumerate the driver info list */
2667 BOOL fFoundDevice = FALSE;
2668 for (DWORD index = 0;; index++)
2669 {
2670 fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
2671 if (!fResult)
2672 {
2673 if (GetLastError() == ERROR_NO_MORE_ITEMS)
2674 break;
2675 continue;
2676 }
2677
2678 /* try to get the hardware ID registry property */
2679 WCHAR *pwszDeviceHwid;
2680 DWORD size = 0;
2681 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2682 &DeviceInfoData,
2683 SPDRP_HARDWAREID,
2684 NULL,
2685 NULL,
2686 0,
2687 &size);
2688 if (!fResult)
2689 {
2690 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2691 continue;
2692
2693 pwszDeviceHwid = (WCHAR *)RTMemAllocZ(size);
2694 if (!pwszDeviceHwid)
2695 continue;
2696
2697 fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
2698 &DeviceInfoData,
2699 SPDRP_HARDWAREID,
2700 NULL,
2701 (PBYTE)pwszDeviceHwid,
2702 size,
2703 &size);
2704 if (!fResult)
2705 {
2706 RTMemFree(pwszDeviceHwid);
2707 continue;
2708 }
2709 }
2710 else
2711 {
2712 /* something is wrong. This shouldn't have worked with a NULL buffer */
2713 continue;
2714 }
2715
2716 for (WCHAR *t = pwszDeviceHwid;
2717 *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)];
2718 t += RTUtf16Len(t) + 1)
2719 {
2720 if (RTUtf16ICmpAscii(t, "vboxtap") == 0)
2721 {
2722 /* get the device instance ID */
2723 WCHAR wszDevID[MAX_DEVICE_ID_LEN];
2724 if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
2725 {
2726 /* compare to what we determined before */
2727 if (RTUtf16Cmp(wszDevID, wszPnPInstanceId) == 0)
2728 {
2729 fFoundDevice = TRUE;
2730 break;
2731 }
2732 }
2733 }
2734 }
2735
2736 RTMemFree(pwszDeviceHwid);
2737
2738 if (fFoundDevice)
2739 break;
2740 }
2741
2742 if (fFoundDevice)
2743 {
2744 fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
2745 if (!fResult)
2746 {
2747 logStringF(hModule, "VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError());
2748 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2749 }
2750
2751 fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
2752 if (!fResult)
2753 {
2754 logStringF(hModule, "VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError());
2755 SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
2756 }
2757 }
2758 else
2759 SetErrBreak((hModule, "VBox HostInterfaces: Host interface network device not found!"));
2760 } while (0);
2761
2762 /* clean up the device info set */
2763 if (hDeviceInfo != INVALID_HANDLE_VALUE)
2764 SetupDiDestroyDeviceInfoList(hDeviceInfo);
2765 } while (0);
2766 return rc;
2767}
2768
2769UINT __stdcall UninstallTAPInstances(MSIHANDLE hModule)
2770{
2771 static const wchar_t s_wszNetworkKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
2772 HKEY hCtrlNet;
2773
2774 LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet);
2775 if (lrc == ERROR_SUCCESS)
2776 {
2777 logStringF(hModule, "VBox HostInterfaces: Enumerating interfaces ...");
2778 for (int i = 0; ; ++i)
2779 {
2780 WCHAR wszNetworkGUID[256] = { 0 };
2781 DWORD dwLen = (DWORD)sizeof(wszNetworkGUID);
2782 lrc = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL);
2783 if (lrc != ERROR_SUCCESS)
2784 {
2785 switch (lrc)
2786 {
2787 case ERROR_NO_MORE_ITEMS:
2788 logStringF(hModule, "VBox HostInterfaces: No interfaces found.");
2789 break;
2790 default:
2791 logStringF(hModule, "VBox HostInterfaces: Enumeration failed: %ld", lrc);
2792 break;
2793 }
2794 break;
2795 }
2796
2797 if (isTAPDevice(wszNetworkGUID))
2798 {
2799 logStringF(hModule, "VBox HostInterfaces: Removing interface \"%ls\" ...", wszNetworkGUID);
2800 removeNetworkInterface(hModule, wszNetworkGUID);
2801 lrc = RegDeleteKeyW(hCtrlNet, wszNetworkGUID);
2802 }
2803 }
2804 RegCloseKey(hCtrlNet);
2805 logStringF(hModule, "VBox HostInterfaces: Removing interfaces done.");
2806 }
2807 return ERROR_SUCCESS;
2808}
2809
2810
2811/**
2812 * This is used to remove the old VBoxDrv service before installation.
2813 *
2814 * The current service name is VBoxSup but the INF file won't remove the old
2815 * one, so we do it manually to try prevent trouble as the device nodes are the
2816 * same and we would fail starting VBoxSup.sys if VBoxDrv.sys is still loading.
2817 *
2818 * Status code is ignored for now as a reboot should fix most potential trouble
2819 * here (and I don't want to break stuff too badly).
2820 *
2821 * @sa @bugref{10162}
2822 */
2823UINT __stdcall UninstallVBoxDrv(MSIHANDLE hModule)
2824{
2825 /*
2826 * Try open the service.
2827 */
2828 SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG | SERVICE_STOP | SERVICE_QUERY_STATUS);
2829 if (hSMgr)
2830 {
2831 SC_HANDLE hService = OpenServiceW(hSMgr, L"VBoxDrv", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
2832 if (hService)
2833 {
2834 /*
2835 * Try stop it before we delete it.
2836 */
2837 SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 };
2838 QueryServiceStatus(hService, &Status);
2839 if (Status.dwCurrentState == SERVICE_STOPPED)
2840 logStringF(hModule, "VBoxDrv: The service old service was already stopped");
2841 else
2842 {
2843 logStringF(hModule, "VBoxDrv: Stopping the service (state %u)", Status.dwCurrentState);
2844 if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
2845 {
2846 /* waiting for it to stop: */
2847 int iWait = 100;
2848 while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
2849 {
2850 Sleep(100);
2851 QueryServiceStatus(hService, &Status);
2852 }
2853
2854 if (Status.dwCurrentState == SERVICE_STOPPED)
2855 logStringF(hModule, "VBoxDrv: Stopped service");
2856 else
2857 logStringF(hModule, "VBoxDrv: Failed to stop the service, status: %u", Status.dwCurrentState);
2858 }
2859 else
2860 {
2861 DWORD const dwErr = GetLastError();
2862 if ( Status.dwCurrentState == SERVICE_STOP_PENDING
2863 && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
2864 logStringF(hModule, "VBoxDrv: Failed to stop the service: stop pending, not accepting control messages");
2865 else
2866 logStringF(hModule, "VBoxDrv: Failed to stop the service: dwErr=%u status=%u", dwErr, Status.dwCurrentState);
2867 }
2868 }
2869
2870 /*
2871 * Delete the service, or at least mark it for deletion.
2872 */
2873 if (DeleteService(hService))
2874 logStringF(hModule, "VBoxDrv: Successfully delete service");
2875 else
2876 logStringF(hModule, "VBoxDrv: Failed to delete the service: %u", GetLastError());
2877
2878 CloseServiceHandle(hService);
2879 }
2880 else
2881 {
2882 DWORD const dwErr = GetLastError();
2883 if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
2884 logStringF(hModule, "VBoxDrv: Nothing to do, the old service does not exist");
2885 else
2886 logStringF(hModule, "VBoxDrv: Failed to open the service: %u", dwErr);
2887 }
2888
2889 CloseServiceHandle(hSMgr);
2890 }
2891 else
2892 logStringF(hModule, "VBoxDrv: Failed to open service manager (%u).", GetLastError());
2893
2894 return ERROR_SUCCESS;
2895}
2896
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