/* $Id: VBoxInstallHelper.cpp 51844 2014-07-03 13:03:19Z vboxsync $ */ /** @file * VBoxInstallHelper - Various helper routines for Windows host installer. */ /* * Copyright (C) 2008-2014 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 #include #include #include #include #include #define _WIN32_DCOM #include #include #include #define INITGUID #include #include #include #include #include #include #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 ul_reason_for_call, LPVOID 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); #endif return ERROR_SUCCESS; } UINT __stdcall CheckSerial(MSIHANDLE hModule) { #ifndef VBOX_OSE /*BOOL bRet =*/ serialIsValid(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\\", 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, '\0'); swprintf_s(wszSource, RT_ELEMENTS(wszSource), L"%s%c", pwszSourceDir, '\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, '\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, '\0'); swprintf_s(wszSource, RT_ELEMENTS(wszSource), L"%s%c", pwszSourceDir, '\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 (?). */ static MSIHANDLE g_hCurrentModule = NULL; 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); } 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*/ UINT __stdcall 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 install even if we did not succeed. */ return ERROR_SUCCESS; } UINT __stdcall 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; } #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 UINT __stdcall CreateHostOnlyInterface(MSIHANDLE hModule) { #ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); BOOL bSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); bool bSetStaticIp = true; logStringW(hModule, L"CreateHostOnlyInterface: Creating host-only interface"); HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(NETADP_ID); if (FAILED(hr)) { logStringW(hModule, L"CreateHostOnlyInterface: VBoxNetCfgWinRemoveAllNetDevicesOfId failed, error = 0x%x", hr); bSetStaticIp = false; } GUID guid; WCHAR wszMpInf[MAX_PATH]; DWORD cchMpInf = RT_ELEMENTS(wszMpInf) - sizeof("VBoxNetAdp.inf") - 1; LPCWSTR pwszInfPath = NULL; bool bIsFile = 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, L"VBoxNetAdp.inf"); pwszInfPath = wszMpInf; bIsFile = 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 && bIsFile) { 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)) { logStringW(hModule, L"CreateHostOnlyInterface: calling VBoxNetCfgWinCreateHostOnlyNetworkInterface"); hr = VBoxNetCfgWinCreateHostOnlyNetworkInterface(pwszInfPath, bIsFile, &guid, NULL, NULL); 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); } 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 (bSetupModeInteractive) SetupSetNonInteractiveMode(bSetupModeInteractive); 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 RemoveHostOnlyInterfaces(MSIHANDLE hModule) { #ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); logStringW(hModule, L"RemoveHostOnlyInterfaces: Removing all host-only interfaces"); BOOL bSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); HRESULT hr = VBoxNetCfgWinRemoveAllNetDevicesOfId(NETADP_ID); if (SUCCEEDED(hr)) { hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, NETADP_ID, L"Net", 0/* 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 uninstall failed, hr = 0x%x", hr); /* Restore original setup mode. */ if (bSetupModeInteractive) SetupSetNonInteractiveMode(bSetupModeInteractive); netCfgLoggerDisable(); #endif /* VBOX_WITH_NETFLT */ /* Never fail the install even if we did not succeed. */ return ERROR_SUCCESS; } UINT __stdcall StopHostOnlyInterfaces(MSIHANDLE hModule) { #ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); logStringW(hModule, L"StopHostOnlyInterfaces: Stopping all host-only interfaces"); BOOL bSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); HRESULT hr = VBoxNetCfgWinPropChangeAllNetDevicesOfId(NETADP_ID, VBOXNECTFGWINPROPCHANGE_TYPE_DISABLE); if (SUCCEEDED(hr)) { hr = VBoxDrvCfgInfUninstallAllSetupDi(&GUID_DEVCLASS_NET, NETADP_ID, L"Net", 0/* could be SUOI_FORCEDELETE */); if (FAILED(hr)) logStringW(hModule, L"StopHostOnlyInterfaces: VBoxDrvCfgInfUninstallAllSetupDi failed, hr = 0x%x", hr); } else logStringW(hModule, L"StopHostOnlyInterfaces: Disabling host interfaces failed, hr = 0x%x", hr); /* Restore original setup mode. */ if (bSetupModeInteractive) SetupSetNonInteractiveMode(bSetupModeInteractive); netCfgLoggerDisable(); #endif /* VBOX_WITH_NETFLT */ /* Never fail the install even if we did not succeed. */ return ERROR_SUCCESS; } UINT __stdcall UpdateHostOnlyInterfaces(MSIHANDLE hModule) { #ifdef VBOX_WITH_NETFLT netCfgLoggerEnable(hModule); logStringW(hModule, L"UpdateHostOnlyInterfaces: Updating all host-only interfaces"); BOOL bSetupModeInteractive = SetupSetNonInteractiveMode(FALSE); WCHAR wszMpInf[MAX_PATH]; DWORD cchMpInf = RT_ELEMENTS(wszMpInf) - sizeof("VBoxNetAdp.inf") - 1; LPCWSTR pwszInfPath = NULL; bool bIsFile = 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, L"VBoxNetAdp.inf"); pwszInfPath = wszMpInf; bIsFile = 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); 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 (bSetupModeInteractive) SetupSetNonInteractiveMode(bSetupModeInteractive); netCfgLoggerDisable(); #endif /* VBOX_WITH_NETFLT */ /* Never fail the install even if we did not succeed. */ return ERROR_SUCCESS; } 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(strAndArgs) \ if (1) { \ rc = 0; \ logStringW(hModule, strAndArgs); \ 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((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((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((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 { DWORD ret = 0; 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(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(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(L"VBox HostInterfaces: Uninstallation failed!"); } } else SetErrBreak(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; }