summaryrefslogtreecommitdiffstats
path: root/src/VBox/Main/src-server/win
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Main/src-server/win')
-rw-r--r--src/VBox/Main/src-server/win/HostDnsServiceWin.cpp483
-rw-r--r--src/VBox/Main/src-server/win/HostPowerWin.cpp236
-rw-r--r--src/VBox/Main/src-server/win/Makefile.kup0
-rw-r--r--src/VBox/Main/src-server/win/NetIf-win.cpp2001
-rw-r--r--src/VBox/Main/src-server/win/PerformanceWin.cpp356
-rw-r--r--src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp274
-rw-r--r--src/VBox/Main/src-server/win/VBoxSVC.rc78
-rw-r--r--src/VBox/Main/src-server/win/precomp_vcc.h48
-rw-r--r--src/VBox/Main/src-server/win/svchlp.cpp308
-rw-r--r--src/VBox/Main/src-server/win/svchlp.h107
-rw-r--r--src/VBox/Main/src-server/win/svcmain.cpp1212
11 files changed, 5103 insertions, 0 deletions
diff --git a/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp b/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp
new file mode 100644
index 00000000..d493a264
--- /dev/null
+++ b/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp
@@ -0,0 +1,483 @@
+/* $Id: HostDnsServiceWin.cpp $ */
+/** @file
+ * Host DNS listener for Windows.
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * XXX: need <winsock2.h> to reveal IP_ADAPTER_ADDRESSES in
+ * <iptypes.h> and it must be included before <windows.h>, which is
+ * pulled in by IPRT headers.
+ */
+#include <iprt/win/winsock2.h>
+
+#include "../HostDnsService.h"
+
+#include <VBox/com/string.h>
+#include <VBox/com/ptr.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+
+#include <iprt/win/windows.h>
+#include <windns.h>
+#include <iptypes.h>
+#include <iprt/win/iphlpapi.h>
+
+#include <algorithm>
+#include <iprt/sanitized/sstream>
+#include <iprt/sanitized/string>
+#include <vector>
+
+
+DECLINLINE(int) registerNotification(const HKEY &hKey, HANDLE &hEvent)
+{
+ LONG lrc = RegNotifyChangeKeyValue(hKey,
+ TRUE,
+ REG_NOTIFY_CHANGE_LAST_SET,
+ hEvent,
+ TRUE);
+ AssertMsgReturn(lrc == ERROR_SUCCESS,
+ ("Failed to register event on the key. Please debug me!"),
+ VERR_INTERNAL_ERROR);
+
+ return VINF_SUCCESS;
+}
+
+static void appendTokenizedStrings(std::vector<std::string> &vecStrings, const std::string &strToAppend, char chDelim = ' ')
+{
+ if (strToAppend.empty())
+ return;
+
+ std::istringstream stream(strToAppend);
+ std::string substr;
+
+ while (std::getline(stream, substr, chDelim))
+ {
+ if (substr.empty())
+ continue;
+
+ if (std::find(vecStrings.cbegin(), vecStrings.cend(), substr) != vecStrings.cend())
+ continue;
+
+ vecStrings.push_back(substr);
+ }
+}
+
+
+struct HostDnsServiceWin::Data
+{
+ HKEY hKeyTcpipParameters;
+ bool fTimerArmed;
+
+#define DATA_SHUTDOWN_EVENT 0
+#define DATA_DNS_UPDATE_EVENT 1
+#define DATA_TIMER 2
+#define DATA_MAX_EVENT 3
+ HANDLE haDataEvent[DATA_MAX_EVENT];
+
+ Data()
+ {
+ hKeyTcpipParameters = NULL;
+ fTimerArmed = false;
+
+ for (size_t i = 0; i < DATA_MAX_EVENT; ++i)
+ haDataEvent[i] = NULL;
+ }
+
+ ~Data()
+ {
+ if (hKeyTcpipParameters != NULL)
+ RegCloseKey(hKeyTcpipParameters);
+
+ for (size_t i = 0; i < DATA_MAX_EVENT; ++i)
+ if (haDataEvent[i] != NULL)
+ CloseHandle(haDataEvent[i]);
+ }
+};
+
+
+HostDnsServiceWin::HostDnsServiceWin()
+ : HostDnsServiceBase(true)
+{
+ m = new Data();
+}
+
+HostDnsServiceWin::~HostDnsServiceWin()
+{
+ if (m != NULL)
+ delete m;
+}
+
+HRESULT HostDnsServiceWin::init(HostDnsMonitorProxy *pProxy)
+{
+ if (m == NULL)
+ return E_FAIL;
+
+ bool fRc = true;
+ LONG lRc = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
+ L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters",
+ 0,
+ KEY_READ|KEY_NOTIFY,
+ &m->hKeyTcpipParameters);
+ if (lRc != ERROR_SUCCESS)
+ {
+ LogRel(("HostDnsServiceWin: failed to open key Tcpip\\Parameters (error %d)\n", lRc));
+ fRc = false;
+ }
+ else
+ {
+ for (size_t i = 0; i < DATA_MAX_EVENT; ++i)
+ {
+ HANDLE h;
+
+ if (i == DATA_TIMER)
+ h = CreateWaitableTimer(NULL, FALSE, NULL);
+ else
+ h = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ if (h == NULL)
+ {
+ LogRel(("HostDnsServiceWin: failed to create event (error %d)\n", GetLastError()));
+ fRc = false;
+ break;
+ }
+
+ m->haDataEvent[i] = h;
+ }
+ }
+
+ if (!fRc)
+ return E_FAIL;
+
+ HRESULT hrc = HostDnsServiceBase::init(pProxy);
+ if (FAILED(hrc))
+ return hrc;
+
+ return updateInfo();
+}
+
+void HostDnsServiceWin::uninit(void)
+{
+ HostDnsServiceBase::uninit();
+}
+
+int HostDnsServiceWin::monitorThreadShutdown(RTMSINTERVAL uTimeoutMs)
+{
+ RT_NOREF(uTimeoutMs);
+
+ AssertPtr(m);
+ SetEvent(m->haDataEvent[DATA_SHUTDOWN_EVENT]);
+ /** @todo r=andy Wait for thread? Check vrc here. Timeouts? */
+
+ return VINF_SUCCESS;
+}
+
+int HostDnsServiceWin::monitorThreadProc(void)
+{
+ Assert(m != NULL);
+
+ registerNotification(m->hKeyTcpipParameters,
+ m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
+
+ onMonitorThreadInitDone();
+
+ for (;;)
+ {
+ DWORD dwReady;
+
+ dwReady = WaitForMultipleObjects(DATA_MAX_EVENT, m->haDataEvent,
+ FALSE, INFINITE);
+
+ if (dwReady == WAIT_OBJECT_0 + DATA_SHUTDOWN_EVENT)
+ break;
+
+ if (dwReady == WAIT_OBJECT_0 + DATA_DNS_UPDATE_EVENT)
+ {
+ /*
+ * Registry updates for multiple values are not atomic, so
+ * wait a bit to avoid racing and reading partial update.
+ */
+ if (!m->fTimerArmed)
+ {
+ LARGE_INTEGER delay; /* in 100ns units */
+ delay.QuadPart = -2 * 1000 * 1000 * 10LL; /* relative: 2s */
+
+ BOOL ok = SetWaitableTimer(m->haDataEvent[DATA_TIMER], &delay,
+ 0, NULL, NULL, FALSE);
+ if (ok)
+ {
+ m->fTimerArmed = true;
+ }
+ else
+ {
+ LogRel(("HostDnsServiceWin: failed to arm timer (error %d)\n", GetLastError()));
+ updateInfo();
+ }
+ }
+
+ ResetEvent(m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
+ registerNotification(m->hKeyTcpipParameters,
+ m->haDataEvent[DATA_DNS_UPDATE_EVENT]);
+ }
+ else if (dwReady == WAIT_OBJECT_0 + DATA_TIMER)
+ {
+ m->fTimerArmed = false;
+ updateInfo();
+ }
+ else if (dwReady == WAIT_FAILED)
+ {
+ LogRel(("HostDnsServiceWin: WaitForMultipleObjects failed: error %d\n", GetLastError()));
+ return VERR_INTERNAL_ERROR;
+ }
+ else
+ {
+ LogRel(("HostDnsServiceWin: WaitForMultipleObjects unexpected return value %d\n", dwReady));
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+HRESULT HostDnsServiceWin::updateInfo(void)
+{
+ HostDnsInformation info;
+
+ std::string strDomain;
+ std::string strSearchList; /* NB: comma separated, no spaces */
+
+ /*
+ * We ignore "DhcpDomain" key here since it's not stable. If
+ * there are two active interfaces that use DHCP (in particular
+ * when host uses OpenVPN) then DHCP ACKs will take turns updating
+ * that key. Instead we call GetAdaptersAddresses() below (which
+ * is what ipconfig.exe seems to do).
+ */
+ for (DWORD regIndex = 0; /**/; ++regIndex)
+ {
+ char keyName[256];
+ DWORD cbKeyName = sizeof(keyName);
+ DWORD keyType = 0;
+ char keyData[1024];
+ DWORD cbKeyData = sizeof(keyData);
+
+/** @todo use unicode API. This isn't UTF-8 clean! */
+ LSTATUS lrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex,
+ keyName, &cbKeyName, 0,
+ &keyType, (LPBYTE)keyData, &cbKeyData);
+
+ if (lrc == ERROR_NO_MORE_ITEMS)
+ break;
+
+ if (lrc == ERROR_MORE_DATA) /* buffer too small; handle? */
+ continue;
+
+ if (lrc != ERROR_SUCCESS)
+ {
+ LogRel2(("HostDnsServiceWin: RegEnumValue error %d\n", (int)lrc));
+ return E_FAIL;
+ }
+
+ if (keyType != REG_SZ)
+ continue;
+
+ if (cbKeyData > 0 && keyData[cbKeyData - 1] == '\0')
+ --cbKeyData; /* don't count trailing NUL if present */
+
+ if (RTStrICmp("Domain", keyName) == 0)
+ {
+ strDomain.assign(keyData, cbKeyData);
+ LogRel2(("HostDnsServiceWin: Domain=\"%s\"\n", strDomain.c_str()));
+ }
+ else if (RTStrICmp("DhcpDomain", keyName) == 0)
+ {
+ std::string strDhcpDomain(keyData, cbKeyData);
+ LogRel2(("HostDnsServiceWin: DhcpDomain=\"%s\"\n", strDhcpDomain.c_str()));
+ }
+ else if (RTStrICmp("SearchList", keyName) == 0)
+ {
+ strSearchList.assign(keyData, cbKeyData);
+ LogRel2(("HostDnsServiceWin: SearchList=\"%s\"\n", strSearchList.c_str()));
+ }
+ }
+
+ /* statically configured domain name */
+ if (!strDomain.empty())
+ {
+ info.domain = strDomain;
+ info.searchList.push_back(strDomain);
+ }
+
+ /* statically configured search list */
+ if (!strSearchList.empty())
+ appendTokenizedStrings(info.searchList, strSearchList, ',');
+
+ /*
+ * When name servers are configured statically it seems that the
+ * value of Tcpip\Parameters\NameServer is NOT set, inly interface
+ * specific NameServer value is (which triggers notification for
+ * us to pick up the change). Fortunately, DnsApi seems to do the
+ * right thing there.
+ */
+ DNS_STATUS status;
+ PIP4_ARRAY pIp4Array = NULL;
+
+ // NB: must be set on input it seems, despite docs' claim to the contrary.
+ DWORD cbBuffer = sizeof(&pIp4Array);
+
+ status = DnsQueryConfig(DnsConfigDnsServerList,
+ DNS_CONFIG_FLAG_ALLOC, NULL, NULL,
+ &pIp4Array, &cbBuffer);
+
+ if (status == NO_ERROR && pIp4Array != NULL)
+ {
+ for (DWORD i = 0; i < pIp4Array->AddrCount; ++i)
+ {
+ char szAddrStr[16] = "";
+ RTStrPrintf(szAddrStr, sizeof(szAddrStr), "%RTnaipv4", pIp4Array->AddrArray[i]);
+
+ LogRel2(("HostDnsServiceWin: server %d: %s\n", i+1, szAddrStr));
+ info.servers.push_back(szAddrStr);
+ }
+
+ LocalFree(pIp4Array);
+ }
+
+
+ /**
+ * DnsQueryConfig(DnsConfigSearchList, ...) is not implemented.
+ * Call GetAdaptersAddresses() that orders the returned list
+ * appropriately and collect IP_ADAPTER_ADDRESSES::DnsSuffix.
+ */
+ do {
+ PIP_ADAPTER_ADDRESSES pAddrBuf = NULL;
+ ULONG cbAddrBuf = 8 * 1024;
+ bool fReallocated = false;
+ ULONG err;
+
+ pAddrBuf = (PIP_ADAPTER_ADDRESSES) malloc(cbAddrBuf);
+ if (pAddrBuf == NULL)
+ {
+ LogRel2(("HostDnsServiceWin: failed to allocate %zu bytes"
+ " of GetAdaptersAddresses buffer\n",
+ (size_t)cbAddrBuf));
+ break;
+ }
+
+ while (pAddrBuf != NULL)
+ {
+ ULONG cbAddrBufProvided = cbAddrBuf;
+
+ err = GetAdaptersAddresses(AF_UNSPEC,
+ GAA_FLAG_SKIP_ANYCAST
+ | GAA_FLAG_SKIP_MULTICAST,
+ NULL,
+ pAddrBuf, &cbAddrBuf);
+ if (err == NO_ERROR)
+ {
+ break;
+ }
+ else if (err == ERROR_BUFFER_OVERFLOW)
+ {
+ LogRel2(("HostDnsServiceWin: provided GetAdaptersAddresses with %zu"
+ " but asked again for %zu bytes\n",
+ (size_t)cbAddrBufProvided, (size_t)cbAddrBuf));
+
+ if (RT_UNLIKELY(fReallocated)) /* what? again?! */
+ {
+ LogRel2(("HostDnsServiceWin: ... not going to realloc again\n"));
+ free(pAddrBuf);
+ pAddrBuf = NULL;
+ break;
+ }
+
+ PIP_ADAPTER_ADDRESSES pNewBuf = (PIP_ADAPTER_ADDRESSES) realloc(pAddrBuf, cbAddrBuf);
+ if (pNewBuf == NULL)
+ {
+ LogRel2(("HostDnsServiceWin: failed to reallocate %zu bytes\n", (size_t)cbAddrBuf));
+ free(pAddrBuf);
+ pAddrBuf = NULL;
+ break;
+ }
+
+ /* try again */
+ pAddrBuf = pNewBuf; /* cbAddrBuf already updated */
+ fReallocated = true;
+ }
+ else
+ {
+ LogRel2(("HostDnsServiceWin: GetAdaptersAddresses error %d\n", err));
+ free(pAddrBuf);
+ pAddrBuf = NULL;
+ break;
+ }
+ }
+
+ if (pAddrBuf == NULL)
+ break;
+
+ for (PIP_ADAPTER_ADDRESSES pAdp = pAddrBuf; pAdp != NULL; pAdp = pAdp->Next)
+ {
+ LogRel2(("HostDnsServiceWin: %ls (status %u) ...\n",
+ pAdp->FriendlyName ? pAdp->FriendlyName : L"(null)", pAdp->OperStatus));
+
+ if (pAdp->OperStatus != IfOperStatusUp)
+ continue;
+
+ if (pAdp->DnsSuffix == NULL || *pAdp->DnsSuffix == L'\0')
+ continue;
+
+ char *pszDnsSuffix = NULL;
+ int vrc = RTUtf16ToUtf8Ex(pAdp->DnsSuffix, RTSTR_MAX, &pszDnsSuffix, 0, /* allocate */ NULL);
+ if (RT_FAILURE(vrc))
+ {
+ LogRel2(("HostDnsServiceWin: failed to convert DNS suffix \"%ls\": %Rrc\n", pAdp->DnsSuffix, vrc));
+ continue;
+ }
+
+ AssertContinue(pszDnsSuffix != NULL);
+ AssertContinue(*pszDnsSuffix != '\0');
+ LogRel2(("HostDnsServiceWin: ... suffix = \"%s\"\n", pszDnsSuffix));
+
+ appendTokenizedStrings(info.searchList, pszDnsSuffix);
+ RTStrFree(pszDnsSuffix);
+ }
+
+ free(pAddrBuf);
+ } while (0);
+
+
+ if (info.domain.empty() && !info.searchList.empty())
+ info.domain = info.searchList[0];
+
+ if (info.searchList.size() == 1)
+ info.searchList.clear();
+
+ HostDnsServiceBase::setInfo(info);
+
+ return S_OK;
+}
+
diff --git a/src/VBox/Main/src-server/win/HostPowerWin.cpp b/src/VBox/Main/src-server/win/HostPowerWin.cpp
new file mode 100644
index 00000000..f41a2112
--- /dev/null
+++ b/src/VBox/Main/src-server/win/HostPowerWin.cpp
@@ -0,0 +1,236 @@
+/* $Id: HostPowerWin.cpp $ */
+/** @file
+ * VirtualBox interface to host's power notification service
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_HOST
+#include <iprt/win/windows.h>
+/* Some SDK versions lack the extern "C" and thus cause linking failures.
+ * This workaround isn't pretty, but there are not many options. */
+extern "C" {
+#include <PowrProf.h>
+}
+
+#include <VBox/com/ptr.h>
+#include <iprt/errcore.h>
+#include "HostPower.h"
+#include "LoggingNew.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static WCHAR gachWindowClassName[] = L"VBoxPowerNotifyClass";
+
+
+HostPowerServiceWin::HostPowerServiceWin(VirtualBox *aVirtualBox) : HostPowerService(aVirtualBox), mThread(NIL_RTTHREAD)
+{
+ mHwnd = 0;
+
+ int vrc = RTThreadCreate(&mThread, HostPowerServiceWin::NotificationThread, this, 65536,
+ RTTHREADTYPE_GUI, RTTHREADFLAGS_WAITABLE, "MainPower");
+
+ if (RT_FAILURE(vrc))
+ {
+ Log(("HostPowerServiceWin::HostPowerServiceWin: RTThreadCreate failed with %Rrc\n", vrc));
+ return;
+ }
+}
+
+HostPowerServiceWin::~HostPowerServiceWin()
+{
+ if (mHwnd)
+ {
+ Log(("HostPowerServiceWin::!HostPowerServiceWin: destroy window %x\n", mHwnd));
+
+ /* Poke the thread out of the event loop and wait for it to clean up. */
+ PostMessage(mHwnd, WM_CLOSE, 0, 0);
+ RTThreadWait(mThread, 5000, NULL);
+ mThread = NIL_RTTHREAD;
+ }
+}
+
+
+
+DECLCALLBACK(int) HostPowerServiceWin::NotificationThread(RTTHREAD hThreadSelf, void *pInstance)
+{
+ RT_NOREF(hThreadSelf);
+ HostPowerServiceWin *pPowerObj = (HostPowerServiceWin *)pInstance;
+ HWND hwnd = 0;
+
+ /* Create a window and make it a power event notification handler. */
+ int vrc = VINF_SUCCESS;
+
+ HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+ /* Register the Window Class. */
+ WNDCLASS wc;
+
+ wc.style = CS_NOCLOSE;
+ wc.lpfnWndProc = HostPowerServiceWin::WndProc;
+ wc.cbClsExtra = 0;
+ wc.cbWndExtra = sizeof(void *);
+ wc.hInstance = hInstance;
+ wc.hIcon = NULL;
+ wc.hCursor = NULL;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszMenuName = NULL;
+ wc.lpszClassName = gachWindowClassName;
+
+ ATOM atomWindowClass = RegisterClass(&wc);
+
+ if (atomWindowClass == 0)
+ {
+ vrc = VERR_NOT_SUPPORTED;
+ Log(("HostPowerServiceWin::NotificationThread: RegisterClassA failed with %x\n", GetLastError()));
+ }
+ else
+ {
+ /* Create the window. */
+ hwnd = pPowerObj->mHwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST,
+ gachWindowClassName, gachWindowClassName,
+ WS_POPUPWINDOW,
+ -200, -200, 100, 100, NULL, NULL, hInstance, NULL);
+
+ if (hwnd == NULL)
+ {
+ Log(("HostPowerServiceWin::NotificationThread: CreateWindowExA failed with %x\n", GetLastError()));
+ vrc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ SetWindowLongPtr(hwnd, 0, (LONG_PTR)pPowerObj);
+ SetWindowPos(hwnd, HWND_TOPMOST, -200, -200, 0, 0,
+ SWP_NOACTIVATE | SWP_HIDEWINDOW | SWP_NOCOPYBITS | SWP_NOREDRAW | SWP_NOSIZE);
+
+ MSG msg;
+ BOOL fRet;
+ while ((fRet = GetMessage(&msg, NULL, 0, 0)) > 0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+ /*
+ * Window procedure can return error,
+ * but this is exceptional situation
+ * that should be identified in testing
+ */
+ Assert(fRet >= 0);
+ }
+ }
+
+ Log(("HostPowerServiceWin::NotificationThread: exit thread\n"));
+
+ if (atomWindowClass != 0)
+ {
+ UnregisterClass(gachWindowClassName, hInstance);
+ atomWindowClass = 0;
+ }
+
+ return 0;
+}
+
+LRESULT CALLBACK HostPowerServiceWin::WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_POWERBROADCAST:
+ {
+ HostPowerServiceWin *pPowerObj;
+
+ pPowerObj = (HostPowerServiceWin *)GetWindowLongPtr(hwnd, 0);
+ if (pPowerObj)
+ {
+ switch(wParam)
+ {
+ case PBT_APMSUSPEND:
+ pPowerObj->notify(Reason_HostSuspend);
+ break;
+
+ case PBT_APMRESUMEAUTOMATIC:
+ pPowerObj->notify(Reason_HostResume);
+ break;
+
+ case PBT_APMPOWERSTATUSCHANGE:
+ {
+ SYSTEM_POWER_STATUS SystemPowerStatus;
+
+ Log(("PBT_APMPOWERSTATUSCHANGE\n"));
+ if (GetSystemPowerStatus(&SystemPowerStatus) == TRUE)
+ {
+ Log(("PBT_APMPOWERSTATUSCHANGE ACLineStatus=%d BatteryFlag=%d\n", SystemPowerStatus.ACLineStatus,
+ SystemPowerStatus.BatteryFlag));
+
+ if (SystemPowerStatus.ACLineStatus == 0) /* offline */
+ {
+ if (SystemPowerStatus.BatteryFlag == 2 /* low > 33% */)
+ {
+ SYSTEM_BATTERY_STATE BatteryState;
+ LONG lrc = CallNtPowerInformation(SystemBatteryState, NULL, 0, (PVOID)&BatteryState,
+ sizeof(BatteryState));
+#ifdef LOG_ENABLED
+ if (lrc == 0 /* STATUS_SUCCESS */)
+ Log(("CallNtPowerInformation claims %d seconds of power left\n",
+ BatteryState.EstimatedTime));
+#endif
+ if ( lrc == 0 /* STATUS_SUCCESS */
+ && BatteryState.EstimatedTime < 60*5)
+ {
+ pPowerObj->notify(Reason_HostBatteryLow);
+ }
+ }
+ /* If the machine has less than 5% battery left (and is not connected
+ * to the AC), then we should save the state. */
+ else if (SystemPowerStatus.BatteryFlag == 4 /* critical battery status; less than 5% */)
+ {
+ pPowerObj->notify(Reason_HostBatteryLow);
+ }
+ }
+ }
+ break;
+ }
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ }
+ return TRUE;
+ }
+
+ case WM_DESTROY:
+ {
+ /* moved here. it can't work across theads */
+ SetWindowLongPtr(hwnd, 0, 0);
+ PostQuitMessage(0);
+ return 0;
+ }
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+}
diff --git a/src/VBox/Main/src-server/win/Makefile.kup b/src/VBox/Main/src-server/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Main/src-server/win/Makefile.kup
diff --git a/src/VBox/Main/src-server/win/NetIf-win.cpp b/src/VBox/Main/src-server/win/NetIf-win.cpp
new file mode 100644
index 00000000..c0518277
--- /dev/null
+++ b/src/VBox/Main/src-server/win/NetIf-win.cpp
@@ -0,0 +1,2001 @@
+/* $Id: NetIf-win.cpp $ */
+/** @file
+ * Main - NetIfList, Windows implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_HOST
+
+#define NETIF_WITHOUT_NETCFG
+
+#include <iprt/errcore.h>
+#include <list>
+
+#define _WIN32_DCOM
+#include <iprt/win/winsock2.h>
+#include <iprt/win/ws2tcpip.h>
+#include <iprt/win/windows.h>
+#include <tchar.h>
+
+#ifdef VBOX_WITH_NETFLT
+# include "VBox/VBoxNetCfg-win.h"
+# include "devguid.h"
+#endif
+
+#include <iprt/win/iphlpapi.h>
+#include <iprt/win/ntddndis.h>
+
+#include "LoggingNew.h"
+#include "HostNetworkInterfaceImpl.h"
+#include "ProgressImpl.h"
+#include "VirtualBoxImpl.h"
+#include "VBoxNls.h"
+#include "Global.h"
+#include "netif.h"
+#include "ThreadTask.h"
+
+DECLARE_TRANSLATION_CONTEXT(NetIfWin);
+
+#ifdef VBOX_WITH_NETFLT
+# include <Wbemidl.h>
+
+# include "svchlp.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>
+
+# define VBOX_APP_NAME L"VirtualBox"
+
+static int getDefaultInterfaceIndex()
+{
+ PMIB_IPFORWARDTABLE pIpTable;
+ DWORD dwSize = sizeof(MIB_IPFORWARDTABLE) * 20;
+ DWORD dwRC = NO_ERROR;
+ int iIndex = -1;
+
+ pIpTable = (MIB_IPFORWARDTABLE *)RTMemAlloc(dwSize);
+ if (GetIpForwardTable(pIpTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER)
+ {
+ RTMemFree(pIpTable);
+ pIpTable = (MIB_IPFORWARDTABLE *)RTMemAlloc(dwSize);
+ if (!pIpTable)
+ return -1;
+ }
+ dwRC = GetIpForwardTable(pIpTable, &dwSize, 0);
+ if (dwRC == NO_ERROR)
+ {
+ for (unsigned int i = 0; i < pIpTable->dwNumEntries; i++)
+ if (pIpTable->table[i].dwForwardDest == 0)
+ {
+ iIndex = pIpTable->table[i].dwForwardIfIndex;
+ break;
+ }
+ }
+ RTMemFree(pIpTable);
+ return iIndex;
+}
+
+static int collectNetIfInfo(Bstr &strName, Guid &guid, PNETIFINFO pInfo, int iDefault)
+{
+ RT_NOREF(strName);
+
+ /*
+ * Most of the hosts probably have less than 10 adapters,
+ * so we'll mostly succeed from the first attempt.
+ */
+ ULONG uBufLen = sizeof(IP_ADAPTER_ADDRESSES) * 10;
+ PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen);
+ if (!pAddresses)
+ return VERR_NO_MEMORY;
+ DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &uBufLen);
+ if (dwRc == ERROR_BUFFER_OVERFLOW)
+ {
+ /* Impressive! More than 10 adapters! Get more memory and try again. */
+ RTMemFree(pAddresses);
+ pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen);
+ if (!pAddresses)
+ return VERR_NO_MEMORY;
+ dwRc = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, pAddresses, &uBufLen);
+ }
+ if (dwRc == NO_ERROR)
+ {
+ PIP_ADAPTER_ADDRESSES pAdapter;
+ for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next)
+ {
+ char *pszUuid = RTStrDup(pAdapter->AdapterName);
+ size_t len = strlen(pszUuid) - 1;
+ if (pszUuid[0] == '{' && pszUuid[len] == '}')
+ {
+ pszUuid[len] = 0;
+ if (!RTUuidCompareStr(&pInfo->Uuid, pszUuid + 1))
+ {
+ bool fIPFound, fIPv6Found;
+ PIP_ADAPTER_UNICAST_ADDRESS pAddr;
+ fIPFound = fIPv6Found = false;
+ for (pAddr = pAdapter->FirstUnicastAddress; pAddr; pAddr = pAddr->Next)
+ {
+ switch (pAddr->Address.lpSockaddr->sa_family)
+ {
+ case AF_INET:
+ if (!fIPFound)
+ {
+ fIPFound = true;
+ memcpy(&pInfo->IPAddress,
+ &((struct sockaddr_in *)pAddr->Address.lpSockaddr)->sin_addr.s_addr,
+ sizeof(pInfo->IPAddress));
+ }
+ break;
+ case AF_INET6:
+ if (!fIPv6Found)
+ {
+ fIPv6Found = true;
+ memcpy(&pInfo->IPv6Address,
+ ((struct sockaddr_in6 *)pAddr->Address.lpSockaddr)->sin6_addr.s6_addr,
+ sizeof(pInfo->IPv6Address));
+ }
+ break;
+ }
+ }
+ PIP_ADAPTER_PREFIX pPrefix;
+ fIPFound = fIPv6Found = false;
+ for (pPrefix = pAdapter->FirstPrefix; pPrefix; pPrefix = pPrefix->Next)
+ {
+ switch (pPrefix->Address.lpSockaddr->sa_family)
+ {
+ case AF_INET:
+ if (!fIPFound)
+ {
+ if (pPrefix->PrefixLength <= sizeof(pInfo->IPNetMask) * 8)
+ {
+ fIPFound = true;
+ RTNetPrefixToMaskIPv4(pPrefix->PrefixLength, &pInfo->IPNetMask);
+ }
+ else
+ LogFunc(("Unexpected IPv4 prefix length of %d\n",
+ pPrefix->PrefixLength));
+ }
+ break;
+ case AF_INET6:
+ if (!fIPv6Found)
+ {
+ if (pPrefix->PrefixLength <= sizeof(pInfo->IPv6NetMask) * 8)
+ {
+ fIPv6Found = true;
+ RTNetPrefixToMaskIPv6(pPrefix->PrefixLength, &pInfo->IPv6NetMask);
+ }
+ else
+ LogFunc(("Unexpected IPv6 prefix length of %d\n",
+ pPrefix->PrefixLength));
+ }
+ break;
+ }
+ }
+ if (sizeof(pInfo->MACAddress) != pAdapter->PhysicalAddressLength)
+ LogFunc(("Unexpected physical address length: %u\n", pAdapter->PhysicalAddressLength));
+ else
+ memcpy(pInfo->MACAddress.au8, pAdapter->PhysicalAddress, sizeof(pInfo->MACAddress));
+ pInfo->enmMediumType = NETIF_T_ETHERNET;
+ pInfo->enmStatus = pAdapter->OperStatus == IfOperStatusUp ? NETIF_S_UP : NETIF_S_DOWN;
+ pInfo->fIsDefault = (pAdapter->IfIndex == (DWORD)iDefault);
+ RTStrFree(pszUuid);
+ break;
+ }
+ }
+ RTStrFree(pszUuid);
+ }
+
+ ADAPTER_SETTINGS Settings;
+ HRESULT hrc = VBoxNetCfgWinGetAdapterSettings((const GUID *)guid.raw(), &Settings);
+ if (hrc == S_OK)
+ {
+ if (Settings.ip)
+ {
+ pInfo->IPAddress.u = Settings.ip;
+ pInfo->IPNetMask.u = Settings.mask;
+ }
+ pInfo->fDhcpEnabled = Settings.bDhcp;
+ }
+ else
+ {
+ pInfo->fDhcpEnabled = false;
+ }
+ }
+ RTMemFree(pAddresses);
+
+ return VINF_SUCCESS;
+}
+
+/* svc helper func */
+
+struct StaticIpConfig
+{
+ ULONG IPAddress;
+ ULONG IPNetMask;
+};
+
+struct StaticIpV6Config
+{
+ char * IPV6Address;
+ ULONG IPV6NetMaskLength;
+};
+
+class NetworkInterfaceHelperClientData : public ThreadVoidData
+{
+public:
+ NetworkInterfaceHelperClientData(){};
+ ~NetworkInterfaceHelperClientData()
+ {
+ if (msgCode == SVCHlpMsg::EnableStaticIpConfigV6 && u.StaticIPV6.IPV6Address)
+ {
+ RTStrFree(u.StaticIPV6.IPV6Address);
+ u.StaticIPV6.IPV6Address = NULL;
+ }
+ };
+
+ SVCHlpMsg::Code msgCode;
+ /* for SVCHlpMsg::CreateHostOnlyNetworkInterface */
+ Bstr name;
+ ComObjPtr<HostNetworkInterface> iface;
+ ComObjPtr<VirtualBox> ptrVBox;
+ /* for SVCHlpMsg::RemoveHostOnlyNetworkInterface */
+ Guid guid;
+
+ union
+ {
+ StaticIpConfig StaticIP;
+ StaticIpV6Config StaticIPV6;
+ } u;
+
+};
+
+static HRESULT netIfNetworkInterfaceHelperClient(SVCHlpClient *aClient,
+ Progress *aProgress,
+ void *aUser, int *aVrc)
+{
+ LogFlowFuncEnter();
+ LogFlowFunc(("aClient={%p}, aProgress={%p}, aUser={%p}\n",
+ aClient, aProgress, aUser));
+
+ AssertReturn( (aClient == NULL && aProgress == NULL && aVrc == NULL)
+ || (aClient != NULL && aProgress != NULL && aVrc != NULL),
+ E_POINTER);
+ AssertReturn(aUser, E_POINTER);
+
+ NetworkInterfaceHelperClientData* d = static_cast<NetworkInterfaceHelperClientData *>(aUser);
+
+ if (aClient == NULL)
+ {
+ /* "cleanup only" mode, just return (it will free aUser) */
+ return S_OK;
+ }
+
+ HRESULT hrc = S_OK;
+ int vrc = VINF_SUCCESS;
+
+ switch (d->msgCode)
+ {
+ case SVCHlpMsg::CreateHostOnlyNetworkInterface:
+ {
+ LogFlowFunc(("CreateHostOnlyNetworkInterface:\n"));
+ LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw()));
+
+ /* write message and parameters */
+ vrc = aClient->write(d->msgCode);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(Utf8Str(d->name));
+ if (RT_FAILURE(vrc)) break;
+
+ /* wait for a reply */
+ bool endLoop = false;
+ while (!endLoop)
+ {
+ SVCHlpMsg::Code reply = SVCHlpMsg::Null;
+
+ vrc = aClient->read(reply);
+ if (RT_FAILURE(vrc)) break;
+
+ switch (reply)
+ {
+ case SVCHlpMsg::CreateHostOnlyNetworkInterface_OK:
+ {
+ /* read the GUID */
+ Guid guid;
+ Utf8Str name;
+ vrc = aClient->read(name);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->read(guid);
+ if (RT_FAILURE(vrc)) break;
+
+ LogFlowFunc(("Network connection GUID = {%RTuuid}\n", guid.raw()));
+
+ /* initialize the object returned to the caller by
+ * CreateHostOnlyNetworkInterface() */
+ hrc = d->iface->init(Bstr(name), Bstr(name), guid, HostNetworkInterfaceType_HostOnly);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = d->iface->i_setVirtualBox(d->ptrVBox);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = d->iface->updateConfig();
+ if (SUCCEEDED(hrc))
+ hrc = d->iface->i_updatePersistentConfig();
+ }
+ }
+ endLoop = true;
+ break;
+ }
+ case SVCHlpMsg::Error:
+ {
+ /* read the error message */
+ Utf8Str errMsg;
+ vrc = aClient->read(errMsg);
+ if (RT_FAILURE(vrc)) break;
+
+ hrc = E_FAIL;
+ d->iface->setError(E_FAIL, errMsg.c_str());
+ endLoop = true;
+ break;
+ }
+ default:
+ {
+ endLoop = true;
+ hrc = E_FAIL;/// @todo ComAssertMsgFailedBreak((
+ //"Invalid message code %d (%08lX)\n",
+ //reply, reply),
+ //hrc = E_FAIL);
+ }
+ }
+ }
+
+ break;
+ }
+ case SVCHlpMsg::RemoveHostOnlyNetworkInterface:
+ {
+ LogFlowFunc(("RemoveHostOnlyNetworkInterface:\n"));
+ LogFlowFunc(("Network connection GUID = {%RTuuid}\n", d->guid.raw()));
+
+ /* write message and parameters */
+ vrc = aClient->write(d->msgCode);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->guid);
+ if (RT_FAILURE(vrc)) break;
+
+ /* wait for a reply */
+ bool endLoop = false;
+ while (!endLoop)
+ {
+ SVCHlpMsg::Code reply = SVCHlpMsg::Null;
+
+ vrc = aClient->read(reply);
+ if (RT_FAILURE(vrc)) break;
+
+ switch (reply)
+ {
+ case SVCHlpMsg::OK:
+ {
+ /* no parameters */
+ hrc = S_OK;
+ endLoop = true;
+ break;
+ }
+ case SVCHlpMsg::Error:
+ {
+ /* read the error message */
+ Utf8Str errMsg;
+ vrc = aClient->read(errMsg);
+ if (RT_FAILURE(vrc)) break;
+
+ hrc = E_FAIL;
+ d->iface->setError(E_FAIL, errMsg.c_str());
+ endLoop = true;
+ break;
+ }
+ default:
+ {
+ endLoop = true;
+ hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak((
+ //"Invalid message code %d (%08lX)\n",
+ //reply, reply),
+ //hrc = E_FAIL);
+ }
+ }
+ }
+
+ break;
+ }
+ case SVCHlpMsg::EnableDynamicIpConfig: /* see usage in code */
+ {
+ LogFlowFunc(("EnableDynamicIpConfig:\n"));
+ LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw()));
+
+ /* write message and parameters */
+ vrc = aClient->write(d->msgCode);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->guid);
+ if (RT_FAILURE(vrc)) break;
+
+ /* wait for a reply */
+ bool endLoop = false;
+ while (!endLoop)
+ {
+ SVCHlpMsg::Code reply = SVCHlpMsg::Null;
+
+ vrc = aClient->read(reply);
+ if (RT_FAILURE(vrc)) break;
+
+ switch (reply)
+ {
+ case SVCHlpMsg::OK:
+ {
+ /* no parameters */
+ hrc = d->iface->updateConfig();
+ endLoop = true;
+ break;
+ }
+ case SVCHlpMsg::Error:
+ {
+ /* read the error message */
+ Utf8Str errMsg;
+ vrc = aClient->read(errMsg);
+ if (RT_FAILURE(vrc)) break;
+
+ hrc = E_FAIL;
+ d->iface->setError(E_FAIL, errMsg.c_str());
+ endLoop = true;
+ break;
+ }
+ default:
+ {
+ endLoop = true;
+ hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak((
+ //"Invalid message code %d (%08lX)\n",
+ //reply, reply),
+ //hrc = E_FAIL);
+ }
+ }
+ }
+
+ break;
+ }
+ case SVCHlpMsg::EnableStaticIpConfig: /* see usage in code */
+ {
+ LogFlowFunc(("EnableStaticIpConfig:\n"));
+ LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw()));
+
+ /* write message and parameters */
+ vrc = aClient->write(d->msgCode);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->guid);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->u.StaticIP.IPAddress);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->u.StaticIP.IPNetMask);
+ if (RT_FAILURE(vrc)) break;
+
+ /* wait for a reply */
+ bool endLoop = false;
+ while (!endLoop)
+ {
+ SVCHlpMsg::Code reply = SVCHlpMsg::Null;
+
+ vrc = aClient->read(reply);
+ if (RT_FAILURE(vrc)) break;
+
+ switch (reply)
+ {
+ case SVCHlpMsg::OK:
+ {
+ /* no parameters */
+ hrc = d->iface->updateConfig();
+ endLoop = true;
+ break;
+ }
+ case SVCHlpMsg::Error:
+ {
+ /* read the error message */
+ Utf8Str errMsg;
+ vrc = aClient->read(errMsg);
+ if (RT_FAILURE(vrc)) break;
+
+ hrc = E_FAIL;
+ d->iface->setError(E_FAIL, errMsg.c_str());
+ endLoop = true;
+ break;
+ }
+ default:
+ {
+ endLoop = true;
+ hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak((
+ //"Invalid message code %d (%08lX)\n",
+ //reply, reply),
+ //hrc = E_FAIL);
+ }
+ }
+ }
+
+ break;
+ }
+ case SVCHlpMsg::EnableStaticIpConfigV6: /* see usage in code */
+ {
+ LogFlowFunc(("EnableStaticIpConfigV6:\n"));
+ LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw()));
+
+ /* write message and parameters */
+ vrc = aClient->write(d->msgCode);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->guid);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->u.StaticIPV6.IPV6Address);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->u.StaticIPV6.IPV6NetMaskLength);
+ if (RT_FAILURE(vrc)) break;
+
+ /* wait for a reply */
+ bool endLoop = false;
+ while (!endLoop)
+ {
+ SVCHlpMsg::Code reply = SVCHlpMsg::Null;
+
+ vrc = aClient->read(reply);
+ if (RT_FAILURE(vrc)) break;
+
+ switch (reply)
+ {
+ case SVCHlpMsg::OK:
+ {
+ /* no parameters */
+ hrc = d->iface->updateConfig();
+ endLoop = true;
+ break;
+ }
+ case SVCHlpMsg::Error:
+ {
+ /* read the error message */
+ Utf8Str errMsg;
+ vrc = aClient->read(errMsg);
+ if (RT_FAILURE(vrc)) break;
+
+ hrc = E_FAIL;
+ d->iface->setError(E_FAIL, errMsg.c_str());
+ endLoop = true;
+ break;
+ }
+ default:
+ {
+ endLoop = true;
+ hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak((
+ //"Invalid message code %d (%08lX)\n",
+ //reply, reply),
+ //hrc = E_FAIL);
+ }
+ }
+ }
+
+ break;
+ }
+ case SVCHlpMsg::DhcpRediscover: /* see usage in code */
+ {
+ LogFlowFunc(("DhcpRediscover:\n"));
+ LogFlowFunc(("Network connection name = '%ls'\n", d->name.raw()));
+
+ /* write message and parameters */
+ vrc = aClient->write(d->msgCode);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(d->guid);
+ if (RT_FAILURE(vrc)) break;
+
+ /* wait for a reply */
+ bool endLoop = false;
+ while (!endLoop)
+ {
+ SVCHlpMsg::Code reply = SVCHlpMsg::Null;
+
+ vrc = aClient->read(reply);
+ if (RT_FAILURE(vrc)) break;
+
+ switch (reply)
+ {
+ case SVCHlpMsg::OK:
+ {
+ /* no parameters */
+ hrc = d->iface->updateConfig();
+ endLoop = true;
+ break;
+ }
+ case SVCHlpMsg::Error:
+ {
+ /* read the error message */
+ Utf8Str errMsg;
+ vrc = aClient->read(errMsg);
+ if (RT_FAILURE(vrc)) break;
+
+ hrc = E_FAIL;
+ d->iface->setError(E_FAIL, errMsg.c_str());
+ endLoop = true;
+ break;
+ }
+ default:
+ {
+ endLoop = true;
+ hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak((
+ //"Invalid message code %d (%08lX)\n",
+ //reply, reply),
+ //hrc = E_FAIL);
+ }
+ }
+ }
+
+ break;
+ }
+ default:
+ hrc = E_FAIL; /// @todo ComAssertMsgFailedBreak((
+// "Invalid message code %d (%08lX)\n",
+// d->msgCode, d->msgCode),
+// hrc = E_FAIL);
+ }
+
+ if (aVrc)
+ *aVrc = vrc;
+
+ LogFlowFunc(("hrc=0x%08X, vrc=%Rrc\n", hrc, vrc));
+ LogFlowFuncLeave();
+ return hrc;
+}
+
+
+int netIfNetworkInterfaceHelperServer(SVCHlpClient *aClient,
+ SVCHlpMsg::Code aMsgCode)
+{
+ LogFlowFuncEnter();
+ LogFlowFunc(("aClient={%p}, aMsgCode=%d\n", aClient, aMsgCode));
+
+ AssertReturn(aClient, VERR_INVALID_POINTER);
+
+ int vrc = VINF_SUCCESS;
+ HRESULT hrc;
+
+ switch (aMsgCode)
+ {
+ case SVCHlpMsg::CreateHostOnlyNetworkInterface:
+ {
+ LogFlowFunc(("CreateHostOnlyNetworkInterface:\n"));
+
+ Utf8Str desiredName;
+ vrc = aClient->read(desiredName);
+ if (RT_FAILURE(vrc)) break;
+
+ Guid guid;
+ Utf8Str errMsg;
+ Bstr name;
+ Bstr bstrErr;
+
+#ifdef VBOXNETCFG_DELAYEDRENAME
+ Bstr devId;
+ hrc = VBoxNetCfgWinCreateHostOnlyNetworkInterface(NULL, false, Bstr(desiredName).raw(), guid.asOutParam(), devId.asOutParam(),
+ bstrErr.asOutParam());
+#else /* !VBOXNETCFG_DELAYEDRENAME */
+ hrc = VBoxNetCfgWinCreateHostOnlyNetworkInterface(NULL, false, Bstr(desiredName).raw(), guid.asOutParam(), name.asOutParam(),
+ bstrErr.asOutParam());
+#endif /* !VBOXNETCFG_DELAYEDRENAME */
+
+ if (hrc == S_OK)
+ {
+ ULONG ip, mask;
+ hrc = VBoxNetCfgWinGenHostOnlyNetworkNetworkIp(&ip, &mask);
+ if (hrc == S_OK)
+ {
+ /* ip returned by VBoxNetCfgWinGenHostOnlyNetworkNetworkIp is a network ip,
+ * i.e. 192.168.xxx.0, assign 192.168.xxx.1 for the hostonly adapter */
+ ip = ip | (1 << 24);
+ hrc = VBoxNetCfgWinEnableStaticIpConfig((const GUID*)guid.raw(), ip, mask);
+ if (hrc != S_OK)
+ LogRel(("VBoxNetCfgWinEnableStaticIpConfig failed (0x%x)\n", hrc));
+ }
+ else
+ LogRel(("VBoxNetCfgWinGenHostOnlyNetworkNetworkIp failed (0x%x)\n", hrc));
+#ifdef VBOXNETCFG_DELAYEDRENAME
+ hrc = VBoxNetCfgWinRenameHostOnlyConnection((const GUID*)guid.raw(), devId.raw(), name.asOutParam());
+ if (hrc != S_OK)
+ LogRel(("VBoxNetCfgWinRenameHostOnlyConnection failed, error = 0x%x", hrc));
+#endif /* VBOXNETCFG_DELAYEDRENAME */
+ /* write success followed by GUID */
+ vrc = aClient->write(SVCHlpMsg::CreateHostOnlyNetworkInterface_OK);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(Utf8Str(name));
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(guid);
+ if (RT_FAILURE(vrc)) break;
+ }
+ else
+ {
+ vrc = VERR_GENERAL_FAILURE;
+ errMsg = Utf8Str(bstrErr);
+ /* write failure followed by error message */
+ if (errMsg.isEmpty())
+ errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc);
+ vrc = aClient->write(SVCHlpMsg::Error);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(errMsg);
+ if (RT_FAILURE(vrc)) break;
+ }
+
+ break;
+ }
+ case SVCHlpMsg::RemoveHostOnlyNetworkInterface:
+ {
+ LogFlowFunc(("RemoveHostOnlyNetworkInterface:\n"));
+
+ Guid guid;
+ Bstr bstrErr;
+
+ vrc = aClient->read(guid);
+ if (RT_FAILURE(vrc)) break;
+
+ Utf8Str errMsg;
+ hrc = VBoxNetCfgWinRemoveHostOnlyNetworkInterface((const GUID*)guid.raw(), bstrErr.asOutParam());
+
+ if (hrc == S_OK)
+ {
+ /* write parameter-less success */
+ vrc = aClient->write(SVCHlpMsg::OK);
+ if (RT_FAILURE(vrc)) break;
+ }
+ else
+ {
+ vrc = VERR_GENERAL_FAILURE;
+ errMsg = Utf8Str(bstrErr);
+ /* write failure followed by error message */
+ if (errMsg.isEmpty())
+ errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc);
+ vrc = aClient->write(SVCHlpMsg::Error);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(errMsg);
+ if (RT_FAILURE(vrc)) break;
+ }
+
+ break;
+ }
+ case SVCHlpMsg::EnableStaticIpConfigV6:
+ {
+ LogFlowFunc(("EnableStaticIpConfigV6:\n"));
+
+ Guid guid;
+ Utf8Str ipV6;
+ ULONG maskLengthV6;
+ vrc = aClient->read(guid);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->read(ipV6);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->read(maskLengthV6);
+ if (RT_FAILURE(vrc)) break;
+
+ Utf8Str errMsg;
+ vrc = VERR_NOT_IMPLEMENTED;
+
+ if (RT_SUCCESS(vrc))
+ {
+ /* write success followed by GUID */
+ vrc = aClient->write(SVCHlpMsg::OK);
+ if (RT_FAILURE(vrc)) break;
+ }
+ else
+ {
+ /* write failure followed by error message */
+ if (errMsg.isEmpty())
+ errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc);
+ vrc = aClient->write(SVCHlpMsg::Error);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(errMsg);
+ if (RT_FAILURE(vrc)) break;
+ }
+
+ break;
+ }
+ case SVCHlpMsg::EnableStaticIpConfig:
+ {
+ LogFlowFunc(("EnableStaticIpConfig:\n"));
+
+ Guid guid;
+ ULONG ip, mask;
+ vrc = aClient->read(guid);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->read(ip);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->read(mask);
+ if (RT_FAILURE(vrc)) break;
+
+ Utf8Str errMsg;
+ hrc = VBoxNetCfgWinEnableStaticIpConfig((const GUID *)guid.raw(), ip, mask);
+
+ if (hrc == S_OK)
+ {
+ /* write success followed by GUID */
+ vrc = aClient->write(SVCHlpMsg::OK);
+ if (RT_FAILURE(vrc)) break;
+ }
+ else
+ {
+ vrc = VERR_GENERAL_FAILURE;
+ /* write failure followed by error message */
+ if (errMsg.isEmpty())
+ errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc);
+ vrc = aClient->write(SVCHlpMsg::Error);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(errMsg);
+ if (RT_FAILURE(vrc)) break;
+ }
+
+ break;
+ }
+ case SVCHlpMsg::EnableDynamicIpConfig:
+ {
+ LogFlowFunc(("EnableDynamicIpConfig:\n"));
+
+ Guid guid;
+ vrc = aClient->read(guid);
+ if (RT_FAILURE(vrc)) break;
+
+ Utf8Str errMsg;
+ hrc = VBoxNetCfgWinEnableDynamicIpConfig((const GUID *)guid.raw());
+
+ if (hrc == S_OK)
+ {
+ /* write success followed by GUID */
+ vrc = aClient->write(SVCHlpMsg::OK);
+ if (RT_FAILURE(vrc)) break;
+ }
+ else
+ {
+ vrc = VERR_GENERAL_FAILURE;
+ /* write failure followed by error message */
+ if (errMsg.isEmpty())
+ errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc);
+ vrc = aClient->write(SVCHlpMsg::Error);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(errMsg);
+ if (RT_FAILURE(vrc)) break;
+ }
+
+ break;
+ }
+ case SVCHlpMsg::DhcpRediscover:
+ {
+ LogFlowFunc(("DhcpRediscover:\n"));
+
+ Guid guid;
+ vrc = aClient->read(guid);
+ if (RT_FAILURE(vrc)) break;
+
+ Utf8Str errMsg;
+ hrc = VBoxNetCfgWinDhcpRediscover((const GUID *)guid.raw());
+
+ if (hrc == S_OK)
+ {
+ /* write success followed by GUID */
+ vrc = aClient->write(SVCHlpMsg::OK);
+ if (RT_FAILURE(vrc)) break;
+ }
+ else
+ {
+ vrc = VERR_GENERAL_FAILURE;
+ /* write failure followed by error message */
+ if (errMsg.isEmpty())
+ errMsg = Utf8StrFmt("Unspecified error (%Rrc)", vrc);
+ vrc = aClient->write(SVCHlpMsg::Error);
+ if (RT_FAILURE(vrc)) break;
+ vrc = aClient->write(errMsg);
+ if (RT_FAILURE(vrc)) break;
+ }
+
+ break;
+ }
+ default:
+ AssertMsgFailedBreakStmt(
+ ("Invalid message code %d (%08lX)\n", aMsgCode, aMsgCode),
+ VERR_GENERAL_FAILURE);
+ }
+
+ LogFlowFunc(("vrc=%Rrc\n", vrc));
+ LogFlowFuncLeave();
+ return vrc;
+}
+
+/** @todo REMOVE. OBSOLETE NOW. */
+/**
+ * Returns TRUE if the Windows version is 6.0 or greater (i.e. it's Vista and
+ * later OSes) and it has the UAC (User Account Control) feature enabled.
+ */
+static BOOL IsUACEnabled()
+{
+ OSVERSIONINFOEX info;
+ ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
+ info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ BOOL frc = GetVersionEx((OSVERSIONINFO *) &info);
+ AssertReturn(frc != FALSE, FALSE);
+
+ LogFlowFunc(("dwMajorVersion=%d, dwMinorVersion=%d\n", info.dwMajorVersion, info.dwMinorVersion));
+
+ /* we are interested only in Vista (and newer versions...). In all
+ * earlier versions UAC is not present. */
+ if (info.dwMajorVersion < 6)
+ return FALSE;
+
+ /* the default EnableLUA value is 1 (Enabled) */
+ DWORD dwEnableLUA = 1;
+
+ HKEY hKey;
+ LSTATUS lrc = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\System",
+ 0, KEY_QUERY_VALUE, &hKey);
+
+ Assert(lrc == ERROR_SUCCESS || lrc == ERROR_PATH_NOT_FOUND);
+ if (lrc == ERROR_SUCCESS)
+ {
+
+ DWORD cbEnableLUA = sizeof(dwEnableLUA);
+ lrc = RegQueryValueExA(hKey, "EnableLUA", NULL, NULL, (LPBYTE) &dwEnableLUA, &cbEnableLUA);
+
+ RegCloseKey(hKey);
+
+ Assert(lrc == ERROR_SUCCESS || lrc == ERROR_FILE_NOT_FOUND);
+ }
+
+ LogFlowFunc(("lrc=%d, dwEnableLUA=%d\n", lrc, dwEnableLUA));
+
+ return dwEnableLUA == 1;
+}
+
+/* end */
+
+static int vboxNetWinAddComponent(std::list<ComObjPtr<HostNetworkInterface> > * pPist,
+ INetCfgComponent * pncc, HostNetworkInterfaceType enmType,
+ int iDefaultInterface)
+{
+ int vrc = VERR_GENERAL_FAILURE;
+
+ LPWSTR lpszName;
+ HRESULT hrc = pncc->GetDisplayName(&lpszName);
+ Assert(hrc == S_OK);
+ if (hrc == S_OK)
+ {
+ Bstr name(lpszName);
+
+ GUID IfGuid;
+ hrc = pncc->GetInstanceGuid(&IfGuid);
+ Assert(hrc == S_OK);
+ if (hrc == S_OK)
+ {
+ Guid guidIfCopy(IfGuid);
+ NETIFINFO Info;
+ RT_ZERO(Info);
+ Info.Uuid = *guidIfCopy.raw();
+ vrc = collectNetIfInfo(name, guidIfCopy, &Info, iDefaultInterface);
+ if (RT_FAILURE(vrc))
+ LogRelFunc(("collectNetIfInfo() -> %Rrc\n", vrc));
+ LogFunc(("adding %ls\n", lpszName));
+ /* create a new object and add it to the list */
+ ComObjPtr<HostNetworkInterface> iface;
+ iface.createObject();
+ /* remove the curly bracket at the end */
+ vrc = iface->init(name, enmType, &Info);
+ if (SUCCEEDED(vrc))
+ {
+ if (Info.fIsDefault)
+ pPist->push_front(iface);
+ else
+ pPist->push_back(iface);
+ }
+ else
+ {
+ LogRelFunc(("HostNetworkInterface::init() -> %Rrc\n", vrc));
+ AssertComRC(vrc);
+ }
+ }
+ else
+ LogRelFunc(("failed to get device instance GUID (0x%x)\n", hrc));
+ CoTaskMemFree(lpszName);
+ }
+ else
+ LogRelFunc(("failed to get device display name (0x%x)\n", hrc));
+
+ return vrc;
+}
+
+#endif /* VBOX_WITH_NETFLT */
+
+
+static int netIfListHostAdapters(INetCfg *pNc, std::list<ComObjPtr<HostNetworkInterface> > &list)
+{
+#ifndef VBOX_WITH_NETFLT
+ /* VBoxNetAdp is available only when VBOX_WITH_NETFLT is enabled */
+ return VERR_NOT_IMPLEMENTED;
+#else /* # if defined VBOX_WITH_NETFLT */
+ IEnumNetCfgComponent *pEnumComponent;
+ HRESULT hrc = pNc->EnumComponents(&GUID_DEVCLASS_NET, &pEnumComponent);
+ if (hrc == S_OK)
+ {
+ INetCfgComponent *pMpNcc;
+ while ((hrc = pEnumComponent->Next(1, &pMpNcc, NULL)) == S_OK)
+ {
+ LPWSTR pwszName;
+ ULONG uComponentStatus;
+ hrc = pMpNcc->GetDisplayName(&pwszName);
+ if (hrc == S_OK)
+ LogFunc(("%ls\n", pwszName));
+ else
+ LogRelFunc(("failed to get device display name (0x%x)\n", hrc));
+ hrc = pMpNcc->GetDeviceStatus(&uComponentStatus);
+ if (hrc == S_OK)
+ {
+ if (uComponentStatus == 0)
+ {
+ LPWSTR pId;
+ hrc = pMpNcc->GetId(&pId);
+ Assert(hrc == S_OK);
+ if (hrc == S_OK)
+ {
+ LogFunc(("id = %ls\n", pId));
+ if (!_wcsnicmp(pId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2))
+ vboxNetWinAddComponent(&list, pMpNcc, HostNetworkInterfaceType_HostOnly, -1);
+ CoTaskMemFree(pId);
+ }
+ else
+ LogRelFunc(("failed to get device id (0x%x)\n", hrc));
+ }
+ }
+ else
+ LogRelFunc(("failed to get device status (0x%x)\n", hrc));
+ pMpNcc->Release();
+ }
+ Assert(hrc == S_OK || hrc == S_FALSE);
+
+ pEnumComponent->Release();
+ }
+ else
+ LogRelFunc(("EnumComponents error (0x%x)\n", hrc));
+#endif /* # if defined VBOX_WITH_NETFLT */
+ return VINF_SUCCESS;
+}
+
+int NetIfGetConfig(HostNetworkInterface * pIf, NETIFINFO *pInfo)
+{
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ Bstr name;
+ HRESULT hrc = pIf->COMGETTER(Name)(name.asOutParam());
+ if (hrc == S_OK)
+ {
+ Bstr IfGuid;
+ hrc = pIf->COMGETTER(Id)(IfGuid.asOutParam());
+ Assert(hrc == S_OK);
+ if (hrc == S_OK)
+ {
+ memset(pInfo, 0, sizeof(NETIFINFO));
+ Guid guid(IfGuid);
+ pInfo->Uuid = *(guid.raw());
+
+ return collectNetIfInfo(name, guid, pInfo, getDefaultInterfaceIndex());
+ }
+ }
+ return VERR_GENERAL_FAILURE;
+#endif
+}
+
+int NetIfGetConfigByName(PNETIFINFO)
+{
+ return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * Obtain the current state of the interface.
+ *
+ * @returns VBox status code.
+ *
+ * @param pcszIfName Interface name.
+ * @param penmState Where to store the retrieved state.
+ */
+int NetIfGetState(const char *pcszIfName, NETIFSTATUS *penmState)
+{
+ RT_NOREF(pcszIfName, penmState);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+/**
+ * Retrieve the physical link speed in megabits per second. If the interface is
+ * not up or otherwise unavailable the zero speed is returned.
+ *
+ * @returns VBox status code.
+ *
+ * @param pcszIfName Interface name.
+ * @param puMbits Where to store the link speed.
+ */
+int NetIfGetLinkSpeed(const char *pcszIfName, uint32_t *puMbits)
+{
+ RT_NOREF(pcszIfName, puMbits);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+int NetIfCreateHostOnlyNetworkInterface(VirtualBox *pVirtualBox,
+ IHostNetworkInterface **aHostNetworkInterface,
+ IProgress **aProgress,
+ IN_BSTR aName)
+{
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ /* create a progress object */
+ ComObjPtr<Progress> progress;
+ HRESULT hrc = progress.createObject();
+ AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc));
+
+ ComPtr<IHost> host;
+ hrc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ hrc = progress->init(pVirtualBox, host,
+ Bstr(NetIfWin::tr("Creating host only network interface")).raw(),
+ FALSE /* aCancelable */);
+ if (SUCCEEDED(hrc))
+ {
+ progress.queryInterfaceTo(aProgress);
+
+ /* create a new uninitialized host interface object */
+ ComObjPtr<HostNetworkInterface> iface;
+ iface.createObject();
+ iface.queryInterfaceTo(aHostNetworkInterface);
+
+ /* create the networkInterfaceHelperClient() argument */
+ NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData();
+
+ d->msgCode = SVCHlpMsg::CreateHostOnlyNetworkInterface;
+ d->name = aName;
+ d->iface = iface;
+ d->ptrVBox = pVirtualBox;
+
+ hrc = pVirtualBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */,
+ netIfNetworkInterfaceHelperClient,
+ static_cast<void *>(d),
+ progress);
+ /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */
+
+ }
+ }
+
+ return Global::vboxStatusCodeFromCOM(hrc);
+#endif
+}
+
+int NetIfRemoveHostOnlyNetworkInterface(VirtualBox *pVirtualBox, const Guid &aId,
+ IProgress **aProgress)
+{
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ /* create a progress object */
+ ComObjPtr<Progress> progress;
+ HRESULT hrc = progress.createObject();
+ AssertComRCReturn(hrc, Global::vboxStatusCodeFromCOM(hrc));
+
+ ComPtr<IHost> host;
+ hrc = pVirtualBox->COMGETTER(Host)(host.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ hrc = progress->init(pVirtualBox, host,
+ Bstr(NetIfWin::tr("Removing host network interface")).raw(),
+ FALSE /* aCancelable */);
+ if (SUCCEEDED(hrc))
+ {
+ progress.queryInterfaceTo(aProgress);
+
+ /* create the networkInterfaceHelperClient() argument */
+ NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData();
+
+ d->msgCode = SVCHlpMsg::RemoveHostOnlyNetworkInterface;
+ d->guid = aId;
+
+ hrc = pVirtualBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */,
+ netIfNetworkInterfaceHelperClient,
+ static_cast<void *>(d),
+ progress);
+ /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */
+
+ }
+ }
+
+ return Global::vboxStatusCodeFromCOM(hrc);
+#endif
+}
+
+int NetIfEnableStaticIpConfig(VirtualBox *pVBox, HostNetworkInterface * pIf, ULONG aOldIp, ULONG ip, ULONG mask)
+{
+ RT_NOREF(aOldIp);
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ Bstr guid;
+ HRESULT hrc = pIf->COMGETTER(Id)(guid.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+// ComPtr<VirtualBox> pVBox;
+// hrc = pIf->getVirtualBox(pVBox.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ /* create a progress object */
+ ComObjPtr<Progress> progress;
+ progress.createObject();
+// ComPtr<IHost> host;
+// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf,
+ Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(),
+ FALSE /* aCancelable */);
+ if (SUCCEEDED(hrc))
+ {
+ if (FAILED(hrc)) return hrc;
+// progress.queryInterfaceTo(aProgress);
+
+ /* create the networkInterfaceHelperClient() argument */
+ NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData();
+
+ d->msgCode = SVCHlpMsg::EnableStaticIpConfig;
+ d->guid = Guid(guid);
+ d->iface = pIf;
+ d->u.StaticIP.IPAddress = ip;
+ d->u.StaticIP.IPNetMask = mask;
+
+ hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */,
+ netIfNetworkInterfaceHelperClient,
+ static_cast<void *>(d),
+ progress);
+ /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */
+
+ if (SUCCEEDED(hrc))
+ {
+ progress->WaitForCompletion(-1);
+ }
+ }
+ }
+ }
+ }
+
+ return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+#endif
+}
+
+int NetIfEnableStaticIpConfigV6(VirtualBox *pVBox, HostNetworkInterface * pIf, const Utf8Str &aOldIPV6Address,
+ const Utf8Str &aIPV6Address, ULONG aIPV6MaskPrefixLength)
+{
+ RT_NOREF(aOldIPV6Address);
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ Bstr guid;
+ HRESULT hrc = pIf->COMGETTER(Id)(guid.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+// ComPtr<VirtualBox> pVBox;
+// hrc = pIf->getVirtualBox(pVBox.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ /* create a progress object */
+ ComObjPtr<Progress> progress;
+ progress.createObject();
+// ComPtr<IHost> host;
+// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf,
+ Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(),
+ FALSE /* aCancelable */);
+ if (SUCCEEDED(hrc))
+ {
+ if (FAILED(hrc)) return hrc;
+// progress.queryInterfaceTo(aProgress);
+
+ /* create the networkInterfaceHelperClient() argument */
+ NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData();
+
+ d->msgCode = SVCHlpMsg::EnableStaticIpConfigV6;
+ d->guid = guid;
+ d->iface = pIf;
+ d->u.StaticIPV6.IPV6Address = RTStrDup(aIPV6Address.c_str());
+ d->u.StaticIPV6.IPV6NetMaskLength = aIPV6MaskPrefixLength;
+
+ hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */,
+ netIfNetworkInterfaceHelperClient,
+ static_cast<void *>(d),
+ progress);
+ /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */
+
+ if (SUCCEEDED(hrc))
+ {
+ progress->WaitForCompletion(-1);
+ }
+ }
+ }
+ }
+ }
+
+ return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+#endif
+}
+
+int NetIfEnableDynamicIpConfig(VirtualBox *pVBox, HostNetworkInterface * pIf)
+{
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ HRESULT hrc;
+ Bstr guid;
+ hrc = pIf->COMGETTER(Id)(guid.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+// ComPtr<VirtualBox> pVBox;
+// hrc = pIf->getVirtualBox(pVBox.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ /* create a progress object */
+ ComObjPtr<Progress> progress;
+ progress.createObject();
+// ComPtr<IHost> host;
+// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf,
+ Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(),
+ FALSE /* aCancelable */);
+ if (SUCCEEDED(hrc))
+ {
+ if (FAILED(hrc)) return hrc;
+// progress.queryInterfaceTo(aProgress);
+
+ /* create the networkInterfaceHelperClient() argument */
+ NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData();
+
+ d->msgCode = SVCHlpMsg::EnableDynamicIpConfig;
+ d->guid = guid;
+ d->iface = pIf;
+
+ hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */,
+ netIfNetworkInterfaceHelperClient,
+ static_cast<void *>(d),
+ progress);
+ /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */
+
+ if (SUCCEEDED(hrc))
+ {
+ progress->WaitForCompletion(-1);
+ }
+ }
+ }
+ }
+ }
+
+ return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+#endif
+}
+
+int NetIfDhcpRediscover(VirtualBox *pVBox, HostNetworkInterface * pIf)
+{
+#ifndef VBOX_WITH_NETFLT
+ return VERR_NOT_IMPLEMENTED;
+#else
+ HRESULT hrc;
+ Bstr guid;
+ hrc = pIf->COMGETTER(Id)(guid.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+// ComPtr<VirtualBox> pVBox;
+// hrc = pIf->getVirtualBox(pVBox.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ /* create a progress object */
+ ComObjPtr<Progress> progress;
+ progress.createObject();
+// ComPtr<IHost> host;
+// HRESULT hrc = pVBox->COMGETTER(Host)(host.asOutParam());
+// if (SUCCEEDED(hrc))
+ {
+ hrc = progress->init(pVBox, (IHostNetworkInterface*)pIf,
+ Bstr(NetIfWin::tr("Enabling Dynamic Ip Configuration")).raw(),
+ FALSE /* aCancelable */);
+ if (SUCCEEDED(hrc))
+ {
+ if (FAILED(hrc)) return hrc;
+// progress.queryInterfaceTo(aProgress);
+
+ /* create the networkInterfaceHelperClient() argument */
+ NetworkInterfaceHelperClientData* d = new NetworkInterfaceHelperClientData();
+
+ d->msgCode = SVCHlpMsg::DhcpRediscover;
+ d->guid = guid;
+ d->iface = pIf;
+
+ hrc = pVBox->i_startSVCHelperClient(IsUACEnabled() == TRUE /* aPrivileged */,
+ netIfNetworkInterfaceHelperClient,
+ static_cast<void *>(d),
+ progress);
+ /* d is now owned by netIfNetworkInterfaceHelperClient(), no need to delete one here */
+
+ if (SUCCEEDED(hrc))
+ {
+ progress->WaitForCompletion(-1);
+ }
+ }
+ }
+ }
+ }
+
+ return SUCCEEDED(hrc) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+#endif
+}
+
+
+#define netIfLog LogFunc
+
+struct BoundAdapter
+{
+ LPWSTR pName;
+ LPWSTR pHwId;
+ RTUUID guid;
+ PIP_ADAPTER_ADDRESSES pAdapter;
+ BOOL fWireless;
+};
+
+static int netIfGetUnboundHostOnlyAdapters(INetCfg *pNetCfg, std::list<BoundAdapter> &adapters)
+{
+ IEnumNetCfgComponent *pEnumComponent;
+ HRESULT hrc = pNetCfg->EnumComponents(&GUID_DEVCLASS_NET, &pEnumComponent);
+ if (hrc != S_OK)
+ LogRelFunc(("failed to enumerate network adapter components (0x%x)\n", hrc));
+ else
+ {
+ INetCfgComponent *pMiniport;
+ while ((hrc = pEnumComponent->Next(1, &pMiniport, NULL)) == S_OK)
+ {
+ GUID guid;
+ ULONG uComponentStatus;
+ struct BoundAdapter adapter;
+ memset(&adapter, 0, sizeof(adapter));
+ if ((hrc = pMiniport->GetDisplayName(&adapter.pName)) != S_OK)
+ LogRelFunc(("failed to get device display name (0x%x)\n", hrc));
+ else if ((hrc = pMiniport->GetDeviceStatus(&uComponentStatus)) != S_OK)
+ netIfLog(("failed to get device status (0x%x)\n", hrc));
+ else if (uComponentStatus != 0)
+ netIfLog(("wrong device status (0x%x)\n", uComponentStatus));
+ else if ((hrc = pMiniport->GetId(&adapter.pHwId)) != S_OK)
+ LogRelFunc(("failed to get device id (0x%x)\n", hrc));
+ else if (_wcsnicmp(adapter.pHwId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2))
+ netIfLog(("not host-only id = %ls, ignored\n", adapter.pHwId));
+ else if ((hrc = pMiniport->GetInstanceGuid(&guid)) != S_OK)
+ LogRelFunc(("failed to get instance id (0x%x)\n", hrc));
+ else
+ {
+ adapter.guid = *(Guid(guid).raw());
+ netIfLog(("guid=%RTuuid, name=%ls id = %ls\n", &adapter.guid, adapter.pName, adapter.pHwId));
+ adapters.push_back(adapter);
+ adapter.pName = adapter.pHwId = NULL; /* do not free, will be done later */
+ }
+ if (adapter.pHwId)
+ CoTaskMemFree(adapter.pHwId);
+ if (adapter.pName)
+ CoTaskMemFree(adapter.pName);
+ pMiniport->Release();
+ }
+ Assert(hrc == S_OK || hrc == S_FALSE);
+
+ pEnumComponent->Release();
+ }
+ netIfLog(("return\n"));
+ return VINF_SUCCESS;
+}
+
+#define DEVNAME_PREFIX L"\\\\.\\"
+
+static BOOL netIfIsWireless(INetCfgComponent *pAdapter)
+{
+ bool fWireless = false;
+
+ /* Construct a device name. */
+ LPWSTR pwszBindName = NULL;
+ HRESULT hrc = pAdapter->GetBindName(&pwszBindName);
+ if (SUCCEEDED(hrc) && pwszBindName)
+ {
+ WCHAR wszFileName[MAX_PATH];
+ int vrc = RTUtf16Copy(wszFileName, MAX_PATH, DEVNAME_PREFIX);
+ if (RT_SUCCESS(vrc))
+ vrc = RTUtf16Cat(wszFileName, MAX_PATH, pwszBindName);
+ if (RT_SUCCESS(vrc))
+ {
+ /* open the device */
+ HANDLE hDevice = CreateFileW(wszFileName,
+ GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ if (hDevice != INVALID_HANDLE_VALUE)
+ {
+ /* now issue the OID_GEN_PHYSICAL_MEDIUM query */
+ DWORD Oid = OID_GEN_PHYSICAL_MEDIUM;
+ NDIS_PHYSICAL_MEDIUM PhMedium = NdisPhysicalMediumUnspecified;
+ DWORD cbResultIgn = 0;
+ if (DeviceIoControl(hDevice,
+ IOCTL_NDIS_QUERY_GLOBAL_STATS,
+ &Oid,
+ sizeof(Oid),
+ &PhMedium,
+ sizeof(PhMedium),
+ &cbResultIgn,
+ NULL))
+ {
+ /* that was simple, now examine PhMedium */
+ fWireless = PhMedium == NdisPhysicalMediumWirelessWan
+ || PhMedium == NdisPhysicalMediumWirelessLan
+ || PhMedium == NdisPhysicalMediumNative802_11
+ || PhMedium == NdisPhysicalMediumBluetooth;
+ }
+ else
+ {
+ DWORD rcWin = GetLastError();
+ LogRel(("netIfIsWireless: DeviceIoControl to '%ls' failed with rcWin=%u (%#x) - ignoring\n",
+ wszFileName, rcWin, rcWin));
+ Assert(rcWin == ERROR_INVALID_PARAMETER || rcWin == ERROR_NOT_SUPPORTED || rcWin == ERROR_BAD_COMMAND);
+ }
+ CloseHandle(hDevice);
+ }
+ else
+ {
+ DWORD rcWin = GetLastError();
+#if 0 /* bird: Triggers on each VBoxSVC startup so, disabled. Whoever want it, can enable using DEBUG_xxxx. */
+ AssertLogRelMsgFailed(("netIfIsWireless: CreateFile on '%ls' failed with rcWin=%u (%#x) - ignoring\n",
+ wszFileName, rcWin, rcWin));
+#else
+ LogRel(("netIfIsWireless: CreateFile on '%ls' failed with rcWin=%u (%#x) - ignoring\n",
+ wszFileName, rcWin, rcWin));
+#endif
+ }
+ }
+ CoTaskMemFree(pwszBindName);
+ }
+ else
+ LogRel(("netIfIsWireless: GetBindName failed hrc=%Rhrc\n", hrc));
+
+ return fWireless;
+}
+
+static HRESULT netIfGetBoundAdapters(std::list<BoundAdapter> &boundAdapters)
+{
+
+ netIfLog(("building the list of interfaces\n"));
+ /* we are using the INetCfg API for getting the list of miniports */
+ INetCfg *pNetCfg = NULL;
+ LPWSTR lpszApp = NULL;
+ HRESULT hrc = VBoxNetCfgWinQueryINetCfg(&pNetCfg, FALSE, VBOX_APP_NAME, 10000, &lpszApp);
+ Assert(hrc == S_OK);
+ if (hrc != S_OK)
+ {
+ LogRelFunc(("failed to query INetCfg (0x%x)\n", hrc));
+ return hrc;
+ }
+
+ INetCfgComponent *pFilter;
+ if ((hrc = pNetCfg->FindComponent(L"oracle_VBoxNetLwf", &pFilter)) != S_OK
+ /* fall back to NDIS5 miniport lookup */
+ && (hrc = pNetCfg->FindComponent(L"sun_VBoxNetFlt", &pFilter)))
+ LogRelFunc(("could not find either 'oracle_VBoxNetLwf' or 'sun_VBoxNetFlt' components (0x%x)\n", hrc));
+ else
+ {
+ INetCfgComponentBindings *pFilterBindings;
+ if ((pFilter->QueryInterface(IID_INetCfgComponentBindings, (PVOID*)&pFilterBindings)) != S_OK)
+ LogRelFunc(("failed to query INetCfgComponentBindings (0x%x)\n", hrc));
+ else
+ {
+ IEnumNetCfgBindingPath *pEnumBp;
+ INetCfgBindingPath *pBp;
+ if ((pFilterBindings->EnumBindingPaths(EBP_BELOW, &pEnumBp)) != S_OK)
+ LogRelFunc(("failed to enumerate binding paths (0x%x)\n", hrc));
+ else
+ {
+ pEnumBp->Reset();
+ while ((hrc = pEnumBp->Next(1, &pBp, NULL)) == S_OK)
+ {
+ IEnumNetCfgBindingInterface *pEnumBi;
+ INetCfgBindingInterface *pBi;
+ if (pBp->IsEnabled() != S_OK)
+ {
+ /** @todo some id of disabled path could be useful. */
+ netIfLog(("INetCfgBindingPath is disabled (0x%x)\n", hrc));
+ pBp->Release();
+ continue;
+ }
+ if ((pBp->EnumBindingInterfaces(&pEnumBi)) != S_OK)
+ LogRelFunc(("failed to enumerate binding interfaces (0x%x)\n", hrc));
+ else
+ {
+ hrc = pEnumBi->Reset();
+ while ((hrc = pEnumBi->Next(1, &pBi, NULL)) == S_OK)
+ {
+ INetCfgComponent *pAdapter;
+ if ((hrc = pBi->GetLowerComponent(&pAdapter)) != S_OK)
+ LogRelFunc(("failed to get lower component (0x%x)\n", hrc));
+ else
+ {
+ LPWSTR pwszName = NULL;
+ if ((hrc = pAdapter->GetDisplayName(&pwszName)) != S_OK)
+ LogRelFunc(("failed to get display name (0x%x)\n", hrc));
+ else
+ {
+ ULONG uStatus;
+ DWORD dwChars;
+ if ((hrc = pAdapter->GetDeviceStatus(&uStatus)) != S_OK)
+ netIfLog(("%ls: failed to get device status (0x%x)\n",
+ pwszName, hrc));
+ else if ((hrc = pAdapter->GetCharacteristics(&dwChars)) != S_OK)
+ netIfLog(("%ls: failed to get device characteristics (0x%x)\n",
+ pwszName, hrc));
+ else if (uStatus != 0)
+ netIfLog(("%ls: wrong status 0x%x\n",
+ pwszName, uStatus));
+ else if (dwChars & NCF_HIDDEN)
+ netIfLog(("%ls: wrong characteristics 0x%x\n",
+ pwszName, dwChars));
+ else
+ {
+ GUID guid;
+ LPWSTR pwszHwId = NULL;
+ if ((hrc = pAdapter->GetId(&pwszHwId)) != S_OK)
+ LogRelFunc(("%ls: failed to get hardware id (0x%x)\n",
+ pwszName, hrc));
+ else if (!_wcsnicmp(pwszHwId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp")/2))
+ netIfLog(("host-only adapter %ls, ignored\n", pwszName));
+ else if ((hrc = pAdapter->GetInstanceGuid(&guid)) != S_OK)
+ LogRelFunc(("%ls: failed to get instance GUID (0x%x)\n",
+ pwszName, hrc));
+ else
+ {
+ struct BoundAdapter adapter;
+ adapter.pName = pwszName;
+ adapter.pHwId = pwszHwId;
+ adapter.guid = *(Guid(guid).raw());
+ adapter.pAdapter = NULL;
+ adapter.fWireless = netIfIsWireless(pAdapter);
+ netIfLog(("guid=%RTuuid, name=%ls, hwid=%ls, status=%x, chars=%x\n",
+ &adapter.guid, pwszName, pwszHwId, uStatus, dwChars));
+ boundAdapters.push_back(adapter);
+ pwszName = pwszHwId = NULL; /* do not free, will be done later */
+ }
+ if (pwszHwId)
+ CoTaskMemFree(pwszHwId);
+ }
+ if (pwszName)
+ CoTaskMemFree(pwszName);
+ }
+
+ pAdapter->Release();
+ }
+ pBi->Release();
+ }
+ pEnumBi->Release();
+ }
+ pBp->Release();
+ }
+ pEnumBp->Release();
+ }
+ pFilterBindings->Release();
+ }
+ pFilter->Release();
+ }
+ /* Host-only adapters are not necessarily bound, add them separately. */
+ netIfGetUnboundHostOnlyAdapters(pNetCfg, boundAdapters);
+ VBoxNetCfgWinReleaseINetCfg(pNetCfg, FALSE);
+
+ return S_OK;
+}
+
+#if 0
+static HRESULT netIfGetBoundAdaptersFallback(std::list<BoundAdapter> &boundAdapters)
+{
+ return CO_E_NOT_SUPPORTED;
+}
+#endif
+
+/**
+ * Walk through the list of adpater addresses and extract the required
+ * information. XP and older don't not have the OnLinkPrefixLength field.
+ */
+static void netIfFillInfoWithAddressesXp(PNETIFINFO pInfo, PIP_ADAPTER_ADDRESSES pAdapter)
+{
+ PIP_ADAPTER_UNICAST_ADDRESS pAddr;
+ bool fIPFound = false;
+ bool fIPv6Found = false;
+ for (pAddr = pAdapter->FirstUnicastAddress; pAddr; pAddr = pAddr->Next)
+ {
+ switch (pAddr->Address.lpSockaddr->sa_family)
+ {
+ case AF_INET:
+ if (!fIPFound)
+ {
+ fIPFound = true;
+ memcpy(&pInfo->IPAddress,
+ &((struct sockaddr_in *)pAddr->Address.lpSockaddr)->sin_addr.s_addr,
+ sizeof(pInfo->IPAddress));
+ }
+ break;
+ case AF_INET6:
+ if (!fIPv6Found)
+ {
+ fIPv6Found = true;
+ memcpy(&pInfo->IPv6Address,
+ ((struct sockaddr_in6 *)pAddr->Address.lpSockaddr)->sin6_addr.s6_addr,
+ sizeof(pInfo->IPv6Address));
+ }
+ break;
+ }
+ }
+ PIP_ADAPTER_PREFIX pPrefix;
+ ULONG uPrefixLenV4 = 0;
+ ULONG uPrefixLenV6 = 0;
+ for (pPrefix = pAdapter->FirstPrefix; pPrefix && !(uPrefixLenV4 && uPrefixLenV6); pPrefix = pPrefix->Next)
+ {
+ switch (pPrefix->Address.lpSockaddr->sa_family)
+ {
+ case AF_INET:
+ if (!uPrefixLenV4)
+ {
+ ULONG ip = ((PSOCKADDR_IN)(pPrefix->Address.lpSockaddr))->sin_addr.s_addr;
+ netIfLog(("prefix=%RTnaipv4 len=%u\n", ip, pPrefix->PrefixLength));
+ if ( pPrefix->PrefixLength < sizeof(pInfo->IPNetMask) * 8
+ && pPrefix->PrefixLength > 0
+ && (ip & 0xF0) < 224)
+ {
+ uPrefixLenV4 = pPrefix->PrefixLength;
+ RTNetPrefixToMaskIPv4(pPrefix->PrefixLength, &pInfo->IPNetMask);
+ }
+ else
+ netIfLog(("Unexpected IPv4 prefix length of %d\n",
+ pPrefix->PrefixLength));
+ }
+ break;
+ case AF_INET6:
+ if (!uPrefixLenV6)
+ {
+ PBYTE ipv6 = ((PSOCKADDR_IN6)(pPrefix->Address.lpSockaddr))->sin6_addr.s6_addr;
+ netIfLog(("prefix=%RTnaipv6 len=%u\n", ipv6, pPrefix->PrefixLength));
+ if ( pPrefix->PrefixLength < sizeof(pInfo->IPv6NetMask) * 8
+ && pPrefix->PrefixLength > 0
+ && ipv6[0] != 0xFF)
+ {
+ uPrefixLenV6 = pPrefix->PrefixLength;
+ RTNetPrefixToMaskIPv6(pPrefix->PrefixLength, &pInfo->IPv6NetMask);
+ }
+ else
+ netIfLog(("Unexpected IPv6 prefix length of %d\n", pPrefix->PrefixLength));
+ }
+ break;
+ }
+ }
+ netIfLog(("%RTnaipv4/%u\n", pInfo->IPAddress, uPrefixLenV4));
+ netIfLog(("%RTnaipv6/%u\n", &pInfo->IPv6Address, uPrefixLenV6));
+}
+
+/**
+ * Walk through the list of adpater addresses and extract the required
+ * information. XP and older don't not have the OnLinkPrefixLength field.
+ */
+static void netIfFillInfoWithAddressesVista(PNETIFINFO pInfo, PIP_ADAPTER_ADDRESSES pAdapter)
+{
+ PIP_ADAPTER_UNICAST_ADDRESS pAddr;
+
+ if (sizeof(pInfo->MACAddress) != pAdapter->PhysicalAddressLength)
+ netIfLog(("Unexpected physical address length: %u\n", pAdapter->PhysicalAddressLength));
+ else
+ memcpy(pInfo->MACAddress.au8, pAdapter->PhysicalAddress, sizeof(pInfo->MACAddress));
+
+ bool fIPFound = false;
+ bool fIPv6Found = false;
+ for (pAddr = pAdapter->FirstUnicastAddress; pAddr; pAddr = pAddr->Next)
+ {
+ PIP_ADAPTER_UNICAST_ADDRESS_LH pAddrLh = (PIP_ADAPTER_UNICAST_ADDRESS_LH)pAddr;
+ switch (pAddrLh->Address.lpSockaddr->sa_family)
+ {
+ case AF_INET:
+ if (!fIPFound)
+ {
+ fIPFound = true;
+ memcpy(&pInfo->IPAddress,
+ &((struct sockaddr_in *)pAddrLh->Address.lpSockaddr)->sin_addr.s_addr,
+ sizeof(pInfo->IPAddress));
+ if (pAddrLh->OnLinkPrefixLength > 32)
+ netIfLog(("Invalid IPv4 prefix length of %d\n", pAddrLh->OnLinkPrefixLength));
+ else
+ RTNetPrefixToMaskIPv4(pAddrLh->OnLinkPrefixLength, &pInfo->IPNetMask);
+ }
+ break;
+ case AF_INET6:
+ if (!fIPv6Found)
+ {
+ fIPv6Found = true;
+ memcpy(&pInfo->IPv6Address,
+ ((struct sockaddr_in6 *)pAddrLh->Address.lpSockaddr)->sin6_addr.s6_addr,
+ sizeof(pInfo->IPv6Address));
+ if (pAddrLh->OnLinkPrefixLength > 128)
+ netIfLog(("Invalid IPv6 prefix length of %d\n", pAddrLh->OnLinkPrefixLength));
+ else
+ RTNetPrefixToMaskIPv6(pAddrLh->OnLinkPrefixLength, &pInfo->IPv6NetMask);
+ }
+ break;
+ }
+ }
+
+ if (fIPFound)
+ {
+ int iPrefixIPv4 = -1;
+ RTNetMaskToPrefixIPv4(&pInfo->IPNetMask, &iPrefixIPv4);
+ netIfLog(("%RTnaipv4/%u\n", pInfo->IPAddress, iPrefixIPv4));
+ }
+ if (fIPv6Found)
+ {
+ int iPrefixIPv6 = -1;
+ RTNetMaskToPrefixIPv6(&pInfo->IPv6NetMask, &iPrefixIPv6);
+ netIfLog(("%RTnaipv6/%u\n", &pInfo->IPv6Address, iPrefixIPv6));
+ }
+}
+
+#if (NTDDI_VERSION >= NTDDI_VISTA)
+#define NETIF_GAA_FLAGS GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
+#else /* (NTDDI_VERSION < NTDDI_VISTA) */
+#define NETIF_GAA_FLAGS GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST
+#endif /* (NTDDI_VERSION < NTDDI_VISTA) */
+
+int NetIfList(std::list<ComObjPtr<HostNetworkInterface> > &list)
+{
+ int iDefault = getDefaultInterfaceIndex();
+ /* MSDN recommends to pre-allocate a 15KB buffer. */
+ ULONG uBufLen = 15 * 1024;
+ PIP_ADAPTER_ADDRESSES pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen);
+ if (!pAddresses)
+ return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
+ DWORD dwRc = GetAdaptersAddresses(AF_UNSPEC, NETIF_GAA_FLAGS, NULL, pAddresses, &uBufLen);
+ for (int tries = 0; tries < 3 && dwRc == ERROR_BUFFER_OVERFLOW; ++tries)
+ {
+ /* Get more memory and try again. */
+ RTMemFree(pAddresses);
+ pAddresses = (PIP_ADAPTER_ADDRESSES)RTMemAlloc(uBufLen);
+ if (!pAddresses)
+ return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
+ dwRc = GetAdaptersAddresses(AF_UNSPEC, NETIF_GAA_FLAGS, NULL, pAddresses, &uBufLen);
+ }
+ HRESULT hrc;
+ if (dwRc != NO_ERROR)
+ {
+ LogRelFunc(("GetAdaptersAddresses failed (0x%x)\n", dwRc));
+ hrc = HRESULT_FROM_WIN32(dwRc);
+ }
+ else
+ {
+ std::list<BoundAdapter> boundAdapters;
+ hrc = netIfGetBoundAdapters(boundAdapters);
+#if 0
+ if (hrc != S_OK)
+ hrc = netIfGetBoundAdaptersFallback(boundAdapters);
+#endif
+ if (hrc != S_OK)
+ LogRelFunc(("netIfGetBoundAdapters failed (0x%x)\n", hrc));
+ else
+ {
+ PIP_ADAPTER_ADDRESSES pAdapter;
+
+ for (pAdapter = pAddresses; pAdapter; pAdapter = pAdapter->Next)
+ {
+ char *pszUuid = RTStrDup(pAdapter->AdapterName);
+ if (!pszUuid)
+ {
+ LogRelFunc(("out of memory\n"));
+ break;
+ }
+ size_t len = strlen(pszUuid) - 1;
+ if (pszUuid[0] != '{' || pszUuid[len] != '}')
+ LogRelFunc(("ignoring invalid GUID %s\n", pAdapter->AdapterName));
+ else
+ {
+ std::list<BoundAdapter>::iterator it;
+ pszUuid[len] = 0;
+ for (it = boundAdapters.begin(); it != boundAdapters.end(); ++it)
+ {
+ if (!RTUuidCompareStr(&(*it).guid, pszUuid + 1))
+ {
+ (*it).pAdapter = pAdapter;
+ break;
+ }
+ }
+ }
+ RTStrFree(pszUuid);
+ }
+ std::list<BoundAdapter>::iterator it;
+ for (it = boundAdapters.begin(); it != boundAdapters.end(); ++it)
+ {
+ NETIFINFO info;
+ memset(&info, 0, sizeof(info));
+ info.Uuid = (*it).guid;
+ info.enmMediumType = NETIF_T_ETHERNET;
+ info.fWireless = (*it).fWireless;
+ pAdapter = (*it).pAdapter;
+ if (pAdapter)
+ {
+ info.enmStatus = pAdapter->OperStatus == IfOperStatusUp ? NETIF_S_UP : NETIF_S_DOWN;
+ info.fIsDefault = (pAdapter->IfIndex == (DWORD)iDefault);
+ info.fDhcpEnabled = pAdapter->Flags & IP_ADAPTER_DHCP_ENABLED;
+ OSVERSIONINFOEX OSInfoEx;
+ RT_ZERO(OSInfoEx);
+ OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ if ( GetVersionEx((LPOSVERSIONINFO)&OSInfoEx)
+ && OSInfoEx.dwMajorVersion < 6)
+ netIfFillInfoWithAddressesXp(&info, pAdapter);
+ else
+ netIfFillInfoWithAddressesVista(&info, pAdapter);
+ }
+ else
+ info.enmStatus = NETIF_S_DOWN;
+ /* create a new object and add it to the list */
+ ComObjPtr<HostNetworkInterface> iface;
+ iface.createObject();
+ HostNetworkInterfaceType enmType = _wcsnicmp((*it).pHwId, L"sun_VBoxNetAdp", sizeof(L"sun_VBoxNetAdp") / 2)
+ ? HostNetworkInterfaceType_Bridged : HostNetworkInterfaceType_HostOnly;
+ netIfLog(("Adding %ls as %s\n", (*it).pName,
+ enmType == HostNetworkInterfaceType_Bridged ? "bridged" :
+ enmType == HostNetworkInterfaceType_HostOnly ? "host-only" : "unknown"));
+ hrc = iface->init((*it).pName, enmType, &info);
+ if (FAILED(hrc))
+ LogRelFunc(("HostNetworkInterface::init() -> %Rrc\n", hrc));
+ else
+ {
+ if (info.fIsDefault)
+ list.push_front(iface);
+ else
+ list.push_back(iface);
+ }
+ if ((*it).pHwId)
+ CoTaskMemFree((*it).pHwId);
+ if ((*it).pName)
+ CoTaskMemFree((*it).pName);
+ }
+ }
+ }
+ RTMemFree(pAddresses);
+
+ return hrc;
+}
diff --git a/src/VBox/Main/src-server/win/PerformanceWin.cpp b/src/VBox/Main/src-server/win/PerformanceWin.cpp
new file mode 100644
index 00000000..c7ae3335
--- /dev/null
+++ b/src/VBox/Main/src-server/win/PerformanceWin.cpp
@@ -0,0 +1,356 @@
+/* $Id: PerformanceWin.cpp $ */
+/** @file
+ * VBox Windows-specific Performance Classes implementation.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN_PERFORMANCECOLLECTOR
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+#else /* !_WIN32_WINNT */
+# if (_WIN32_WINNT < 0x0500)
+# error Win XP or later required!
+# endif /* _WIN32_WINNT < 0x0500 */
+#endif /* !_WIN32_WINNT */
+
+#include <iprt/win/windows.h>
+#include <winternl.h>
+#include <psapi.h>
+extern "C" {
+#include <powrprof.h>
+}
+
+#include <iprt/errcore.h>
+#include <iprt/ldr.h>
+#include <iprt/mp.h>
+#include <iprt/mem.h>
+#include <iprt/system.h>
+
+#include <map>
+
+#include "LoggingNew.h"
+#include "Performance.h"
+
+#ifndef NT_ERROR
+#define NT_ERROR(Status) ((ULONG)(Status) >> 30 == 3)
+#endif
+
+namespace pm {
+
+class CollectorWin : public CollectorHAL
+{
+public:
+ CollectorWin();
+ virtual ~CollectorWin();
+ virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */);
+ virtual int getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle);
+ virtual int getHostCpuMHz(ULONG *mhz);
+ virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available);
+ virtual int getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel);
+ virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used);
+
+ virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle);
+ virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total);
+
+private:
+ struct VMProcessStats
+ {
+ uint64_t cpuUser;
+ uint64_t cpuKernel;
+ uint64_t cpuTotal;
+ uint64_t ramUsed;
+ };
+
+ typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap;
+
+ VMProcessMap mProcessStats;
+
+ typedef BOOL (WINAPI *PFNGST)(LPFILETIME lpIdleTime,
+ LPFILETIME lpKernelTime,
+ LPFILETIME lpUserTime);
+ typedef NTSTATUS (WINAPI *PFNNQSI)(SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength,
+ PULONG ReturnLength);
+
+ PFNGST mpfnGetSystemTimes;
+ PFNNQSI mpfnNtQuerySystemInformation;
+
+ ULONG totalRAM;
+};
+
+CollectorHAL *createHAL()
+{
+ return new CollectorWin();
+}
+
+CollectorWin::CollectorWin() : CollectorHAL(), mpfnNtQuerySystemInformation(NULL)
+{
+ /* Note! Both kernel32.dll and ntdll.dll can be assumed to always be present. */
+ mpfnGetSystemTimes = (PFNGST)RTLdrGetSystemSymbol("kernel32.dll", "GetSystemTimes");
+ if (!mpfnGetSystemTimes)
+ {
+ /* Fall back to deprecated NtQuerySystemInformation */
+ mpfnNtQuerySystemInformation = (PFNNQSI)RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation");
+ if (!mpfnNtQuerySystemInformation)
+ LogRel(("Warning! Neither GetSystemTimes() nor NtQuerySystemInformation() is not available.\n"
+ " CPU and VM metrics will not be collected! (lasterr %u)\n", GetLastError()));
+ }
+
+ uint64_t cb;
+ int vrc = RTSystemQueryTotalRam(&cb);
+ if (RT_FAILURE(vrc))
+ totalRAM = 0;
+ else
+ totalRAM = (ULONG)(cb / 1024);
+}
+
+CollectorWin::~CollectorWin()
+{
+}
+
+#define FILETTIME_TO_100NS(ft) (((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime)
+
+int CollectorWin::preCollect(const CollectorHints& hints, uint64_t /* iTick */)
+{
+ LogFlowThisFuncEnter();
+
+ uint64_t user, kernel, idle, total;
+ int vrc = getRawHostCpuLoad(&user, &kernel, &idle);
+ if (RT_FAILURE(vrc))
+ return vrc;
+ total = user + kernel + idle;
+
+ DWORD dwError;
+ const CollectorHints::ProcessList& processes = hints.getProcessFlags();
+ CollectorHints::ProcessList::const_iterator it;
+
+ mProcessStats.clear();
+
+ for (it = processes.begin(); it != processes.end() && RT_SUCCESS(vrc); ++it)
+ {
+ RTPROCESS process = it->first;
+ HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, process);
+
+ if (!h)
+ {
+ dwError = GetLastError();
+ Log (("OpenProcess() -> 0x%x\n", dwError));
+ vrc = RTErrConvertFromWin32(dwError);
+ break;
+ }
+
+ VMProcessStats vmStats;
+ RT_ZERO(vmStats);
+ if ((it->second & COLLECT_CPU_LOAD) != 0)
+ {
+ FILETIME ftCreate, ftExit, ftKernel, ftUser;
+ if (!GetProcessTimes(h, &ftCreate, &ftExit, &ftKernel, &ftUser))
+ {
+ dwError = GetLastError();
+ Log (("GetProcessTimes() -> 0x%x\n", dwError));
+ vrc = RTErrConvertFromWin32(dwError);
+ }
+ else
+ {
+ vmStats.cpuKernel = FILETTIME_TO_100NS(ftKernel);
+ vmStats.cpuUser = FILETTIME_TO_100NS(ftUser);
+ vmStats.cpuTotal = total;
+ }
+ }
+ if (RT_SUCCESS(vrc) && (it->second & COLLECT_RAM_USAGE) != 0)
+ {
+ PROCESS_MEMORY_COUNTERS pmc;
+ if (!GetProcessMemoryInfo(h, &pmc, sizeof(pmc)))
+ {
+ dwError = GetLastError();
+ Log (("GetProcessMemoryInfo() -> 0x%x\n", dwError));
+ vrc = RTErrConvertFromWin32(dwError);
+ }
+ else
+ vmStats.ramUsed = pmc.WorkingSetSize;
+ }
+ CloseHandle(h);
+ mProcessStats[process] = vmStats;
+ }
+
+ LogFlowThisFuncLeave();
+
+ return vrc;
+}
+
+int CollectorWin::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle)
+{
+ RT_NOREF(user, kernel, idle);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION
+{
+ LARGE_INTEGER IdleTime;
+ LARGE_INTEGER KernelTime;
+ LARGE_INTEGER UserTime;
+ LARGE_INTEGER Reserved1[2];
+ ULONG Reserved2;
+} SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION;
+
+int CollectorWin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle)
+{
+ LogFlowThisFuncEnter();
+
+ FILETIME ftIdle, ftKernel, ftUser;
+
+ if (mpfnGetSystemTimes)
+ {
+ if (!mpfnGetSystemTimes(&ftIdle, &ftKernel, &ftUser))
+ {
+ DWORD dwError = GetLastError();
+ Log (("GetSystemTimes() -> 0x%x\n", dwError));
+ return RTErrConvertFromWin32(dwError);
+ }
+
+ *user = FILETTIME_TO_100NS(ftUser);
+ *idle = FILETTIME_TO_100NS(ftIdle);
+ *kernel = FILETTIME_TO_100NS(ftKernel) - *idle;
+ }
+ else
+ {
+ /* GetSystemTimes is not available, fall back to NtQuerySystemInformation */
+ if (!mpfnNtQuerySystemInformation)
+ return VERR_NOT_IMPLEMENTED;
+
+ SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION sppi[MAXIMUM_PROCESSORS];
+ ULONG ulReturned;
+ NTSTATUS status = mpfnNtQuerySystemInformation(
+ SystemProcessorPerformanceInformation, &sppi, sizeof(sppi), &ulReturned);
+ if (NT_ERROR(status))
+ {
+ Log(("NtQuerySystemInformation() -> 0x%x\n", status));
+ return RTErrConvertFromNtStatus(status);
+ }
+ /* Sum up values across all processors */
+ *user = *kernel = *idle = 0;
+ for (unsigned i = 0; i < ulReturned / sizeof(sppi[0]); ++i)
+ {
+ *idle += sppi[i].IdleTime.QuadPart;
+ *kernel += sppi[i].KernelTime.QuadPart - sppi[i].IdleTime.QuadPart;
+ *user += sppi[i].UserTime.QuadPart;
+ }
+ }
+
+ LogFlowThisFunc(("user=%lu kernel=%lu idle=%lu\n", *user, *kernel, *idle));
+ LogFlowThisFuncLeave();
+
+ return VINF_SUCCESS;
+}
+
+typedef struct _PROCESSOR_POWER_INFORMATION {
+ ULONG Number;
+ ULONG MaxMhz;
+ ULONG CurrentMhz;
+ ULONG MhzLimit;
+ ULONG MaxIdleState;
+ ULONG CurrentIdleState;
+} PROCESSOR_POWER_INFORMATION , *PPROCESSOR_POWER_INFORMATION;
+
+int CollectorWin::getHostCpuMHz(ULONG *mhz)
+{
+ uint64_t uTotalMhz = 0;
+ RTCPUID nProcessors = RTMpGetCount();
+ PPROCESSOR_POWER_INFORMATION ppi = (PPROCESSOR_POWER_INFORMATION)
+ RTMemAllocZ(nProcessors * sizeof(PROCESSOR_POWER_INFORMATION));
+
+ if (!ppi)
+ return VERR_NO_MEMORY;
+
+ LONG ns = CallNtPowerInformation(ProcessorInformation, NULL, 0, ppi,
+ nProcessors * sizeof(PROCESSOR_POWER_INFORMATION));
+ if (ns)
+ {
+ Log(("CallNtPowerInformation() -> %x\n", ns));
+ RTMemFree(ppi);
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /* Compute an average over all CPUs */
+ for (unsigned i = 0; i < nProcessors; i++)
+ uTotalMhz += ppi[i].CurrentMhz;
+ *mhz = (ULONG)(uTotalMhz / nProcessors);
+
+ RTMemFree(ppi);
+ LogFlowThisFunc(("mhz=%u\n", *mhz));
+ LogFlowThisFuncLeave();
+
+ return VINF_SUCCESS;
+}
+
+int CollectorWin::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available)
+{
+ AssertReturn(totalRAM, VERR_INTERNAL_ERROR);
+ uint64_t cb;
+ int vrc = RTSystemQueryAvailableRam(&cb);
+ if (RT_SUCCESS(vrc))
+ {
+ *total = totalRAM;
+ *available = (ULONG)(cb / 1024);
+ *used = *total - *available;
+ }
+ return vrc;
+}
+
+int CollectorWin::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel)
+{
+ RT_NOREF(process, user, kernel);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+int CollectorWin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total)
+{
+ VMProcessMap::const_iterator it = mProcessStats.find(process);
+
+ if (it == mProcessStats.end())
+ {
+ Log (("No stats pre-collected for process %x\n", process));
+ return VERR_INTERNAL_ERROR;
+ }
+ *user = it->second.cpuUser;
+ *kernel = it->second.cpuKernel;
+ *total = it->second.cpuTotal;
+ return VINF_SUCCESS;
+}
+
+int CollectorWin::getProcessMemoryUsage(RTPROCESS process, ULONG *used)
+{
+ VMProcessMap::const_iterator it = mProcessStats.find(process);
+
+ if (it == mProcessStats.end())
+ {
+ Log (("No stats pre-collected for process %x\n", process));
+ return VERR_INTERNAL_ERROR;
+ }
+ *used = (ULONG)(it->second.ramUsed / 1024);
+ return VINF_SUCCESS;
+}
+
+}
diff --git a/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp b/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp
new file mode 100644
index 00000000..c76671f0
--- /dev/null
+++ b/src/VBox/Main/src-server/win/USBProxyBackendWindows.cpp
@@ -0,0 +1,274 @@
+/* $Id: USBProxyBackendWindows.cpp $ */
+/** @file
+ * VirtualBox USB Proxy Service, Windows Specialization.
+ */
+
+/*
+ * Copyright (C) 2005-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_USBPROXYBACKEND
+#include "USBProxyBackend.h"
+#include "LoggingNew.h"
+
+#include <VBox/usb.h>
+#include <iprt/errcore.h>
+
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/errcore.h>
+
+#include <VBox/usblib.h>
+
+
+/**
+ * Initialize data members.
+ */
+USBProxyBackendWindows::USBProxyBackendWindows()
+ : USBProxyBackend(), mhEventInterrupt(INVALID_HANDLE_VALUE)
+{
+ LogFlowThisFunc(("\n"));
+}
+
+USBProxyBackendWindows::~USBProxyBackendWindows()
+{
+}
+
+/**
+ * Initializes the object (called right after construction).
+ *
+ * @returns S_OK on success and non-fatal failures, some COM error otherwise.
+ */
+int USBProxyBackendWindows::init(USBProxyService *aUsbProxyService, const com::Utf8Str &strId,
+ const com::Utf8Str &strAddress, bool fLoadingSettings)
+{
+ USBProxyBackend::init(aUsbProxyService, strId, strAddress, fLoadingSettings);
+
+ unconst(m_strBackend) = Utf8Str("host");
+
+ /*
+ * Create the semaphore (considered fatal).
+ */
+ mhEventInterrupt = CreateEvent(NULL, FALSE, FALSE, NULL);
+ AssertReturn(mhEventInterrupt != INVALID_HANDLE_VALUE, VERR_OUT_OF_RESOURCES);
+
+ /*
+ * Initialize the USB lib and stuff.
+ */
+ int vrc = USBLibInit();
+ if (RT_SUCCESS(vrc))
+ {
+ /*
+ * Start the poller thread.
+ */
+ vrc = start();
+ if (RT_SUCCESS(vrc))
+ {
+ LogFlowThisFunc(("returns successfully\n"));
+ return VINF_SUCCESS;
+ }
+
+ USBLibTerm();
+ }
+
+ CloseHandle(mhEventInterrupt);
+ mhEventInterrupt = INVALID_HANDLE_VALUE;
+
+ LogFlowThisFunc(("returns failure!!! (vrc=%Rrc)\n", vrc));
+ return vrc;
+}
+
+
+/**
+ * Stop all service threads and free the device chain.
+ */
+void USBProxyBackendWindows::uninit()
+{
+ LogFlowThisFunc(("\n"));
+
+ /*
+ * Stop the service.
+ */
+ if (isActive())
+ stop();
+
+ if (mhEventInterrupt != INVALID_HANDLE_VALUE)
+ CloseHandle(mhEventInterrupt);
+ mhEventInterrupt = INVALID_HANDLE_VALUE;
+
+ /*
+ * Terminate the library...
+ */
+ int vrc = USBLibTerm();
+ AssertRC(vrc);
+ USBProxyBackend::uninit();
+}
+
+
+void *USBProxyBackendWindows::insertFilter(PCUSBFILTER aFilter)
+{
+ AssertReturn(aFilter, NULL);
+
+ LogFlow(("USBProxyBackendWindows::insertFilter()\n"));
+
+ void *pvId = USBLibAddFilter(aFilter);
+
+ LogFlow(("USBProxyBackendWindows::insertFilter(): returning pvId=%p\n", pvId));
+
+ return pvId;
+}
+
+
+void USBProxyBackendWindows::removeFilter(void *aID)
+{
+ LogFlow(("USBProxyBackendWindows::removeFilter(): id=%p\n", aID));
+
+ AssertReturnVoid(aID);
+
+ USBLibRemoveFilter(aID);
+}
+
+
+int USBProxyBackendWindows::captureDevice(HostUSBDevice *aDevice)
+{
+ /*
+ * Check preconditions.
+ */
+ AssertReturn(aDevice, VERR_GENERAL_FAILURE);
+ AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
+
+ AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
+
+ Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_Capturing);
+
+ /*
+ * Create a one-shot ignore filter for the device
+ * and trigger a re-enumeration of it.
+ */
+ USBFILTER Filter;
+ USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_CAPTURE);
+ initFilterFromDevice(&Filter, aDevice);
+ Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
+ Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS)));
+
+ void *pvId = USBLibAddFilter(&Filter);
+ if (!pvId)
+ {
+ AssertMsgFailed(("Add one-shot Filter failed\n"));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ int vrc = USBLibRunFilters();
+ if (!RT_SUCCESS(vrc))
+ {
+ AssertMsgFailed(("Run Filters failed\n"));
+ USBLibRemoveFilter(pvId);
+ return vrc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+int USBProxyBackendWindows::releaseDevice(HostUSBDevice *aDevice)
+{
+ /*
+ * Check preconditions.
+ */
+ AssertReturn(aDevice, VERR_GENERAL_FAILURE);
+ AssertReturn(!aDevice->isWriteLockOnCurrentThread(), VERR_GENERAL_FAILURE);
+
+ AutoReadLock devLock(aDevice COMMA_LOCKVAL_SRC_POS);
+ LogFlowThisFunc(("aDevice=%s\n", aDevice->i_getName().c_str()));
+
+ Assert(aDevice->i_getUnistate() == kHostUSBDeviceState_ReleasingToHost);
+
+ /*
+ * Create a one-shot ignore filter for the device
+ * and trigger a re-enumeration of it.
+ */
+ USBFILTER Filter;
+ USBFilterInit(&Filter, USBFILTERTYPE_ONESHOT_IGNORE);
+ initFilterFromDevice(&Filter, aDevice);
+ Log(("USBFILTERIDX_PORT=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_PORT)));
+ Log(("USBFILTERIDX_BUS=%#x\n", USBFilterGetNum(&Filter, USBFILTERIDX_BUS)));
+
+ void *pvId = USBLibAddFilter(&Filter);
+ if (!pvId)
+ {
+ AssertMsgFailed(("Add one-shot Filter failed\n"));
+ return VERR_GENERAL_FAILURE;
+ }
+
+ int vrc = USBLibRunFilters();
+ if (!RT_SUCCESS(vrc))
+ {
+ AssertMsgFailed(("Run Filters failed\n"));
+ USBLibRemoveFilter(pvId);
+ return vrc;
+ }
+
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Returns whether devices reported by this backend go through a de/re-attach
+ * and device re-enumeration cycle when they are captured or released.
+ */
+bool USBProxyBackendWindows::i_isDevReEnumerationRequired()
+{
+ return true;
+}
+
+
+int USBProxyBackendWindows::wait(unsigned aMillies)
+{
+ return USBLibWaitChange(aMillies);
+}
+
+
+int USBProxyBackendWindows::interruptWait(void)
+{
+ return USBLibInterruptWaitChange();
+}
+
+/**
+ * Gets a list of all devices the VM can grab
+ */
+PUSBDEVICE USBProxyBackendWindows::getDevices(void)
+{
+ PUSBDEVICE pDevices = NULL;
+ uint32_t cDevices = 0;
+
+ Log(("USBProxyBackendWindows::getDevices\n"));
+ USBLibGetDevices(&pDevices, &cDevices);
+ return pDevices;
+}
+
diff --git a/src/VBox/Main/src-server/win/VBoxSVC.rc b/src/VBox/Main/src-server/win/VBoxSVC.rc
new file mode 100644
index 00000000..e50a85e6
--- /dev/null
+++ b/src/VBox/Main/src-server/win/VBoxSVC.rc
@@ -0,0 +1,78 @@
+/* $Id: VBoxSVC.rc $ */
+/** @file
+ * VBoxSVC - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+#include "win/resource.h"
+
+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_APP
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilingual
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Interface\0"
+ VALUE "InternalName", "VBoxSVC\0"
+ VALUE "OriginalFilename", "VBoxSVC.exe\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
+
+ VALUE "OLESelfRegister", "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+/* Creates the application icon. */
+#include "VBoxSVC-icon.rc"
+
+
+#ifndef VBOX_WITH_MIDL_PROXY_STUB
+/////////////////////////////////////////////////////////////////////////////
+//
+// REGISTRY
+//
+
+IDR_VIRTUALBOX REGISTRY "VBoxSVC.rgs"
+#endif
+
+1 TYPELIB "VirtualBox.tlb"
diff --git a/src/VBox/Main/src-server/win/precomp_vcc.h b/src/VBox/Main/src-server/win/precomp_vcc.h
new file mode 100644
index 00000000..64b62d1c
--- /dev/null
+++ b/src/VBox/Main/src-server/win/precomp_vcc.h
@@ -0,0 +1,48 @@
+/* $Id: precomp_vcc.h $ */
+/** @file
+ * VirtualBox COM - Visual C++ precompiled header for VBoxSVC.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+#include <iprt/cdefs.h>
+#include <iprt/win/winsock2.h>
+#include <iprt/win/windows.h>
+#include <VBox/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/cpp/list.h>
+#include <iprt/cpp/meta.h>
+#include <iprt/cpp/ministring.h>
+#include <VBox/com/microatl.h>
+#include <VBox/com/com.h>
+#include <VBox/com/array.h>
+#include <VBox/com/Guid.h>
+#include <VBox/com/string.h>
+
+#include "VBox/com/VirtualBox.h"
+
+#if defined(Log) || defined(LogIsEnabled)
+# error "Log() from iprt/log.h cannot be defined in the precompiled header!"
+#endif
+
diff --git a/src/VBox/Main/src-server/win/svchlp.cpp b/src/VBox/Main/src-server/win/svchlp.cpp
new file mode 100644
index 00000000..262c32a6
--- /dev/null
+++ b/src/VBox/Main/src-server/win/svchlp.cpp
@@ -0,0 +1,308 @@
+/* $Id: svchlp.cpp $ */
+/** @file
+ * Definition of SVC Helper Process control routines.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_MAIN
+#include "svchlp.h"
+
+//#include "HostImpl.h"
+#include "LoggingNew.h"
+
+#include <iprt/errcore.h>
+
+int netIfNetworkInterfaceHelperServer(SVCHlpClient *aClient, SVCHlpMsg::Code aMsgCode);
+
+using namespace com;
+
+enum { PipeBufSize = 1024 };
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * GetLastError() is known to return NO_ERROR even after the Win32 API
+ * function (i.e. Write() to a non-connected server end of a pipe) returns
+ * FALSE... This method ensures that at least VERR_GENERAL_FAILURE is returned
+ * in cases like that. Intended to be called immediately after a failed API
+ * call.
+ */
+static inline int rtErrConvertFromWin32OnFailure()
+{
+ DWORD err = GetLastError();
+ return err == NO_ERROR ? VERR_GENERAL_FAILURE
+ : RTErrConvertFromWin32 (err);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SVCHlpClient::SVCHlpClient()
+ : mIsOpen (false), mIsServer (false)
+ , mReadEnd (NULL), mWriteEnd (NULL)
+{
+}
+
+SVCHlpClient::~SVCHlpClient()
+{
+ close();
+}
+
+int SVCHlpClient::create(const char *aName)
+{
+ AssertReturn(aName, VERR_INVALID_PARAMETER);
+
+ if (mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ Bstr pipeName = Utf8StrFmt("\\\\.\\pipe\\%s", aName);
+
+ HANDLE pipe = CreateNamedPipe(pipeName.raw(),
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
+ 1, // PIPE_UNLIMITED_INSTANCES,
+ PipeBufSize, PipeBufSize,
+ NMPWAIT_USE_DEFAULT_WAIT,
+ NULL);
+
+ if (pipe == INVALID_HANDLE_VALUE)
+ rtErrConvertFromWin32OnFailure();
+
+ mIsOpen = true;
+ mIsServer = true;
+ mReadEnd = pipe;
+ mWriteEnd = pipe;
+ mName = aName;
+
+ return VINF_SUCCESS;
+}
+
+int SVCHlpClient::open(const char *aName)
+{
+ AssertReturn(aName, VERR_INVALID_PARAMETER);
+
+ if (mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ Bstr pipeName = Utf8StrFmt("\\\\.\\pipe\\%s", aName);
+
+ HANDLE pipe = CreateFile(pipeName.raw(),
+ GENERIC_READ | GENERIC_WRITE,
+ 0,
+ NULL,
+ OPEN_EXISTING,
+ 0,
+ NULL);
+
+ if (pipe == INVALID_HANDLE_VALUE)
+ rtErrConvertFromWin32OnFailure();
+
+ mIsOpen = true;
+ mIsServer = false;
+ mReadEnd = pipe;
+ mWriteEnd = pipe;
+ mName = aName;
+
+ return VINF_SUCCESS;
+}
+
+int SVCHlpClient::connect()
+{
+ if (!mIsOpen || !mIsServer)
+ return VERR_WRONG_ORDER;
+
+ BOOL ok = ConnectNamedPipe (mReadEnd, NULL);
+ if (!ok && GetLastError() != ERROR_PIPE_CONNECTED)
+ rtErrConvertFromWin32OnFailure();
+
+ return VINF_SUCCESS;
+}
+
+int SVCHlpClient::close()
+{
+ if (!mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ if (mWriteEnd != NULL && mWriteEnd != mReadEnd)
+ {
+ if (!CloseHandle (mWriteEnd))
+ rtErrConvertFromWin32OnFailure();
+ mWriteEnd = NULL;
+ }
+
+ if (mReadEnd != NULL)
+ {
+ if (!CloseHandle (mReadEnd))
+ rtErrConvertFromWin32OnFailure();
+ mReadEnd = NULL;
+ }
+
+ mIsOpen = false;
+ mIsServer = false;
+ mName.setNull();
+
+ return VINF_SUCCESS;
+}
+
+int SVCHlpClient::write (const void *aVal, size_t aLen)
+{
+ AssertReturn(aVal != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(aLen != 0, VERR_INVALID_PARAMETER);
+
+ if (!mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ DWORD written = 0;
+ BOOL ok = WriteFile (mWriteEnd, aVal, (ULONG)aLen, &written, NULL);
+ AssertReturn(!ok || written == aLen, VERR_GENERAL_FAILURE);
+ return ok ? VINF_SUCCESS : rtErrConvertFromWin32OnFailure();
+}
+
+int SVCHlpClient::write (const Utf8Str &aVal)
+{
+ if (!mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ /* write -1 for NULL strings */
+ if (aVal.isEmpty())
+ return write ((size_t) ~0);
+
+ size_t len = aVal.length();
+
+ /* write string length */
+ int vrc = write (len);
+ if (RT_SUCCESS(vrc))
+ {
+ /* write string data */
+ vrc = write (aVal.c_str(), len);
+ }
+
+ return vrc;
+}
+
+int SVCHlpClient::write (const Guid &aGuid)
+{
+ Utf8Str guidStr = aGuid.toString();
+ return write (guidStr);
+}
+
+int SVCHlpClient::read (void *aVal, size_t aLen)
+{
+ AssertReturn(aVal != NULL, VERR_INVALID_PARAMETER);
+ AssertReturn(aLen != 0, VERR_INVALID_PARAMETER);
+
+ if (!mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ DWORD read = 0;
+ BOOL ok = ReadFile (mReadEnd, aVal, (ULONG)aLen, &read, NULL);
+ AssertReturn(!ok || read == aLen, VERR_GENERAL_FAILURE);
+ return ok ? VINF_SUCCESS : rtErrConvertFromWin32OnFailure();
+}
+
+int SVCHlpClient::read (Utf8Str &aVal)
+{
+ if (!mIsOpen)
+ return VERR_WRONG_ORDER;
+
+ size_t len = 0;
+
+ /* read string length */
+ int vrc = read (len);
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* length -1 means a NULL string */
+ if (len == (size_t) ~0)
+ {
+ aVal.setNull();
+ return VINF_SUCCESS;
+ }
+
+ aVal.reserve(len + 1);
+ aVal.mutableRaw()[len] = 0;
+
+ /* read string data */
+ vrc = read (aVal.mutableRaw(), len);
+
+ return vrc;
+}
+
+int SVCHlpClient::read (Guid &aGuid)
+{
+ Utf8Str guidStr;
+ int vrc = read (guidStr);
+ if (RT_SUCCESS(vrc))
+ aGuid = Guid (guidStr.c_str());
+ return vrc;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+SVCHlpServer::SVCHlpServer ()
+{
+}
+
+int SVCHlpServer::run()
+{
+ int vrc = VINF_SUCCESS;
+ SVCHlpMsg::Code msgCode = SVCHlpMsg::Null;
+
+ do
+ {
+ vrc = read (msgCode);
+ if (RT_FAILURE(vrc))
+ return vrc;
+
+ /* terminate request received */
+ if (msgCode == SVCHlpMsg::Null)
+ return VINF_SUCCESS;
+
+ switch (msgCode)
+ {
+ case SVCHlpMsg::CreateHostOnlyNetworkInterface:
+ case SVCHlpMsg::RemoveHostOnlyNetworkInterface:
+ case SVCHlpMsg::EnableDynamicIpConfig:
+ case SVCHlpMsg::EnableStaticIpConfig:
+ case SVCHlpMsg::EnableStaticIpConfigV6:
+ case SVCHlpMsg::DhcpRediscover:
+ {
+#ifdef VBOX_WITH_NETFLT
+ vrc = netIfNetworkInterfaceHelperServer(this, msgCode);
+#endif
+ break;
+ }
+ default:
+ AssertMsgFailedReturn(("Invalid message code %d (%08lX)\n", msgCode, msgCode),
+ VERR_GENERAL_FAILURE);
+ }
+
+ if (RT_FAILURE(vrc))
+ return vrc;
+ }
+ while (1);
+
+ /* we never get here */
+ AssertFailed();
+ return VERR_GENERAL_FAILURE;
+}
diff --git a/src/VBox/Main/src-server/win/svchlp.h b/src/VBox/Main/src-server/win/svchlp.h
new file mode 100644
index 00000000..a02111ba
--- /dev/null
+++ b/src/VBox/Main/src-server/win/svchlp.h
@@ -0,0 +1,107 @@
+/* $Id: svchlp.h $ */
+/** @file
+ * Declaration of SVC Helper Process control routines.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef MAIN_INCLUDED_SRC_src_server_win_svchlp_h
+#define MAIN_INCLUDED_SRC_src_server_win_svchlp_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "VBox/com/string.h"
+#include "VBox/com/guid.h"
+
+#include <VBox/err.h>
+
+#include <iprt/win/windows.h>
+
+struct SVCHlpMsg
+{
+ enum Code
+ {
+ Null = 0, /* no parameters */
+ OK, /* no parameters */
+ Error, /* Utf8Str string (may be null but must present) */
+
+ CreateHostOnlyNetworkInterface = 100, /* see usage in code */
+ CreateHostOnlyNetworkInterface_OK, /* see usage in code */
+ RemoveHostOnlyNetworkInterface, /* see usage in code */
+ EnableDynamicIpConfig, /* see usage in code */
+ EnableStaticIpConfig, /* see usage in code */
+ EnableStaticIpConfigV6, /* see usage in code */
+ DhcpRediscover, /* see usage in code */
+ };
+};
+
+class SVCHlpClient
+{
+public:
+
+ SVCHlpClient();
+ virtual ~SVCHlpClient();
+
+ int create (const char *aName);
+ int connect();
+ int open (const char *aName);
+ int close();
+
+ bool isOpen() const { return mIsOpen; }
+ bool isServer() const { return mIsServer; }
+ const com::Utf8Str &name() const { return mName; }
+
+ int write (const void *aVal, size_t aLen);
+ template <typename Scalar>
+ int write (Scalar aVal) { return write (&aVal, sizeof (aVal)); }
+ int write (const com::Utf8Str &aVal);
+ int write (const com::Guid &aGuid);
+
+ int read (void *aVal, size_t aLen);
+ template <typename Scalar>
+ int read (Scalar &aVal) { return read (&aVal, sizeof (aVal)); }
+ int read (com::Utf8Str &aVal);
+ int read (com::Guid &aGuid);
+
+private:
+
+ bool mIsOpen : 1;
+ bool mIsServer : 1;
+
+ HANDLE mReadEnd;
+ HANDLE mWriteEnd;
+ com::Utf8Str mName;
+};
+
+class SVCHlpServer : public SVCHlpClient
+{
+public:
+
+ SVCHlpServer();
+
+ int run();
+};
+
+#endif /* !MAIN_INCLUDED_SRC_src_server_win_svchlp_h */
+
diff --git a/src/VBox/Main/src-server/win/svcmain.cpp b/src/VBox/Main/src-server/win/svcmain.cpp
new file mode 100644
index 00000000..c1aa56a7
--- /dev/null
+++ b/src/VBox/Main/src-server/win/svcmain.cpp
@@ -0,0 +1,1212 @@
+/* $Id: svcmain.cpp $ */
+/** @file
+ * SVCMAIN - COM out-of-proc server main entry
+ */
+
+/*
+ * Copyright (C) 2004-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_MAIN_VBOXSVC
+#include <iprt/win/windows.h>
+#ifdef DEBUG_bird
+# include <RpcAsync.h>
+#endif
+
+#include "VBox/com/defs.h"
+#include "VBox/com/com.h"
+#include "VBox/com/VirtualBox.h"
+
+#include "VirtualBoxImpl.h"
+#include "LoggingNew.h"
+
+#include "svchlp.h"
+
+#include <iprt/errcore.h>
+#include <iprt/buildconfig.h>
+#include <iprt/initterm.h>
+#include <iprt/string.h>
+#include <iprt/path.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/asm.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define MAIN_WND_CLASS L"VirtualBox Interface"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+class CExeModule : public ATL::CComModule
+{
+public:
+ LONG Unlock() throw();
+ DWORD dwThreadID;
+ HANDLE hEventShutdown;
+ void MonitorShutdown();
+ bool StartMonitor();
+ bool HasActiveConnection();
+ bool bActivity;
+ static bool isIdleLockCount(LONG cLocks);
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+BEGIN_OBJECT_MAP(ObjectMap)
+ OBJECT_ENTRY(CLSID_VirtualBox, VirtualBox)
+END_OBJECT_MAP()
+
+CExeModule *g_pModule = NULL;
+HWND g_hMainWindow = NULL;
+HINSTANCE g_hInstance = NULL;
+#ifdef VBOX_WITH_SDS
+/** This is set if we're connected to SDS.
+ *
+ * It means that we should discount a server lock that it is holding when
+ * deciding whether we're idle or not.
+ *
+ * Also, when set we deregister with SDS during class factory destruction. We
+ * exploit this to prevent attempts to deregister during or after COM shutdown.
+ */
+bool g_fRegisteredWithVBoxSDS = false;
+#endif
+
+/* Normal timeout usually used in Shutdown Monitor */
+const DWORD dwNormalTimeout = 5000;
+volatile uint32_t dwTimeOut = dwNormalTimeout; /* time for EXE to be idle before shutting down. Can be decreased at system shutdown phase. */
+
+
+
+/** Passed to CreateThread to monitor the shutdown event. */
+static DWORD WINAPI MonitorProc(void *pv) RT_NOTHROW_DEF
+{
+ CExeModule *p = (CExeModule *)pv;
+ p->MonitorShutdown();
+ return 0;
+}
+
+LONG CExeModule::Unlock() throw()
+{
+ LONG cLocks = ATL::CComModule::Unlock();
+ if (isIdleLockCount(cLocks))
+ {
+ bActivity = true;
+ SetEvent(hEventShutdown); /* tell monitor that we transitioned to zero */
+ }
+ return cLocks;
+}
+
+bool CExeModule::HasActiveConnection()
+{
+ return bActivity || !isIdleLockCount(GetLockCount());
+}
+
+/**
+ * Checks if @a cLocks signifies an IDLE server lock load.
+ *
+ * This takes VBoxSDS into account (i.e. ignores it).
+ */
+/*static*/ bool CExeModule::isIdleLockCount(LONG cLocks)
+{
+#ifdef VBOX_WITH_SDS
+ if (g_fRegisteredWithVBoxSDS)
+ return cLocks <= 1;
+#endif
+ return cLocks <= 0;
+}
+
+/* Monitors the shutdown event */
+void CExeModule::MonitorShutdown()
+{
+ while (1)
+ {
+ WaitForSingleObject(hEventShutdown, INFINITE);
+ DWORD dwWait;
+ do
+ {
+ bActivity = false;
+ dwWait = WaitForSingleObject(hEventShutdown, dwTimeOut);
+ } while (dwWait == WAIT_OBJECT_0);
+ /* timed out */
+ if (!HasActiveConnection()) /* if no activity let's really bail */
+ {
+ /* Disable log rotation at this point, worst case a log file
+ * becomes slightly bigger than it should. Avoids quirks with
+ * log rotation: there might be another API service process
+ * running at this point which would rotate the logs concurrently,
+ * creating a mess. */
+ PRTLOGGER pReleaseLogger = RTLogRelGetDefaultInstance();
+ if (pReleaseLogger)
+ {
+ char szDest[1024];
+ int vrc = RTLogQueryDestinations(pReleaseLogger, szDest, sizeof(szDest));
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTStrCat(szDest, sizeof(szDest), " nohistory");
+ if (RT_SUCCESS(vrc))
+ {
+ vrc = RTLogDestinations(pReleaseLogger, szDest);
+ AssertRC(vrc);
+ }
+ }
+ }
+#if _WIN32_WINNT >= 0x0400
+ CoSuspendClassObjects();
+ if (!HasActiveConnection())
+#endif
+ break;
+ }
+ }
+ CloseHandle(hEventShutdown);
+ PostThreadMessage(dwThreadID, WM_QUIT, 0, 0);
+}
+
+bool CExeModule::StartMonitor()
+{
+ hEventShutdown = CreateEvent(NULL, false, false, NULL);
+ if (hEventShutdown == NULL)
+ return false;
+ DWORD idThreadIgnored;
+ HANDLE h = CreateThread(NULL, 0, MonitorProc, this, 0, &idThreadIgnored);
+ return (h != NULL);
+}
+
+
+#ifdef VBOX_WITH_SDS
+
+class VBoxSVCRegistration;
+
+/**
+ * Custom class factory for the VirtualBox singleton.
+ *
+ * The implementation of CreateInstance is found in win/svcmain.cpp.
+ */
+class VirtualBoxClassFactory : public ATL::CComClassFactory
+{
+private:
+ /** Tri state: 0=uninitialized or initializing; 1=success; -1=failure.
+ * This will be updated after both m_hrcCreate and m_pObj have been set. */
+ volatile int32_t m_iState;
+ /** The result of the instantiation attempt. */
+ HRESULT m_hrcCreate;
+ /** The IUnknown of the VirtualBox object/interface we're working with. */
+ IUnknown *m_pObj;
+ /** Pointer to the IVBoxSVCRegistration implementation that VBoxSDS works with. */
+ VBoxSVCRegistration *m_pVBoxSVC;
+ /** The VBoxSDS interface. */
+ ComPtr<IVirtualBoxSDS> m_ptrVirtualBoxSDS;
+
+public:
+ VirtualBoxClassFactory() : m_iState(0), m_hrcCreate(S_OK), m_pObj(NULL), m_pVBoxSVC(NULL)
+ { }
+
+ virtual ~VirtualBoxClassFactory()
+ {
+ if (m_pObj)
+ {
+ m_pObj->Release();
+ m_pObj = NULL;
+ }
+
+ /* We usually get here during g_pModule->Term() via CoRevokeClassObjec, so COM
+ probably working well enough to talk to SDS when we get here. */
+ if (g_fRegisteredWithVBoxSDS)
+ i_deregisterWithSds();
+ }
+
+ // IClassFactory
+ STDMETHOD(CreateInstance)(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj);
+
+ /** Worker for VBoxSVCRegistration::getVirtualBox. */
+ HRESULT i_getVirtualBox(IUnknown **ppResult);
+
+private:
+ HRESULT i_registerWithSds(IUnknown **ppOtherVirtualBox);
+ void i_deregisterWithSds(void);
+
+ friend VBoxSVCRegistration;
+};
+
+
+/**
+ * The VBoxSVC class is handed to VBoxSDS so it can call us back and ask for the
+ * VirtualBox object when the next VBoxSVC for this user registers itself.
+ */
+class VBoxSVCRegistration : public IVBoxSVCRegistration
+{
+private:
+ /** Number of references. */
+ uint32_t volatile m_cRefs;
+
+public:
+ /** Pointer to the factory. */
+ VirtualBoxClassFactory *m_pFactory;
+
+public:
+ VBoxSVCRegistration(VirtualBoxClassFactory *pFactory)
+ : m_cRefs(1), m_pFactory(pFactory)
+ { }
+ virtual ~VBoxSVCRegistration()
+ {
+ if (m_pFactory)
+ {
+ if (m_pFactory->m_pVBoxSVC)
+ m_pFactory->m_pVBoxSVC = NULL;
+ m_pFactory = NULL;
+ }
+ }
+ RTMEMEF_NEW_AND_DELETE_OPERATORS();
+
+ // IUnknown
+ STDMETHOD(QueryInterface)(REFIID riid, void **ppvObject)
+ {
+ if (riid == __uuidof(IUnknown))
+ *ppvObject = (void *)(IUnknown *)this;
+ else if (riid == __uuidof(IVBoxSVCRegistration))
+ *ppvObject = (void *)(IVBoxSVCRegistration *)this;
+ else
+ {
+ return E_NOINTERFACE;
+ }
+ AddRef();
+ return S_OK;
+
+ }
+
+ STDMETHOD_(ULONG,AddRef)(void)
+ {
+ uint32_t cRefs = ASMAtomicIncU32(&m_cRefs);
+ return cRefs;
+ }
+
+ STDMETHOD_(ULONG,Release)(void)
+ {
+ uint32_t cRefs = ASMAtomicDecU32(&m_cRefs);
+ if (cRefs == 0)
+ delete this;
+ return cRefs;
+ }
+
+ // IVBoxSVCRegistration
+ STDMETHOD(GetVirtualBox)(IUnknown **ppResult)
+ {
+ if (m_pFactory)
+ return m_pFactory->i_getVirtualBox(ppResult);
+ return E_FAIL;
+ }
+};
+
+
+HRESULT VirtualBoxClassFactory::i_registerWithSds(IUnknown **ppOtherVirtualBox)
+{
+# ifdef DEBUG_bird
+ RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
+ RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
+ LogRel(("i_registerWithSds: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
+ rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
+ CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
+# endif
+
+ /*
+ * Connect to VBoxSDS.
+ */
+ HRESULT hrc = CoCreateInstance(CLSID_VirtualBoxSDS, NULL, CLSCTX_LOCAL_SERVER, IID_IVirtualBoxSDS,
+ (void **)m_ptrVirtualBoxSDS.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
+ ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
+ elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
+ service to access the files. */
+ hrc = CoSetProxyBlanket(m_ptrVirtualBoxSDS,
+ RPC_C_AUTHN_DEFAULT,
+ RPC_C_AUTHZ_DEFAULT,
+ COLE_DEFAULT_PRINCIPAL,
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL,
+ EOAC_DEFAULT);
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Create VBoxSVCRegistration object and hand that to VBoxSDS.
+ */
+ m_pVBoxSVC = new VBoxSVCRegistration(this);
+ hrc = E_PENDING;
+ /* we try to register IVirtualBox 10 times */
+ for (int regTimes = 0; hrc == E_PENDING && regTimes < 10; --regTimes)
+ {
+ hrc = m_ptrVirtualBoxSDS->RegisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId(), ppOtherVirtualBox);
+ if (SUCCEEDED(hrc))
+ {
+ g_fRegisteredWithVBoxSDS = !*ppOtherVirtualBox;
+ return hrc;
+ }
+ /* sleep to give a time for windows session 0 registration */
+ if (hrc == E_PENDING)
+ RTThreadSleep(1000);
+ }
+ m_pVBoxSVC->Release();
+ }
+ }
+ m_ptrVirtualBoxSDS.setNull();
+ m_pVBoxSVC = NULL;
+ *ppOtherVirtualBox = NULL;
+ return hrc;
+}
+
+
+void VirtualBoxClassFactory::i_deregisterWithSds(void)
+{
+ Log(("VirtualBoxClassFactory::i_deregisterWithSds\n"));
+
+ if (m_ptrVirtualBoxSDS.isNotNull())
+ {
+ if (m_pVBoxSVC)
+ {
+ HRESULT hrc = m_ptrVirtualBoxSDS->DeregisterVBoxSVC(m_pVBoxSVC, GetCurrentProcessId());
+ NOREF(hrc);
+ }
+ m_ptrVirtualBoxSDS.setNull();
+ g_fRegisteredWithVBoxSDS = false;
+ }
+ if (m_pVBoxSVC)
+ {
+ m_pVBoxSVC->m_pFactory = NULL;
+ m_pVBoxSVC->Release();
+ m_pVBoxSVC = NULL;
+ }
+}
+
+
+HRESULT VirtualBoxClassFactory::i_getVirtualBox(IUnknown **ppResult)
+{
+# ifdef DEBUG_bird
+ RPC_CALL_ATTRIBUTES_V2_W CallAttribs = { RPC_CALL_ATTRIBUTES_VERSION, RPC_QUERY_CLIENT_PID | RPC_QUERY_IS_CLIENT_LOCAL };
+ RPC_STATUS rcRpc = RpcServerInqCallAttributesW(NULL, &CallAttribs);
+ LogRel(("i_getVirtualBox: RpcServerInqCallAttributesW -> %#x ClientPID=%#x IsClientLocal=%d ProtocolSequence=%#x CallStatus=%#x CallType=%#x OpNum=%#x InterfaceUuid=%RTuuid\n",
+ rcRpc, CallAttribs.ClientPID, CallAttribs.IsClientLocal, CallAttribs.ProtocolSequence, CallAttribs.CallStatus,
+ CallAttribs.CallType, CallAttribs.OpNum, &CallAttribs.InterfaceUuid));
+# endif
+ IUnknown *pObj = m_pObj;
+ if (pObj)
+ {
+ /** @todo Do we need to do something regarding server locking? Hopefully COM
+ * deals with that........... */
+ pObj->AddRef();
+ *ppResult = pObj;
+ Log(("VirtualBoxClassFactory::GetVirtualBox: S_OK - %p\n", pObj));
+ return S_OK;
+ }
+ *ppResult = NULL;
+ Log(("VirtualBoxClassFactory::GetVirtualBox: E_FAIL\n"));
+ return E_FAIL;
+}
+
+
+/**
+ * Custom instantiation of CComObjectCached.
+ *
+ * This catches certain QueryInterface callers for the purpose of watching for
+ * abnormal client process termination (@bugref{3300}).
+ *
+ * @todo just merge this into class VirtualBox VirtualBoxImpl.h
+ */
+class VirtualBoxObjectCached : public VirtualBox
+{
+public:
+ VirtualBoxObjectCached(void * = NULL)
+ : VirtualBox()
+ {
+ }
+
+ virtual ~VirtualBoxObjectCached()
+ {
+ m_iRef = LONG_MIN / 2; /* Catch refcount screwups by setting refcount something insane. */
+ FinalRelease();
+ }
+
+ /** @name IUnknown implementation for VirtualBox
+ * @{ */
+
+ STDMETHOD_(ULONG, AddRef)() throw()
+ {
+ ULONG cRefs = InternalAddRef();
+ if (cRefs == 2)
+ {
+ AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
+ ATL::_pAtlModule->Lock();
+ }
+ return cRefs;
+ }
+
+ STDMETHOD_(ULONG, Release)() throw()
+ {
+ ULONG cRefs = InternalRelease();
+ if (cRefs == 0)
+ delete this;
+ else if (cRefs == 1)
+ {
+ AssertMsg(ATL::_pAtlModule, ("ATL: referring to ATL module without having one declared in this linking namespace\n"));
+ ATL::_pAtlModule->Unlock();
+ }
+ return cRefs;
+ }
+
+ STDMETHOD(QueryInterface)(REFIID iid, void **ppvObj) throw()
+ {
+ HRESULT hrc = _InternalQueryInterface(iid, ppvObj);
+#ifdef VBOXSVC_WITH_CLIENT_WATCHER
+ i_logCaller("QueryInterface %RTuuid -> %Rhrc %p", &iid, hrc, *ppvObj);
+#endif
+ return hrc;
+ }
+
+ /** @} */
+
+ static HRESULT WINAPI CreateInstance(VirtualBoxObjectCached **ppObj) throw()
+ {
+ AssertReturn(ppObj, E_POINTER);
+ *ppObj = NULL;
+
+ HRESULT hrc = E_OUTOFMEMORY;
+ VirtualBoxObjectCached *p = new (std::nothrow) VirtualBoxObjectCached();
+ if (p)
+ {
+ p->SetVoid(NULL);
+ p->InternalFinalConstructAddRef();
+ hrc = p->_AtlInitialConstruct();
+ if (SUCCEEDED(hrc))
+ hrc = p->FinalConstruct();
+ p->InternalFinalConstructRelease();
+ if (FAILED(hrc))
+ delete p;
+ else
+ *ppObj = p;
+ }
+ return hrc;
+ }
+};
+
+
+/**
+ * Custom class factory impl for the VirtualBox singleton.
+ *
+ * This will consult with VBoxSDS on whether this VBoxSVC instance should
+ * provide the actual VirtualBox instance or just forward the instance from
+ * some other SVC instance.
+ *
+ * @param pUnkOuter This must be NULL.
+ * @param riid Reference to the interface ID to provide.
+ * @param ppvObj Where to return the pointer to the riid instance.
+ *
+ * @return COM status code.
+ */
+STDMETHODIMP VirtualBoxClassFactory::CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void **ppvObj)
+{
+# ifdef VBOXSVC_WITH_CLIENT_WATCHER
+ VirtualBox::i_logCaller("VirtualBoxClassFactory::CreateInstance: %RTuuid", riid);
+# endif
+ HRESULT hrc = E_POINTER;
+ if (ppvObj != NULL)
+ {
+ *ppvObj = NULL;
+ // no aggregation for singletons
+ AssertReturn(pUnkOuter == NULL, CLASS_E_NOAGGREGATION);
+
+ /*
+ * We must make sure there is only one instance around.
+ * So, we check without locking and then again after locking.
+ */
+ if (ASMAtomicReadS32(&m_iState) == 0)
+ {
+ Lock();
+ __try
+ {
+ if (ASMAtomicReadS32(&m_iState) == 0)
+ {
+ /*
+ * lock the module to indicate activity
+ * (necessary for the monitor shutdown thread to correctly
+ * terminate the module in case when CreateInstance() fails)
+ */
+ ATL::_pAtlModule->Lock();
+ __try
+ {
+ /*
+ * Now we need to connect to VBoxSDS to register ourselves.
+ */
+ IUnknown *pOtherVirtualBox = NULL;
+ m_hrcCreate = hrc = i_registerWithSds(&pOtherVirtualBox);
+ if (SUCCEEDED(hrc) && pOtherVirtualBox)
+ m_pObj = pOtherVirtualBox;
+ else if (SUCCEEDED(hrc))
+ {
+ ATL::_pAtlModule->Lock();
+ VirtualBoxObjectCached *p;
+ m_hrcCreate = hrc = VirtualBoxObjectCached::CreateInstance(&p);
+ if (SUCCEEDED(hrc))
+ {
+ m_hrcCreate = hrc = p->QueryInterface(IID_IUnknown, (void **)&m_pObj);
+ if (SUCCEEDED(hrc))
+ RTLogClearFileDelayFlag(RTLogRelGetDefaultInstance(), NULL);
+ else
+ {
+ delete p;
+ i_deregisterWithSds();
+ m_pObj = NULL;
+ }
+ }
+ }
+ ASMAtomicWriteS32(&m_iState, SUCCEEDED(hrc) ? 1 : -1);
+ }
+ __finally
+ {
+ ATL::_pAtlModule->Unlock();
+ }
+ }
+ }
+ __finally
+ {
+ if (ASMAtomicReadS32(&m_iState) == 0)
+ {
+ ASMAtomicWriteS32(&m_iState, -1);
+ if (SUCCEEDED(m_hrcCreate))
+ m_hrcCreate = E_FAIL;
+ }
+ Unlock();
+ }
+ }
+
+ /*
+ * Query the requested interface from the IUnknown one we're keeping around.
+ */
+ if (m_hrcCreate == S_OK)
+ hrc = m_pObj->QueryInterface(riid, ppvObj);
+ else
+ hrc = m_hrcCreate;
+ }
+ return hrc;
+}
+
+#endif // VBOX_WITH_SDS
+
+
+/*
+* Wrapper for Win API function ShutdownBlockReasonCreate
+* This function defined starting from Vista only.
+*/
+static BOOL ShutdownBlockReasonCreateAPI(HWND hWnd, LPCWSTR pwszReason)
+{
+ typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONCREATE,(HWND hWnd, LPCWSTR pwszReason));
+
+ PFNSHUTDOWNBLOCKREASONCREATE pfn
+ = (PFNSHUTDOWNBLOCKREASONCREATE)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonCreate");
+ AssertPtr(pfn);
+
+ BOOL fResult = FALSE;
+ if (pfn)
+ fResult = pfn(hWnd, pwszReason);
+ return fResult;
+}
+
+/*
+* Wrapper for Win API function ShutdownBlockReasonDestroy
+* This function defined starting from Vista only.
+*/
+static BOOL ShutdownBlockReasonDestroyAPI(HWND hWnd)
+{
+ typedef DECLCALLBACKPTR_EX(BOOL, WINAPI, PFNSHUTDOWNBLOCKREASONDESTROY,(HWND hWnd));
+ PFNSHUTDOWNBLOCKREASONDESTROY pfn
+ = (PFNSHUTDOWNBLOCKREASONDESTROY)GetProcAddress(GetModuleHandle(L"User32.dll"), "ShutdownBlockReasonDestroy");
+ AssertPtr(pfn);
+
+ BOOL fResult = FALSE;
+ if (pfn)
+ fResult = pfn(hWnd);
+ return fResult;
+}
+
+static LRESULT CALLBACK WinMainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ LRESULT lResult = 0;
+
+ switch (msg)
+ {
+ case WM_QUERYENDSESSION:
+ {
+ LogRel(("WM_QUERYENDSESSION:%s%s%s%s (0x%08lx)\n",
+ lParam == 0 ? " shutdown" : "",
+ lParam & ENDSESSION_CRITICAL ? " critical" : "",
+ lParam & ENDSESSION_LOGOFF ? " logoff" : "",
+ lParam & ENDSESSION_CLOSEAPP ? " close" : "",
+ (unsigned long)lParam));
+ if (g_pModule)
+ {
+ bool fActiveConnection = g_pModule->HasActiveConnection();
+ if (fActiveConnection)
+ {
+ lResult = FALSE;
+ LogRel(("VBoxSvc has active connections:"
+ " bActivity = %RTbool, lock count = %d\n",
+ g_pModule->bActivity, g_pModule->GetLockCount()));
+
+ /* place the VBoxSVC into system shutdown list */
+ ShutdownBlockReasonCreateAPI(hwnd, L"Has active connections.");
+ /* decrease a latency of MonitorShutdown loop */
+ ASMAtomicXchgU32(&dwTimeOut, 100);
+ Log(("VBoxSVCWinMain: WM_QUERYENDSESSION: VBoxSvc has active connections."
+ " bActivity = %d. Lock count = %d\n",
+ g_pModule->bActivity, g_pModule->GetLockCount()));
+ }
+ else
+ {
+ LogRel(("No active connections:"
+ " bActivity = %RTbool, lock count = %d\n",
+ g_pModule->bActivity, g_pModule->GetLockCount()));
+ lResult = TRUE;
+ }
+ }
+ else
+ AssertMsgFailed(("VBoxSVCWinMain: WM_QUERYENDSESSION: Error: g_pModule is NULL"));
+ break;
+ }
+ case WM_ENDSESSION:
+ {
+ LogRel(("WM_ENDSESSION:%s%s%s%s%s (%s/0x%08lx)\n",
+ lParam == 0 ? " shutdown" : "",
+ lParam & ENDSESSION_CRITICAL ? " critical" : "",
+ lParam & ENDSESSION_LOGOFF ? " logoff" : "",
+ lParam & ENDSESSION_CLOSEAPP ? " close" : "",
+ wParam == FALSE ? " cancelled" : "",
+ wParam ? "TRUE" : "FALSE",
+ (unsigned long)lParam));
+
+ /* Restore timeout of Monitor Shutdown if user canceled system shutdown */
+ if (wParam == FALSE)
+ {
+ Log(("VBoxSVCWinMain: user canceled system shutdown.\n"));
+ ASMAtomicXchgU32(&dwTimeOut, dwNormalTimeout);
+ ShutdownBlockReasonDestroyAPI(hwnd);
+ }
+ break;
+ }
+ case WM_DESTROY:
+ {
+ ShutdownBlockReasonDestroyAPI(hwnd);
+ PostQuitMessage(0);
+ break;
+ }
+
+ default:
+ {
+ lResult = DefWindowProc(hwnd, msg, wParam, lParam);
+ break;
+ }
+ }
+ return lResult;
+}
+
+static int CreateMainWindow()
+{
+ int vrc = VINF_SUCCESS;
+ Assert(g_hMainWindow == NULL);
+
+ LogFlow(("CreateMainWindow\n"));
+
+ g_hInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+ /* Register the Window Class. */
+ WNDCLASS wc;
+ RT_ZERO(wc);
+
+ wc.style = CS_NOCLOSE;
+ wc.lpfnWndProc = WinMainWndProc;
+ wc.hInstance = g_hInstance;
+ wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
+ wc.lpszClassName = MAIN_WND_CLASS;
+
+ ATOM atomWindowClass = RegisterClass(&wc);
+ if (atomWindowClass == 0)
+ {
+ LogRel(("Failed to register window class for session monitoring\n"));
+ vrc = VERR_NOT_SUPPORTED;
+ }
+ else
+ {
+ /* Create the window. */
+ g_hMainWindow = CreateWindowEx(0, MAIN_WND_CLASS, MAIN_WND_CLASS, 0,
+ 0, 0, 1, 1, NULL, NULL, g_hInstance, NULL);
+ if (g_hMainWindow == NULL)
+ {
+ LogRel(("Failed to create window for session monitoring\n"));
+ vrc = VERR_NOT_SUPPORTED;
+ }
+ }
+ return vrc;
+}
+
+
+static void DestroyMainWindow()
+{
+ Assert(g_hMainWindow != NULL);
+ Log(("SVCMain: DestroyMainWindow \n"));
+ if (g_hMainWindow != NULL)
+ {
+ DestroyWindow(g_hMainWindow);
+ g_hMainWindow = NULL;
+ if (g_hInstance != NULL)
+ {
+ UnregisterClass(MAIN_WND_CLASS, g_hInstance);
+ g_hInstance = NULL;
+ }
+ }
+}
+
+
+static const char * const ctrl_event_names[] = {
+ "CTRL_C_EVENT",
+ "CTRL_BREAK_EVENT",
+ "CTRL_CLOSE_EVENT",
+ /* reserved, not used */
+ "<console control event 3>",
+ "<console control event 4>",
+ /* not sent to processes that load gdi32.dll or user32.dll */
+ "CTRL_LOGOFF_EVENT",
+ "CTRL_SHUTDOWN_EVENT",
+};
+
+/** @todo r=uwe placeholder */
+BOOL WINAPI
+ConsoleCtrlHandler(DWORD dwCtrlType) RT_NOTHROW_DEF
+{
+ const char *signame;
+ char namebuf[48];
+ // int vrc;
+
+ if (dwCtrlType < RT_ELEMENTS(ctrl_event_names))
+ signame = ctrl_event_names[dwCtrlType];
+ else
+ {
+ /* should not happen, but be prepared */
+ RTStrPrintf(namebuf, sizeof(namebuf),
+ "<console control event %lu>", (unsigned long)dwCtrlType);
+ signame = namebuf;
+ }
+ LogRel(("Got %s\n", signame));
+
+ if (RT_UNLIKELY(g_pModule == NULL))
+ {
+ LogRel(("%s: g_pModule == NULL\n", __FUNCTION__));
+ return TRUE;
+ }
+
+ /* decrease latency of the MonitorShutdown loop */
+ ASMAtomicXchgU32(&dwTimeOut, 100);
+
+ bool fHasClients = g_pModule->HasActiveConnection();
+ if (!fHasClients)
+ {
+ LogRel(("No clients, closing the shop.\n"));
+ return TRUE;
+ }
+
+ LogRel(("VBoxSvc has clients: bActivity = %RTbool, lock count = %d\n",
+ g_pModule->bActivity, g_pModule->GetLockCount()));
+
+ /** @todo r=uwe wait for clients to disconnect */
+ return TRUE;
+}
+
+
+
+/** Special export that make VBoxProxyStub not register this process as one that
+ * VBoxSDS should be watching.
+ */
+extern "C" DECLEXPORT(void) VBOXCALL Is_VirtualBox_service_process_like_VBoxSDS_And_VBoxSDS(void)
+{
+ /* never called, just need to be here */
+}
+
+
+/* thread for registering the VBoxSVC started in session 0 */
+static DWORD WINAPI threadRegisterVirtualBox(LPVOID lpParam) throw()
+{
+ HANDLE hEvent = (HANDLE)lpParam;
+ HRESULT hrc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (SUCCEEDED(hrc))
+ {
+ /* create IVirtualBox instance */
+ ComPtr<IVirtualBox> pVirtualBox;
+ hrc = CoCreateInstance(CLSID_VirtualBox, NULL, CLSCTX_INPROC_SERVER /*CLSCTX_LOCAL_SERVER */, IID_IVirtualBox,
+ (void **)pVirtualBox.asOutParam());
+ if (SUCCEEDED(hrc))
+ {
+ /* wait a minute allowing clients to connect to the instance */
+ WaitForSingleObject(hEvent, 60 * 1000);
+ /* remove reference. If anybody connected to IVirtualBox it will stay alive. */
+ pVirtualBox.setNull();
+ }
+ CoUninitialize();
+ }
+ return 0L;
+}
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpCmdLine*/, int /*nShowCmd*/)
+{
+ int argc = __argc;
+ char **argv = __argv;
+
+ /*
+ * Need to parse the command line before initializing the VBox runtime so we can
+ * change to the user home directory before logs are being created.
+ */
+ for (int i = 1; i < argc; i++)
+ if ( (argv[i][0] == '/' || argv[i][0] == '-')
+ && stricmp(&argv[i][1], "embedding") == 0) /* ANSI */
+ {
+ /* %HOMEDRIVE%%HOMEPATH% */
+ wchar_t wszHome[RTPATH_MAX];
+ DWORD cEnv = GetEnvironmentVariable(L"HOMEDRIVE", &wszHome[0], RTPATH_MAX);
+ if (cEnv && cEnv < RTPATH_MAX)
+ {
+ DWORD cwc = cEnv; /* doesn't include NUL */
+ cEnv = GetEnvironmentVariable(L"HOMEPATH", &wszHome[cEnv], RTPATH_MAX - cwc);
+ if (cEnv && cEnv < RTPATH_MAX - cwc)
+ {
+ /* If this fails there is nothing we can do. Ignore. */
+ SetCurrentDirectory(wszHome);
+ }
+ }
+ }
+
+ /*
+ * Initialize the VBox runtime without loading
+ * the support driver.
+ */
+ RTR3InitExe(argc, &argv, 0);
+
+ static const RTGETOPTDEF s_aOptions[] =
+ {
+ { "--embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "-embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "/embedding", 'e', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "--unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "-unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "/unregserver", 'u', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "--regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "-regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "/regserver", 'r', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "--reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "-reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "/reregserver", 'f', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "--helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+ { "-helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+ { "/helper", 'H', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+ { "--logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+ { "-logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+ { "/logfile", 'F', RTGETOPT_REQ_STRING | RTGETOPT_FLAG_ICASE },
+ { "--logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+ { "-logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+ { "/logrotate", 'R', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+ { "--logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
+ { "-logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
+ { "/logsize", 'S', RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_ICASE },
+ { "--loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+ { "-loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+ { "/loginterval", 'I', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_ICASE },
+ { "--registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "-registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ { "/registervbox", 'b', RTGETOPT_REQ_NOTHING | RTGETOPT_FLAG_ICASE },
+ };
+
+ bool fRun = true;
+ bool fRegister = false;
+ bool fUnregister = false;
+ const char *pszPipeName = NULL;
+ const char *pszLogFile = NULL;
+ uint32_t cHistory = 10; // enable log rotation, 10 files
+ uint32_t uHistoryFileTime = RT_SEC_1DAY; // max 1 day per file
+ uint64_t uHistoryFileSize = 100 * _1M; // max 100MB per file
+ bool fRegisterVBox = false;
+
+ RTGETOPTSTATE GetOptState;
+ int vrc = RTGetOptInit(&GetOptState, argc, argv, &s_aOptions[0], RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
+ AssertRC(vrc);
+
+ RTGETOPTUNION ValueUnion;
+ while ((vrc = RTGetOpt(&GetOptState, &ValueUnion)))
+ {
+ switch (vrc)
+ {
+ case 'e':
+ /* already handled above */
+ break;
+
+ case 'u':
+ fUnregister = true;
+ fRun = false;
+ break;
+
+ case 'r':
+ fRegister = true;
+ fRun = false;
+ break;
+
+ case 'f':
+ fUnregister = true;
+ fRegister = true;
+ fRun = false;
+ break;
+
+ case 'H':
+ pszPipeName = ValueUnion.psz;
+ if (!pszPipeName)
+ pszPipeName = "";
+ fRun = false;
+ break;
+
+ case 'F':
+ pszLogFile = ValueUnion.psz;
+ break;
+
+ case 'R':
+ cHistory = ValueUnion.u32;
+ break;
+
+ case 'S':
+ uHistoryFileSize = ValueUnion.u64;
+ break;
+
+ case 'I':
+ uHistoryFileTime = ValueUnion.u32;
+ break;
+
+ case 'h':
+ {
+ static const WCHAR s_wszText[] = L"Options:\n\n"
+ L"/RegServer:\tregister COM out-of-proc server\n"
+ L"/UnregServer:\tunregister COM out-of-proc server\n"
+ L"/ReregServer:\tunregister and register COM server\n"
+ L"no options:\trun the server";
+ static const WCHAR s_wszTitle[] = L"Usage";
+ fRun = false;
+ MessageBoxW(NULL, s_wszText, s_wszTitle, MB_OK);
+ return 0;
+ }
+
+ case 'V':
+ {
+ static const WCHAR s_wszTitle[] = L"Version";
+ char *pszText = NULL;
+ RTStrAPrintf(&pszText, "%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+ PRTUTF16 pwszText = NULL;
+ RTStrToUtf16(pszText, &pwszText);
+ RTStrFree(pszText);
+ MessageBoxW(NULL, pwszText, s_wszTitle, MB_OK);
+ RTUtf16Free(pwszText);
+ fRun = false;
+ return 0;
+ }
+
+ case 'b':
+ fRegisterVBox = true;
+ break;
+
+ default:
+ /** @todo this assumes that stderr is visible, which is not
+ * true for standard Windows applications. */
+ /* continue on command line errors... */
+ RTGetOptPrintError(vrc, &ValueUnion);
+ }
+ }
+
+ /* Only create the log file when running VBoxSVC normally, but not when
+ * registering/unregistering or calling the helper functionality. */
+ if (fRun)
+ {
+ /** @todo Merge this code with server.cpp (use Logging.cpp?). */
+ char szLogFile[RTPATH_MAX];
+ if (!pszLogFile || !*pszLogFile)
+ {
+ vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile));
+ if (RT_SUCCESS(vrc))
+ vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log");
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to construct release log filename, vrc=%Rrc", vrc);
+ pszLogFile = szLogFile;
+ }
+
+ RTERRINFOSTATIC ErrInfo;
+ vrc = com::VBoxLogRelCreate("COM Server", pszLogFile,
+ RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG,
+ VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG",
+#ifdef VBOX_WITH_SDS
+ RTLOGDEST_FILE | RTLOGDEST_F_DELAY_FILE,
+#else
+ RTLOGDEST_FILE,
+#endif
+ UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize,
+ RTErrInfoInitStatic(&ErrInfo));
+ if (RT_FAILURE(vrc))
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open release log (%s, %Rrc)", ErrInfo.Core.pszMsg, vrc);
+ }
+
+ /* Set up a build identifier so that it can be seen from core dumps what
+ * exact build was used to produce the core. Same as in Console::i_powerUpThread(). */
+ static char saBuildID[48];
+ RTStrPrintf(saBuildID, sizeof(saBuildID), "%s%s%s%s VirtualBox %s r%u %s%s%s%s",
+ "BU", "IL", "DI", "D", RTBldCfgVersion(), RTBldCfgRevision(), "BU", "IL", "DI", "D");
+
+ AssertCompile(VBOX_COM_INIT_F_DEFAULT == VBOX_COM_INIT_F_AUTO_REG_UPDATE);
+ HRESULT hRes = com::Initialize(fRun ? VBOX_COM_INIT_F_AUTO_REG_UPDATE : 0);
+ AssertLogRelMsg(SUCCEEDED(hRes), ("SVCMAIN: init failed: %Rhrc\n", hRes));
+
+ g_pModule = new CExeModule();
+ if(g_pModule == NULL)
+ return RTMsgErrorExit(RTEXITCODE_FAILURE, "not enough memory to create ExeModule.");
+ g_pModule->Init(ObjectMap, hInstance, &LIBID_VirtualBox);
+ g_pModule->dwThreadID = GetCurrentThreadId();
+
+ int nRet = 0;
+ if (!fRun)
+ {
+#ifndef VBOX_WITH_MIDL_PROXY_STUB /* VBoxProxyStub.dll does all the registration work. */
+ if (fUnregister)
+ {
+ g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, FALSE);
+ nRet = g_pModule->UnregisterServer(TRUE);
+ }
+ if (fRegister)
+ {
+ g_pModule->UpdateRegistryFromResource(IDR_VIRTUALBOX, TRUE);
+ nRet = g_pModule->RegisterServer(TRUE);
+ }
+#endif
+ if (pszPipeName)
+ {
+ Log(("SVCMAIN: Processing Helper request (cmdline=\"%s\")...\n", pszPipeName));
+
+ if (!*pszPipeName)
+ vrc = VERR_INVALID_PARAMETER;
+
+ if (RT_SUCCESS(vrc))
+ {
+ /* do the helper job */
+ SVCHlpServer server;
+ vrc = server.open(pszPipeName);
+ if (RT_SUCCESS(vrc))
+ vrc = server.run();
+ }
+ if (RT_FAILURE(vrc))
+ {
+ Log(("SVCMAIN: Failed to process Helper request (%Rrc).\n", vrc));
+ nRet = 1;
+ }
+ }
+ }
+ else
+ {
+
+ g_pModule->StartMonitor();
+#if _WIN32_WINNT >= 0x0400
+ hRes = g_pModule->RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE | REGCLS_SUSPENDED);
+ _ASSERTE(SUCCEEDED(hRes));
+ hRes = CoResumeClassObjects();
+#else
+ hRes = _Module.RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE);
+#endif
+ _ASSERTE(SUCCEEDED(hRes));
+
+ /*
+ * Register windows console signal handler to react to Ctrl-C,
+ * Ctrl-Break, Close; but more importantly - to get notified
+ * about shutdown when we are running in the context of the
+ * autostart service - we won't get WM_ENDSESSION in that
+ * case.
+ */
+ ::SetConsoleCtrlHandler(ConsoleCtrlHandler, TRUE);
+
+
+ if (RT_SUCCESS(CreateMainWindow()))
+ Log(("SVCMain: Main window succesfully created\n"));
+ else
+ Log(("SVCMain: Failed to create main window\n"));
+
+ /* create thread to register IVirtualBox in VBoxSDS
+ * It is used for starting the VBoxSVC in the windows
+ * session 0. */
+ HANDLE hWaitEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ HANDLE hRegisterVBoxThread = NULL;
+ if (fRegisterVBox)
+ {
+ DWORD dwThreadId = 0;
+ hRegisterVBoxThread = CreateThread(NULL, 0, threadRegisterVirtualBox, (LPVOID)hWaitEvent,
+ 0, &dwThreadId);
+ }
+
+ MSG msg;
+ while (GetMessage(&msg, 0, 0, 0) > 0)
+ {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ DestroyMainWindow();
+
+ if (fRegisterVBox)
+ {
+ SetEvent(hWaitEvent);
+ WaitForSingleObject(hRegisterVBoxThread, INFINITE);
+ CloseHandle(hRegisterVBoxThread);
+ CloseHandle(hWaitEvent);
+ }
+
+ g_pModule->RevokeClassObjects();
+ }
+
+ g_pModule->Term();
+
+#ifdef VBOX_WITH_SDS
+ g_fRegisteredWithVBoxSDS = false; /* Don't trust COM LPC to work right from now on. */
+#endif
+ com::Shutdown();
+
+ if(g_pModule)
+ delete g_pModule;
+ g_pModule = NULL;
+
+ Log(("SVCMAIN: Returning, COM server process ends.\n"));
+ return nRet;
+}