VirtualBox

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

Last change on this file since 107995 was 107995, checked in by vboxsync, 3 weeks ago

Host installer/win: Fixed timeout value handling in ServiceControl() of VBoxInstallHelper.dll. ​bugref:10762

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

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