summaryrefslogtreecommitdiffstats
path: root/src/VBox/Installer/win/InstallHelper
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Installer/win/InstallHelper
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/VBox/Installer/win/InstallHelper/Makefile.kmk65
-rw-r--r--src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp103
-rw-r--r--src/VBox/Installer/win/InstallHelper/VBoxCommon.h44
-rw-r--r--src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp2097
-rw-r--r--src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def51
-rw-r--r--src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.rc61
6 files changed, 2421 insertions, 0 deletions
diff --git a/src/VBox/Installer/win/InstallHelper/Makefile.kmk b/src/VBox/Installer/win/InstallHelper/Makefile.kmk
new file mode 100644
index 00000000..cad3c4c3
--- /dev/null
+++ b/src/VBox/Installer/win/InstallHelper/Makefile.kmk
@@ -0,0 +1,65 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for VBoxInstallHelper.dll.
+#
+
+#
+# Copyright (C) 2008-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+
+DLLS += VBoxInstallHelper
+VBoxInstallHelper_TEMPLATE = VBoxR3StaticDllNoAsan
+VBoxInstallHelper_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK)
+VBoxInstallHelper_DEFS = _WIN32_WINNT=0x0501 _UNICODE UNICODE VBOX_SVN_REV=$(VBOX_SVN_REV)
+ifdef VBOX_WITH_NETFLT
+ VBoxInstallHelper_SDKS += VBoxWinNewDevLib
+ VBoxInstallHelper_DEFS += VBOX_WITH_NETFLT=1
+endif
+VBoxInstallHelper_DEPS = $(VBOX_SVN_REV_KMK)
+VBoxInstallHelper_SOURCES = \
+ VBoxInstallHelper.cpp \
+ VBoxInstallHelper.def \
+ VBoxInstallHelper.rc \
+ VBoxCommon.cpp
+ifndef VBOX_OSE
+ VBoxInstallHelper_SOURCES += \
+ internal/VBoxSerial.cpp
+endif
+VBoxInstallHelper_LIBS = \
+ $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/Msi.lib
+ifdef VBOX_WITH_NETFLT
+ VBoxInstallHelper_LIBS += \
+ $(PATH_STAGE_LIB)/WinNetConfigSharedStatic.lib \
+ $(PATH_STAGE_LIB)/VBoxDrvCfgSharedStatic.lib \
+ $(PATH_TOOL_$(VBOX_VCC_TOOL)_LIB)/comsupp.lib \
+ $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/WbemUuid.Lib
+endif
+
+if "$(KBUILD_TARGET)" == "win" && defined(VBOX_WITH_TESTCASES) && !defined(VBOX_OSE)
+ include $(PATH_SUB_CURRENT)/testcase/Makefile.kmk
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
+
diff --git a/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp b/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp
new file mode 100644
index 00000000..f02447bc
--- /dev/null
+++ b/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp
@@ -0,0 +1,103 @@
+/* $Id: VBoxCommon.cpp $ */
+/** @file
+ * VBoxCommon - Misc helper routines for install helper.
+ *
+ * This is used by internal/serial.cpp and VBoxInstallHelper.cpp.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <msi.h>
+#include <msiquery.h>
+
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+UINT VBoxGetMsiProp(MSIHANDLE hMsi, const WCHAR *pwszName, WCHAR *pwszValueBuf, DWORD cwcValueBuf)
+{
+ RT_BZERO(pwszValueBuf, cwcValueBuf * sizeof(pwszValueBuf[0]));
+
+ /** @todo r=bird: why do we need to query the size first and then the data.
+ * The API should be perfectly capable of doing that without our help. */
+ DWORD cwcNeeded = 0;
+ UINT uiRet = MsiGetPropertyW(hMsi, pwszName, L"", &cwcNeeded);
+ if (uiRet == ERROR_MORE_DATA)
+ {
+ ++cwcNeeded; /* On output does not include terminating null, so add 1. */
+
+ if (cwcNeeded > cwcValueBuf)
+ return ERROR_MORE_DATA;
+ uiRet = MsiGetPropertyW(hMsi, pwszName, pwszValueBuf, &cwcNeeded);
+ }
+ return uiRet;
+}
+
+#if 0 /* unused */
+/**
+ * Retrieves a MSI property (in UTF-8).
+ *
+ * Convenience function for VBoxGetMsiProp().
+ *
+ * @returns VBox status code.
+ * @param hMsi MSI handle to use.
+ * @param pcszName Name of property to retrieve.
+ * @param ppszValue Where to store the allocated value on success.
+ * Must be free'd using RTStrFree() by the caller.
+ */
+int VBoxGetMsiPropUtf8(MSIHANDLE hMsi, const char *pcszName, char **ppszValue)
+{
+ PRTUTF16 pwszName;
+ int rc = RTStrToUtf16(pcszName, &pwszName);
+ if (RT_SUCCESS(rc))
+ {
+ WCHAR wszValue[1024]; /* 1024 should be enough for everybody (tm). */
+ if (VBoxGetMsiProp(hMsi, pwszName, wszValue, sizeof(wszValue)) == ERROR_SUCCESS)
+ rc = RTUtf16ToUtf8(wszValue, ppszValue);
+ else
+ rc = VERR_NOT_FOUND;
+
+ RTUtf16Free(pwszName);
+ }
+
+ return rc;
+}
+#endif
+
+UINT VBoxSetMsiProp(MSIHANDLE hMsi, const WCHAR *pwszName, const WCHAR *pwszValue)
+{
+ return MsiSetPropertyW(hMsi, pwszName, pwszValue);
+}
+
+UINT VBoxSetMsiPropDWORD(MSIHANDLE hMsi, const WCHAR *pwszName, DWORD dwVal)
+{
+ wchar_t wszTemp[32];
+ RTUtf16Printf(wszTemp, RT_ELEMENTS(wszTemp), "%u", dwVal);
+ return VBoxSetMsiProp(hMsi, pwszName, wszTemp);
+}
+
diff --git a/src/VBox/Installer/win/InstallHelper/VBoxCommon.h b/src/VBox/Installer/win/InstallHelper/VBoxCommon.h
new file mode 100644
index 00000000..1d8d0014
--- /dev/null
+++ b/src/VBox/Installer/win/InstallHelper/VBoxCommon.h
@@ -0,0 +1,44 @@
+/* $Id: VBoxCommon.h $ */
+/** @file
+ * VBoxCommon - Misc helper routines for install helper.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_InstallHelper_VBoxCommon_h
+#define VBOX_INCLUDED_SRC_InstallHelper_VBoxCommon_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#if (_MSC_VER < 1400) /* Provide _stprintf_s to VC < 8.0. */
+int swprintf_s(WCHAR *buffer, size_t cbBuffer, const WCHAR *format, ...);
+#endif
+
+UINT VBoxGetMsiProp(MSIHANDLE hMsi, const WCHAR *pwszName, WCHAR *pwszValueBuf, DWORD cwcValueBuf);
+int VBoxGetMsiPropUtf8(MSIHANDLE hMsi, const char *pcszName, char **ppszValue);
+UINT VBoxSetMsiProp(MSIHANDLE hMsi, const WCHAR *pwszName, const WCHAR *pwszValue);
+UINT VBoxSetMsiPropDWORD(MSIHANDLE hMsi, const WCHAR *pwszName, DWORD dwVal);
+
+#endif /* !VBOX_INCLUDED_SRC_InstallHelper_VBoxCommon_h */
+
diff --git a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp
new file mode 100644
index 00000000..f82f0848
--- /dev/null
+++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp
@@ -0,0 +1,2097 @@
+/* $Id: VBoxInstallHelper.cpp $ */
+/** @file
+ * VBoxInstallHelper - Various helper routines for Windows host installer.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef VBOX_WITH_NETFLT
+# include "VBox/VBoxNetCfg-win.h"
+# include "VBox/VBoxDrvCfg-win.h"
+#endif
+
+#include <msi.h>
+#include <msiquery.h>
+
+#define _WIN32_DCOM
+#include <iprt/win/windows.h>
+
+#include <shellapi.h>
+#define INITGUID
+#include <guiddef.h>
+#include <cfgmgr32.h>
+#include <devguid.h>
+
+#include <iprt/win/objbase.h>
+#include <iprt/win/setupapi.h>
+#include <iprt/win/shlobj.h>
+
+#include <VBox/version.h>
+
+#include <iprt/assert.h>
+#include <iprt/alloca.h>
+#include <iprt/mem.h>
+#include <iprt/path.h> /* RTPATH_MAX, RTPATH_IS_SLASH */
+#include <iprt/string.h> /* RT_ZERO */
+#include <iprt/utf16.h>
+
+#include "VBoxCommon.h"
+#ifndef VBOX_OSE
+# include "internal/VBoxSerial.h"
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifdef DEBUG
+# define NonStandardAssert(_expr) Assert(_expr)
+#else
+# define NonStandardAssert(_expr) do{ }while(0)
+#endif
+
+#define MY_WTEXT_HLP(a_str) L##a_str
+#define MY_WTEXT(a_str) MY_WTEXT_HLP(a_str)
+
+
+
+/**
+ * DLL entry point.
+ */
+BOOL WINAPI DllMain(HANDLE hInst, ULONG uReason, LPVOID pReserved)
+{
+ RT_NOREF(hInst, uReason, pReserved);
+
+#if 0
+ /*
+ * This is a trick for allowing the debugger to be attached, don't know if
+ * there is an official way to do that, but this is a pretty efficient.
+ *
+ * Monitor the debug output in DbgView and be ready to start windbg when
+ * the message below appear. This will happen 3-4 times during install,
+ * and 2-3 times during uninstall.
+ *
+ * Note! The DIFxApp.DLL will automatically trigger breakpoints when a
+ * debugger is attached. Just continue on these.
+ */
+ if (uReason == DLL_PROCESS_ATTACH)
+ {
+ WCHAR wszMsg[128];
+ RTUtf16Printf(wszMsg, RT_ELEMENTS(wszMsg), "Waiting for debugger to attach: windbg -g -G -p %u\n", GetCurrentProcessId());
+ for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
+ {
+ OutputDebugStringW(wszMsg);
+ Sleep(1001);
+ }
+ Sleep(1002);
+ __debugbreak();
+ }
+#endif
+
+ return TRUE;
+}
+
+/**
+ * Format and add message to the MSI log.
+ *
+ * UTF-16 strings are formatted using '%ls' (lowercase).
+ * ANSI strings are formatted using '%s' (uppercase).
+ */
+static UINT logStringF(MSIHANDLE hInstall, const char *pszFmt, ...)
+{
+ PMSIHANDLE hMSI = MsiCreateRecord(2 /* cParms */);
+ if (hMSI)
+ {
+ wchar_t wszBuf[RTPATH_MAX + 256];
+ va_list va;
+ va_start(va, pszFmt);
+ ssize_t cwc = RTUtf16PrintfV(wszBuf, RT_ELEMENTS(wszBuf), pszFmt, va);
+ va_end(va);
+
+ MsiRecordSetStringW(hMSI, 0, wszBuf);
+ MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), hMSI);
+
+ MsiCloseHandle(hMSI);
+ return cwc < RT_ELEMENTS(wszBuf) ? ERROR_SUCCESS : ERROR_BUFFER_OVERFLOW;
+ }
+ return ERROR_ACCESS_DENIED;
+}
+
+UINT __stdcall IsSerialCheckNeeded(MSIHANDLE hModule)
+{
+#ifndef VBOX_OSE
+ /*BOOL fRet =*/ serialCheckNeeded(hModule);
+#else
+ RT_NOREF(hModule);
+#endif
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall CheckSerial(MSIHANDLE hModule)
+{
+#ifndef VBOX_OSE
+ /*BOOL bRet =*/ serialIsValid(hModule);
+#else
+ RT_NOREF(hModule);
+#endif
+ return ERROR_SUCCESS;
+}
+
+/**
+ * Runs an executable on the OS.
+ *
+ * @returns Windows error code.
+ * @param hModule Windows installer module handle.
+ * @param pwszImage The executable to run.
+ * @param pwszArgs The arguments (command line w/o executable).
+ */
+static UINT procRun(MSIHANDLE hModule, const wchar_t *pwszImage, wchar_t const *pwszArgs)
+{
+ /*
+ * Construct a full command line.
+ */
+ size_t const cwcImage = RTUtf16Len(pwszImage);
+ size_t const cwcArgs = RTUtf16Len(pwszArgs);
+
+ wchar_t *pwszCmdLine = (wchar_t *)alloca((1 + cwcImage + 1 + 1 + cwcArgs + 1) * sizeof(wchar_t));
+ pwszCmdLine[0] = '"';
+ memcpy(&pwszCmdLine[1], pwszImage, cwcImage * sizeof(wchar_t));
+ pwszCmdLine[1 + cwcImage] = '"';
+ pwszCmdLine[1 + cwcImage + 1] = ' ';
+ memcpy(&pwszCmdLine[1 + cwcImage + 1 + 1], pwszArgs, (cwcArgs + 1) * sizeof(wchar_t));
+
+ /*
+ * Construct startup info.
+ */
+ STARTUPINFOW StartupInfo;
+ RT_ZERO(StartupInfo);
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+#ifndef DEBUG
+ StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ StartupInfo.wShowWindow = SW_HIDE;
+#endif
+
+ /*
+ * Start it.
+ */
+ UINT rcWin;
+ PROCESS_INFORMATION ChildInfo = { NULL, NULL, 0, 0 };
+ if (CreateProcessW(pwszImage, pwszCmdLine, NULL /*pProcessAttribs*/, NULL /*pThreadAttribs*/, TRUE /*fInheritHandles*/,
+ 0 /*fFlags*/, NULL /*pwszEnv*/, NULL /*pwszCwd*/, &StartupInfo, &ChildInfo))
+ {
+ logStringF(hModule, "procRun: Info: Started process %u: %ls", ChildInfo.dwProcessId, pwszCmdLine);
+ CloseHandle(ChildInfo.hThread);
+ DWORD const dwWait = WaitForSingleObject(ChildInfo.hProcess, RT_MS_30SEC);
+ DWORD dwExitCode = 0xf00dface;
+ if (GetExitCodeProcess(ChildInfo.hProcess, &dwExitCode))
+ {
+ if (dwExitCode == 0)
+ {
+ logStringF(hModule, "procRun: Info: Process '%ls' terminated exit code zero", pwszCmdLine);
+ rcWin = ERROR_SUCCESS;
+ }
+ else
+ {
+ logStringF(hModule, "procRun: Process '%ls' terminated with non-zero exit code: %u (%#x)",
+ pwszCmdLine, dwExitCode, dwExitCode);
+ rcWin = ERROR_GEN_FAILURE;
+ }
+ }
+ else
+ {
+ rcWin = GetLastError();
+ logStringF(hModule, "procRun: Process '%ls' is probably still running: rcWin=%u dwWait=%u (%#x)",
+ pwszCmdLine, rcWin, dwWait, dwWait);
+ }
+ }
+ else
+ {
+ rcWin = GetLastError();
+ logStringF(hModule, "procRun: Creating process '%ls' failed: rcWin=%u\n", pwszCmdLine, rcWin);
+ }
+ return rcWin;
+}
+
+/**
+ * Tries to retrieve the Python installation path on the system, extended version.
+ *
+ * @returns Windows error code.
+ * @param hModule Windows installer module handle.
+ * @param hKeyRoot Registry root key to use, e.g. HKEY_LOCAL_MACHINE.
+ * @param pwszPythonPath Buffer to return the path for python.exe in.
+ * @param cwcPythonPath Buffer size in UTF-16 units.
+ * @param fReturnExe Return the path to python.exe if true, otherwise
+ * just the python install directory.
+ */
+static UINT getPythonPathEx(MSIHANDLE hModule, HKEY hKeyRoot, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe)
+{
+ *pwszPythonPath = '\0';
+
+ /*
+ * Enumerate the subkeys of python core installation key.
+ *
+ * Note: The loop ASSUMES that later found versions are higher, e.g. newer
+ * Python versions. For now we always go by the newest version.
+ */
+ HKEY hKeyPythonCore = NULL;
+ LSTATUS dwErr = RegOpenKeyExW(hKeyRoot, L"SOFTWARE\\Python\\PythonCore", 0, KEY_READ, &hKeyPythonCore);
+ if (dwErr != ERROR_SUCCESS)
+ return dwErr;
+
+ UINT rcWinRet = ERROR_PATH_NOT_FOUND;
+ for (DWORD i = 0; i < 16384; ++i)
+ {
+ static wchar_t const s_wszInstallPath[] = L"\\InstallPath";
+ static wchar_t const s_wszPythonExe[] = L"python.exe";
+
+ /* Get key name: */
+ wchar_t wszBuf[RTPATH_MAX + RT_MAX(RT_ELEMENTS(s_wszInstallPath), RT_ELEMENTS(s_wszPythonExe)) + 2];
+ DWORD cwcKeyNm = RTPATH_MAX;
+ DWORD dwKeyType = REG_SZ;
+ dwErr = RegEnumKeyExW(hKeyPythonCore, i, wszBuf, &cwcKeyNm, NULL, NULL, NULL, NULL);
+ if (dwErr == ERROR_NO_MORE_ITEMS)
+ break;
+ if (dwErr != ERROR_SUCCESS)
+ continue;
+ if (dwKeyType != REG_SZ)
+ continue;
+ if (cwcKeyNm == 0)
+ continue;
+ NonStandardAssert(cwcKeyNm <= sizeof(wszBuf));
+
+ /* Try Open the InstallPath subkey: */
+ memcpy(&wszBuf[cwcKeyNm], s_wszInstallPath, sizeof(s_wszInstallPath));
+
+ HKEY hKeyInstallPath = NULL;
+ dwErr = RegOpenKeyExW(hKeyPythonCore, wszBuf, 0, KEY_READ, &hKeyInstallPath);
+ if (dwErr != ERROR_SUCCESS)
+ continue;
+
+ /* Query the value. We double buffer this so we don't overwrite an okay
+ return value with this. Use the smaller of cwcPythonPath and wszValue
+ so RegQueryValueExW can do all the buffer overflow checking for us.
+ For paranoid reasons, we reserve a space for a terminator as well as
+ a slash. (ASSUMES reasonably sized output buffer.) */
+ NonStandardAssert(cwcPythonPath > RT_ELEMENTS(s_wszPythonExe) + 16);
+ DWORD cbValue = (DWORD)RT_MIN( cwcPythonPath * sizeof(wchar_t)
+ - (fReturnExe ? sizeof(s_wszInstallPath) - sizeof(wchar_t) * 2 : sizeof(wchar_t) * 2),
+ RTPATH_MAX * sizeof(wchar_t));
+ DWORD dwValueType = REG_SZ;
+ dwErr = RegQueryValueExW(hKeyInstallPath, L"", NULL, &dwValueType, (LPBYTE)wszBuf, &cbValue);
+ RegCloseKey(hKeyInstallPath);
+ if ( dwErr == ERROR_SUCCESS
+ && dwValueType == REG_SZ
+ && cbValue >= sizeof(L"C:\\") - sizeof(L""))
+ {
+ /* Find length in wchar_t unit w/o terminator: */
+ DWORD cwc = cbValue / sizeof(wchar_t);
+ while (cwc > 0 && wszBuf[cwc - 1] == '\0')
+ cwc--;
+ wszBuf[cwc] = '\0';
+ if (cwc > 2)
+ {
+ /* Check if the path leads to a directory with a python.exe file in it. */
+ if (!RTPATH_IS_SLASH(wszBuf[cwc - 1]))
+ wszBuf[cwc++] = '\\';
+ memcpy(&wszBuf[cwc], s_wszPythonExe, sizeof(s_wszPythonExe));
+ DWORD const fAttribs = GetFileAttributesW(wszBuf);
+ if (fAttribs != INVALID_FILE_ATTRIBUTES)
+ {
+ if (!(fAttribs & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /* Okay, we found something that can be returned. */
+ if (fReturnExe)
+ cwc += RT_ELEMENTS(s_wszPythonExe) - 1;
+ wszBuf[cwc] = '\0';
+ logStringF(hModule, "getPythonPath: Found: \"%ls\"", wszBuf);
+
+ NonStandardAssert(cwcPythonPath > cwc);
+ memcpy(pwszPythonPath, wszBuf, cwc * sizeof(wchar_t));
+ pwszPythonPath[cwc] = '\0';
+ rcWinRet = ERROR_SUCCESS;
+ }
+ else
+ logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": is a directory (%#x)", wszBuf, fAttribs);
+ }
+ else
+ logStringF(hModule, "getPythonPath: Warning: Skipping \"%ls\": Does not exist (%u)", wszBuf, GetLastError());
+ }
+ }
+ }
+
+ RegCloseKey(hKeyPythonCore);
+ if (rcWinRet != ERROR_SUCCESS)
+ logStringF(hModule, "getPythonPath: Unable to find python");
+ return rcWinRet;
+}
+
+/**
+ * Retrieves the absolute path of the Python installation.
+ *
+ * @returns Windows error code.
+ * @param hModule Windows installer module handle.
+ * @param pwszPythonPath Buffer to return the path for python.exe in.
+ * @param cwcPythonPath Buffer size in UTF-16 units.
+ * @param fReturnExe Return the path to python.exe if true, otherwise
+ * just the python install directory.
+ */
+static UINT getPythonPath(MSIHANDLE hModule, wchar_t *pwszPythonPath, size_t cwcPythonPath, bool fReturnExe = false)
+{
+ UINT rcWin = getPythonPathEx(hModule, HKEY_LOCAL_MACHINE, pwszPythonPath, cwcPythonPath, fReturnExe);
+ if (rcWin != ERROR_SUCCESS)
+ rcWin = getPythonPathEx(hModule, HKEY_CURRENT_USER, pwszPythonPath, cwcPythonPath, fReturnExe);
+ return rcWin;
+}
+
+/**
+ * Retrieves the absolute path of the Python executable.
+ *
+ * @returns Windows error code.
+ * @param hModule Windows installer module handle.
+ * @param pwszPythonExe Buffer to return the path for python.exe in.
+ * @param cwcPythonExe Buffer size in UTF-16 units.
+ */
+static UINT getPythonExe(MSIHANDLE hModule, wchar_t *pwszPythonExe, size_t cwcPythonExe)
+{
+ return getPythonPath(hModule, pwszPythonExe, cwcPythonExe, true /*fReturnExe*/);
+}
+
+/**
+ * Checks if all dependencies for running the VBox Python API bindings are met.
+ *
+ * @returns VBox status code, or error if depedencies are not met.
+ * @param hModule Windows installer module handle.
+ * @param pwszPythonExe Path to Python interpreter image (.exe).
+ */
+static int checkPythonDependencies(MSIHANDLE hModule, const wchar_t *pwszPythonExe)
+{
+ /*
+ * Check if importing the win32api module works.
+ * This is a prerequisite for setting up the VBox API.
+ */
+ logStringF(hModule, "checkPythonDependencies: Checking for win32api extensions ...");
+
+ UINT rcWin = procRun(hModule, pwszPythonExe, L"-c \"import win32api\"");
+ if (rcWin == ERROR_SUCCESS)
+ logStringF(hModule, "checkPythonDependencies: win32api found\n");
+ else
+ logStringF(hModule, "checkPythonDependencies: Importing win32api failed with %u (%#x)\n", rcWin, rcWin);
+
+ return rcWin;
+}
+
+/**
+ * Checks for a valid Python installation on the system.
+ *
+ * Called from the MSI installer as custom action.
+ *
+ * @returns Always ERROR_SUCCESS.
+ * Sets public property VBOX_PYTHON_INSTALLED to "0" (false) or "1" (success).
+ * Sets public property VBOX_PYTHON_PATH to the Python installation path (if found).
+ *
+ * @param hModule Windows installer module handle.
+ */
+UINT __stdcall IsPythonInstalled(MSIHANDLE hModule)
+{
+ wchar_t wszPythonPath[RTPATH_MAX];
+ UINT rcWin = getPythonPath(hModule, wszPythonPath, RTPATH_MAX);
+ if (rcWin == ERROR_SUCCESS)
+ {
+ logStringF(hModule, "IsPythonInstalled: Python installation found at \"%ls\"", wszPythonPath);
+ VBoxSetMsiProp(hModule, L"VBOX_PYTHON_PATH", wszPythonPath);
+ VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", L"1");
+ }
+ else
+ {
+ logStringF(hModule, "IsPythonInstalled: Error: No suitable Python installation found (%u), skipping installation.", rcWin);
+ logStringF(hModule, "IsPythonInstalled: Python seems not to be installed; please download + install the Python Core package.");
+ VBoxSetMsiProp(hModule, L"VBOX_PYTHON_INSTALLED", L"0");
+ }
+
+ return ERROR_SUCCESS; /* Never return failure. */
+}
+
+/**
+ * Checks if all dependencies for running the VBox Python API bindings are met.
+ *
+ * Called from the MSI installer as custom action.
+ *
+ * @returns Always ERROR_SUCCESS.
+ * Sets public property VBOX_PYTHON_DEPS_INSTALLED to "0" (false) or "1" (success).
+ *
+ * @param hModule Windows installer module handle.
+ */
+UINT __stdcall ArePythonAPIDepsInstalled(MSIHANDLE hModule)
+{
+ wchar_t wszPythonExe[RTPATH_MAX];
+ UINT dwErr = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
+ if (dwErr == ERROR_SUCCESS)
+ {
+ dwErr = checkPythonDependencies(hModule, wszPythonExe);
+ if (dwErr == ERROR_SUCCESS)
+ logStringF(hModule, "ArePythonAPIDepsInstalled: Dependencies look good.");
+ }
+
+ if (dwErr != ERROR_SUCCESS)
+ logStringF(hModule, "ArePythonAPIDepsInstalled: Failed with dwErr=%u", dwErr);
+
+ VBoxSetMsiProp(hModule, L"VBOX_PYTHON_DEPS_INSTALLED", dwErr == ERROR_SUCCESS ? L"1" : L"0");
+ return ERROR_SUCCESS; /* Never return failure. */
+}
+
+/**
+ * Checks if all required MS CRTs (Visual Studio Redistributable Package) are installed on the system.
+ *
+ * Called from the MSI installer as custom action.
+ *
+ * @returns Always ERROR_SUCCESS.
+ * Sets public property VBOX_MSCRT_INSTALLED to "" (false, to use "NOT" in WiX) or "1" (success).
+ *
+ * Also exposes public properties VBOX_MSCRT_VER_MIN + VBOX_MSCRT_VER_MAJ strings
+ * with the most recent MSCRT version detected.
+ *
+ * @param hModule Windows installer module handle.
+ *
+ * @sa https://docs.microsoft.com/en-us/cpp/windows/redistributing-visual-cpp-files?view=msvc-170
+ */
+UINT __stdcall IsMSCRTInstalled(MSIHANDLE hModule)
+{
+ HKEY hKeyVS = NULL;
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Microsoft\\VisualStudio\\14.0\\VC\\Runtimes\\X64",
+ 0, KEY_READ, &hKeyVS);
+ if (lrc == ERROR_SUCCESS)
+ {
+ DWORD dwVal = 0;
+ DWORD cbVal = sizeof(dwVal);
+ DWORD dwValueType = REG_DWORD; /** @todo r=bird: output only parameter, optional, so pointless. */
+ lrc = RegQueryValueExW(hKeyVS, L"Installed", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
+ if (lrc == ERROR_SUCCESS)
+ {
+ if (dwVal >= 1)
+ {
+ DWORD dwMaj = 0; /** @todo r=bird: It's purdent to initialize values if you don't bother to check the type and size! */
+ lrc = RegQueryValueExW(hKeyVS, L"Major", NULL, &dwValueType, (LPBYTE)&dwMaj, &cbVal);
+ if (lrc == ERROR_SUCCESS)
+ {
+ VBoxSetMsiPropDWORD(hModule, L"VBOX_MSCRT_VER_MAJ", dwMaj);
+
+ DWORD dwMin = 0;
+ lrc = RegQueryValueExW(hKeyVS, L"Minor", NULL, &dwValueType, (LPBYTE)&dwMin, &cbVal);
+ if (lrc == ERROR_SUCCESS)
+ {
+ VBoxSetMsiPropDWORD(hModule, L"VBOX_MSCRT_VER_MIN", dwMin);
+
+ logStringF(hModule, "IsMSCRTInstalled: Found v%u.%u\n", dwMaj, dwMin);
+
+ /* Check for at least 2019. */
+ if (dwMaj > 14 || (dwMaj == 14 && dwMin >= 20))
+ VBoxSetMsiProp(hModule, L"VBOX_MSCRT_INSTALLED", L"1");
+ }
+ else
+ logStringF(hModule, "IsMSCRTInstalled: Found, but 'Minor' key not present (lrc=%d)", lrc);
+ }
+ else
+ logStringF(hModule, "IsMSCRTInstalled: Found, but 'Major' key not present (lrc=%d)", lrc);
+ }
+ else
+ {
+ logStringF(hModule, "IsMSCRTInstalled: Found, but not marked as installed");
+ lrc = ERROR_NOT_INSTALLED;
+ }
+ }
+ else
+ logStringF(hModule, "IsMSCRTInstalled: Found, but 'Installed' key not present (lrc=%d)", lrc);
+ }
+
+ if (lrc != ERROR_SUCCESS)
+ logStringF(hModule, "IsMSCRTInstalled: Failed with lrc=%ld", lrc);
+
+ return ERROR_SUCCESS; /* Never return failure. */
+}
+
+/**
+ * Checks if the running OS is (at least) Windows 10 (e.g. >= build 10000).
+ *
+ * Called from the MSI installer as custom action.
+ *
+ * @returns Always ERROR_SUCCESS.
+ * Sets public property VBOX_IS_WINDOWS_10 to "" (empty / false) or "1" (success).
+ *
+ * @param hModule Windows installer module handle.
+ */
+UINT __stdcall IsWindows10(MSIHANDLE hModule)
+{
+ /*
+ * Note: We cannot use RtlGetVersion() / GetVersionExW() here, as the Windows Installer service
+ * all shims this, unfortunately. So we have to go another route by querying the major version
+ * number from the registry.
+ */
+ HKEY hKeyCurVer = NULL;
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", 0, KEY_READ, &hKeyCurVer);
+ if (lrc == ERROR_SUCCESS)
+ {
+ DWORD dwVal = 0;
+ DWORD cbVal = sizeof(dwVal);
+ DWORD dwValueType = REG_DWORD; /** @todo r=bird: Again, the type is an optional output parameter. pointless to init or pass it unless you check. */
+ lrc = RegQueryValueExW(hKeyCurVer, L"CurrentMajorVersionNumber", NULL, &dwValueType, (LPBYTE)&dwVal, &cbVal);
+ if (lrc == ERROR_SUCCESS)
+ {
+ logStringF(hModule, "IsWindows10/CurrentMajorVersionNumber: %u", dwVal);
+
+ VBoxSetMsiProp(hModule, L"VBOX_IS_WINDOWS_10", dwVal >= 10 ? L"1" : L"");
+ }
+ else
+ logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error reading CurrentMajorVersionNumber (%ld)", lrc);
+
+ RegCloseKey(hKeyCurVer);
+ }
+ else
+ logStringF(hModule, "IsWindows10/RegOpenKeyExW: Error opening CurrentVersion key (%ld)", lrc);
+
+ return ERROR_SUCCESS; /* Never return failure. */
+}
+
+/**
+ * Installs and compiles the VBox Python bindings.
+ *
+ * Called from the MSI installer as custom action.
+ *
+ * @returns Always ERROR_SUCCESS.
+ * Sets public property VBOX_API_INSTALLED to "0" (false) or "1" (success).
+ *
+ * @param hModule Windows installer module handle.
+ */
+UINT __stdcall InstallPythonAPI(MSIHANDLE hModule)
+{
+ logStringF(hModule, "InstallPythonAPI: Checking for installed Python environment(s) ...");
+
+ /** @todo r=bird: Can't we get the VBOX_PYTHON_PATH property here? */
+ wchar_t wszPythonExe[RTPATH_MAX];
+ UINT rcWin = getPythonExe(hModule, wszPythonExe, RTPATH_MAX);
+ if (rcWin != ERROR_SUCCESS)
+ {
+ VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
+ return ERROR_SUCCESS;
+ }
+
+ /*
+ * Set up the VBox API.
+ */
+ /* Get the VBox API setup string. */
+ WCHAR wszVBoxSDKPath[RTPATH_MAX];
+ rcWin = VBoxGetMsiProp(hModule, L"CustomActionData", wszVBoxSDKPath, RT_ELEMENTS(wszVBoxSDKPath));
+ if (rcWin == ERROR_SUCCESS)
+ {
+ /* Make sure our current working directory is the VBox installation path. */
+ if (SetCurrentDirectoryW(wszVBoxSDKPath))
+ {
+ /* Set required environment variables. */
+ if (SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszVBoxSDKPath))
+ {
+ logStringF(hModule, "InstallPythonAPI: Invoking vboxapisetup.py in \"%ls\" ...", wszVBoxSDKPath);
+
+ rcWin = procRun(hModule, wszPythonExe, L"vboxapisetup.py install");
+ if (rcWin == ERROR_SUCCESS)
+ {
+ logStringF(hModule, "InstallPythonAPI: Installation of vboxapisetup.py successful");
+
+ /*
+ * Do some sanity checking if the VBox API works.
+ */
+ logStringF(hModule, "InstallPythonAPI: Validating VBox API ...");
+
+ rcWin = procRun(hModule, wszPythonExe, L"-c \"from vboxapi import VirtualBoxManager\"");
+ if (rcWin == ERROR_SUCCESS)
+ {
+ logStringF(hModule, "InstallPythonAPI: VBox API looks good.");
+ VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"1");
+ return ERROR_SUCCESS;
+ }
+
+ /* failed */
+ logStringF(hModule, "InstallPythonAPI: Validating VBox API failed with %u (%#x)", rcWin, rcWin);
+ }
+ else
+ logStringF(hModule, "InstallPythonAPI: Calling vboxapisetup.py failed with %u (%#x)", rcWin, rcWin);
+ }
+ else
+ logStringF(hModule, "InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH: LastError=%u",
+ GetLastError());
+ }
+ else
+ logStringF(hModule, "InstallPythonAPI: Could set working directory to \"%ls\": LastError=%u",
+ wszVBoxSDKPath, GetLastError());
+ }
+ else
+ logStringF(hModule, "InstallPythonAPI: Unable to retrieve VBox installation directory: rcWin=%u (%#x)", rcWin, rcWin);
+
+ VBoxSetMsiProp(hModule, L"VBOX_API_INSTALLED", L"0");
+ logStringF(hModule, "InstallPythonAPI: Installation failed");
+ return ERROR_SUCCESS; /* Do not fail here. */
+}
+
+static LONG installBrandingValue(MSIHANDLE hModule,
+ const WCHAR *pwszFileName,
+ const WCHAR *pwszSection,
+ const WCHAR *pwszValue)
+{
+ LONG rc;
+ WCHAR wszValue[MAX_PATH];
+ if (GetPrivateProfileStringW(pwszSection, pwszValue, NULL, wszValue, sizeof(wszValue), pwszFileName) > 0)
+ {
+ WCHAR wszKey[MAX_PATH + 64];
+ if (RTUtf16ICmpAscii(pwszSection, "General") != 0)
+ RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding\\%ls", VBOX_VENDOR_SHORT, pwszSection);
+ else
+ RTUtf16Printf(wszKey, RT_ELEMENTS(wszKey), "SOFTWARE\\%s\\VirtualBox\\Branding", VBOX_VENDOR_SHORT);
+
+ HKEY hkBranding = NULL;
+ rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKey, 0, KEY_WRITE, &hkBranding);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = RegSetValueExW(hkBranding,
+ pwszValue,
+ NULL,
+ REG_SZ,
+ (BYTE *)wszValue,
+ (DWORD)RTUtf16Len(wszValue));
+ if (rc != ERROR_SUCCESS)
+ logStringF(hModule, "InstallBranding: Could not write value %s! Error %d", pwszValue, rc);
+ RegCloseKey(hkBranding);
+ }
+ }
+ else
+ rc = ERROR_NOT_FOUND;
+ return rc;
+}
+
+/**
+ * @note Both paths strings must have an extra terminator.
+ */
+static UINT CopyDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
+{
+ NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
+ NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
+
+ SHFILEOPSTRUCTW s = {0};
+ s.hwnd = NULL;
+ s.wFunc = FO_COPY;
+ s.pTo = pwszzDstDir;
+ s.pFrom = pwszzSrcDir;
+ s.fFlags = FOF_SILENT
+ | FOF_NOCONFIRMATION
+ | FOF_NOCONFIRMMKDIR
+ | FOF_NOERRORUI;
+
+ logStringF(hModule, "CopyDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
+ int r = SHFileOperationW(&s);
+ if (r == 0)
+ return ERROR_SUCCESS;
+ logStringF(hModule, "CopyDir: Copy operation returned status %#x", r);
+ return ERROR_GEN_FAILURE;
+}
+
+/**
+ * @note The directory string must have two zero terminators!
+ */
+static UINT RemoveDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir)
+{
+ NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
+
+ SHFILEOPSTRUCTW s = {0};
+ s.hwnd = NULL;
+ s.wFunc = FO_DELETE;
+ s.pFrom = pwszzDstDir;
+ s.fFlags = FOF_SILENT
+ | FOF_NOCONFIRMATION
+ | FOF_NOCONFIRMMKDIR
+ | FOF_NOERRORUI;
+
+ logStringF(hModule, "RemoveDir: pwszzDstDir=%ls", pwszzDstDir);
+ int r = SHFileOperationW(&s);
+ if (r == 0)
+ return ERROR_SUCCESS;
+ logStringF(hModule, "RemoveDir: Remove operation returned status %#x", r);
+ return ERROR_GEN_FAILURE;
+}
+
+/**
+ * @note Both paths strings must have an extra terminator.
+ */
+static UINT RenameDir(MSIHANDLE hModule, const WCHAR *pwszzDstDir, const WCHAR *pwszzSrcDir)
+{
+ NonStandardAssert(pwszzDstDir[RTUtf16Len(pwszzDstDir) + 1] == '\0');
+ NonStandardAssert(pwszzSrcDir[RTUtf16Len(pwszzSrcDir) + 1] == '\0');
+
+ SHFILEOPSTRUCTW s = {0};
+ s.hwnd = NULL;
+ s.wFunc = FO_RENAME;
+ s.pTo = pwszzDstDir;
+ s.pFrom = pwszzSrcDir;
+ s.fFlags = FOF_SILENT
+ | FOF_NOCONFIRMATION
+ | FOF_NOCONFIRMMKDIR
+ | FOF_NOERRORUI;
+
+ logStringF(hModule, "RenameDir: pwszzDstDir=%ls, pwszzSrcDir=%ls", pwszzDstDir, pwszzSrcDir);
+ int r = SHFileOperationW(&s);
+ if (r == 0)
+ return ERROR_SUCCESS;
+ logStringF(hModule, "RenameDir: Rename operation returned status %#x", r);
+ return ERROR_GEN_FAILURE;
+}
+
+/** RTPathAppend-like function. */
+static UINT AppendToPath(wchar_t *pwszPath, size_t cwcPath, wchar_t *pwszAppend, bool fDoubleTerm = false)
+{
+ size_t cwcCurPath = RTUtf16Len(pwszPath);
+ size_t cwcSlash = cwcCurPath > 1 && RTPATH_IS_SLASH(pwszPath[cwcCurPath - 1]) ? 0 : 1;
+ while (RTPATH_IS_SLASH(*pwszAppend))
+ pwszAppend++;
+ size_t cwcAppend = RTUtf16Len(pwszAppend);
+ if (cwcCurPath + cwcCurPath + cwcAppend + fDoubleTerm < cwcPath)
+ {
+ if (cwcSlash)
+ pwszPath[cwcCurPath++] = '\\';
+ memcpy(&pwszPath[cwcCurPath], pwszAppend, (cwcAppend + 1) * sizeof(wchar_t));
+ if (fDoubleTerm)
+ pwszPath[cwcCurPath + cwcAppend + 1] = '\0';
+ return ERROR_SUCCESS;
+ }
+ return ERROR_BUFFER_OVERFLOW;
+}
+
+/** RTPathJoin-like function. */
+static UINT JoinPaths(wchar_t *pwszPath, size_t cwcPath, wchar_t *pwszPath1, wchar_t *pwszAppend, bool fDoubleTerm = false)
+{
+ size_t cwcCurPath = RTUtf16Len(pwszPath1);
+ if (cwcCurPath < cwcPath)
+ {
+ memcpy(pwszPath, pwszPath1, (cwcCurPath + 1) * sizeof(wchar_t));
+ return AppendToPath(pwszPath, cwcPath, pwszAppend, fDoubleTerm);
+ }
+ return ERROR_BUFFER_OVERFLOW;
+}
+
+UINT __stdcall UninstallBranding(MSIHANDLE hModule)
+{
+ logStringF(hModule, "UninstallBranding: Handling branding file ...");
+
+ WCHAR wszPath[RTPATH_MAX];
+ UINT rc = VBoxGetMsiProp(hModule, L"CustomActionData", wszPath, RT_ELEMENTS(wszPath));
+ if (rc == ERROR_SUCCESS)
+ {
+ size_t const cwcPath = RTUtf16Len(wszPath);
+ rc = AppendToPath(wszPath, RTPATH_MAX, L"custom", true /*fDoubleTerm*/);
+ if (rc == ERROR_SUCCESS)
+ rc = RemoveDir(hModule, wszPath);
+
+ /* Check for .custom directory from a failed install and remove it. */
+ wszPath[cwcPath] = '\0';
+ rc = AppendToPath(wszPath, RTPATH_MAX, L".custom", true /*fDoubleTerm*/);
+ if (rc == ERROR_SUCCESS)
+ rc = RemoveDir(hModule, wszPath);
+ }
+
+ logStringF(hModule, "UninstallBranding: Handling done. (rc=%u (ignored))", rc);
+ return ERROR_SUCCESS; /* Do not fail here. */
+}
+
+UINT __stdcall InstallBranding(MSIHANDLE hModule)
+{
+ logStringF(hModule, "InstallBranding: Handling branding file ...");
+
+ /*
+ * Get the paths.
+ */
+ wchar_t wszSrcPath[RTPATH_MAX];
+ UINT rc = VBoxGetMsiProp(hModule, L"SOURCEDIR", wszSrcPath, RT_ELEMENTS(wszSrcPath));
+ if (rc == ERROR_SUCCESS)
+ {
+ wchar_t wszDstPath[RTPATH_MAX];
+ rc = VBoxGetMsiProp(hModule, L"CustomActionData", wszDstPath, RT_ELEMENTS(wszDstPath) - 1);
+ if (rc == ERROR_SUCCESS)
+ {
+ /*
+ * First we copy the src\.custom dir to the target.
+ */
+ rc = AppendToPath(wszSrcPath, RT_ELEMENTS(wszSrcPath) - 1, L".custom", true /*fDoubleTerm*/);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = CopyDir(hModule, wszDstPath, wszSrcPath);
+ if (rc == ERROR_SUCCESS)
+ {
+ /*
+ * The rename the '.custom' directory we now got in the target area to 'custom'.
+ */
+ rc = JoinPaths(wszSrcPath, RT_ELEMENTS(wszSrcPath), wszDstPath, L".custom", true /*fDoubleTerm*/);
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = AppendToPath(wszDstPath, RT_ELEMENTS(wszDstPath), L"custom", true /*fDoubleTerm*/);
+ if (rc == ERROR_SUCCESS)
+ rc = RenameDir(hModule, wszDstPath, wszSrcPath);
+ }
+ }
+ }
+ }
+ }
+
+ logStringF(hModule, "InstallBranding: Handling done. (rc=%u (ignored))", rc);
+ return ERROR_SUCCESS; /* Do not fail here. */
+}
+
+#ifdef VBOX_WITH_NETFLT
+
+/** @todo should use some real VBox app name */
+#define VBOX_NETCFG_APP_NAME L"VirtualBox Installer"
+#define VBOX_NETCFG_MAX_RETRIES 10
+#define NETFLT_PT_INF_REL_PATH L"VBoxNetFlt.inf"
+#define NETFLT_MP_INF_REL_PATH L"VBoxNetFltM.inf"
+#define NETFLT_ID L"sun_VBoxNetFlt" /** @todo Needs to be changed (?). */
+#define NETADP_ID L"sun_VBoxNetAdp" /** @todo Needs to be changed (?). */
+
+#define NETLWF_INF_NAME L"VBoxNetLwf.inf"
+
+static MSIHANDLE g_hCurrentModule = NULL;
+
+static UINT _uninstallNetFlt(MSIHANDLE hModule);
+static UINT _uninstallNetLwf(MSIHANDLE hModule);
+
+static VOID vboxDrvLoggerCallback(VBOXDRVCFG_LOG_SEVERITY_T enmSeverity, char *pszMsg, void *pvContext)
+{
+ RT_NOREF1(pvContext);
+ switch (enmSeverity)
+ {
+ case VBOXDRVCFG_LOG_SEVERITY_FLOW:
+ case VBOXDRVCFG_LOG_SEVERITY_REGULAR:
+ break;
+ case VBOXDRVCFG_LOG_SEVERITY_REL:
+ if (g_hCurrentModule)
+ logStringF(g_hCurrentModule, "%s", pszMsg);
+ break;
+ default:
+ break;
+ }
+}
+
+static DECLCALLBACK(void) netCfgLoggerCallback(const char *pszString)
+{
+ if (g_hCurrentModule)
+ logStringF(g_hCurrentModule, "%s", pszString);
+}
+
+static VOID netCfgLoggerDisable()
+{
+ if (g_hCurrentModule)
+ {
+ VBoxNetCfgWinSetLogging(NULL);
+ g_hCurrentModule = NULL;
+ }
+}
+
+static VOID netCfgLoggerEnable(MSIHANDLE hModule)
+{
+ NonStandardAssert(hModule);
+
+ if (g_hCurrentModule)
+ netCfgLoggerDisable();
+
+ g_hCurrentModule = hModule;
+
+ VBoxNetCfgWinSetLogging(netCfgLoggerCallback);
+ /* uncomment next line if you want to add logging information from VBoxDrvCfg.cpp */
+// VBoxDrvCfgLoggerSet(vboxDrvLoggerCallback, NULL);
+}
+
+static UINT errorConvertFromHResult(MSIHANDLE hModule, HRESULT hr)
+{
+ UINT uRet;
+ switch (hr)
+ {
+ case S_OK:
+ uRet = ERROR_SUCCESS;
+ break;
+
+ case NETCFG_S_REBOOT:
+ {
+ logStringF(hModule, "Reboot required, setting REBOOT property to \"force\"");
+ HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
+ if (hr2 != ERROR_SUCCESS)
+ logStringF(hModule, "Failed to set REBOOT property, error = %#x", hr2);
+ uRet = ERROR_SUCCESS; /* Never fail here. */
+ break;
+ }
+
+ default:
+ logStringF(hModule, "Converting unhandled HRESULT (%#x) to ERROR_GEN_FAILURE", hr);
+ uRet = ERROR_GEN_FAILURE;
+ }
+ return uRet;
+}
+
+static MSIHANDLE createNetCfgLockedMsgRecord(MSIHANDLE hModule)
+{
+ MSIHANDLE hRecord = MsiCreateRecord(2);
+ if (hRecord)
+ {
+ UINT uErr = MsiRecordSetInteger(hRecord, 1, 25001);
+ if (uErr != ERROR_SUCCESS)
+ {
+ logStringF(hModule, "createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = %#x", uErr);
+ MsiCloseHandle(hRecord);
+ hRecord = NULL;
+ }
+ }
+ else
+ logStringF(hModule, "createNetCfgLockedMsgRecord: Failed to create a record");
+
+ return hRecord;
+}
+
+static UINT doNetCfgInit(MSIHANDLE hModule, INetCfg **ppnc, BOOL bWrite)
+{
+ MSIHANDLE hMsg = NULL;
+ UINT uErr = ERROR_GEN_FAILURE;
+ int MsgResult;
+ int cRetries = 0;
+
+ do
+ {
+ LPWSTR lpszLockedBy;
+ HRESULT hr = VBoxNetCfgWinQueryINetCfg(ppnc, bWrite, VBOX_NETCFG_APP_NAME, 10000, &lpszLockedBy);
+ if (hr != NETCFG_E_NO_WRITE_LOCK)
+ {
+ if (FAILED(hr))
+ logStringF(hModule, "doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = %#x", hr);
+ uErr = errorConvertFromHResult(hModule, hr);
+ break;
+ }
+
+ /* hr == NETCFG_E_NO_WRITE_LOCK */
+
+ if (!lpszLockedBy)
+ {
+ logStringF(hModule, "doNetCfgInit: lpszLockedBy == NULL, breaking");
+ break;
+ }
+
+ /* on vista the 6to4svc.dll periodically maintains the lock for some reason,
+ * if this is the case, increase the wait period by retrying multiple times
+ * NOTE: we could alternatively increase the wait timeout,
+ * however it seems unneeded for most cases, e.g. in case some network connection property
+ * dialog is opened, it would be better to post a notification to the user as soon as possible
+ * rather than waiting for a longer period of time before displaying it */
+ if ( cRetries < VBOX_NETCFG_MAX_RETRIES
+ && RTUtf16ICmpAscii(lpszLockedBy, "6to4svc.dll") == 0)
+ {
+ cRetries++;
+ logStringF(hModule, "doNetCfgInit: lpszLockedBy is 6to4svc.dll, retrying %d out of %d", cRetries, VBOX_NETCFG_MAX_RETRIES);
+ MsgResult = IDRETRY;
+ }
+ else
+ {
+ if (!hMsg)
+ {
+ hMsg = createNetCfgLockedMsgRecord(hModule);
+ if (!hMsg)
+ {
+ logStringF(hModule, "doNetCfgInit: Failed to create a message record, breaking");
+ CoTaskMemFree(lpszLockedBy);
+ break;
+ }
+ }
+
+ UINT rTmp = MsiRecordSetStringW(hMsg, 2, lpszLockedBy);
+ NonStandardAssert(rTmp == ERROR_SUCCESS);
+ if (rTmp != ERROR_SUCCESS)
+ {
+ logStringF(hModule, "doNetCfgInit: MsiRecordSetStringW failed, error = #%x", rTmp);
+ CoTaskMemFree(lpszLockedBy);
+ break;
+ }
+
+ MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg);
+ NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL);
+ logStringF(hModule, "doNetCfgInit: MsiProcessMessage returned (%#x)", MsgResult);
+ }
+ CoTaskMemFree(lpszLockedBy);
+ } while(MsgResult == IDRETRY);
+
+ if (hMsg)
+ MsiCloseHandle(hMsg);
+
+ return uErr;
+}
+
+static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, DWORD cwcPtInf,
+ OUT LPWSTR pwszMpInf, DWORD cwcMpInf)
+{
+ DWORD cwcEffBuf = cwcPtInf - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)) / sizeof(WCHAR);
+ UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &cwcEffBuf);
+ if ( uErr == ERROR_SUCCESS
+ && cwcEffBuf > 0)
+ {
+ int vrc = RTUtf16Copy(pwszMpInf, cwcMpInf, pwszPtInf);
+ AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
+
+ vrc = RTUtf16Cat(pwszPtInf, cwcPtInf, NETFLT_PT_INF_REL_PATH);
+ AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
+ logStringF(hModule, "vboxNetFltQueryInfArray: INF 1: %ls", pwszPtInf);
+
+ vrc = RTUtf16Cat(pwszMpInf, cwcMpInf, NETFLT_MP_INF_REL_PATH);
+ AssertRCReturn(vrc, ERROR_BUFFER_OVERFLOW);
+ logStringF(hModule, "vboxNetFltQueryInfArray: INF 2: %ls", pwszMpInf);
+ }
+ else if (uErr != ERROR_SUCCESS)
+ logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
+ else
+ {
+ logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
+ uErr = ERROR_GEN_FAILURE;
+ }
+
+ return uErr;
+}
+
+#endif /*VBOX_WITH_NETFLT*/
+
+/*static*/ UINT _uninstallNetFlt(MSIHANDLE hModule)
+{
+#ifdef VBOX_WITH_NETFLT
+ INetCfg *pNetCfg;
+ UINT uErr;
+
+ netCfgLoggerEnable(hModule);
+
+ BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
+
+ __try
+ {
+ logStringF(hModule, "Uninstalling NetFlt");
+
+ uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
+ if (uErr == ERROR_SUCCESS)
+ {
+ HRESULT hr = VBoxNetCfgWinNetFltUninstall(pNetCfg);
+ if (hr != S_OK)
+ logStringF(hModule, "UninstallNetFlt: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
+
+ uErr = errorConvertFromHResult(hModule, hr);
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+
+ logStringF(hModule, "Uninstalling NetFlt done, error = %#x", uErr);
+ }
+ else
+ logStringF(hModule, "UninstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
+ }
+ __finally
+ {
+ if (bOldIntMode)
+ {
+ /* The prev mode != FALSE, i.e. non-interactive. */
+ SetupSetNonInteractiveMode(bOldIntMode);
+ }
+ netCfgLoggerDisable();
+ }
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the uninstall even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall UninstallNetFlt(MSIHANDLE hModule)
+{
+ (void)_uninstallNetLwf(hModule);
+ return _uninstallNetFlt(hModule);
+}
+
+static UINT _installNetFlt(MSIHANDLE hModule)
+{
+#ifdef VBOX_WITH_NETFLT
+ UINT uErr;
+ INetCfg *pNetCfg;
+
+ netCfgLoggerEnable(hModule);
+
+ BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
+
+ __try
+ {
+
+ logStringF(hModule, "InstallNetFlt: Installing NetFlt");
+
+ uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
+ if (uErr == ERROR_SUCCESS)
+ {
+ WCHAR wszPtInf[MAX_PATH];
+ WCHAR wszMpInf[MAX_PATH];
+ uErr = vboxNetFltQueryInfArray(hModule, wszPtInf, RT_ELEMENTS(wszPtInf), wszMpInf, RT_ELEMENTS(wszMpInf));
+ if (uErr == ERROR_SUCCESS)
+ {
+ LPCWSTR const apwszInfs[] = { wszPtInf, wszMpInf };
+ HRESULT hr = VBoxNetCfgWinNetFltInstall(pNetCfg, &apwszInfs[0], RT_ELEMENTS(apwszInfs));
+ if (FAILED(hr))
+ logStringF(hModule, "InstallNetFlt: VBoxNetCfgWinNetFltInstall failed, error = %#x", hr);
+
+ uErr = errorConvertFromHResult(hModule, hr);
+ }
+ else
+ logStringF(hModule, "InstallNetFlt: vboxNetFltQueryInfArray failed, error = %#x", uErr);
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+
+ logStringF(hModule, "InstallNetFlt: Done");
+ }
+ else
+ logStringF(hModule, "InstallNetFlt: doNetCfgInit failed, error = %#x", uErr);
+ }
+ __finally
+ {
+ if (bOldIntMode)
+ {
+ /* The prev mode != FALSE, i.e. non-interactive. */
+ SetupSetNonInteractiveMode(bOldIntMode);
+ }
+ netCfgLoggerDisable();
+ }
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the install even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall InstallNetFlt(MSIHANDLE hModule)
+{
+ (void)_uninstallNetLwf(hModule);
+ return _installNetFlt(hModule);
+}
+
+
+/*static*/ UINT _uninstallNetLwf(MSIHANDLE hModule)
+{
+#ifdef VBOX_WITH_NETFLT
+ INetCfg *pNetCfg;
+ UINT uErr;
+
+ netCfgLoggerEnable(hModule);
+
+ BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
+
+ __try
+ {
+ logStringF(hModule, "Uninstalling NetLwf");
+
+ uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
+ if (uErr == ERROR_SUCCESS)
+ {
+ HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg);
+ if (hr != S_OK)
+ logStringF(hModule, "UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
+
+ uErr = errorConvertFromHResult(hModule, hr);
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+
+ logStringF(hModule, "Uninstalling NetLwf done, error = %#x", uErr);
+ }
+ else
+ logStringF(hModule, "UninstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
+ }
+ __finally
+ {
+ if (bOldIntMode)
+ {
+ /* The prev mode != FALSE, i.e. non-interactive. */
+ SetupSetNonInteractiveMode(bOldIntMode);
+ }
+ netCfgLoggerDisable();
+ }
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the uninstall even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall UninstallNetLwf(MSIHANDLE hModule)
+{
+ (void)_uninstallNetFlt(hModule);
+ return _uninstallNetLwf(hModule);
+}
+
+static UINT _installNetLwf(MSIHANDLE hModule)
+{
+#ifdef VBOX_WITH_NETFLT
+ UINT uErr;
+ INetCfg *pNetCfg;
+
+ netCfgLoggerEnable(hModule);
+
+ BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
+
+ __try
+ {
+
+ logStringF(hModule, "InstallNetLwf: Installing NetLwf");
+
+ uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
+ if (uErr == ERROR_SUCCESS)
+ {
+ WCHAR wszInf[MAX_PATH];
+ DWORD cwcInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1;
+ uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cwcInf);
+ if (uErr == ERROR_SUCCESS)
+ {
+ if (cwcInf)
+ {
+ if (wszInf[cwcInf - 1] != L'\\')
+ {
+ wszInf[cwcInf++] = L'\\';
+ wszInf[cwcInf] = L'\0';
+ }
+
+ int vrc = RTUtf16Cat(wszInf, RT_ELEMENTS(wszInf), NETLWF_INF_NAME);
+ AssertRC(vrc);
+
+ HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf);
+ if (FAILED(hr))
+ logStringF(hModule, "InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = %#x", hr);
+
+ uErr = errorConvertFromHResult(hModule, hr);
+ }
+ else
+ {
+ logStringF(hModule, "vboxNetFltQueryInfArray: Empty installation directory");
+ uErr = ERROR_GEN_FAILURE;
+ }
+ }
+ else
+ logStringF(hModule, "vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = %#x", uErr);
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+
+ logStringF(hModule, "InstallNetLwf: Done");
+ }
+ else
+ logStringF(hModule, "InstallNetLwf: doNetCfgInit failed, error = %#x", uErr);
+ }
+ __finally
+ {
+ if (bOldIntMode)
+ {
+ /* The prev mode != FALSE, i.e. non-interactive. */
+ SetupSetNonInteractiveMode(bOldIntMode);
+ }
+ netCfgLoggerDisable();
+ }
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the install even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall InstallNetLwf(MSIHANDLE hModule)
+{
+ (void)_uninstallNetFlt(hModule);
+ return _installNetLwf(hModule);
+}
+
+
+#if 0
+static BOOL RenameHostOnlyConnectionsCallback(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDev, PVOID pContext)
+{
+ WCHAR DevName[256];
+ DWORD winEr;
+
+ if (SetupDiGetDeviceRegistryPropertyW(hDevInfo, pDev,
+ SPDRP_FRIENDLYNAME , /* IN DWORD Property,*/
+ NULL, /*OUT PDWORD PropertyRegDataType, OPTIONAL*/
+ (PBYTE)DevName, /*OUT PBYTE PropertyBuffer,*/
+ sizeof(DevName), /* IN DWORD PropertyBufferSize,*/
+ NULL /*OUT PDWORD RequiredSize OPTIONAL*/
+ ))
+ {
+ HKEY hKey = SetupDiOpenDevRegKey(hDevInfo, pDev,
+ DICS_FLAG_GLOBAL, /* IN DWORD Scope,*/
+ 0, /*IN DWORD HwProfile, */
+ DIREG_DRV, /* IN DWORD KeyType, */
+ KEY_READ /*IN REGSAM samDesired*/
+ );
+ NonStandardAssert(hKey != INVALID_HANDLE_VALUE);
+ if (hKey != INVALID_HANDLE_VALUE)
+ {
+ WCHAR guid[50];
+ DWORD cbGuid=sizeof(guid);
+ winEr = RegQueryValueExW(hKey,
+ L"NetCfgInstanceId", /*__in_opt LPCTSTR lpValueName,*/
+ NULL, /*__reserved LPDWORD lpReserved,*/
+ NULL, /*__out_opt LPDWORD lpType,*/
+ (LPBYTE)guid, /*__out_opt LPBYTE lpData,*/
+ &cbGuid /*guid__inout_opt LPDWORD lpcbData*/
+ );
+ NonStandardAssert(winEr == ERROR_SUCCESS);
+ if (winEr == ERROR_SUCCESS)
+ {
+ WCHAR ConnectoinName[128];
+ ULONG cbName = sizeof(ConnectoinName);
+
+ HRESULT hr = VBoxNetCfgWinGenHostonlyConnectionName(DevName, ConnectoinName, &cbName);
+ NonStandardAssert(hr == S_OK);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxNetCfgWinRenameConnection(guid, ConnectoinName);
+ NonStandardAssert(hr == S_OK);
+ }
+ }
+ }
+ RegCloseKey(hKey);
+ }
+ else
+ {
+ NonStandardAssert(0);
+ }
+
+ return TRUE;
+}
+#endif
+
+static UINT _createHostOnlyInterface(MSIHANDLE hModule, LPCWSTR pwszId, LPCWSTR pwszInfName)
+{
+#ifdef VBOX_WITH_NETFLT
+ netCfgLoggerEnable(hModule);
+
+ BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
+
+ logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface");
+
+ HRESULT hr = E_FAIL;
+ GUID guid;
+ WCHAR wszMpInf[MAX_PATH];
+ DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
+ LPCWSTR pwszInfPath = NULL;
+ bool fIsFile = false;
+ UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
+ if (uErr == ERROR_SUCCESS)
+ {
+ if (cwcMpInf)
+ {
+ logStringF(hModule, "CreateHostOnlyInterface: NetAdpDir property = %ls", wszMpInf);
+ if (wszMpInf[cwcMpInf - 1] != L'\\')
+ {
+ wszMpInf[cwcMpInf++] = L'\\';
+ wszMpInf[cwcMpInf] = L'\0';
+ }
+
+ int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
+ AssertRC(vrc);
+
+ pwszInfPath = wszMpInf;
+ fIsFile = true;
+
+ logStringF(hModule, "CreateHostOnlyInterface: Resulting INF path = %ls", pwszInfPath);
+ }
+ else
+ logStringF(hModule, "CreateHostOnlyInterface: VBox installation path is empty");
+ }
+ else
+ logStringF(hModule, "CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = %#x", uErr);
+
+ /* Make sure the inf file is installed. */
+ if (pwszInfPath != NULL && fIsFile)
+ {
+ logStringF(hModule, "CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%ls)", pwszInfPath);
+ hr = VBoxDrvCfgInfInstall(pwszInfPath);
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns %#x", hr);
+ if (FAILED(hr))
+ logStringF(hModule, "CreateHostOnlyInterface: Failed to install INF file, error = %#x", hr);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ //first, try to update Host Only Network Interface
+ BOOL fRebootRequired = FALSE;
+ hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
+ if (SUCCEEDED(hr))
+ {
+ if (fRebootRequired)
+ {
+ logStringF(hModule, "CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force");
+ HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
+ if (hr2 != ERROR_SUCCESS)
+ logStringF(hModule, "CreateHostOnlyInterface: Failed to set REBOOT property for update, error = %#x", hr2);
+ }
+ }
+ else
+ {
+ //in fail case call CreateHostOnlyInterface
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
+ logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface");
+#ifdef VBOXNETCFG_DELAYEDRENAME
+ BSTR devId;
+ hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, &devId, NULL);
+#else /* !VBOXNETCFG_DELAYEDRENAME */
+ hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, NULL, &guid, NULL, NULL);
+#endif /* !VBOXNETCFG_DELAYEDRENAME */
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns %#x", hr);
+ if (SUCCEEDED(hr))
+ {
+ ULONG ip = inet_addr("192.168.56.1");
+ ULONG mask = inet_addr("255.255.255.0");
+ logStringF(hModule, "CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig");
+ hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask);
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns %#x", hr);
+ if (FAILED(hr))
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = %#x", hr);
+#ifdef VBOXNETCFG_DELAYEDRENAME
+ hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL);
+ if (FAILED(hr))
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = %#x", hr);
+ SysFreeString(devId);
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+ }
+ else
+ logStringF(hModule, "CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = %#x", hr);
+ }
+ }
+
+ if (SUCCEEDED(hr))
+ logStringF(hModule, "CreateHostOnlyInterface: Creating host-only interface done");
+
+ /* Restore original setup mode. */
+ logStringF(hModule, "CreateHostOnlyInterface: Almost done...");
+ if (fSetupModeInteractive)
+ SetupSetNonInteractiveMode(fSetupModeInteractive);
+
+ netCfgLoggerDisable();
+
+#endif /* VBOX_WITH_NETFLT */
+
+ logStringF(hModule, "CreateHostOnlyInterface: Returns success (ignoring all failures)");
+ /* Never fail the install even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule)
+{
+ return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp.inf");
+}
+
+UINT __stdcall Ndis6CreateHostOnlyInterface(MSIHANDLE hModule)
+{
+#if 0 /* Trick for allowing the debugger to be attached. */
+ for (unsigned i = 0; i < 128 && !IsDebuggerPresent(); i++)
+ {
+ logStringF(hModule, "Waiting for debugger to attach: windbg -p %u", GetCurrentProcessId());
+ Sleep(1001);
+ }
+ Sleep(1002);
+ __debugbreak();
+#endif
+ return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf");
+}
+
+static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
+{
+#ifdef VBOX_WITH_NETFLT
+ netCfgLoggerEnable(hModule);
+
+ logStringF(hModule, "RemoveHostOnlyInterfaces: Removing all host-only interfaces");
+
+ BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
+
+ HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(pwszId);
+ if (SUCCEEDED(hr))
+ {
+ hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, L"Net", pwszId, SUOI_FORCEDELETE/* could be SUOI_FORCEDELETE */);
+ if (FAILED(hr))
+ logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files");
+ else
+ logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstalled successfully");
+ }
+ else
+ logStringF(hModule, "RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = %#x", hr);
+
+ /* Restore original setup mode. */
+ if (fSetupModeInteractive)
+ SetupSetNonInteractiveMode(fSetupModeInteractive);
+
+ netCfgLoggerDisable();
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the uninstall even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall RemoveHostOnlyInterfaces(MSIHANDLE hModule)
+{
+ return _removeHostOnlyInterfaces(hModule, NETADP_ID);
+}
+
+static UINT _stopHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId)
+{
+#ifdef VBOX_WITH_NETFLT
+ netCfgLoggerEnable(hModule);
+
+ logStringF(hModule, "StopHostOnlyInterfaces: Stopping all host-only interfaces");
+
+ BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
+
+ HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE);
+ if (SUCCEEDED(hr))
+ logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = %#x", hr);
+ else
+ logStringF(hModule, "StopHostOnlyInterfaces: Disabling host interfaces failed, hr = %#x", hr);
+
+ /* Restore original setup mode. */
+ if (fSetupModeInteractive)
+ SetupSetNonInteractiveMode(fSetupModeInteractive);
+
+ netCfgLoggerDisable();
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the uninstall even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule)
+{
+ return _stopHostOnlyInterfaces(hModule, NETADP_ID);
+}
+
+static UINT _updateHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszInfName, LPCWSTR pwszId)
+{
+#ifdef VBOX_WITH_NETFLT
+ netCfgLoggerEnable(hModule);
+
+ logStringF(hModule, "UpdateHostOnlyInterfaces: Updating all host-only interfaces");
+
+ BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE);
+
+ WCHAR wszMpInf[MAX_PATH];
+ DWORD cwcMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)RTUtf16Len(pwszInfName) - 1 - 1;
+ LPCWSTR pwszInfPath = NULL;
+ bool fIsFile = false;
+ UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cwcMpInf);
+ if (uErr == ERROR_SUCCESS)
+ {
+ if (cwcMpInf)
+ {
+ logStringF(hModule, "UpdateHostOnlyInterfaces: NetAdpDir property = %ls", wszMpInf);
+ if (wszMpInf[cwcMpInf - 1] != L'\\')
+ {
+ wszMpInf[cwcMpInf++] = L'\\';
+ wszMpInf[cwcMpInf] = L'\0';
+ }
+
+ int vrc = RTUtf16Cat(wszMpInf, RT_ELEMENTS(wszMpInf), pwszInfName);
+ AssertRC(vrc);
+ pwszInfPath = wszMpInf;
+ fIsFile = true;
+
+ logStringF(hModule, "UpdateHostOnlyInterfaces: Resulting INF path = %ls", pwszInfPath);
+
+ DWORD attrFile = GetFileAttributesW(pwszInfPath);
+ if (attrFile == INVALID_FILE_ATTRIBUTES)
+ {
+ DWORD dwErr = GetLastError();
+ logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" not found, dwErr=%ld", pwszInfPath, dwErr);
+ }
+ else
+ {
+ logStringF(hModule, "UpdateHostOnlyInterfaces: File \"%ls\" exists", pwszInfPath);
+
+ BOOL fRebootRequired = FALSE;
+ HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId);
+ if (SUCCEEDED(hr))
+ {
+ if (fRebootRequired)
+ {
+ logStringF(hModule, "UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force");
+ HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force");
+ if (hr2 != ERROR_SUCCESS)
+ logStringF(hModule, "UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = %#x", hr2);
+ }
+ }
+ else
+ logStringF(hModule, "UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = %#x", hr);
+ }
+ }
+ else
+ logStringF(hModule, "UpdateHostOnlyInterfaces: VBox installation path is empty");
+ }
+ else
+ logStringF(hModule, "UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = %#x", uErr);
+
+ /* Restore original setup mode. */
+ if (fSetupModeInteractive)
+ SetupSetNonInteractiveMode(fSetupModeInteractive);
+
+ netCfgLoggerDisable();
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the update even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule)
+{
+ return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp.inf", NETADP_ID);
+}
+
+UINT __stdcall Ndis6UpdateHostOnlyInterfaces(MSIHANDLE hModule)
+{
+ return _updateHostOnlyInterfaces(hModule, L"VBoxNetAdp6.inf", NETADP_ID);
+}
+
+static UINT _uninstallNetAdp(MSIHANDLE hModule, LPCWSTR pwszId)
+{
+#ifdef VBOX_WITH_NETFLT
+ INetCfg *pNetCfg;
+ UINT uErr;
+
+ netCfgLoggerEnable(hModule);
+
+ BOOL bOldIntMode = SetupSetNonInteractiveMode(FALSE);
+
+ __try
+ {
+ logStringF(hModule, "Uninstalling NetAdp");
+
+ uErr = doNetCfgInit(hModule, &pNetCfg, TRUE);
+ if (uErr == ERROR_SUCCESS)
+ {
+ HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId);
+ if (hr != S_OK)
+ logStringF(hModule, "UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = %#x", hr);
+
+ uErr = errorConvertFromHResult(hModule, hr);
+
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE);
+
+ logStringF(hModule, "Uninstalling NetAdp done, error = %#x", uErr);
+ }
+ else
+ logStringF(hModule, "UninstallNetAdp: doNetCfgInit failed, error = %#x", uErr);
+ }
+ __finally
+ {
+ if (bOldIntMode)
+ {
+ /* The prev mode != FALSE, i.e. non-interactive. */
+ SetupSetNonInteractiveMode(bOldIntMode);
+ }
+ netCfgLoggerDisable();
+ }
+#endif /* VBOX_WITH_NETFLT */
+
+ /* Never fail the uninstall even if we did not succeed. */
+ return ERROR_SUCCESS;
+}
+
+UINT __stdcall UninstallNetAdp(MSIHANDLE hModule)
+{
+ return _uninstallNetAdp(hModule, NETADP_ID);
+}
+
+static bool isTAPDevice(const WCHAR *pwszGUID)
+{
+ HKEY hNetcard;
+ bool bIsTapDevice = false;
+ LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}",
+ 0, KEY_READ, &hNetcard);
+ if (lStatus != ERROR_SUCCESS)
+ return false;
+
+ int i = 0;
+ for (;;)
+ {
+ WCHAR wszEnumName[256];
+ WCHAR wszNetCfgInstanceId[256];
+ DWORD dwKeyType;
+ HKEY hNetCardGUID;
+
+ DWORD dwLen = sizeof(wszEnumName);
+ lStatus = RegEnumKeyExW(hNetcard, i, wszEnumName, &dwLen, NULL, NULL, NULL, NULL);
+ if (lStatus != ERROR_SUCCESS)
+ break;
+
+ lStatus = RegOpenKeyExW(hNetcard, wszEnumName, 0, KEY_READ, &hNetCardGUID);
+ if (lStatus == ERROR_SUCCESS)
+ {
+ dwLen = sizeof(wszNetCfgInstanceId);
+ lStatus = RegQueryValueExW(hNetCardGUID, L"NetCfgInstanceId", NULL, &dwKeyType, (LPBYTE)wszNetCfgInstanceId, &dwLen);
+ if ( lStatus == ERROR_SUCCESS
+ && dwKeyType == REG_SZ)
+ {
+ WCHAR wszNetProductName[256];
+ WCHAR wszNetProviderName[256];
+
+ wszNetProductName[0] = 0;
+ dwLen = sizeof(wszNetProductName);
+ lStatus = RegQueryValueExW(hNetCardGUID, L"ProductName", NULL, &dwKeyType, (LPBYTE)wszNetProductName, &dwLen);
+
+ wszNetProviderName[0] = 0;
+ dwLen = sizeof(wszNetProviderName);
+ lStatus = RegQueryValueExW(hNetCardGUID, L"ProviderName", NULL, &dwKeyType, (LPBYTE)wszNetProviderName, &dwLen);
+
+ if ( !RTUtf16Cmp(wszNetCfgInstanceId, pwszGUID)
+ && !RTUtf16Cmp(wszNetProductName, L"VirtualBox TAP Adapter")
+ && ( (!RTUtf16Cmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */
+ || (!RTUtf16Cmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */
+ || (!RTUtf16Cmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */
+ )
+ )
+ {
+ bIsTapDevice = true;
+ RegCloseKey(hNetCardGUID);
+ break;
+ }
+ }
+ RegCloseKey(hNetCardGUID);
+ }
+ ++i;
+ }
+
+ RegCloseKey(hNetcard);
+ return bIsTapDevice;
+}
+
+/** @todo r=andy BUGBUG WTF! Why do we a) set the rc to 0 (success), and b) need this macro at all!?
+ *
+ * @todo r=bird: Because it's returning a bool, not int? The return code is
+ * ignored anyway, both internally in removeNetworkInterface and in it's caller.
+ * There is similar code in VBoxNetCfg.cpp, which is probably where it was copied from. */
+#define SetErrBreak(args) \
+ if (1) { \
+ rc = 0; \
+ logStringF args; \
+ break; \
+ } else do {} while (0)
+
+int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID)
+{
+ int rc = 1;
+ do /* break-loop */
+ {
+ WCHAR wszPnPInstanceId[512] = {0};
+
+ /* We have to find the device instance ID through a registry search */
+
+ HKEY hkeyNetwork = 0;
+ HKEY hkeyConnection = 0;
+
+ do /* break-loop */
+ {
+ WCHAR wszRegLocation[256];
+ RTUtf16Printf(wszRegLocation, RT_ELEMENTS(wszRegLocation),
+ "SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%ls", pwszGUID);
+ LONG lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork);
+ if (lrc != ERROR_SUCCESS || !hkeyNetwork)
+ SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [1]",
+ wszRegLocation, lrc));
+
+ lrc = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection);
+ if (lrc != ERROR_SUCCESS || !hkeyConnection)
+ SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [2]",
+ wszRegLocation, lrc));
+
+ DWORD len = sizeof(wszPnPInstanceId);
+ DWORD dwKeyType;
+ lrc = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len);
+ if (lrc != ERROR_SUCCESS || dwKeyType != REG_SZ)
+ SetErrBreak((hModule, "VBox HostInterfaces: Host interface network was not found in registry (%ls)! (lrc=%d) [3]",
+ wszRegLocation, lrc));
+ }
+ while (0);
+
+ if (hkeyConnection)
+ RegCloseKey(hkeyConnection);
+ if (hkeyNetwork)
+ RegCloseKey(hkeyNetwork);
+
+ /*
+ * Now we are going to enumerate all network devices and
+ * wait until we encounter the right device instance ID
+ */
+
+ HDEVINFO hDeviceInfo = INVALID_HANDLE_VALUE;
+ BOOL fResult;
+
+ do /* break-loop */
+ {
+ /* initialize the structure size */
+ SP_DEVINFO_DATA DeviceInfoData = { sizeof(DeviceInfoData) };
+
+ /* copy the net class GUID */
+ GUID netGuid;
+ memcpy(&netGuid, &GUID_DEVCLASS_NET, sizeof (GUID_DEVCLASS_NET));
+
+ /* return a device info set contains all installed devices of the Net class */
+ hDeviceInfo = SetupDiGetClassDevs(&netGuid, NULL, NULL, DIGCF_PRESENT);
+ if (hDeviceInfo == INVALID_HANDLE_VALUE)
+ {
+ logStringF(hModule, "VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError());
+ SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
+ }
+
+ /* enumerate the driver info list */
+ BOOL fFoundDevice = FALSE;
+ for (DWORD index = 0;; index++)
+ {
+ fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData);
+ if (!fResult)
+ {
+ if (GetLastError() == ERROR_NO_MORE_ITEMS)
+ break;
+ continue;
+ }
+
+ /* try to get the hardware ID registry property */
+ WCHAR *pwszDeviceHwid;
+ DWORD size = 0;
+ fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
+ &DeviceInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ NULL,
+ 0,
+ &size);
+ if (!fResult)
+ {
+ if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ continue;
+
+ pwszDeviceHwid = (WCHAR *)RTMemAllocZ(size);
+ if (!pwszDeviceHwid)
+ continue;
+
+ fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo,
+ &DeviceInfoData,
+ SPDRP_HARDWAREID,
+ NULL,
+ (PBYTE)pwszDeviceHwid,
+ size,
+ &size);
+ if (!fResult)
+ {
+ RTMemFree(pwszDeviceHwid);
+ continue;
+ }
+ }
+ else
+ {
+ /* something is wrong. This shouldn't have worked with a NULL buffer */
+ continue;
+ }
+
+ for (WCHAR *t = pwszDeviceHwid;
+ *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)];
+ t += RTUtf16Len(t) + 1)
+ {
+ if (RTUtf16ICmpAscii(t, "vboxtap") == 0)
+ {
+ /* get the device instance ID */
+ WCHAR wszDevID[MAX_DEVICE_ID_LEN];
+ if (CM_Get_Device_IDW(DeviceInfoData.DevInst, wszDevID, MAX_DEVICE_ID_LEN, 0) == CR_SUCCESS)
+ {
+ /* compare to what we determined before */
+ if (RTUtf16Cmp(wszDevID, wszPnPInstanceId) == 0)
+ {
+ fFoundDevice = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ RTMemFree(pwszDeviceHwid);
+
+ if (fFoundDevice)
+ break;
+ }
+
+ if (fFoundDevice)
+ {
+ fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData);
+ if (!fResult)
+ {
+ logStringF(hModule, "VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError());
+ SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
+ }
+
+ fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData);
+ if (!fResult)
+ {
+ logStringF(hModule, "VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError());
+ SetErrBreak((hModule, "VBox HostInterfaces: Uninstallation failed!"));
+ }
+ }
+ else
+ SetErrBreak((hModule, "VBox HostInterfaces: Host interface network device not found!"));
+ } while (0);
+
+ /* clean up the device info set */
+ if (hDeviceInfo != INVALID_HANDLE_VALUE)
+ SetupDiDestroyDeviceInfoList(hDeviceInfo);
+ } while (0);
+ return rc;
+}
+
+UINT __stdcall UninstallTAPInstances(MSIHANDLE hModule)
+{
+ static const wchar_t s_wszNetworkKey[] = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}";
+ HKEY hCtrlNet;
+
+ LSTATUS lrc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet);
+ if (lrc == ERROR_SUCCESS)
+ {
+ logStringF(hModule, "VBox HostInterfaces: Enumerating interfaces ...");
+ for (int i = 0; ; ++i)
+ {
+ WCHAR wszNetworkGUID[256] = { 0 };
+ DWORD dwLen = (DWORD)sizeof(wszNetworkGUID);
+ lrc = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL);
+ if (lrc != ERROR_SUCCESS)
+ {
+ switch (lrc)
+ {
+ case ERROR_NO_MORE_ITEMS:
+ logStringF(hModule, "VBox HostInterfaces: No interfaces found.");
+ break;
+ default:
+ logStringF(hModule, "VBox HostInterfaces: Enumeration failed: %ld", lrc);
+ break;
+ }
+ break;
+ }
+
+ if (isTAPDevice(wszNetworkGUID))
+ {
+ logStringF(hModule, "VBox HostInterfaces: Removing interface \"%ls\" ...", wszNetworkGUID);
+ removeNetworkInterface(hModule, wszNetworkGUID);
+ lrc = RegDeleteKeyW(hCtrlNet, wszNetworkGUID);
+ }
+ }
+ RegCloseKey(hCtrlNet);
+ logStringF(hModule, "VBox HostInterfaces: Removing interfaces done.");
+ }
+ return ERROR_SUCCESS;
+}
+
+
+/**
+ * This is used to remove the old VBoxDrv service before installation.
+ *
+ * The current service name is VBoxSup but the INF file won't remove the old
+ * one, so we do it manually to try prevent trouble as the device nodes are the
+ * same and we would fail starting VBoxSup.sys if VBoxDrv.sys is still loading.
+ *
+ * Status code is ignored for now as a reboot should fix most potential trouble
+ * here (and I don't want to break stuff too badly).
+ *
+ * @sa @bugref{10162}
+ */
+UINT __stdcall UninstallVBoxDrv(MSIHANDLE hModule)
+{
+ /*
+ * Try open the service.
+ */
+ SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG | SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hSMgr)
+ {
+ SC_HANDLE hService = OpenServiceW(hSMgr, L"VBoxDrv", DELETE | SERVICE_STOP | SERVICE_QUERY_STATUS);
+ if (hService)
+ {
+ /*
+ * Try stop it before we delete it.
+ */
+ SERVICE_STATUS Status = { 0, 0, 0, 0, 0, 0, 0 };
+ QueryServiceStatus(hService, &Status);
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ logStringF(hModule, "VBoxDrv: The service old service was already stopped");
+ else
+ {
+ logStringF(hModule, "VBoxDrv: Stopping the service (state %u)", Status.dwCurrentState);
+ if (ControlService(hService, SERVICE_CONTROL_STOP, &Status))
+ {
+ /* waiting for it to stop: */
+ int iWait = 100;
+ while (Status.dwCurrentState == SERVICE_STOP_PENDING && iWait-- > 0)
+ {
+ Sleep(100);
+ QueryServiceStatus(hService, &Status);
+ }
+
+ if (Status.dwCurrentState == SERVICE_STOPPED)
+ logStringF(hModule, "VBoxDrv: Stopped service");
+ else
+ logStringF(hModule, "VBoxDrv: Failed to stop the service, status: %u", Status.dwCurrentState);
+ }
+ else
+ {
+ DWORD const dwErr = GetLastError();
+ if ( Status.dwCurrentState == SERVICE_STOP_PENDING
+ && dwErr == ERROR_SERVICE_CANNOT_ACCEPT_CTRL)
+ logStringF(hModule, "VBoxDrv: Failed to stop the service: stop pending, not accepting control messages");
+ else
+ logStringF(hModule, "VBoxDrv: Failed to stop the service: dwErr=%u status=%u", dwErr, Status.dwCurrentState);
+ }
+ }
+
+ /*
+ * Delete the service, or at least mark it for deletion.
+ */
+ if (DeleteService(hService))
+ logStringF(hModule, "VBoxDrv: Successfully delete service");
+ else
+ logStringF(hModule, "VBoxDrv: Failed to delete the service: %u", GetLastError());
+
+ CloseServiceHandle(hService);
+ }
+ else
+ {
+ DWORD const dwErr = GetLastError();
+ if (dwErr == ERROR_SERVICE_DOES_NOT_EXIST)
+ logStringF(hModule, "VBoxDrv: Nothing to do, the old service does not exist");
+ else
+ logStringF(hModule, "VBoxDrv: Failed to open the service: %u", dwErr);
+ }
+
+ CloseServiceHandle(hSMgr);
+ }
+ else
+ logStringF(hModule, "VBoxDrv: Failed to open service manager (%u).", GetLastError());
+
+ return ERROR_SUCCESS;
+}
+
diff --git a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def
new file mode 100644
index 00000000..03cb5d60
--- /dev/null
+++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def
@@ -0,0 +1,51 @@
+; $Id: VBoxInstallHelper.def $
+;; @file
+; VBoxInstallHelper - Defines the exports the MSI engine uses.
+;
+
+;
+; Copyright (C) 2008-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; SPDX-License-Identifier: GPL-3.0-only
+;
+
+LIBRARY "VBoxInstallHelper"
+EXPORTS
+ IsSerialCheckNeeded
+ CheckSerial
+ IsMSCRTInstalled
+ IsPythonInstalled
+ IsWindows10
+ ArePythonAPIDepsInstalled
+ InstallPythonAPI
+ InstallBranding
+ UninstallBranding
+ InstallNetFlt
+ UninstallNetFlt
+ UninstallNetAdp
+ InstallNetLwf
+ UninstallNetLwf
+ UninstallTAPInstances
+ UninstallVBoxDrv
+ CreateHostOnlyInterface
+ StopHostOnlyInterfaces
+ UpdateHostOnlyInterfaces
+ RemoveHostOnlyInterfaces
+ Ndis6CreateHostOnlyInterface
+ Ndis6UpdateHostOnlyInterfaces
diff --git a/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.rc b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.rc
new file mode 100644
index 00000000..ad8e363e
--- /dev/null
+++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.rc
@@ -0,0 +1,61 @@
+/* $Id: VBoxInstallHelper.rc $ */
+/** @file
+ * VBoxInstallHelper - Resource file containing version info.
+ */
+
+/*
+ * Copyright (C) 2015-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Installation Helper\0"
+ VALUE "InternalName", "VBoxInstallHelper\0"
+ VALUE "OriginalFilename", "VBoxInstallHelper.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END