VirtualBox

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

Last change on this file since 108071 was 108069, checked in by vboxsync, 3 months ago

Windows/InstallHelper: Resolved some @todos. We should use something more generic (from IPRT probably) there anyway. Later.

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