From f215e02bf85f68d3a6106c2a1f4f7f063f819064 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Thu, 11 Apr 2024 10:17:27 +0200 Subject: Adding upstream version 7.0.14-dfsg. Signed-off-by: Daniel Baumann --- src/VBox/Installer/win/InstallHelper/Makefile.kmk | 65 + .../Installer/win/InstallHelper/VBoxCommon.cpp | 103 + src/VBox/Installer/win/InstallHelper/VBoxCommon.h | 44 + .../win/InstallHelper/VBoxInstallHelper.cpp | 2097 ++++++++++++++++++++ .../win/InstallHelper/VBoxInstallHelper.def | 51 + .../win/InstallHelper/VBoxInstallHelper.rc | 61 + 6 files changed, 2421 insertions(+) create mode 100644 src/VBox/Installer/win/InstallHelper/Makefile.kmk create mode 100644 src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp create mode 100644 src/VBox/Installer/win/InstallHelper/VBoxCommon.h create mode 100644 src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp create mode 100644 src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def create mode 100644 src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.rc (limited to 'src/VBox/Installer/win/InstallHelper') 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 . +# +# 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 . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include +#include +#include + +#include +#include + + +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 . + * + * 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 . + * + * 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 +#include + +#define _WIN32_DCOM +#include + +#include +#define INITGUID +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include /* RTPATH_MAX, RTPATH_IS_SLASH */ +#include /* RT_ZERO */ +#include + +#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 . +; +; 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 . + * + * SPDX-License-Identifier: GPL-3.0-only + */ + +#include +#include + +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 -- cgit v1.2.3