diff options
Diffstat (limited to 'src/VBox/Installer/win/InstallHelper')
6 files changed, 1876 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..99a74998 --- /dev/null +++ b/src/VBox/Installer/win/InstallHelper/Makefile.kmk @@ -0,0 +1,53 @@ +# $Id: Makefile.kmk $ +## @file +# Sub-Makefile for VBoxInstallHelper.dll. +# + +# +# Copyright (C) 2008-2019 Oracle Corporation +# +# This file is part of VirtualBox Open Source Edition (OSE), as +# available from http://www.virtualbox.org. This file is free software; +# you can redistribute it and/or modify it under the terms of the GNU +# General Public License (GPL) as published by the Free Software +# Foundation, in version 2 as it comes in the "COPYING" file of the +# VirtualBox OSE distribution. VirtualBox OSE is distributed in the +# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +# + +SUB_DEPTH = ../../../../.. +include $(KBUILD_PATH)/subheader.kmk + +DLLS += VBoxInstallHelper +VBoxInstallHelper_TEMPLATE = VBOXR3STATIC +VBoxInstallHelper_DEFS = _WIN32_WINNT=0x0501 _UNICODE UNICODE +VBoxInstallHelper_DEFS += VBOX_SVN_REV=$(VBOX_SVN_REV) +VBoxInstallHelper_DEPS = $(VBOX_SVN_REV_KMK) +ifdef VBOX_WITH_NETFLT + VBoxInstallHelper_DEFS += VBOX_WITH_NETFLT=1 +endif +VBoxInstallHelper_SOURCES = \ + VBoxInstallHelper.cpp \ + VBoxInstallHelper.def \ + VBoxInstallHelper.rc \ + VBoxCommon.cpp +ifndef VBOX_OSE + VBoxInstallHelper_SOURCES += internal/VBoxSerial.cpp +endif +VBoxInstallHelper_SDKS = ReorderCompilerIncs $(VBOX_WINPSDK) $(VBOX_WINDDK) +VBoxInstallHelper_LIBS = $(PATH_SDK_$(VBOX_WINPSDK)_LIB)/Msi.lib +ifdef VBOX_WITH_NETFLT + VBoxInstallHelper_LIBS += \ + $(PATH_STAGE_LIB)/WinNetConfig.lib \ + $(PATH_STAGE_LIB)/VBoxDrvCfg.lib \ + $(PATH_SDK_$(VBOX_WINDDK)_LIB)/Newdev.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..8dd656c1 --- /dev/null +++ b/src/VBox/Installer/win/InstallHelper/VBoxCommon.cpp @@ -0,0 +1,63 @@ +/* $Id: VBoxCommon.cpp $ */ +/** @file + * VBoxCommon - Misc helper routines for install helper. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include <iprt/win/windows.h> +#include <tchar.h> +#include <stdio.h> + +#include <msi.h> +#include <msiquery.h> + + +#if (_MSC_VER < 1400) /* Provide swprintf_s to VC < 8.0. */ +int swprintf_s(WCHAR *buffer, size_t cbBuffer, const WCHAR *format, ...) +{ + int ret; + va_list va; + va_start(va, format); + ret = _vsnwprintf(buffer, cbBuffer, format, va); + va_end(va); + return ret; +} +#endif + +UINT VBoxGetProperty(MSIHANDLE a_hModule, WCHAR *a_pwszName, WCHAR *a_pwszValue, DWORD a_dwSize) +{ + DWORD dwBuffer = 0; + UINT uiRet = MsiGetPropertyW(a_hModule, a_pwszName, L"", &dwBuffer); + if (uiRet == ERROR_MORE_DATA) + { + ++dwBuffer; /* On output does not include terminating null, so add 1. */ + + if (dwBuffer > a_dwSize) + return ERROR_MORE_DATA; + + ZeroMemory(a_pwszValue, a_dwSize); + uiRet = MsiGetPropertyW(a_hModule, a_pwszName, a_pwszValue, &dwBuffer); + } + return uiRet; +} + +UINT VBoxSetProperty(MSIHANDLE a_hModule, WCHAR *a_pwszName, WCHAR *a_pwszValue) +{ + return MsiSetPropertyW(a_hModule, a_pwszName, a_pwszValue); +} + diff --git a/src/VBox/Installer/win/InstallHelper/VBoxCommon.h b/src/VBox/Installer/win/InstallHelper/VBoxCommon.h new file mode 100644 index 00000000..e1c628a9 --- /dev/null +++ b/src/VBox/Installer/win/InstallHelper/VBoxCommon.h @@ -0,0 +1,32 @@ +/* $Id: VBoxCommon.h $ */ +/** @file + * VBoxCommon - Misc helper routines for install helper. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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 VBoxGetProperty(MSIHANDLE a_hModule, WCHAR *a_pwszName, WCHAR *a_pwszValue, DWORD a_dwSize); +UINT VBoxSetProperty(MSIHANDLE a_hModule, WCHAR *a_pwszName, WCHAR *a_pwszValue); + +#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..c56801f9 --- /dev/null +++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.cpp @@ -0,0 +1,1640 @@ +/* $Id: VBoxInstallHelper.cpp $ */ +/** @file + * VBoxInstallHelper - Various helper routines for Windows host installer. + */ + +/* + * Copyright (C) 2008-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef VBOX_WITH_NETFLT +# include "VBox/VBoxNetCfg-win.h" +# include "VBox/VBoxDrvCfg-win.h" +#endif /* VBOX_WITH_NETFLT */ + +#include <VBox/version.h> + +#include <iprt/win/windows.h> +#include <wchar.h> +#include <stdio.h> + +#include <msi.h> +#include <msiquery.h> + +#define _WIN32_DCOM +#include <iprt/win/windows.h> +#include <assert.h> +#include <shellapi.h> +#define INITGUID +#include <guiddef.h> +#include <devguid.h> +#include <iprt/win/objbase.h> +#include <iprt/win/setupapi.h> +#include <iprt/win/shlobj.h> +#include <cfgmgr32.h> + +#include "VBoxCommon.h" + +#ifndef VBOX_OSE +# include "internal/VBoxSerial.h" +#endif + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#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) + + +BOOL APIENTRY DllMain(HANDLE hModule, DWORD uReason, LPVOID lpReserved) +{ + RT_NOREF3(hModule, uReason, lpReserved); + return TRUE; +} + + +static void logString(MSIHANDLE hInstall, LPCSTR szString, ...) +{ + PMSIHANDLE newHandle = MsiCreateRecord(2); + + char szBuffer[1024]; + va_list va; + va_start(va, szString); + _vsnprintf(szBuffer, sizeof(szBuffer), szString, va); + va_end(va); + + MsiRecordSetStringA(newHandle, 0, szBuffer); + MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), newHandle); + MsiCloseHandle(newHandle); + +#if 0/*def DEBUG - wrong? What does '%s' expect, wchar or char? */ + _tprintf(_T("Debug: %s\n"), szBuffer); +#endif +} + +static void logStringW(MSIHANDLE hInstall, LPCWSTR pwszString, ...) +{ + PMSIHANDLE newHandle = MsiCreateRecord(2); + + WCHAR szBuffer[1024]; + va_list va; + va_start(va, pwszString); + _vsnwprintf(szBuffer, RT_ELEMENTS(szBuffer), pwszString, va); + va_end(va); + + MsiRecordSetStringW(newHandle, 0, szBuffer); + MsiProcessMessage(hInstall, INSTALLMESSAGE(INSTALLMESSAGE_INFO), newHandle); + MsiCloseHandle(newHandle); +} + +UINT __stdcall IsSerialCheckNeeded(MSIHANDLE hModule) +{ +#ifndef VBOX_OSE + /*BOOL bRet =*/ serialCheckNeeded(hModule); +#else + RT_NOREF1(hModule); +#endif + return ERROR_SUCCESS; +} + +UINT __stdcall CheckSerial(MSIHANDLE hModule) +{ +#ifndef VBOX_OSE + /*BOOL bRet =*/ serialIsValid(hModule); +#else + RT_NOREF1(hModule); +#endif + return ERROR_SUCCESS; +} + +DWORD Exec(MSIHANDLE hModule, + const WCHAR *pwszAppName, WCHAR *pwszCmdLine, const WCHAR *pwszWorkDir, + DWORD *pdwExitCode) +{ + STARTUPINFOW si; + PROCESS_INFORMATION pi; + DWORD rc = ERROR_SUCCESS; + + ZeroMemory(&si, sizeof(si)); + si.dwFlags = STARTF_USESHOWWINDOW; +#ifdef UNICODE + si.dwFlags |= CREATE_UNICODE_ENVIRONMENT; +#endif + si.wShowWindow = SW_HIDE; /* For debugging: SW_SHOW; */ + si.cb = sizeof(si); + ZeroMemory(&pi, sizeof(pi)); + + logStringW(hModule, L"Executing command line: %s %s (Working Dir: %s)", + pwszAppName, pwszCmdLine, pwszWorkDir == NULL ? L"Current" : pwszWorkDir); + + SetLastError(0); + if (!CreateProcessW(pwszAppName, /* Module name. */ + pwszCmdLine, /* Command line. */ + NULL, /* Process handle not inheritable. */ + NULL, /* Thread handle not inheritable. */ + FALSE, /* Set handle inheritance to FALSE .*/ + 0, /* No creation flags. */ + NULL, /* Use parent's environment block. */ + pwszWorkDir, /* Use parent's starting directory. */ + &si, /* Pointer to STARTUPINFO structure. */ + &pi)) /* Pointer to PROCESS_INFORMATION structure. */ + { + rc = GetLastError(); + logStringW(hModule, L"Executing command line: CreateProcess() failed! Error: %ld", rc); + return rc; + } + + /* Wait until child process exits. */ + if (WAIT_FAILED == WaitForSingleObject(pi.hProcess, 30 * 1000 /* Wait 30 secs max. */)) + { + rc = GetLastError(); + logStringW(hModule, L"Executing command line: WaitForSingleObject() failed! Error: %u", rc); + } + else + { + if (!GetExitCodeProcess(pi.hProcess, pdwExitCode)) + { + rc = GetLastError(); + logStringW(hModule, L"Executing command line: GetExitCodeProcess() failed! Error: %u", rc); + } + } + + /* Close process and thread handles. */ + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + logStringW(hModule, L"Executing command returned: %u (exit code %u)", rc, *pdwExitCode); + return rc; +} + +UINT __stdcall InstallPythonAPI(MSIHANDLE hModule) +{ + logStringW(hModule, L"InstallPythonAPI: Checking for installed Python environment ..."); + + HKEY hkPythonCore = NULL; + BOOL bFound = FALSE; + LONG rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Python\\PythonCore", 0, KEY_READ, &hkPythonCore); + if (rc != ERROR_SUCCESS) + { + logStringW(hModule, L"InstallPythonAPI: Python seems not to be installed."); + return ERROR_SUCCESS; + } + + WCHAR wszPath[MAX_PATH] = { 0 }; + WCHAR wszVal[MAX_PATH] = { 0 }; + + for (int i = 0;; ++i) + { + WCHAR wszRoot[MAX_PATH] = { 0 }; + DWORD dwLen = sizeof(wszPath); + DWORD dwKeyType = REG_SZ; + + rc = RegEnumKeyExW(hkPythonCore, i, wszRoot, &dwLen, NULL, NULL, NULL, NULL); + if (rc != ERROR_SUCCESS || dwLen <= 0) + break; + + swprintf_s(wszPath, RT_ELEMENTS(wszPath), L"%s\\InstallPath", wszRoot); + dwLen = sizeof(wszVal); + + HKEY hkPythonInstPath = NULL; + rc = RegOpenKeyExW(hkPythonCore, wszPath, 0, KEY_READ, &hkPythonInstPath); + if (rc != ERROR_SUCCESS) + continue; + + rc = RegQueryValueExW(hkPythonInstPath, L"", NULL, &dwKeyType, (LPBYTE)wszVal, &dwLen); + if (rc == ERROR_SUCCESS) + logStringW(hModule, L"InstallPythonAPI: Path \"%s\" detected.", wszVal); + + RegCloseKey(hkPythonInstPath); + } + RegCloseKey(hkPythonCore); + + /* Python path found? */ + WCHAR wszExec[MAX_PATH] = { 0 }; + WCHAR wszCmdLine[MAX_PATH] = { 0 }; + DWORD dwExitCode = 0; + if (wcslen(wszVal) > 0) + { + /* Cool, check for installed Win32 extensions. */ + logStringW(hModule, L"InstallPythonAPI: Python installed. Checking for Win32 extensions ..."); + swprintf_s(wszExec, RT_ELEMENTS(wszExec), L"%s\\python.exe", wszVal); + swprintf_s(wszCmdLine, RT_ELEMENTS(wszCmdLine), L"%s\\python.exe -c \"import win32api\"", wszVal); + + DWORD dwRetExec = Exec(hModule, wszExec, wszCmdLine, NULL, &dwExitCode); + if ( (ERROR_SUCCESS == dwRetExec) + && ( 0 == dwExitCode)) + { + /* Did we get the correct error level (=0)? */ + logStringW(hModule, L"InstallPythonAPI: Win32 extensions installed."); + bFound = TRUE; + } + else + logStringW(hModule, L"InstallPythonAPI: Win32 extensions not found."); + } + + BOOL bInstalled = FALSE; + if (bFound) /* Is Python and all required stuff installed? */ + { + /* Get the VBoxAPI setup string. */ + WCHAR wszPathTargetDir[MAX_PATH] = {0}; + VBoxGetProperty(hModule, L"CustomActionData", wszPathTargetDir, sizeof(wszPathTargetDir)); + if (wcslen(wszPathTargetDir)) + { + /* Set final path. */ + swprintf_s(wszPath, RT_ELEMENTS(wszPath), L"%s", wszPathTargetDir); + + /* Install our API module. */ + swprintf_s(wszCmdLine, RT_ELEMENTS(wszCmdLine), L"%s\\python.exe vboxapisetup.py install", wszVal); + + /* Set required environment variables. */ + if (!SetEnvironmentVariableW(L"VBOX_INSTALL_PATH", wszPathTargetDir)) + logStringW(hModule, L"InstallPythonAPI: Could set environment variable VBOX_INSTALL_PATH!"); + else + { + DWORD dwRetExec = Exec(hModule, wszExec, wszCmdLine, wszPath, &dwExitCode); + if ( (ERROR_SUCCESS == dwRetExec) + && ( 0 == dwExitCode)) + { + /* All done! */ + logStringW(hModule, L"InstallPythonAPI: VBoxAPI for Python successfully installed."); + bInstalled = TRUE; + } + else + { + if (dwRetExec) + logStringW(hModule, L"InstallPythonAPI: Error while executing installation of VBox API: %ld", dwRetExec); + else + logStringW(hModule, L"InstallPythonAPI: Python reported an error while installing VBox API: %ld", dwExitCode); + } + } + } + else + logStringW(hModule, L"InstallPythonAPI: Unable to retrieve VBox installation path!"); + } + + VBoxSetProperty(hModule, L"VBOX_PYTHON_IS_INSTALLED", bInstalled ? L"1" : L"0"); + + if (!bInstalled) + logStringW(hModule, L"InstallPythonAPI: VBox API not installed."); + 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) + { + HKEY hkBranding; + WCHAR wszKey[_MAX_PATH]; + + if (wcsicmp(L"General", pwszSection) != 0) + swprintf_s(wszKey, RT_ELEMENTS(wszKey), L"SOFTWARE\\%s\\VirtualBox\\Branding\\%s", VBOX_VENDOR_SHORT, pwszSection); + else + swprintf_s(wszKey, RT_ELEMENTS(wszKey), L"SOFTWARE\\%s\\VirtualBox\\Branding", VBOX_VENDOR_SHORT); + + rc = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszKey, 0, KEY_WRITE, &hkBranding); + if (rc == ERROR_SUCCESS) + { + rc = RegSetValueExW(hkBranding, + pwszValue, + NULL, + REG_SZ, + (BYTE *)wszValue, + (DWORD)wcslen(wszValue)); + if (rc != ERROR_SUCCESS) + logStringW(hModule, L"InstallBranding: Could not write value %s! Error %ld", pwszValue, rc); + RegCloseKey (hkBranding); + } + } + else + rc = ERROR_NOT_FOUND; + return rc; +} + +UINT CopyDir(MSIHANDLE hModule, const WCHAR *pwszDestDir, const WCHAR *pwszSourceDir) +{ + UINT rc; + WCHAR wszDest[_MAX_PATH + 1]; + WCHAR wszSource[_MAX_PATH + 1]; + + swprintf_s(wszDest, RT_ELEMENTS(wszDest), L"%s%c", pwszDestDir, L'\0'); + swprintf_s(wszSource, RT_ELEMENTS(wszSource), L"%s%c", pwszSourceDir, L'\0'); + + SHFILEOPSTRUCTW s = {0}; + s.hwnd = NULL; + s.wFunc = FO_COPY; + s.pTo = wszDest; + s.pFrom = wszSource; + s.fFlags = FOF_SILENT | + FOF_NOCONFIRMATION | + FOF_NOCONFIRMMKDIR | + FOF_NOERRORUI; + + logStringW(hModule, L"CopyDir: DestDir=%s, SourceDir=%s", wszDest, wszSource); + int r = SHFileOperationW(&s); + if (r != 0) + { + logStringW(hModule, L"CopyDir: Copy operation returned status 0x%x", r); + rc = ERROR_GEN_FAILURE; + } + else + rc = ERROR_SUCCESS; + return rc; +} + +UINT RemoveDir(MSIHANDLE hModule, const WCHAR *pwszDestDir) +{ + UINT rc; + WCHAR wszDest[_MAX_PATH + 1]; + + swprintf_s(wszDest, RT_ELEMENTS(wszDest), L"%s%c", pwszDestDir, L'\0'); + + SHFILEOPSTRUCTW s = {0}; + s.hwnd = NULL; + s.wFunc = FO_DELETE; + s.pFrom = wszDest; + s.fFlags = FOF_SILENT + | FOF_NOCONFIRMATION + | FOF_NOCONFIRMMKDIR + | FOF_NOERRORUI; + + logStringW(hModule, L"RemoveDir: DestDir=%s", wszDest); + int r = SHFileOperationW(&s); + if (r != 0) + { + logStringW(hModule, L"RemoveDir: Remove operation returned status 0x%x", r); + rc = ERROR_GEN_FAILURE; + } + else + rc = ERROR_SUCCESS; + return rc; +} + +UINT RenameDir(MSIHANDLE hModule, const WCHAR *pwszDestDir, const WCHAR *pwszSourceDir) +{ + UINT rc; + WCHAR wszDest[_MAX_PATH + 1]; + WCHAR wszSource[_MAX_PATH + 1]; + + swprintf_s(wszDest, RT_ELEMENTS(wszDest), L"%s%c", pwszDestDir, L'\0'); + swprintf_s(wszSource, RT_ELEMENTS(wszSource), L"%s%c", pwszSourceDir, L'\0'); + + SHFILEOPSTRUCTW s = {0}; + s.hwnd = NULL; + s.wFunc = FO_RENAME; + s.pTo = wszDest; + s.pFrom = wszSource; + s.fFlags = FOF_SILENT + | FOF_NOCONFIRMATION + | FOF_NOCONFIRMMKDIR + | FOF_NOERRORUI; + + logStringW(hModule, L"RenameDir: DestDir=%s, SourceDir=%s", wszDest, wszSource); + int r = SHFileOperationW(&s); + if (r != 0) + { + logStringW(hModule, L"RenameDir: Rename operation returned status 0x%x", r); + rc = ERROR_GEN_FAILURE; + } + else + rc = ERROR_SUCCESS; + return rc; +} + +UINT __stdcall UninstallBranding(MSIHANDLE hModule) +{ + UINT rc; + logStringW(hModule, L"UninstallBranding: Handling branding file ..."); + + WCHAR wszPathTargetDir[_MAX_PATH]; + WCHAR wszPathDest[_MAX_PATH]; + + rc = VBoxGetProperty(hModule, L"CustomActionData", wszPathTargetDir, sizeof(wszPathTargetDir)); + if (rc == ERROR_SUCCESS) + { + /** @todo Check trailing slash after %s. */ +/** @todo r=bird: using swprintf_s for formatting paths without checking for + * overflow not good. You're dodging the buffer overflow issue only to end up + * with incorrect behavior! (Truncated file/dir names) + * + * This applies almost to all swprintf_s calls in this file!! + */ + swprintf_s(wszPathDest, RT_ELEMENTS(wszPathDest), L"%scustom", wszPathTargetDir); + rc = RemoveDir(hModule, wszPathDest); + if (rc != ERROR_SUCCESS) + { + /* Check for hidden .custom directory and remove it. */ + swprintf_s(wszPathDest, RT_ELEMENTS(wszPathDest), L"%s.custom", wszPathTargetDir); + rc = RemoveDir(hModule, wszPathDest); + } + } + + logStringW(hModule, L"UninstallBranding: Handling done. (rc=%u (ignored))", rc); + return ERROR_SUCCESS; /* Do not fail here. */ +} + +UINT __stdcall InstallBranding(MSIHANDLE hModule) +{ + UINT rc; + logStringW(hModule, L"InstallBranding: Handling branding file ..."); + + WCHAR wszPathMSI[_MAX_PATH]; + rc = VBoxGetProperty(hModule, L"SOURCEDIR", wszPathMSI, sizeof(wszPathMSI)); + if (rc == ERROR_SUCCESS) + { + WCHAR wszPathTargetDir[_MAX_PATH]; + rc = VBoxGetProperty(hModule, L"CustomActionData", wszPathTargetDir, sizeof(wszPathTargetDir)); + if (rc == ERROR_SUCCESS) + { + /** @todo Check for trailing slash after %s. */ + WCHAR wszPathDest[_MAX_PATH]; + swprintf_s(wszPathDest, RT_ELEMENTS(wszPathDest), L"%s", wszPathTargetDir); + + WCHAR wszPathSource[_MAX_PATH]; + swprintf_s(wszPathSource, RT_ELEMENTS(wszPathSource), L"%s.custom", wszPathMSI); + rc = CopyDir(hModule, wszPathDest, wszPathSource); + if (rc == ERROR_SUCCESS) + { + swprintf_s(wszPathDest, RT_ELEMENTS(wszPathDest), L"%scustom", wszPathTargetDir); + swprintf_s(wszPathSource, RT_ELEMENTS(wszPathSource), L"%s.custom", wszPathTargetDir); + rc = RenameDir(hModule, wszPathDest, wszPathSource); + } + } + } + + logStringW(hModule, L"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 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) + logString(g_hCurrentModule, pszMsg); + break; + default: + break; + } +} + +static VOID netCfgLoggerCallback(LPCSTR szString) +{ + if (g_hCurrentModule) + logString(g_hCurrentModule, szString); +} + +static VOID netCfgLoggerDisable() +{ + if (g_hCurrentModule) + { + VBoxNetCfgWinSetLogging((LOG_ROUTINE)NULL); + g_hCurrentModule = NULL; + } +} + +static VOID netCfgLoggerEnable(MSIHANDLE hModule) +{ + NonStandardAssert(hModule); + + if (g_hCurrentModule) + netCfgLoggerDisable(); + + g_hCurrentModule = hModule; + + VBoxNetCfgWinSetLogging((LOG_ROUTINE)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: + { + logStringW(hModule, L"Reboot required, setting REBOOT property to \"force\""); + HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force"); + if (hr2 != ERROR_SUCCESS) + logStringW(hModule, L"Failed to set REBOOT property, error = 0x%x", hr2); + uRet = ERROR_SUCCESS; /* Never fail here. */ + break; + } + + default: + logStringW(hModule, L"Converting unhandled HRESULT (0x%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) + { + logStringW(hModule, L"createNetCfgLockedMsgRecord: MsiRecordSetInteger failed, error = 0x%x", uErr); + MsiCloseHandle(hRecord); + hRecord = NULL; + } + } + else + logStringW(hModule, L"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)) + logStringW(hModule, L"doNetCfgInit: VBoxNetCfgWinQueryINetCfg failed, error = 0x%x", hr); + uErr = errorConvertFromHResult(hModule, hr); + break; + } + + /* hr == NETCFG_E_NO_WRITE_LOCK */ + + if (!lpszLockedBy) + { + logStringW(hModule, L"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 + && !wcscmp(lpszLockedBy, L"6to4svc.dll")) + { + cRetries++; + logStringW(hModule, L"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) + { + logStringW(hModule, L"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) + { + logStringW(hModule, L"doNetCfgInit: MsiRecordSetStringW failed, error = 0x%x", rTmp); + CoTaskMemFree(lpszLockedBy); + break; + } + + MsgResult = MsiProcessMessage(hModule, (INSTALLMESSAGE)(INSTALLMESSAGE_USER | MB_RETRYCANCEL), hMsg); + NonStandardAssert(MsgResult == IDRETRY || MsgResult == IDCANCEL); + logStringW(hModule, L"doNetCfgInit: MsiProcessMessage returned (0x%x)", MsgResult); + } + CoTaskMemFree(lpszLockedBy); + } while(MsgResult == IDRETRY); + + if (hMsg) + MsiCloseHandle(hMsg); + + return uErr; +} + +static UINT vboxNetFltQueryInfArray(MSIHANDLE hModule, OUT LPWSTR pwszPtInf, OUT LPWSTR pwszMpInf, DWORD dwSize) +{ + DWORD dwBuf = dwSize - RT_MAX(sizeof(NETFLT_PT_INF_REL_PATH), sizeof(NETFLT_MP_INF_REL_PATH)); + UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", pwszPtInf, &dwBuf); + if ( uErr == ERROR_SUCCESS + && dwBuf) + { + wcscpy(pwszMpInf, pwszPtInf); + + wcsncat(pwszPtInf, NETFLT_PT_INF_REL_PATH, sizeof(NETFLT_PT_INF_REL_PATH)); + logStringW(hModule, L"vboxNetFltQueryInfArray: INF 1: %s", pwszPtInf); + + wcsncat(pwszMpInf, NETFLT_MP_INF_REL_PATH, sizeof(NETFLT_MP_INF_REL_PATH)); + logStringW(hModule, L"vboxNetFltQueryInfArray: INF 2: %s", pwszMpInf); + } + else if (uErr != ERROR_SUCCESS) + logStringW(hModule, L"vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = 0x%x", uErr); + else + { + logStringW(hModule, L"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 + { + logStringW(hModule, L"Uninstalling NetFlt"); + + uErr = doNetCfgInit(hModule, &pNetCfg, TRUE); + if (uErr == ERROR_SUCCESS) + { + HRESULT hr = VBoxNetCfgWinNetFltUninstall(pNetCfg); + if (hr != S_OK) + logStringW(hModule, L"UninstallNetFlt: VBoxNetCfgWinUninstallComponent failed, error = 0x%x", hr); + + uErr = errorConvertFromHResult(hModule, hr); + + VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE); + + logStringW(hModule, L"Uninstalling NetFlt done, error = 0x%x", uErr); + } + else + logStringW(hModule, L"UninstallNetFlt: doNetCfgInit failed, error = 0x%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 + { + + logStringW(hModule, L"InstallNetFlt: Installing NetFlt"); + + uErr = doNetCfgInit(hModule, &pNetCfg, TRUE); + if (uErr == ERROR_SUCCESS) + { + WCHAR wszPtInf[MAX_PATH]; + WCHAR wszMpInf[MAX_PATH]; + uErr = vboxNetFltQueryInfArray(hModule, wszPtInf, wszMpInf, sizeof(wszMpInf)); + if (uErr == ERROR_SUCCESS) + { + LPCWSTR const apwszInfs[] = { wszPtInf, wszMpInf }; + HRESULT hr = VBoxNetCfgWinNetFltInstall(pNetCfg, &apwszInfs[0], RT_ELEMENTS(apwszInfs)); + if (FAILED(hr)) + logStringW(hModule, L"InstallNetFlt: VBoxNetCfgWinNetFltInstall failed, error = 0x%x", hr); + + uErr = errorConvertFromHResult(hModule, hr); + } + else + logStringW(hModule, L"InstallNetFlt: vboxNetFltQueryInfArray failed, error = 0x%x", uErr); + + VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE); + + logStringW(hModule, L"InstallNetFlt: Done"); + } + else + logStringW(hModule, L"InstallNetFlt: doNetCfgInit failed, error = 0x%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 + { + logStringW(hModule, L"Uninstalling NetLwf"); + + uErr = doNetCfgInit(hModule, &pNetCfg, TRUE); + if (uErr == ERROR_SUCCESS) + { + HRESULT hr = VBoxNetCfgWinNetLwfUninstall(pNetCfg); + if (hr != S_OK) + logStringW(hModule, L"UninstallNetLwf: VBoxNetCfgWinUninstallComponent failed, error = 0x%x", hr); + + uErr = errorConvertFromHResult(hModule, hr); + + VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE); + + logStringW(hModule, L"Uninstalling NetLwf done, error = 0x%x", uErr); + } + else + logStringW(hModule, L"UninstallNetLwf: doNetCfgInit failed, error = 0x%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 + { + + logStringW(hModule, L"InstallNetLwf: Installing NetLwf"); + + uErr = doNetCfgInit(hModule, &pNetCfg, TRUE); + if (uErr == ERROR_SUCCESS) + { + WCHAR wszInf[MAX_PATH]; + DWORD cchInf = RT_ELEMENTS(wszInf) - sizeof(NETLWF_INF_NAME) - 1; + UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszInf, &cchInf); + if (uErr == ERROR_SUCCESS) + { + if (cchInf) + { + if (wszInf[cchInf - 1] != L'\\') + { + wszInf[cchInf++] = L'\\'; + wszInf[cchInf] = L'\0'; + } + + wcscat(wszInf, NETLWF_INF_NAME); + + HRESULT hr = VBoxNetCfgWinNetLwfInstall(pNetCfg, wszInf); + if (FAILED(hr)) + logStringW(hModule, L"InstallNetLwf: VBoxNetCfgWinNetLwfInstall failed, error = 0x%x", hr); + + uErr = errorConvertFromHResult(hModule, hr); + } + else + { + logStringW(hModule, L"vboxNetFltQueryInfArray: Empty installation directory"); + uErr = ERROR_GEN_FAILURE; + } + } + else + logStringW(hModule, L"vboxNetFltQueryInfArray: MsiGetPropertyW failed, error = 0x%x", uErr); + + VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE); + + logStringW(hModule, L"InstallNetLwf: Done"); + } + else + logStringW(hModule, L"InstallNetLwf: doNetCfgInit failed, error = 0x%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); + + logStringW(hModule, L"CreateHostOnlyInterface: Creating host-only interface"); + + HRESULT hr = E_FAIL; + GUID guid; + WCHAR wszMpInf[MAX_PATH]; + DWORD cchMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)wcslen(pwszInfName) - 1 - 1; + LPCWSTR pwszInfPath = NULL; + bool fIsFile = false; + UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cchMpInf); + if (uErr == ERROR_SUCCESS) + { + if (cchMpInf) + { + logStringW(hModule, L"CreateHostOnlyInterface: NetAdpDir property = %s", wszMpInf); + if (wszMpInf[cchMpInf - 1] != L'\\') + { + wszMpInf[cchMpInf++] = L'\\'; + wszMpInf[cchMpInf] = L'\0'; + } + + wcscat(wszMpInf, pwszInfName); + pwszInfPath = wszMpInf; + fIsFile = true; + + logStringW(hModule, L"CreateHostOnlyInterface: Resulting INF path = %s", pwszInfPath); + } + else + logStringW(hModule, L"CreateHostOnlyInterface: VBox installation path is empty"); + } + else + logStringW(hModule, L"CreateHostOnlyInterface: Unable to retrieve VBox installation path, error = 0x%x", uErr); + + /* Make sure the inf file is installed. */ + if (pwszInfPath != NULL && fIsFile) + { + logStringW(hModule, L"CreateHostOnlyInterface: Calling VBoxDrvCfgInfInstall(%s)", pwszInfPath); + hr = VBoxDrvCfgInfInstall(pwszInfPath); + logStringW(hModule, L"CreateHostOnlyInterface: VBoxDrvCfgInfInstall returns 0x%x", hr); + if (FAILED(hr)) + logStringW(hModule, L"CreateHostOnlyInterface: Failed to install INF file, error = 0x%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) + { + logStringW(hModule, L"CreateHostOnlyInterface: Reboot required for update, setting REBOOT property to force"); + HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force"); + if (hr2 != ERROR_SUCCESS) + logStringW(hModule, L"CreateHostOnlyInterface: Failed to set REBOOT property for update, error = 0x%x", hr2); + } + } + else + { + //in fail case call CreateHostOnlyInterface + logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = 0x%x", hr); + logStringW(hModule, L"CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface"); +#ifdef VBOXNETCFG_DELAYEDRENAME + BSTR devId; + hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, &guid, &devId, NULL); +#else /* !VBOXNETCFG_DELAYEDRENAME */ + hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, fIsFile, &guid, NULL, NULL); +#endif /* !VBOXNETCFG_DELAYEDRENAME */ + logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface returns 0x%x", hr); + if (SUCCEEDED(hr)) + { + ULONG ip = inet_addr("192.168.56.1"); + ULONG mask = inet_addr("255.255.255.0"); + logStringW(hModule, L"CreateHostOnlyInterface: calling VBoxNetCfgWinEnableStaticIpConfig"); + hr = VBoxNetCfgWinEnableStaticIpConfig(&guid, ip, mask); + logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig returns 0x%x", hr); + if (FAILED(hr)) + logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinEnableStaticIpConfig failed, error = 0x%x", hr); +#ifdef VBOXNETCFG_DELAYEDRENAME + hr = VBoxNetCfgWinRenameHostOnlyConnection(&guid, devId, NULL); + if (FAILED(hr)) + logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinRenameHostOnlyConnection failed, error = 0x%x", hr); + SysFreeString(devId); +#endif /* VBOXNETCFG_DELAYEDRENAME */ + } + else + logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinCreateHostOnlyNetworkInterface failed, error = 0x%x", hr); + } + } + + if (SUCCEEDED(hr)) + logStringW(hModule, L"CreateHostOnlyInterface: Creating host-only interface done"); + + /* Restore original setup mode. */ + logStringW(hModule, L"CreateHostOnlyInterface: Almost done..."); + if (fSetupModeInteractive) + SetupSetNonInteractiveMode(fSetupModeInteractive); + + netCfgLoggerDisable(); + +#endif /* VBOX_WITH_NETFLT */ + + logStringW(hModule, L"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) +{ + return _createHostOnlyInterface(hModule, NETADP_ID, L"VBoxNetAdp6.inf"); +} + +static UINT _removeHostOnlyInterfaces(MSIHANDLE hModule, LPCWSTR pwszId) +{ +#ifdef VBOX_WITH_NETFLT + netCfgLoggerEnable(hModule); + + logStringW(hModule, L"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)) + { + logStringW(hModule, L"RemoveHostOnlyInterfaces: NetAdp uninstalled successfully, but failed to remove INF files"); + } + else + logStringW(hModule, L"RemoveHostOnlyInterfaces: NetAdp uninstalled successfully"); + + } + else + logStringW(hModule, L"RemoveHostOnlyInterfaces: NetAdp uninstall failed, hr = 0x%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); + + logStringW(hModule, L"StopHostOnlyInterfaces: Stopping all host-only interfaces"); + + BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); + + HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(pwszId, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE); + if (SUCCEEDED(hr)) + { + logStringW(hModule, L"StopHostOnlyInterfaces: Disabling host interfaces was successful, hr = 0x%x", hr); + } + else + logStringW(hModule, L"StopHostOnlyInterfaces: Disabling host interfaces failed, hr = 0x%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); + + logStringW(hModule, L"UpdateHostOnlyInterfaces: Updating all host-only interfaces"); + + BOOL fSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); + + WCHAR wszMpInf[MAX_PATH]; + DWORD cchMpInf = RT_ELEMENTS(wszMpInf) - (DWORD)wcslen(pwszInfName) - 1 - 1; + LPCWSTR pwszInfPath = NULL; + bool fIsFile = false; + UINT uErr = MsiGetPropertyW(hModule, L"CustomActionData", wszMpInf, &cchMpInf); + if (uErr == ERROR_SUCCESS) + { + if (cchMpInf) + { + logStringW(hModule, L"UpdateHostOnlyInterfaces: NetAdpDir property = %s", wszMpInf); + if (wszMpInf[cchMpInf - 1] != L'\\') + { + wszMpInf[cchMpInf++] = L'\\'; + wszMpInf[cchMpInf] = L'\0'; + } + + wcscat(wszMpInf, pwszInfName); + pwszInfPath = wszMpInf; + fIsFile = true; + + logStringW(hModule, L"UpdateHostOnlyInterfaces: Resulting INF path = %s", pwszInfPath); + + DWORD attrFile = GetFileAttributesW(pwszInfPath); + if (attrFile == INVALID_FILE_ATTRIBUTES) + { + DWORD dwErr = GetLastError(); + logStringW(hModule, L"UpdateHostOnlyInterfaces: File \"%s\" not found, dwErr=%ld", + pwszInfPath, dwErr); + } + else + { + logStringW(hModule, L"UpdateHostOnlyInterfaces: File \"%s\" exists", + pwszInfPath); + + BOOL fRebootRequired = FALSE; + HRESULT hr = VBoxNetCfgWinUpdateHostOnlyNetworkInterface(pwszInfPath, &fRebootRequired, pwszId); + if (SUCCEEDED(hr)) + { + if (fRebootRequired) + { + logStringW(hModule, L"UpdateHostOnlyInterfaces: Reboot required, setting REBOOT property to force"); + HRESULT hr2 = MsiSetPropertyW(hModule, L"REBOOT", L"Force"); + if (hr2 != ERROR_SUCCESS) + logStringW(hModule, L"UpdateHostOnlyInterfaces: Failed to set REBOOT property, error = 0x%x", hr2); + } + } + else + logStringW(hModule, L"UpdateHostOnlyInterfaces: VBoxNetCfgWinUpdateHostOnlyNetworkInterface failed, hr = 0x%x", hr); + } + } + else + logStringW(hModule, L"UpdateHostOnlyInterfaces: VBox installation path is empty"); + } + else + logStringW(hModule, L"UpdateHostOnlyInterfaces: Unable to retrieve VBox installation path, error = 0x%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 + { + logStringW(hModule, L"Uninstalling NetAdp"); + + uErr = doNetCfgInit(hModule, &pNetCfg, TRUE); + if (uErr == ERROR_SUCCESS) + { + HRESULT hr = VBoxNetCfgWinNetAdpUninstall(pNetCfg, pwszId); + if (hr != S_OK) + logStringW(hModule, L"UninstallNetAdp: VBoxNetCfgWinUninstallComponent failed, error = 0x%x", hr); + + uErr = errorConvertFromHResult(hModule, hr); + + VBoxNetCfgWinReleaseINetCfg(pNetCfg, TRUE); + + logStringW(hModule, L"Uninstalling NetAdp done, error = 0x%x", uErr); + } + else + logStringW(hModule, L"UninstallNetAdp: doNetCfgInit failed, error = 0x%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 ( !wcscmp(wszNetCfgInstanceId, pwszGUID) + && !wcscmp(wszNetProductName, L"VirtualBox TAP Adapter") + && ( (!wcscmp(wszNetProviderName, L"innotek GmbH")) /* Legacy stuff. */ + || (!wcscmp(wszNetProviderName, L"Sun Microsystems, Inc.")) /* Legacy stuff. */ + || (!wcscmp(wszNetProviderName, MY_WTEXT(VBOX_VENDOR))) /* Reflects current vendor string. */ + ) + ) + { + bIsTapDevice = true; + RegCloseKey(hNetCardGUID); + break; + } + } + RegCloseKey(hNetCardGUID); + } + ++i; + } + + RegCloseKey(hNetcard); + return bIsTapDevice; +} + +#define SetErrBreak(args) \ + if (1) { \ + rc = 0; \ + logStringW args; \ + break; \ + } else do {} while (0) + +int removeNetworkInterface(MSIHANDLE hModule, const WCHAR *pwszGUID) +{ + int rc = 1; + do + { + 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]; + swprintf(wszRegLocation, + L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\%s", + pwszGUID); + LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, wszRegLocation, 0, KEY_READ, &hkeyNetwork); + if ((lStatus != ERROR_SUCCESS) || !hkeyNetwork) + SetErrBreak((hModule, L"VBox HostInterfaces: Host interface network was not found in registry (%s)! [1]", + wszRegLocation)); + + lStatus = RegOpenKeyExW(hkeyNetwork, L"Connection", 0, KEY_READ, &hkeyConnection); + if ((lStatus != ERROR_SUCCESS) || !hkeyConnection) + SetErrBreak((hModule, L"VBox HostInterfaces: Host interface network was not found in registry (%s)! [2]", + wszRegLocation)); + + DWORD len = sizeof(wszPnPInstanceId); + DWORD dwKeyType; + lStatus = RegQueryValueExW(hkeyConnection, L"PnPInstanceID", NULL, + &dwKeyType, (LPBYTE)&wszPnPInstanceId[0], &len); + if ((lStatus != ERROR_SUCCESS) || (dwKeyType != REG_SZ)) + SetErrBreak((hModule, L"VBox HostInterfaces: Host interface network was not found in registry (%s)! [3]", + wszRegLocation)); + } + 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 + { + GUID netGuid; + SP_DEVINFO_DATA DeviceInfoData; + DWORD index = 0; + DWORD size = 0; + + /* initialize the structure size */ + DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + + /* copy the net class GUID */ + 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) + { + logStringW(hModule, L"VBox HostInterfaces: SetupDiGetClassDevs failed (0x%08X)!", GetLastError()); + SetErrBreak((hModule, L"VBox HostInterfaces: Uninstallation failed!")); + } + + BOOL fFoundDevice = FALSE; + + /* enumerate the driver info list */ + while (TRUE) + { + WCHAR *pwszDeviceHwid; + + fResult = SetupDiEnumDeviceInfo(hDeviceInfo, index, &DeviceInfoData); + if (!fResult) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + break; + else + { + index++; + continue; + } + } + + /* try to get the hardware ID registry property */ + fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo, + &DeviceInfoData, + SPDRP_HARDWAREID, + NULL, + NULL, + 0, + &size); + if (!fResult) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + index++; + continue; + } + + pwszDeviceHwid = (WCHAR *)malloc(size); + if (pwszDeviceHwid) + { + fResult = SetupDiGetDeviceRegistryProperty(hDeviceInfo, + &DeviceInfoData, + SPDRP_HARDWAREID, + NULL, + (PBYTE)pwszDeviceHwid, + size, + NULL); + if (!fResult) + { + free(pwszDeviceHwid); + pwszDeviceHwid = NULL; + index++; + continue; + } + } + } + else + { + /* something is wrong. This shouldn't have worked with a NULL buffer */ + index++; + continue; + } + + for (WCHAR *t = pwszDeviceHwid; + t && *t && t < &pwszDeviceHwid[size / sizeof(WCHAR)]; + t += wcslen(t) + 1) + { + if (!_wcsicmp(L"vboxtap", t)) + { + /* 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 (!wcscmp(wszDevID, wszPnPInstanceId)) + { + fFoundDevice = TRUE; + break; + } + } + } + } + + if (pwszDeviceHwid) + { + free(pwszDeviceHwid); + pwszDeviceHwid = NULL; + } + + if (fFoundDevice) + break; + + index++; + } + + if (fFoundDevice) + { + fResult = SetupDiSetSelectedDevice(hDeviceInfo, &DeviceInfoData); + if (!fResult) + { + logStringW(hModule, L"VBox HostInterfaces: SetupDiSetSelectedDevice failed (0x%08X)!", GetLastError()); + SetErrBreak((hModule, L"VBox HostInterfaces: Uninstallation failed!")); + } + + fResult = SetupDiCallClassInstaller(DIF_REMOVE, hDeviceInfo, &DeviceInfoData); + if (!fResult) + { + logStringW(hModule, L"VBox HostInterfaces: SetupDiCallClassInstaller (DIF_REMOVE) failed (0x%08X)!", GetLastError()); + SetErrBreak((hModule, L"VBox HostInterfaces: Uninstallation failed!")); + } + } + else + SetErrBreak((hModule, L"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 *s_wszNetworkKey = L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}"; + HKEY hCtrlNet; + + LONG lStatus = RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_wszNetworkKey, 0, KEY_READ, &hCtrlNet); + if (lStatus == ERROR_SUCCESS) + { + logStringW(hModule, L"VBox HostInterfaces: Enumerating interfaces ..."); + for (int i = 0; ; ++i) + { + WCHAR wszNetworkGUID[256] = { 0 }; + DWORD dwLen = (DWORD)sizeof(wszNetworkGUID); + lStatus = RegEnumKeyExW(hCtrlNet, i, wszNetworkGUID, &dwLen, NULL, NULL, NULL, NULL); + if (lStatus != ERROR_SUCCESS) + { + switch (lStatus) + { + case ERROR_NO_MORE_ITEMS: + logStringW(hModule, L"VBox HostInterfaces: No interfaces found."); + break; + default: + logStringW(hModule, L"VBox HostInterfaces: Enumeration failed: %ld", lStatus); + break; + } + break; + } + + if (isTAPDevice(wszNetworkGUID)) + { + logStringW(hModule, L"VBox HostInterfaces: Removing interface \"%s\" ...", wszNetworkGUID); + removeNetworkInterface(hModule, wszNetworkGUID); + lStatus = RegDeleteKeyW(hCtrlNet, wszNetworkGUID); + } + } + RegCloseKey(hCtrlNet); + logStringW(hModule, L"VBox HostInterfaces: Removing interfaces done."); + } + 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..0645a08d --- /dev/null +++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.def @@ -0,0 +1,37 @@ +; $Id: VBoxInstallHelper.def $ +;; @file +; VBoxInstallHelper - Defines the exports the MSI engine uses. +; + +; +; Copyright (C) 2008-2019 Oracle Corporation +; +; This file is part of VirtualBox Open Source Edition (OSE), as +; available from http://www.virtualbox.org. This file is free software; +; you can redistribute it and/or modify it under the terms of the GNU +; General Public License (GPL) as published by the Free Software +; Foundation, in version 2 as it comes in the "COPYING" file of the +; VirtualBox OSE distribution. VirtualBox OSE is distributed in the +; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. +; + +LIBRARY "VBoxInstallHelper" +EXPORTS + IsSerialCheckNeeded + CheckSerial + InstallPythonAPI + InstallBranding + UninstallBranding + InstallNetFlt + UninstallNetFlt + UninstallNetAdp + InstallNetLwf + UninstallNetLwf + UninstallTAPInstances + 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..1da555c3 --- /dev/null +++ b/src/VBox/Installer/win/InstallHelper/VBoxInstallHelper.rc @@ -0,0 +1,51 @@ +/* $Id: VBoxInstallHelper.rc $ */ +/** @file + * VBoxInstallHelper - Resource file containing version info. + */ + +/* + * Copyright (C) 2015-2019 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#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 |