summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/r3/win
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/Runtime/r3/win')
-rw-r--r--src/VBox/Runtime/r3/win/Makefile.kup0
-rw-r--r--src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp194
-rw-r--r--src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp71
-rw-r--r--src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp143
-rw-r--r--src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp116
-rw-r--r--src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp129
-rw-r--r--src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp54
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp223
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp269
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp358
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp131
-rw-r--r--src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp183
-rw-r--r--src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp117
-rw-r--r--src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp82
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def1678
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def1640
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def395
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def430
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-win32.def62
-rw-r--r--src/VBox/Runtime/r3/win/VBoxRT-win64.def74
-rw-r--r--src/VBox/Runtime/r3/win/alloc-win.cpp210
-rw-r--r--src/VBox/Runtime/r3/win/allocex-win.cpp133
-rw-r--r--src/VBox/Runtime/r3/win/dir-win.cpp169
-rw-r--r--src/VBox/Runtime/r3/win/direnum-win.cpp404
-rw-r--r--src/VBox/Runtime/r3/win/dllmain-win.cpp100
-rw-r--r--src/VBox/Runtime/r3/win/env-win.cpp520
-rw-r--r--src/VBox/Runtime/r3/win/errvars-win.cpp108
-rw-r--r--src/VBox/Runtime/r3/win/fileaio-win.cpp554
-rw-r--r--src/VBox/Runtime/r3/win/fileio-win.cpp1549
-rw-r--r--src/VBox/Runtime/r3/win/fs-win.cpp440
-rw-r--r--src/VBox/Runtime/r3/win/init-win.cpp919
-rw-r--r--src/VBox/Runtime/r3/win/internal-r3-win.h244
-rw-r--r--src/VBox/Runtime/r3/win/krnlmod-win.cpp331
-rw-r--r--src/VBox/Runtime/r3/win/ldrNative-win.cpp326
-rw-r--r--src/VBox/Runtime/r3/win/localipc-win.cpp1778
-rw-r--r--src/VBox/Runtime/r3/win/mp-win.cpp822
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp57
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp58
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm44
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp115
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-atexit-win.asm44
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp150
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm44
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp211
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp162
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp174
-rw-r--r--src/VBox/Runtime/r3/win/nocrt-streams-win.cpp223
-rw-r--r--src/VBox/Runtime/r3/win/ntdll-mini-implib.def163
-rw-r--r--src/VBox/Runtime/r3/win/path-win.cpp743
-rw-r--r--src/VBox/Runtime/r3/win/pathint-win.cpp204
-rw-r--r--src/VBox/Runtime/r3/win/pipe-win.cpp1537
-rw-r--r--src/VBox/Runtime/r3/win/process-win.cpp2750
-rw-r--r--src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp72
-rw-r--r--src/VBox/Runtime/r3/win/sched-win.cpp333
-rw-r--r--src/VBox/Runtime/r3/win/semevent-win.cpp315
-rw-r--r--src/VBox/Runtime/r3/win/semeventmulti-win.cpp389
-rw-r--r--src/VBox/Runtime/r3/win/semmutex-win.cpp356
-rw-r--r--src/VBox/Runtime/r3/win/serialport-win.cpp1056
-rw-r--r--src/VBox/Runtime/r3/win/shmem-win.cpp473
-rw-r--r--src/VBox/Runtime/r3/win/symlink-win.cpp367
-rw-r--r--src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp68
-rw-r--r--src/VBox/Runtime/r3/win/thread-win.cpp588
-rw-r--r--src/VBox/Runtime/r3/win/thread2-win.cpp84
-rw-r--r--src/VBox/Runtime/r3/win/time-win.cpp228
-rw-r--r--src/VBox/Runtime/r3/win/time2-win.cpp164
-rw-r--r--src/VBox/Runtime/r3/win/timer-win.cpp520
-rw-r--r--src/VBox/Runtime/r3/win/tls-win.cpp230
-rw-r--r--src/VBox/Runtime/r3/win/tpm-win.cpp307
-rw-r--r--src/VBox/Runtime/r3/win/utf16locale-win.cpp58
-rw-r--r--src/VBox/Runtime/r3/win/utf8-win.cpp204
-rw-r--r--src/VBox/Runtime/r3/win/uuid-win.cpp195
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h43
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h42
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm56
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp891
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp93
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm57
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp84
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm59
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp103
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm58
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp113
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes.h155
-rw-r--r--src/VBox/Runtime/r3/win/vcc-fakes.mac76
84 files changed, 29472 insertions, 0 deletions
diff --git a/src/VBox/Runtime/r3/win/Makefile.kup b/src/VBox/Runtime/r3/win/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/Makefile.kup
diff --git a/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp b/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp
new file mode 100644
index 00000000..1eead2b1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTCrStoreCreateSnapshotById-win.cpp
@@ -0,0 +1,194 @@
+/* $Id: RTCrStoreCreateSnapshotById-win.cpp $ */
+/** @file
+ * IPRT - RTCrStoreCreateSnapshotById, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/crypto/store.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/once.h>
+#include <iprt/ldr.h>
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef HCERTSTORE (WINAPI *PFNCERTOPENSTORE)(PCSTR pszStoreProvider, DWORD dwEncodingType, HCRYPTPROV_LEGACY hCryptProv,
+ DWORD dwFlags, const void *pvParam);
+typedef BOOL (WINAPI *PFNCERTCLOSESTORE)(HCERTSTORE hCertStore, DWORD dwFlags);
+typedef PCCERT_CONTEXT (WINAPI *PFNCERTENUMCERTIFICATESINSTORE)(HCERTSTORE hCertStore, PCCERT_CONTEXT pPrevCertContext);
+
+
+
+static int rtCrStoreAddCertsFromNative(RTCRSTORE hStore, DWORD fStore, PCRTUTF16 pwszStoreName,
+ PFNCERTOPENSTORE pfnOpenStore, PFNCERTCLOSESTORE pfnCloseStore,
+ PFNCERTENUMCERTIFICATESINSTORE pfnEnumCerts, int rc, PRTERRINFO pErrInfo)
+{
+ DWORD fOpenStore = CERT_STORE_OPEN_EXISTING_FLAG | CERT_STORE_READONLY_FLAG;
+ HCERTSTORE hNativeStore = pfnOpenStore(CERT_STORE_PROV_SYSTEM_W, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
+ NULL /* hCryptProv = default */, fStore | fOpenStore, pwszStoreName);
+ if (hNativeStore)
+ {
+ PCCERT_CONTEXT pCurCtx = NULL;
+ while ((pCurCtx = pfnEnumCerts(hNativeStore, pCurCtx)) != NULL)
+ {
+ if (pCurCtx->dwCertEncodingType & X509_ASN_ENCODING)
+ {
+ RTERRINFOSTATIC StaticErrInfo;
+ RTASN1CURSORPRIMARY PrimaryCursor;
+ RTAsn1CursorInitPrimary(&PrimaryCursor, pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
+ RTErrInfoInitStatic(&StaticErrInfo),
+ &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "CurCtx");
+ RTCRX509CERTIFICATE MyCert;
+ int rc2 = RTCrX509Certificate_DecodeAsn1(&PrimaryCursor.Cursor, 0, &MyCert, "Cert");
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = RTCrStoreCertAddEncoded(hStore, RTCRCERTCTX_F_ENC_X509_DER | RTCRCERTCTX_F_ADD_IF_NOT_FOUND,
+ pCurCtx->pbCertEncoded, pCurCtx->cbCertEncoded,
+ RTErrInfoInitStatic(&StaticErrInfo));
+ RTCrX509Certificate_Delete(&MyCert);
+ }
+ if (RT_FAILURE(rc2))
+ {
+ if (RTErrInfoIsSet(&StaticErrInfo.Core))
+ RTErrInfoAddF(pErrInfo, -rc2, " %s", StaticErrInfo.Core.pszMsg);
+ else
+ RTErrInfoAddF(pErrInfo, -rc2, " %Rrc adding cert", rc2);
+ rc = -rc2;
+ }
+ }
+ }
+ pfnCloseStore(hNativeStore, CERT_CLOSE_STORE_CHECK_FLAG);
+ }
+ else
+ {
+ DWORD uLastErr = GetLastError();
+ if (uLastErr != ERROR_FILE_NOT_FOUND)
+ rc = RTErrInfoAddF(pErrInfo, -RTErrConvertFromWin32(uLastErr),
+ " CertOpenStore(%#x,'%ls') failed: %u", fStore, pwszStoreName);
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTCrStoreCreateSnapshotById(PRTCRSTORE phStore, RTCRSTOREID enmStoreId, PRTERRINFO pErrInfo)
+{
+ AssertReturn(enmStoreId > RTCRSTOREID_INVALID && enmStoreId < RTCRSTOREID_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Create an empty in-memory store.
+ */
+ RTCRSTORE hStore;
+ int rc = RTCrStoreCreateInMem(&hStore, 128);
+ if (RT_SUCCESS(rc))
+ {
+ *phStore = hStore;
+
+ /*
+ * Resolve the APIs we need to do this job.
+ */
+ RTLDRMOD hLdrMod;
+ int rc2 = RTLdrLoadSystem("crypt32.dll", false /*NoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc2))
+ {
+ PFNCERTOPENSTORE pfnOpenStore = NULL;
+ rc2 = RTLdrGetSymbol(hLdrMod, "CertOpenStore", (void **)&pfnOpenStore);
+
+ PFNCERTCLOSESTORE pfnCloseStore = NULL;
+ if (RT_SUCCESS(rc2))
+ rc2 = RTLdrGetSymbol(hLdrMod, "CertCloseStore", (void **)&pfnCloseStore);
+
+ PFNCERTENUMCERTIFICATESINSTORE pfnEnumCerts = NULL;
+ if (RT_SUCCESS(rc2))
+ rc2 = RTLdrGetSymbol(hLdrMod, "CertEnumCertificatesInStore", (void **)&pfnEnumCerts);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Do the work.
+ */
+ DWORD fStore = CERT_SYSTEM_STORE_CURRENT_USER;
+ switch (enmStoreId)
+ {
+ case RTCRSTOREID_SYSTEM_TRUSTED_CAS_AND_CERTIFICATES:
+ case RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS:
+ fStore = CERT_SYSTEM_STORE_LOCAL_MACHINE;
+ RT_FALL_THRU();
+ case RTCRSTOREID_USER_TRUSTED_CAS_AND_CERTIFICATES:
+ case RTCRSTOREID_USER_INTERMEDIATE_CAS:
+ {
+ /** @todo CA and MY in s_apwszRootStores are _very_ questionable!!! However,
+ * curl may need them to work correct and it doesn't seem to have any
+ * intermediate ca file. :/ */
+ static PCRTUTF16 const s_apwszRootStores[] = { L"AuthRoot", L"CA", L"MY", L"Root" };
+ static PCRTUTF16 const s_apwszIntermediateStores[] = { L"CA", L"MY" };
+ PCRTUTF16 const *papwszStores = s_apwszRootStores;
+ uint32_t cStores = RT_ELEMENTS(s_apwszRootStores);
+ if (enmStoreId == RTCRSTOREID_USER_INTERMEDIATE_CAS || enmStoreId == RTCRSTOREID_SYSTEM_INTERMEDIATE_CAS)
+ {
+ papwszStores = s_apwszIntermediateStores;
+ cStores = RT_ELEMENTS(s_apwszIntermediateStores);
+ }
+
+ for (uint32_t i = 0; i < cStores; i++)
+ rc = rtCrStoreAddCertsFromNative(hStore, fStore, papwszStores[i], pfnOpenStore, pfnCloseStore,
+ pfnEnumCerts, rc, pErrInfo);
+ break;
+ }
+
+ default:
+ AssertFailed(); /* implement me */
+ }
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, -rc2, "Error resolving crypt32.dll APIs");
+ RTLdrClose(hLdrMod);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, -rc2, "Error loading crypt32.dll");
+ }
+ else
+ RTErrInfoSet(pErrInfo, rc, "RTCrStoreCreateInMem failed");
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCreateSnapshotById);
+
diff --git a/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp b/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp
new file mode 100644
index 00000000..97a38271
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTFileQuerySectorSize-win.cpp
@@ -0,0 +1,71 @@
+/* $Id: RTFileQuerySectorSize-win.cpp $ */
+/** @file
+ * IPRT - RTFileQuerySectorSize, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/file.h>
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+
+#include <iprt/win/windows.h>
+
+
+
+RTDECL(int) RTFileQuerySectorSize(RTFILE hFile, uint32_t *pcbSector)
+{
+ AssertPtrReturn(pcbSector, VERR_INVALID_PARAMETER);
+
+ DISK_GEOMETRY DriveGeo;
+ RT_ZERO(DriveGeo);
+ DWORD cbDriveGeo = 0;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
+ {
+ AssertReturn(DriveGeo.BytesPerSector > 0, VERR_INVALID_PARAMETER);
+ *pcbSector = DriveGeo.BytesPerSector;
+ return VINF_SUCCESS;
+ }
+ int rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsg(rc == VERR_IO_NOT_READY, ("%d / %Rrc\n", GetLastError(), rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp b/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp
new file mode 100644
index 00000000..c0b7f780
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTHandleGetStandard-win.cpp
@@ -0,0 +1,143 @@
+/* $Id: RTHandleGetStandard-win.cpp $ */
+/** @file
+ * IPRT - RTHandleGetStandard, Windows.
+ */
+
+/*
+ * Copyright (C) 2010-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/handle.h>
+
+#include <iprt/file.h>
+#include <iprt/pipe.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+
+#include <iprt/win/windows.h>
+
+#include "internal/socket.h" /* (Needs Windows.h.) */
+#include "internal-r3-win.h" /* (Needs Windows.h.) */
+
+
+RTDECL(int) RTHandleGetStandard(RTHANDLESTD enmStdHandle, bool fLeaveOpen, PRTHANDLE ph)
+{
+ /*
+ * Validate and convert input.
+ */
+ AssertPtrReturn(ph, VERR_INVALID_POINTER);
+ DWORD dwStdHandle;
+ switch (enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
+ case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
+ case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Is the requested descriptor valid and which IPRT handle type does it
+ * best map on to?
+ */
+ HANDLE hNative = GetStdHandle(dwStdHandle);
+ if (hNative == INVALID_HANDLE_VALUE)
+ return RTErrConvertFromWin32(GetLastError());
+
+ DWORD dwInfo = 0;
+ if (g_pfnGetHandleInformation && !g_pfnGetHandleInformation(hNative, &dwInfo))
+ return RTErrConvertFromWin32(GetLastError());
+ bool const fInherit = RT_BOOL(dwInfo & HANDLE_FLAG_INHERIT);
+
+ SetLastError(NO_ERROR);
+ RTHANDLE h;
+ DWORD dwType = GetFileType(hNative);
+ switch (dwType & ~FILE_TYPE_REMOTE)
+ {
+ case FILE_TYPE_UNKNOWN:
+ if (GetLastError() != NO_ERROR)
+ return RTErrConvertFromWin32(GetLastError());
+ RT_FALL_THROUGH();
+ default:
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_DISK:
+ h.enmType = RTHANDLETYPE_FILE;
+ break;
+
+ case FILE_TYPE_PIPE:
+ {
+ DWORD cMaxInstances;
+ DWORD fInfo;
+ if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
+ h.enmType = RTHANDLETYPE_SOCKET;
+ else
+ h.enmType = RTHANDLETYPE_PIPE;
+ break;
+ }
+ }
+
+ /*
+ * Create the IPRT handle.
+ */
+ int rc;
+ switch (h.enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ /** @todo fLeaveOpen */
+ rc = RTFileFromNative(&h.u.hFile, (RTHCUINTPTR)hNative);
+ break;
+
+ case RTHANDLETYPE_PIPE:
+ rc = RTPipeFromNative(&h.u.hPipe, (RTHCUINTPTR)hNative,
+ (enmStdHandle == RTHANDLESTD_INPUT ? RTPIPE_N_READ : RTPIPE_N_WRITE)
+ | (fInherit ? RTPIPE_N_INHERIT : 0)
+ | (fLeaveOpen ? RTPIPE_N_LEAVE_OPEN : 0));
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ rc = rtSocketCreateForNative(&h.u.hSocket, (RTHCUINTPTR)hNative, fLeaveOpen);
+ break;
+
+ default: /* shut up gcc */
+ return VERR_INTERNAL_ERROR;
+ }
+
+ if (RT_SUCCESS(rc))
+ *ph = h;
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp b/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp
new file mode 100644
index 00000000..10efd342
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTLocaleQueryNormalizedBaseLocaleName-win.cpp
@@ -0,0 +1,116 @@
+/* $Id: RTLocaleQueryNormalizedBaseLocaleName-win.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryNormalizedBaseLocaleName, ring-3, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+
+RTDECL(int) RTLocaleQueryNormalizedBaseLocaleName(char *pszName, size_t cbName)
+{
+ /*
+ * Note! This part is duplicate of r3/generic/RTLocaleQueryNormalizedBaseLocaleName-r3-generic.cpp!
+ */
+ char szLocale[_1K];
+ int rc = RTLocaleQueryLocaleName(szLocale, sizeof(szLocale));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * May return some complicated "LC_XXX=yyy;LC.." sequence if
+ * partially set (like IPRT does). Try get xx_YY sequence first
+ * because 'C' or 'POSIX' may be LC_xxx variants that haven't been
+ * set yet.
+ *
+ * ASSUMES complicated locale mangling is done in a certain way...
+ */
+ const char *pszLocale = strchr(szLocale, '=');
+ if (!pszLocale)
+ pszLocale = szLocale;
+ else
+ pszLocale++;
+ bool fSeenC = false;
+ bool fSeenPOSIX = false;
+ do
+ {
+ const char *pszEnd = strchr(pszLocale, ';');
+
+ if ( RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(pszLocale)
+ && ( pszLocale[5] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[5])) )
+ return RTStrCopyEx(pszName, cbName, pszLocale, 5);
+
+ if ( pszLocale[0] == 'C'
+ && ( pszLocale[1] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[1])) )
+ fSeenC = true;
+ else if ( strncmp(pszLocale, "POSIX", 5) == 0
+ && ( pszLocale[5] == '\0'
+ || RT_C_IS_PUNCT(pszLocale[5])) )
+ fSeenPOSIX = true;
+
+ /* advance */
+ pszLocale = pszEnd ? strchr(pszEnd + 1, '=') : NULL;
+ } while (pszLocale++);
+
+ if (fSeenC || fSeenPOSIX)
+ return RTStrCopy(pszName, cbName, "C"); /* C and POSIX should be identical IIRC, so keep it simple. */
+
+ rc = VERR_NOT_AVAILABLE;
+ }
+
+ /*
+ * Fallback.
+ */
+ if ( GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SISO639LANGNAME, szLocale, sizeof(szLocale)) == 3
+ && GetLocaleInfoA(GetUserDefaultLCID(), LOCALE_SISO3166CTRYNAME, &szLocale[3], sizeof(szLocale) - 4) == 3)
+ {
+ szLocale[2] = '_';
+ Assert(RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(szLocale));
+ return RTStrCopy(pszName, cbName, szLocale);
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp b/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp
new file mode 100644
index 00000000..a95b1c22
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTLocaleQueryUserCountryCode-win.cpp
@@ -0,0 +1,129 @@
+/* $Id: RTLocaleQueryUserCountryCode-win.cpp $ */
+/** @file
+ * IPRT - RTLocaleQueryUserCountryCode, ring-3, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/locale.h>
+#include "internal/iprt.h"
+
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef GEOID (WINAPI *PFNGETUSERGEOID)(GEOCLASS);
+typedef INT (WINAPI *PFNGETGEOINFOW)(GEOID,GEOTYPE,LPWSTR,INT,LANGID);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to GetUserGeoID. */
+static PFNGETUSERGEOID g_pfnGetUserGeoID = NULL;
+/** Pointer to GetGeoInfoW. */
+static PFNGETGEOINFOW g_pfnGetGeoInfoW = NULL;
+/** Set if we've tried to resolve the APIs. */
+static bool volatile g_fResolvedApis = false;
+
+
+RTDECL(int) RTLocaleQueryUserCountryCode(char pszCountryCode[3])
+{
+ /*
+ * Get API pointers.
+ */
+ PFNGETUSERGEOID pfnGetUserGeoID;
+ PFNGETGEOINFOW pfnGetGeoInfoW;
+ if (g_fResolvedApis)
+ {
+ pfnGetUserGeoID = g_pfnGetUserGeoID;
+ pfnGetGeoInfoW = g_pfnGetGeoInfoW;
+ }
+ else
+ {
+ pfnGetUserGeoID = (PFNGETUSERGEOID)GetProcAddress(g_hModKernel32, "GetUserGeoID");
+ pfnGetGeoInfoW = (PFNGETGEOINFOW)GetProcAddress(g_hModKernel32, "GetGeoInfoW");
+ g_pfnGetUserGeoID = pfnGetUserGeoID;
+ g_pfnGetGeoInfoW = pfnGetGeoInfoW;
+ g_fResolvedApis = true;
+ }
+
+ int rc;
+ if ( pfnGetGeoInfoW
+ && pfnGetUserGeoID)
+ {
+ /*
+ * Call the API and retrieve the two letter ISO country code.
+ */
+ GEOID idGeo = pfnGetUserGeoID(GEOCLASS_NATION);
+ if (idGeo != GEOID_NOT_AVAILABLE)
+ {
+ RTUTF16 wszName[16];
+ RT_ZERO(wszName);
+ DWORD cwcReturned = pfnGetGeoInfoW(idGeo, GEO_ISO2, wszName, RT_ELEMENTS(wszName), LOCALE_NEUTRAL);
+ if ( cwcReturned >= 2
+ && cwcReturned <= 3
+ && wszName[2] == '\0'
+ && wszName[1] != '\0'
+ && RT_C_IS_ALPHA(wszName[1])
+ && wszName[0] != '\0'
+ && RT_C_IS_ALPHA(wszName[0]) )
+ {
+ pszCountryCode[0] = RT_C_TO_UPPER(wszName[0]);
+ pszCountryCode[1] = RT_C_TO_UPPER(wszName[1]);
+ pszCountryCode[2] = '\0';
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("cwcReturned=%d err=%u wszName='%.16ls'\n", cwcReturned, GetLastError(), wszName));
+ }
+ rc = VERR_NOT_AVAILABLE;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ pszCountryCode[0] = 'Z';
+ pszCountryCode[1] = 'Z';
+ pszCountryCode[2] = '\0';
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp b/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp
new file mode 100644
index 00000000..27d3bc05
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTLogWriteDebugger-win.cpp
@@ -0,0 +1,54 @@
+/* $Id: RTLogWriteDebugger-win.cpp $ */
+/** @file
+ * IPRT - Log To Debugger, Win32.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+
+#include <iprt/log.h>
+#include <iprt/assert.h>
+
+
+RTDECL(void) RTLogWriteDebugger(const char *pch, size_t cb)
+{
+ if (pch[cb] != '\0')
+ AssertBreakpoint();
+ OutputDebugStringA(pch);
+ return;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp b/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp
new file mode 100644
index 00000000..d00dacb1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemFirmware-win.cpp
@@ -0,0 +1,223 @@
+/* $Id: RTSystemFirmware-win.cpp $ */
+/** @file
+ * IPRT - System firmware information, Win32.
+ */
+
+/*
+ * Copyright (C) 2019-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/system.h>
+
+#include <iprt/nt/nt-and-windows.h>
+#include <WinSDKVer.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/ldr.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+#if _WIN32_MAXVER < 0x0602 /* Windows 7 or older, supply missing GetFirmwareType bits. */
+typedef enum _FIRMWARE_TYPE
+{
+ FirmwareTypeUnknown,
+ FirmwareTypeBios,
+ FirmwareTypeUefi,
+ FirmwareTypeMax
+} FIRMWARE_TYPE;
+typedef FIRMWARE_TYPE *PFIRMWARE_TYPE;
+WINBASEAPI BOOL WINAPI GetFirmwareType(PFIRMWARE_TYPE);
+#endif
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Defines the UEFI Globals UUID. */
+#define VBOX_UEFI_UUID_GLOBALS L"{8BE4DF61-93CA-11D2-AA0D-00E098032B8C}"
+/** Defines an UEFI dummy UUID, see MSDN docs of the API. */
+#define VBOX_UEFI_UUID_DUMMY L"{00000000-0000-0000-0000-000000000000}"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile bool g_fResolvedApis = false;
+static decltype(GetFirmwareType) *g_pfnGetFirmwareType;
+static decltype(GetFirmwareEnvironmentVariableW) *g_pfnGetFirmwareEnvironmentVariableW;
+
+
+static void rtSystemFirmwareResolveApis(void)
+{
+ FARPROC pfnTmp1 = GetProcAddress(g_hModKernel32, "GetFirmwareType");
+ FARPROC pfnTmp2 = GetProcAddress(g_hModKernel32, "GetFirmwareEnvironmentVariableW");
+ ASMCompilerBarrier(); /* paranoia^2 */
+
+ g_pfnGetFirmwareType = (decltype(GetFirmwareType) *)pfnTmp1;
+ g_pfnGetFirmwareEnvironmentVariableW = (decltype(GetFirmwareEnvironmentVariableW) *)pfnTmp2;
+ ASMAtomicWriteBool(&g_fResolvedApis, true);
+}
+
+
+static int rtSystemFirmwareGetPrivileges(LPCTSTR pcszPrivilege)
+{
+ HANDLE hToken;
+ BOOL fRc = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
+ if (!fRc)
+ return RTErrConvertFromWin32(GetLastError());
+
+ int rc = VINF_SUCCESS;
+
+ TOKEN_PRIVILEGES tokenPriv;
+ fRc = LookupPrivilegeValue(NULL, pcszPrivilege, &tokenPriv.Privileges[0].Luid);
+ if (fRc)
+ {
+ tokenPriv.PrivilegeCount = 1;
+ tokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ fRc = AdjustTokenPrivileges(hToken, FALSE, &tokenPriv, 0, (PTOKEN_PRIVILEGES)NULL, 0);
+ if (!fRc)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ CloseHandle(hToken);
+
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryFirmwareType(PRTSYSFWTYPE penmFirmwareType)
+{
+ AssertPtrReturn(penmFirmwareType, VERR_INVALID_POINTER);
+
+ if (!g_fResolvedApis)
+ rtSystemFirmwareResolveApis();
+
+ *penmFirmwareType = RTSYSFWTYPE_INVALID;
+ int rc = VERR_NOT_SUPPORTED;
+
+ /* GetFirmwareType is Windows 8 and later. */
+ if (g_pfnGetFirmwareType)
+ {
+ FIRMWARE_TYPE enmWinFwType;
+ if (g_pfnGetFirmwareType(&enmWinFwType))
+ {
+ switch (enmWinFwType)
+ {
+ case FirmwareTypeBios:
+ *penmFirmwareType = RTSYSFWTYPE_BIOS;
+ break;
+ case FirmwareTypeUefi:
+ *penmFirmwareType = RTSYSFWTYPE_UEFI;
+ break;
+ default:
+ *penmFirmwareType = RTSYSFWTYPE_UNKNOWN;
+ AssertMsgFailed(("%d\n", enmWinFwType));
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ /* GetFirmwareEnvironmentVariableW is XP and later. */
+ else if (g_pfnGetFirmwareEnvironmentVariableW)
+ {
+ rtSystemFirmwareGetPrivileges(SE_SYSTEM_ENVIRONMENT_NAME);
+
+ /* On a non-UEFI system (or such a system in legacy boot mode), we will get
+ back ERROR_INVALID_FUNCTION when querying any firmware variable. While on a
+ UEFI system we'll typically get ERROR_ACCESS_DENIED or similar as the dummy
+ is a non-exising dummy namespace. See the API docs. */
+ SetLastError(0);
+ uint8_t abWhatever[64];
+ DWORD cbRet = g_pfnGetFirmwareEnvironmentVariableW(L"", VBOX_UEFI_UUID_DUMMY, abWhatever, sizeof(abWhatever));
+ DWORD dwErr = GetLastError();
+ *penmFirmwareType = cbRet != 0 || dwErr != ERROR_INVALID_FUNCTION ? RTSYSFWTYPE_UEFI : RTSYSFWTYPE_BIOS;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryFirmwareBoolean(RTSYSFWBOOL enmBoolean, bool *pfValue)
+{
+ *pfValue = false;
+
+ /*
+ * Translate the enmBoolean to a name:
+ */
+ const wchar_t *pwszName = NULL;
+ switch (enmBoolean)
+ {
+ case RTSYSFWBOOL_SECURE_BOOT:
+ pwszName = L"SecureBoot";
+ break;
+
+ default:
+ AssertReturn(enmBoolean > RTSYSFWBOOL_INVALID && enmBoolean < RTSYSFWBOOL_END, VERR_INVALID_PARAMETER);
+ return VERR_SYS_UNSUPPORTED_FIRMWARE_PROPERTY;
+ }
+
+ /*
+ * Do the query.
+ * Note! This will typically fail with access denied unless we're in an elevated process.
+ */
+ if (!g_pfnGetFirmwareEnvironmentVariableW)
+ return VERR_NOT_SUPPORTED;
+ rtSystemFirmwareGetPrivileges(SE_SYSTEM_ENVIRONMENT_NAME);
+
+ uint8_t bValue = 0;
+ DWORD cbRet = g_pfnGetFirmwareEnvironmentVariableW(pwszName, VBOX_UEFI_UUID_GLOBALS, &bValue, sizeof(bValue));
+ *pfValue = cbRet != 0 && bValue != 0;
+ if (cbRet != 0)
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ if ( dwErr == ERROR_INVALID_FUNCTION
+ || dwErr == ERROR_ENVVAR_NOT_FOUND)
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(dwErr);
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp
new file mode 100644
index 00000000..bd9c97e5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemQueryDmiString-win.cpp
@@ -0,0 +1,269 @@
+/* $Id: RTSystemQueryDmiString-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryDmiString, windows ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define _WIN32_DCOM
+#include <iprt/win/windows.h>
+#include <WbemCli.h>
+
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/**
+ * Initialize COM.
+ *
+ * @returns COM status code.
+ */
+static HRESULT rtSystemDmiWinInitialize(void)
+{
+ HRESULT hrc = CoInitializeEx(0, COINIT_MULTITHREADED);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = CoInitializeSecurity(NULL,
+ -1, /* COM authentication. */
+ NULL, /* Which authentication services. */
+ NULL, /* Reserved. */
+ RPC_C_AUTHN_LEVEL_DEFAULT, /* Default authentication. */
+ RPC_C_IMP_LEVEL_IMPERSONATE, /* Default impersonation. */
+ NULL, /* Authentication info. */
+ EOAC_NONE, /* Additional capabilities. */
+ NULL); /* Reserved. */
+ if (hrc == RPC_E_TOO_LATE)
+ hrc = S_OK;
+ else if (FAILED(hrc))
+ CoUninitialize();
+ }
+ return hrc;
+}
+
+
+/**
+ * Undo what rtSystemDmiWinInitialize did.
+ */
+static void rtSystemDmiWinTerminate(void)
+{
+ CoUninitialize();
+}
+
+
+/**
+ * Convert a UTF-8 string to a BSTR.
+ *
+ * @returns BSTR pointer.
+ * @param psz The UTF-8 string.
+ */
+static BSTR rtSystemWinBstrFromUtf8(const char *psz)
+{
+ PRTUTF16 pwsz = NULL;
+ int rc = RTStrToUtf16(psz, &pwsz);
+ if (RT_FAILURE(rc))
+ return NULL;
+ BSTR pBStr = SysAllocString((const OLECHAR *)pwsz);
+ RTUtf16Free(pwsz);
+ return pBStr;
+}
+
+
+/**
+ * Connect to the DMI server.
+ *
+ * @returns COM status code.
+ * @param pLocator The locator.
+ * @param pszServer The server name.
+ * @param ppServices Where to return the services interface.
+ */
+static HRESULT rtSystemDmiWinConnectToServer(IWbemLocator *pLocator, const char *pszServer, IWbemServices **ppServices)
+{
+ AssertPtr(pLocator);
+ AssertPtrNull(pszServer);
+ AssertPtr(ppServices);
+
+ BSTR pBStrServer = rtSystemWinBstrFromUtf8(pszServer);
+ if (!pBStrServer)
+ return E_OUTOFMEMORY;
+
+ HRESULT hrc = pLocator->ConnectServer(pBStrServer,
+ NULL,
+ NULL,
+ 0,
+ NULL,
+ 0,
+ 0,
+ ppServices);
+ if (SUCCEEDED(hrc))
+ {
+ hrc = CoSetProxyBlanket(*ppServices,
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ NULL,
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ NULL,
+ EOAC_NONE);
+ if (FAILED(hrc))
+ (*ppServices)->Release();
+ }
+ SysFreeString(pBStrServer);
+ return hrc;
+}
+
+
+RTDECL(int) RTSystemQueryDmiString(RTSYSDMISTR enmString, char *pszBuf, size_t cbBuf)
+{
+ AssertPtrReturn(pszBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbBuf > 0, VERR_INVALID_PARAMETER);
+ *pszBuf = '\0';
+ AssertReturn(enmString > RTSYSDMISTR_INVALID && enmString < RTSYSDMISTR_END, VERR_INVALID_PARAMETER);
+
+ /*
+ * Figure the property name before we start.
+ */
+ const char *pszPropName;
+ switch (enmString)
+ {
+ case RTSYSDMISTR_PRODUCT_NAME: pszPropName = "Name"; break;
+ case RTSYSDMISTR_PRODUCT_VERSION: pszPropName = "Version"; break;
+ case RTSYSDMISTR_PRODUCT_UUID: pszPropName = "UUID"; break;
+ case RTSYSDMISTR_PRODUCT_SERIAL: pszPropName = "IdentifyingNumber"; break;
+ case RTSYSDMISTR_MANUFACTURER: pszPropName = "Vendor"; break;
+
+ default:
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Before we do anything with COM, we have to initialize it.
+ */
+ bool fUninit = true;
+ HRESULT hrc = rtSystemDmiWinInitialize();
+ if (hrc == RPC_E_CHANGED_MODE)
+ fUninit = false; /* don't fail if already initialized */
+ else if (FAILED(hrc))
+ return VERR_NOT_SUPPORTED;
+
+ int rc = VERR_NOT_SUPPORTED;
+ BSTR pBstrPropName = rtSystemWinBstrFromUtf8(pszPropName);
+ if (pBstrPropName)
+ {
+ /*
+ * Instantiate the IWbemLocator, whatever that is and connect to the
+ * DMI serve.
+ */
+ IWbemLocator *pLoc;
+ hrc = CoCreateInstance(CLSID_WbemLocator,
+ 0,
+ CLSCTX_INPROC_SERVER,
+ IID_IWbemLocator,
+ (LPVOID *)&pLoc);
+ if (SUCCEEDED(hrc))
+ {
+ IWbemServices *pServices;
+ hrc = rtSystemDmiWinConnectToServer(pLoc, "ROOT\\CIMV2", &pServices);
+ if (SUCCEEDED(hrc))
+ {
+ /*
+ * Enumerate whatever it is we're looking at and try get
+ * the desired property.
+ */
+ BSTR pBstrFilter = rtSystemWinBstrFromUtf8("Win32_ComputerSystemProduct");
+ if (pBstrFilter)
+ {
+ IEnumWbemClassObject *pEnum;
+ hrc = pServices->CreateInstanceEnum(pBstrFilter, 0, NULL, &pEnum);
+ if (SUCCEEDED(hrc))
+ {
+ do
+ {
+ IWbemClassObject *pObj;
+ ULONG cObjRet;
+ hrc = pEnum->Next(WBEM_INFINITE, 1, &pObj, &cObjRet);
+ if ( SUCCEEDED(hrc)
+ && cObjRet >= 1)
+ {
+ VARIANT Var;
+ VariantInit(&Var);
+ hrc = pObj->Get(pBstrPropName, 0, &Var, 0, 0);
+ if ( SUCCEEDED(hrc)
+ && V_VT(&Var) == VT_BSTR)
+ {
+ /*
+ * Convert the BSTR to UTF-8 and copy it
+ * into the return buffer.
+ */
+ char *pszValue;
+ rc = RTUtf16ToUtf8(Var.bstrVal, &pszValue);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCopy(pszBuf, cbBuf, pszValue);
+ RTStrFree(pszValue);
+ hrc = WBEM_S_FALSE;
+ }
+ }
+ VariantClear(&Var);
+ pObj->Release();
+ }
+ } while (hrc != WBEM_S_FALSE);
+
+ pEnum->Release();
+ }
+ SysFreeString(pBstrFilter);
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+ pServices->Release();
+ }
+ pLoc->Release();
+ }
+ SysFreeString(pBstrPropName);
+ }
+ else
+ hrc = E_OUTOFMEMORY;
+ if (fUninit)
+ rtSystemDmiWinTerminate();
+ if (FAILED(hrc) && rc == VERR_NOT_SUPPORTED)
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp
new file mode 100644
index 00000000..56688e42
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemQueryOSInfo-win.cpp
@@ -0,0 +1,358 @@
+/* $Id: RTSystemQueryOSInfo-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, generic stub.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/win/windows.h>
+#include <WinUser.h>
+
+#include "internal-r3-win.h"
+#include <iprt/system.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * These are the PRODUCT_* defines found in the Vista Platform SDK and returned
+ * by GetProductInfo().
+ *
+ * We define them ourselves because we don't necessarily have any Vista PSDK around.
+ */
+typedef enum RTWINPRODTYPE
+{
+ kRTWinProdType_UNDEFINED = 0x00000000, ///< An unknown product
+ kRTWinProdType_BUSINESS = 0x00000006, ///< Business Edition
+ kRTWinProdType_BUSINESS_N = 0x00000010, ///< Business Edition
+ kRTWinProdType_CLUSTER_SERVER = 0x00000012, ///< Cluster Server Edition
+ kRTWinProdType_DATACENTER_SERVER = 0x00000008, ///< Server Datacenter Edition (full installation)
+ kRTWinProdType_DATACENTER_SERVER_CORE = 0x0000000C, ///< Server Datacenter Edition (core installation)
+ kRTWinProdType_ENTERPRISE = 0x00000004, ///< Enterprise Edition
+ kRTWinProdType_ENTERPRISE_N = 0x0000001B, ///< Enterprise Edition
+ kRTWinProdType_ENTERPRISE_SERVER = 0x0000000A, ///< Server Enterprise Edition (full installation)
+ kRTWinProdType_ENTERPRISE_SERVER_CORE = 0x0000000E, ///< Server Enterprise Edition (core installation)
+ kRTWinProdType_ENTERPRISE_SERVER_IA64 = 0x0000000F, ///< Server Enterprise Edition for Itanium-based Systems
+ kRTWinProdType_HOME_BASIC = 0x00000002, ///< Home Basic Edition
+ kRTWinProdType_HOME_BASIC_N = 0x00000005, ///< Home Basic Edition
+ kRTWinProdType_HOME_PREMIUM = 0x00000003, ///< Home Premium Edition
+ kRTWinProdType_HOME_PREMIUM_N = 0x0000001A, ///< Home Premium Edition
+ kRTWinProdType_HOME_SERVER = 0x00000013, ///< Home Server Edition
+ kRTWinProdType_SERVER_FOR_SMALLBUSINESS = 0x00000018, ///< Server for Small Business Edition
+ kRTWinProdType_SMALLBUSINESS_SERVER = 0x00000009, ///< Small Business Server
+ kRTWinProdType_SMALLBUSINESS_SERVER_PREMIUM = 0x00000019, ///< Small Business Server Premium Edition
+ kRTWinProdType_STANDARD_SERVER = 0x00000007, ///< Server Standard Edition (full installation)
+ kRTWinProdType_STANDARD_SERVER_CORE = 0x0000000D, ///< Server Standard Edition (core installation)
+ kRTWinProdType_STARTER = 0x0000000B, ///< Starter Edition
+ kRTWinProdType_STORAGE_ENTERPRISE_SERVER = 0x00000017, ///< Storage Server Enterprise Edition
+ kRTWinProdType_STORAGE_EXPRESS_SERVER = 0x00000014, ///< Storage Server Express Edition
+ kRTWinProdType_STORAGE_STANDARD_SERVER = 0x00000015, ///< Storage Server Standard Edition
+ kRTWinProdType_STORAGE_WORKGROUP_SERVER = 0x00000016, ///< Storage Server Workgroup Edition
+ kRTWinProdType_ULTIMATE = 0x00000001, ///< Ultimate Edition
+ kRTWinProdType_ULTIMATE_N = 0x0000001C, ///< Ultimate Edition
+ kRTWinProdType_WEB_SERVER = 0x00000011, ///< Web Server Edition (full)
+ kRTWinProdType_WEB_SERVER_CORE = 0x0000001D ///< Web Server Edition (core)
+} RTWINPRODTYPE;
+
+
+/**
+ * Wrapper around the GetProductInfo API.
+ *
+ * @returns The vista type.
+ */
+static RTWINPRODTYPE rtSystemWinGetProductInfo(DWORD dwOSMajorVersion, DWORD dwOSMinorVersion, DWORD dwSpMajorVersion, DWORD dwSpMinorVersion)
+{
+ BOOL (WINAPI *pfnGetProductInfo)(DWORD, DWORD, DWORD, DWORD, PDWORD);
+ pfnGetProductInfo = (BOOL (WINAPI *)(DWORD, DWORD, DWORD, DWORD, PDWORD))GetProcAddress(GetModuleHandle("kernel32.dll"),
+ "GetProductInfo");
+ if (pfnGetProductInfo)
+ {
+ DWORD dwProductType = kRTWinProdType_UNDEFINED;
+ if (pfnGetProductInfo(dwOSMajorVersion, dwOSMinorVersion, dwSpMajorVersion, dwSpMinorVersion, &dwProductType))
+ return (RTWINPRODTYPE)dwProductType;
+ }
+ return kRTWinProdType_UNDEFINED;
+}
+
+
+
+/**
+ * Appends the product type if available (Vista & 2003 only for some reason).
+ *
+ * @param pszTmp The buffer.
+ * @param cbTmp The buffer size.
+ */
+static void rtSystemWinAppendProductType(char *pszTmp, size_t cbTmp)
+{
+ RTWINPRODTYPE enmVistaType = rtSystemWinGetProductInfo(6, 0, 0, 0);
+ switch (enmVistaType)
+ {
+ case kRTWinProdType_BUSINESS: RTStrCat(pszTmp, cbTmp, " Business Edition"); break;
+ case kRTWinProdType_BUSINESS_N: RTStrCat(pszTmp, cbTmp, " Business Edition"); break;
+ case kRTWinProdType_CLUSTER_SERVER: RTStrCat(pszTmp, cbTmp, " Cluster Server Edition"); break;
+ case kRTWinProdType_DATACENTER_SERVER: RTStrCat(pszTmp, cbTmp, " Server Datacenter Edition (full installation)"); break;
+ case kRTWinProdType_DATACENTER_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Datacenter Edition (core installation)"); break;
+ case kRTWinProdType_ENTERPRISE: RTStrCat(pszTmp, cbTmp, " Enterprise Edition"); break;
+ case kRTWinProdType_ENTERPRISE_N: RTStrCat(pszTmp, cbTmp, " Enterprise Edition"); break;
+ case kRTWinProdType_ENTERPRISE_SERVER: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition (full installation)"); break;
+ case kRTWinProdType_ENTERPRISE_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition (core installation)"); break;
+ case kRTWinProdType_ENTERPRISE_SERVER_IA64: RTStrCat(pszTmp, cbTmp, " Server Enterprise Edition for Itanium-based Systems"); break;
+ case kRTWinProdType_HOME_BASIC: RTStrCat(pszTmp, cbTmp, " Home Basic Edition"); break;
+ case kRTWinProdType_HOME_BASIC_N: RTStrCat(pszTmp, cbTmp, " Home Basic Edition"); break;
+ case kRTWinProdType_HOME_PREMIUM: RTStrCat(pszTmp, cbTmp, " Home Premium Edition"); break;
+ case kRTWinProdType_HOME_PREMIUM_N: RTStrCat(pszTmp, cbTmp, " Home Premium Edition"); break;
+ case kRTWinProdType_HOME_SERVER: RTStrCat(pszTmp, cbTmp, " Home Server Edition"); break;
+ case kRTWinProdType_SERVER_FOR_SMALLBUSINESS: RTStrCat(pszTmp, cbTmp, " Server for Small Business Edition"); break;
+ case kRTWinProdType_SMALLBUSINESS_SERVER: RTStrCat(pszTmp, cbTmp, " Small Business Server"); break;
+ case kRTWinProdType_SMALLBUSINESS_SERVER_PREMIUM: RTStrCat(pszTmp, cbTmp, " Small Business Server Premium Edition"); break;
+ case kRTWinProdType_STANDARD_SERVER: RTStrCat(pszTmp, cbTmp, " Server Standard Edition (full installation)"); break;
+ case kRTWinProdType_STANDARD_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Server Standard Edition (core installation)"); break;
+ case kRTWinProdType_STARTER: RTStrCat(pszTmp, cbTmp, " Starter Edition"); break;
+ case kRTWinProdType_STORAGE_ENTERPRISE_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Enterprise Edition"); break;
+ case kRTWinProdType_STORAGE_EXPRESS_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Express Edition"); break;
+ case kRTWinProdType_STORAGE_STANDARD_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Standard Edition"); break;
+ case kRTWinProdType_STORAGE_WORKGROUP_SERVER: RTStrCat(pszTmp, cbTmp, " Storage Server Workgroup Edition"); break;
+ case kRTWinProdType_ULTIMATE: RTStrCat(pszTmp, cbTmp, " Ultimate Edition"); break;
+ case kRTWinProdType_ULTIMATE_N: RTStrCat(pszTmp, cbTmp, " Ultimate Edition"); break;
+ case kRTWinProdType_WEB_SERVER: RTStrCat(pszTmp, cbTmp, " Web Server Edition (full installation)"); break;
+ case kRTWinProdType_WEB_SERVER_CORE: RTStrCat(pszTmp, cbTmp, " Web Server Edition (core installation)"); break;
+ case kRTWinProdType_UNDEFINED: break;
+ }
+}
+
+
+/**
+ * Services the RTSYSOSINFO_PRODUCT, RTSYSOSINFO_RELEASE
+ * and RTSYSOSINFO_SERVICE_PACK requests.
+ *
+ * @returns See RTSystemQueryOSInfo.
+ * @param enmInfo See RTSystemQueryOSInfo.
+ * @param pszInfo See RTSystemQueryOSInfo.
+ * @param cchInfo See RTSystemQueryOSInfo.
+ */
+static int rtSystemWinQueryOSVersion(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo)
+{
+ /*
+ * Make sure it's terminated correctly in case of error.
+ */
+ *pszInfo = '\0';
+
+ /*
+ * Check that we got the windows version at init time.
+ */
+ AssertReturn(g_WinOsInfoEx.dwOSVersionInfoSize, VERR_WRONG_ORDER);
+
+ /*
+ * Service the request.
+ */
+ char szTmp[512];
+ szTmp[0] = '\0';
+ switch (enmInfo)
+ {
+ /*
+ * The product name.
+ */
+ case RTSYSOSINFO_PRODUCT:
+ {
+ switch (g_enmWinVer)
+ {
+ case kRTWinOSType_95: strcpy(szTmp, "Windows 95"); break;
+ case kRTWinOSType_95SP1: strcpy(szTmp, "Windows 95 (Service Pack 1)"); break;
+ case kRTWinOSType_95OSR2: strcpy(szTmp, "Windows 95 (OSR 2)"); break;
+ case kRTWinOSType_98: strcpy(szTmp, "Windows 98"); break;
+ case kRTWinOSType_98SP1: strcpy(szTmp, "Windows 98 (Service Pack 1)"); break;
+ case kRTWinOSType_98SE: strcpy(szTmp, "Windows 98 (Second Edition)"); break;
+ case kRTWinOSType_ME: strcpy(szTmp, "Windows Me"); break;
+ case kRTWinOSType_NT310: strcpy(szTmp, "Windows NT 3.10"); break;
+ case kRTWinOSType_NT350: strcpy(szTmp, "Windows NT 3.50"); break;
+ case kRTWinOSType_NT351: strcpy(szTmp, "Windows NT 3.51"); break;
+ case kRTWinOSType_NT4: strcpy(szTmp, "Windows NT 4.0"); break;
+ case kRTWinOSType_2K: strcpy(szTmp, "Windows 2000"); break;
+ case kRTWinOSType_XP:
+ strcpy(szTmp, "Windows XP");
+ if (g_WinOsInfoEx.wSuiteMask & VER_SUITE_PERSONAL)
+ RTStrCat(szTmp, sizeof(szTmp), " Home");
+ if ( g_WinOsInfoEx.wProductType == VER_NT_WORKSTATION
+ && !(g_WinOsInfoEx.wSuiteMask & VER_SUITE_PERSONAL))
+ RTStrCat(szTmp, sizeof(szTmp), " Professional");
+#if 0 /** @todo fixme */
+ if (GetSystemMetrics(SM_MEDIACENTER))
+ RTStrCat(szTmp, sizeof(szTmp), " Media Center");
+#endif
+ break;
+
+ case kRTWinOSType_2003: strcpy(szTmp, "Windows 2003"); break;
+ case kRTWinOSType_VISTA:
+ {
+ strcpy(szTmp, "Windows Vista");
+ rtSystemWinAppendProductType(szTmp, sizeof(szTmp));
+ break;
+ }
+ case kRTWinOSType_2008: strcpy(szTmp, "Windows 2008"); break;
+ case kRTWinOSType_7: strcpy(szTmp, "Windows 7"); break;
+ case kRTWinOSType_2008R2: strcpy(szTmp, "Windows 2008 R2"); break;
+ case kRTWinOSType_8: strcpy(szTmp, "Windows 8"); break;
+ case kRTWinOSType_2012: strcpy(szTmp, "Windows 2012"); break;
+ case kRTWinOSType_81: strcpy(szTmp, "Windows 8.1"); break;
+ case kRTWinOSType_2012R2: strcpy(szTmp, "Windows 2012 R2"); break;
+ case kRTWinOSType_10: strcpy(szTmp, "Windows 10"); break;
+ case kRTWinOSType_2016: strcpy(szTmp, "Windows 2016"); break;
+ case kRTWinOSType_2019: strcpy(szTmp, "Windows 2019"); break;
+ case kRTWinOSType_2022: strcpy(szTmp, "Windows 2022"); break;
+ case kRTWinOSType_11: strcpy(szTmp, "Windows 11"); break;
+
+ case kRTWinOSType_NT_UNKNOWN:
+ RTStrPrintf(szTmp, sizeof(szTmp), "Unknown NT v%u.%u",
+ g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion);
+ break;
+
+ default:
+ AssertFailed();
+ case kRTWinOSType_UNKNOWN:
+ RTStrPrintf(szTmp, sizeof(szTmp), "Unknown %d v%u.%u",
+ g_WinOsInfoEx.dwPlatformId, g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion);
+ break;
+ }
+ break;
+ }
+
+ /*
+ * The release.
+ */
+ case RTSYSOSINFO_RELEASE:
+ {
+ RTStrPrintf(szTmp, sizeof(szTmp), "%u.%u.%u",
+ g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion, g_WinOsInfoEx.dwBuildNumber);
+ break;
+ }
+
+
+ /*
+ * Get the service pack.
+ */
+ case RTSYSOSINFO_SERVICE_PACK:
+ {
+ if (g_WinOsInfoEx.wServicePackMajor)
+ {
+ if (g_WinOsInfoEx.wServicePackMinor)
+ RTStrPrintf(szTmp, sizeof(szTmp), "%u.%u",
+ (unsigned)g_WinOsInfoEx.wServicePackMajor, (unsigned)g_WinOsInfoEx.wServicePackMinor);
+ else
+ RTStrPrintf(szTmp, sizeof(szTmp), "%u",
+ (unsigned)g_WinOsInfoEx.wServicePackMajor);
+ }
+ else if (g_WinOsInfoEx.szCSDVersion[0])
+ {
+ /* just copy the entire string. */
+ char *pszTmp = szTmp;
+ int rc = RTUtf16ToUtf8Ex(g_WinOsInfoEx.szCSDVersion, RT_ELEMENTS(g_WinOsInfoEx.szCSDVersion),
+ &pszTmp, sizeof(szTmp), NULL);
+ if (RT_SUCCESS(rc))
+ RTStrStripR(szTmp);
+ else
+ szTmp[0] = '\0';
+ AssertCompile(sizeof(szTmp) > sizeof(g_WinOsInfoEx.szCSDVersion));
+ }
+ else
+ {
+ switch (g_enmWinVer)
+ {
+ case kRTWinOSType_95SP1: strcpy(szTmp, "1"); break;
+ case kRTWinOSType_98SP1: strcpy(szTmp, "1"); break;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+
+ default:
+ AssertFatalFailed();
+ }
+
+ /*
+ * Copy the result to the return buffer.
+ */
+ size_t cchTmp = strlen(szTmp);
+ Assert(cchTmp < sizeof(szTmp));
+ if (cchTmp < cchInfo)
+ {
+ memcpy(pszInfo, szTmp, cchTmp + 1);
+ return VINF_SUCCESS;
+ }
+ memcpy(pszInfo, szTmp, cchInfo - 1);
+ pszInfo[cchInfo - 1] = '\0';
+ return VERR_BUFFER_OVERFLOW;
+}
+
+
+RTDECL(int) RTSystemQueryOSInfo(RTSYSOSINFO enmInfo, char *pszInfo, size_t cchInfo)
+{
+ /*
+ * Quick validation.
+ */
+ AssertReturn(enmInfo > RTSYSOSINFO_INVALID && enmInfo < RTSYSOSINFO_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszInfo, VERR_INVALID_POINTER);
+ if (!cchInfo)
+ return VERR_BUFFER_OVERFLOW;
+
+
+ /*
+ * Handle the request.
+ */
+ switch (enmInfo)
+ {
+ case RTSYSOSINFO_PRODUCT:
+ case RTSYSOSINFO_RELEASE:
+ case RTSYSOSINFO_SERVICE_PACK:
+ return rtSystemWinQueryOSVersion(enmInfo, pszInfo, cchInfo);
+
+ case RTSYSOSINFO_VERSION:
+ default:
+ *pszInfo = '\0';
+ }
+
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp b/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp
new file mode 100644
index 00000000..49465166
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemQueryTotalRam-win.cpp
@@ -0,0 +1,131 @@
+/* $Id: RTSystemQueryTotalRam-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryTotalRam, windows ring-3.
+ */
+
+/*
+ * Copyright (C) 2010-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static bool volatile g_fInitialized = false;
+typedef BOOL (WINAPI *PFNGLOBALMEMORYSTATUSEX)(LPMEMORYSTATUSEX);
+static PFNGLOBALMEMORYSTATUSEX g_pfnGlobalMemoryStatusEx = NULL;
+
+
+/**
+ * The GlobalMemoryStatusEx API is not available on older Windows version.
+ *
+ * @returns Pointer to GlobalMemoryStatusEx or NULL if not available.
+ */
+DECLINLINE(PFNGLOBALMEMORYSTATUSEX) rtSystemWinGetExApi(void)
+{
+ PFNGLOBALMEMORYSTATUSEX pfnEx;
+ if (g_fInitialized)
+ pfnEx = g_pfnGlobalMemoryStatusEx;
+ else
+ {
+ pfnEx = (PFNGLOBALMEMORYSTATUSEX)GetProcAddress(g_hModKernel32, "GlobalMemoryStatusEx");
+ g_pfnGlobalMemoryStatusEx = pfnEx;
+ g_fInitialized = true;
+ }
+ return pfnEx;
+}
+
+
+RTDECL(int) RTSystemQueryTotalRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PFNGLOBALMEMORYSTATUSEX pfnGlobalMemoryStatusEx = rtSystemWinGetExApi();
+ if (pfnGlobalMemoryStatusEx)
+ {
+ MEMORYSTATUSEX MemStatus;
+ MemStatus.dwLength = sizeof(MemStatus);
+ if (pfnGlobalMemoryStatusEx(&MemStatus))
+ *pcb = MemStatus.ullTotalPhys;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ MEMORYSTATUS MemStatus;
+ RT_ZERO(MemStatus);
+ MemStatus.dwLength = sizeof(MemStatus);
+ GlobalMemoryStatus(&MemStatus);
+ *pcb = MemStatus.dwTotalPhys;
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTSystemQueryAvailableRam(uint64_t *pcb)
+{
+ AssertPtrReturn(pcb, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+ PFNGLOBALMEMORYSTATUSEX pfnGlobalMemoryStatusEx = rtSystemWinGetExApi();
+ if (pfnGlobalMemoryStatusEx)
+ {
+ MEMORYSTATUSEX MemStatus;
+ MemStatus.dwLength = sizeof(MemStatus);
+ if (pfnGlobalMemoryStatusEx(&MemStatus))
+ *pcb = MemStatus.ullAvailPhys;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ MEMORYSTATUS MemStatus;
+ RT_ZERO(MemStatus);
+ MemStatus.dwLength = sizeof(MemStatus);
+ GlobalMemoryStatus(&MemStatus);
+ *pcb = MemStatus.dwAvailPhys;
+ }
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp b/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp
new file mode 100644
index 00000000..9a19ef3c
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTSystemShutdown-win.cpp
@@ -0,0 +1,183 @@
+/* $Id: RTSystemShutdown-win.cpp $ */
+/** @file
+ * IPRT - RTSystemShutdown, Windows.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/system.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include <iprt/win/windows.h>
+
+
+RTDECL(int) RTSystemShutdown(RTMSINTERVAL cMsDelay, uint32_t fFlags, const char *pszLogMsg)
+{
+ AssertPtrReturn(pszLogMsg, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTSYSTEM_SHUTDOWN_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Before we start, try grant the necessary privileges.
+ */
+ DWORD dwErr;
+ HANDLE hToken = NULL;
+ if (OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, TRUE /*OpenAsSelf*/, &hToken))
+ dwErr = NO_ERROR;
+ else
+ {
+ dwErr = GetLastError();
+ if (dwErr == ERROR_NO_TOKEN)
+ {
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
+ dwErr = NO_ERROR;
+ else
+ dwErr = GetLastError();
+ }
+ }
+ if (dwErr == NO_ERROR)
+ {
+ union
+ {
+ TOKEN_PRIVILEGES TokenPriv;
+ char ab[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
+ } u;
+ u.TokenPriv.PrivilegeCount = 1;
+ u.TokenPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ if (LookupPrivilegeValue(NULL /*localhost*/, SE_SHUTDOWN_NAME, &u.TokenPriv.Privileges[0].Luid))
+ {
+ if (!AdjustTokenPrivileges(hToken,
+ FALSE /*DisableAllPrivileges*/,
+ &u.TokenPriv,
+ RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges[1]),
+ NULL,
+ NULL) )
+ dwErr = GetLastError();
+ }
+ else
+ dwErr = GetLastError();
+ CloseHandle(hToken);
+ }
+
+ /*
+ * Do some parameter conversion.
+ */
+ PRTUTF16 pwszLogMsg;
+ int rc = RTStrToUtf16(pszLogMsg, &pwszLogMsg);
+ if (RT_FAILURE(rc))
+ return rc;
+ DWORD cSecsTimeout = (cMsDelay + 499) / 1000;
+
+ /*
+ * If we're told to power off the system, we should try use InitiateShutdownW (6.0+)
+ * or ExitWindowsEx (3.50) rather than InitiateSystemShutdownW, because these other
+ * APIs allows us to explicitly specify that we want to power off.
+ *
+ * Note! For NT version 4, 3.51, and 3.50 the system may instaed reboot since the
+ * x86 HALs typically didn't know how to perform a power off.
+ */
+ bool fDone = false;
+ if ( (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_POWER_OFF
+ || (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_POWER_OFF_HALT)
+ {
+ /* This API has the grace period thing. */
+ decltype(InitiateShutdownW) *pfnInitiateShutdownW;
+ pfnInitiateShutdownW = (decltype(InitiateShutdownW) *)GetProcAddress(GetModuleHandleW(L"ADVAPI32.DLL"), "InitiateShutdownW");
+ if (pfnInitiateShutdownW)
+ {
+ DWORD fShutdownFlags = SHUTDOWN_POWEROFF;
+ if (fFlags & RTSYSTEM_SHUTDOWN_FORCE)
+ fShutdownFlags |= SHUTDOWN_FORCE_OTHERS | SHUTDOWN_FORCE_SELF;
+ DWORD fReason = SHTDN_REASON_MAJOR_OTHER | (fFlags & RTSYSTEM_SHUTDOWN_PLANNED ? SHTDN_REASON_FLAG_PLANNED : 0);
+ dwErr = pfnInitiateShutdownW(NULL /*pwszMachineName*/, pwszLogMsg, cSecsTimeout, fShutdownFlags, fReason);
+ if (dwErr == ERROR_INVALID_PARAMETER)
+ {
+ fReason &= ~SHTDN_REASON_FLAG_PLANNED; /* just in case... */
+ dwErr = pfnInitiateShutdownW(NULL /*pwszMachineName*/, pwszLogMsg, cSecsTimeout, fShutdownFlags, fReason);
+ }
+ if (dwErr == ERROR_SUCCESS)
+ {
+ rc = VINF_SUCCESS;
+ fDone = true;
+ }
+ }
+
+ if (!fDone)
+ {
+ /* No grace period here, too bad. */
+ decltype(ExitWindowsEx) *pfnExitWindowsEx;
+ pfnExitWindowsEx = (decltype(ExitWindowsEx) *)GetProcAddress(GetModuleHandleW(L"USER32.DLL"), "ExitWindowsEx");
+ if (pfnExitWindowsEx)
+ {
+ DWORD fExitWindows = EWX_POWEROFF | EWX_SHUTDOWN;
+ if (fFlags & RTSYSTEM_SHUTDOWN_FORCE)
+ fExitWindows |= EWX_FORCE | EWX_FORCEIFHUNG;
+
+ if (pfnExitWindowsEx(fExitWindows, SHTDN_REASON_MAJOR_OTHER))
+ fDone = true;
+ else if (pfnExitWindowsEx(fExitWindows & ~EWX_FORCEIFHUNG, SHTDN_REASON_MAJOR_OTHER))
+ fDone = true;
+ }
+ }
+ }
+
+ /*
+ * Fall back on the oldest API.
+ */
+ if (!fDone)
+ {
+ BOOL fRebootAfterShutdown = (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_REBOOT
+ ? TRUE : FALSE;
+ BOOL fForceAppsClosed = fFlags & RTSYSTEM_SHUTDOWN_FORCE ? TRUE : FALSE;
+ if (InitiateSystemShutdownW(NULL /*pwszMachineName = NULL = localhost*/,
+ pwszLogMsg,
+ cSecsTimeout,
+ fForceAppsClosed,
+ fRebootAfterShutdown))
+ rc = (fFlags & RTSYSTEM_SHUTDOWN_ACTION_MASK) == RTSYSTEM_SHUTDOWN_HALT ? VINF_SYS_MAY_POWER_OFF : VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ RTUtf16Free(pwszLogMsg);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTSystemShutdown);
+
diff --git a/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp b/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp
new file mode 100644
index 00000000..310d8507
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTTimeZoneGetCurrent-win.cpp
@@ -0,0 +1,117 @@
+/* $Id: RTTimeZoneGetCurrent-win.cpp $ */
+/** @file
+ * IPRT - RTTimeZoneGetCurrent, generic.
+ */
+
+/*
+ * Copyright (C) 2017-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/errcore.h>
+#include <iprt/env.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include <iprt/win/windows.h>
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef DWORD (WINAPI *PFNGETDYNAMICTIMEZONEINFORMATION)(PDYNAMIC_TIME_ZONE_INFORMATION);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the GetDynamicTimeZoneInformation API if present. */
+static PFNGETDYNAMICTIMEZONEINFORMATION g_pfnGetDynamicTimeZoneInformation = NULL;
+/** Flipped after we've tried to resolve g_pfnGetDynamicTimeZoneInformation. */
+static bool volatile g_fResolvedApi = false;
+
+
+RTDECL(int) RTTimeZoneGetCurrent(char *pszName, size_t cbName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(cbName > 0, VERR_BUFFER_OVERFLOW);
+
+ /*
+ * Resolve API.
+ */
+ PFNGETDYNAMICTIMEZONEINFORMATION pfnApi;
+ if (g_fResolvedApi)
+ pfnApi = g_pfnGetDynamicTimeZoneInformation;
+ else
+ {
+ pfnApi = (PFNGETDYNAMICTIMEZONEINFORMATION)GetProcAddress(g_hModKernel32, "GetDynamicTimeZoneInformation");
+ g_pfnGetDynamicTimeZoneInformation = pfnApi;
+ g_fResolvedApi = true;
+ }
+
+ /*
+ * Call the API and convert the name we get.
+ */
+ union
+ {
+ TIME_ZONE_INFORMATION Tzi;
+ DYNAMIC_TIME_ZONE_INFORMATION DynTzi;
+ } uBuf;
+ RT_ZERO(uBuf);
+ DWORD dwRc;
+ PCRTUTF16 pwszSrcName;
+ size_t cwcSrcName;
+ if (pfnApi)
+ {
+ dwRc = pfnApi(&uBuf.DynTzi);
+ pwszSrcName = uBuf.DynTzi.TimeZoneKeyName;
+ cwcSrcName = RT_ELEMENTS(uBuf.DynTzi.TimeZoneKeyName);
+ }
+ else
+ {
+ /* Not sure how helpful this fallback really is... */
+ dwRc = GetTimeZoneInformation(&uBuf.Tzi);
+ pwszSrcName = uBuf.Tzi.StandardName;
+ cwcSrcName = RT_ELEMENTS(uBuf.Tzi.StandardName);
+ }
+ if (dwRc != TIME_ZONE_ID_INVALID)
+ {
+ Assert(*pwszSrcName != '\0');
+ return RTUtf16ToUtf8Ex(pwszSrcName, cwcSrcName, &pszName, cbName, &cbName);
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
diff --git a/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp b/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp
new file mode 100644
index 00000000..99a5692f
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/RTUuidCreate-win.cpp
@@ -0,0 +1,82 @@
+/* $Id: RTUuidCreate-win.cpp $ */
+/** @file
+ * IPRT - UUID, Windows RTUuidCreate implementation.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UUID
+#include <iprt/win/windows.h>
+
+#include <iprt/uuid.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/rand.h>
+
+#include "internal-r3-win.h"
+
+
+RTDECL(int) RTUuidCreate(PRTUUID pUuid)
+{
+ /*
+ * Input validation.
+ */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+
+ /*
+ * When using the UuidCreate API shortly after boot on NT 3.1 it typcially
+ * hangs for a long long time while polling for some service to start.
+ * What then usually happens next is a failure because it couldn't figure
+ * out the MAC address of the NIC. So, on NT 3.1 we always use the fallback.
+ */
+ if (g_enmWinVer != kRTWinOSType_NT310)
+ {
+ RPC_STATUS rc = UuidCreate((UUID *)pUuid);
+ if ( rc == RPC_S_OK
+ || rc == RPC_S_UUID_LOCAL_ONLY)
+ return VINF_SUCCESS;
+ AssertMsg(rc == RPC_S_UUID_NO_ADDRESS, ("UuidCreate -> %u (%#x)\n", rc, rc));
+ }
+
+ /*
+ * Use generic implementation as fallback (copy of RTUuidCreate-generic.cpp).
+ */
+ RTRandBytes(pUuid, sizeof(*pUuid));
+ pUuid->Gen.u8ClockSeqHiAndReserved = (pUuid->Gen.u8ClockSeqHiAndReserved & 0x3f) | 0x80;
+ pUuid->Gen.u16TimeHiAndVersion = (pUuid->Gen.u16TimeHiAndVersion & 0x0fff) | 0x4000;
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def
new file mode 100644
index 00000000..5395dc50
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-msvcp100-win32.def
@@ -0,0 +1,1678 @@
+
+ ??0?$_Yarn@D@std@@QAE@ABV01@@Z
+ ??0?$_Yarn@D@std@@QAE@PBD@Z
+ ??0?$_Yarn@D@std@@QAE@XZ
+ ??0?$basic_ios@DU?$char_traits@D@std@@@std@@IAE@XZ
+ ??0?$basic_ios@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??0?$basic_ios@GU?$char_traits@G@std@@@std@@IAE@XZ
+ ??0?$basic_ios@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??0?$basic_ios@_WU?$char_traits@_W@std@@@std@@IAE@XZ
+ ??0?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??0?$basic_iostream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_iostream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??0?$basic_iostream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_iostream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??0?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N1@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N@Z
+ ??0?$basic_istream@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N1@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N@Z
+ ??0?$basic_istream@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N1@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N@Z
+ ??0?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE@W4_Uninitialized@1@@Z
+ ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@_N@Z
+ ??0?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE@W4_Uninitialized@1@_N@Z
+ ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@_N@Z
+ ??0?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE@W4_Uninitialized@1@_N@Z
+ ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@$$QAV01@@Z
+ ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@_N@Z
+ ??0?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE@W4_Uninitialized@1@_N@Z
+ ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@ABV01@@Z
+ ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@W4_Uninitialized@1@@Z
+ ??0?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAE@XZ
+ ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@ABV01@@Z
+ ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@W4_Uninitialized@1@@Z
+ ??0?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAE@XZ
+ ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@ABV01@@Z
+ ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@W4_Uninitialized@1@@Z
+ ??0?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAE@XZ
+ ??0?$codecvt@DDH@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$codecvt@DDH@std@@QAE@I@Z
+ ??0?$codecvt@GDH@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$codecvt@GDH@std@@QAE@I@Z
+ ??0?$codecvt@_WDH@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$codecvt@_WDH@std@@QAE@I@Z
+ ??0?$ctype@D@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$ctype@D@std@@QAE@PBF_NI@Z
+ ??0?$ctype@G@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$ctype@G@std@@QAE@I@Z
+ ??0?$ctype@_W@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$ctype@_W@std@@QAE@I@Z
+ ??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAE@I@Z
+ ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAE@PBDI@Z
+ ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@ABV_Locinfo@1@I@Z
+ ??0?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAE@I@Z
+ ??0Init@ios_base@std@@QAE@XZ
+ ??0_Concurrent_queue_base_v4@details@Concurrency@@IAE@I@Z
+ ??0_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAE@ABV_Concurrent_queue_base_v4@12@@Z
+ ??0_Container_base12@std@@QAE@ABU01@@Z
+ ??0_Container_base12@std@@QAE@XZ
+ ??0_Init_locks@std@@QAE@XZ
+ ??0_Locimp@locale@std@@AAE@ABV012@@Z
+ ??0_Locimp@locale@std@@AAE@_N@Z
+ ??0_Locinfo@std@@QAE@HPBD@Z
+ ??0_Locinfo@std@@QAE@PBD@Z
+ ??0_Lockit@std@@QAE@H@Z
+ ??0_Lockit@std@@QAE@XZ
+ ??0_Mutex@std@@QAE@W4_Uninitialized@1@@Z
+ ??0_Mutex@std@@QAE@XZ
+ ??0_Runtime_object@details@Concurrency@@QAE@H@Z
+ ??0_Runtime_object@details@Concurrency@@QAE@XZ
+ ??0_Timevec@std@@QAE@ABV01@@Z
+ ??0_Timevec@std@@QAE@PAX@Z
+ ??0_UShinit@std@@QAE@XZ
+ ??0_Winit@std@@QAE@XZ
+ ??0agent@Concurrency@@QAE@AAVScheduleGroup@1@@Z
+ ??0agent@Concurrency@@QAE@AAVScheduler@1@@Z
+ ??0agent@Concurrency@@QAE@XZ
+ ??0codecvt_base@std@@QAE@I@Z
+ ??0ctype_base@std@@QAE@I@Z
+ ??0facet@locale@std@@IAE@I@Z
+ ??0id@locale@std@@QAE@I@Z
+ ??0ios_base@std@@IAE@XZ
+ ??0ios_base@std@@QAE@ABV01@@Z
+ ??0time_base@std@@QAE@I@Z
+ ??1?$_Yarn@D@std@@QAE@XZ
+ ??1?$basic_ios@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_ios@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_ios@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_iostream@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_iostream@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_iostream@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_istream@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_istream@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_istream@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_ostream@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_ostream@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_ostream@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAE@XZ
+ ??1?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAE@XZ
+ ??1?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAE@XZ
+ ??1?$codecvt@DDH@std@@MAE@XZ
+ ??1?$codecvt@GDH@std@@MAE@XZ
+ ??1?$codecvt@_WDH@std@@MAE@XZ
+ ??1?$ctype@D@std@@MAE@XZ
+ ??1?$ctype@G@std@@MAE@XZ
+ ??1?$ctype@_W@std@@MAE@XZ
+ ??1?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MAE@XZ
+ ??1?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MAE@XZ
+ ??1?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MAE@XZ
+ ??1Init@ios_base@std@@QAE@XZ
+ ??1_Concurrent_queue_base_v4@details@Concurrency@@MAE@XZ
+ ??1_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAE@XZ
+ ??1_Concurrent_vector_base_v4@details@Concurrency@@IAE@XZ
+ ??1_Container_base12@std@@QAE@XZ
+ ??1_Init_locks@std@@QAE@XZ
+ ??1_Locimp@locale@std@@MAE@XZ
+ ??1_Locinfo@std@@QAE@XZ
+ ??1_Lockit@std@@QAE@XZ
+ ??1_Mutex@std@@QAE@XZ
+ ??1_Timevec@std@@QAE@XZ
+ ??1_UShinit@std@@QAE@XZ
+ ??1_Winit@std@@QAE@XZ
+ ??1agent@Concurrency@@UAE@XZ
+ ??1codecvt_base@std@@UAE@XZ
+ ??1ctype_base@std@@UAE@XZ
+ ??1facet@locale@std@@UAE@XZ
+ ??1ios_base@std@@UAE@XZ
+ ??1time_base@std@@UAE@XZ
+ ??4?$_Iosb@H@std@@QAEAAV01@ABV01@@Z
+ ??4?$_Yarn@D@std@@QAEAAV01@ABV01@@Z
+ ??4?$_Yarn@D@std@@QAEAAV01@PBD@Z
+ ??4?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@$$QAV01@@Z
+ ??4?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEAAV01@ABV01@@Z
+ ??4?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEAAV01@ABV01@@Z
+ ??4?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@C@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@D@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@E@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@F@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@G@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@H@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@I@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@J@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@K@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@M@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@N@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@O@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_J@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_K@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_N@std@@QAEAAV01@ABV01@@Z
+ ??4?$numeric_limits@_W@std@@QAEAAV01@ABV01@@Z
+ ??4Init@ios_base@std@@QAEAAV012@ABV012@@Z
+ ??4_Container_base0@std@@QAEAAU01@ABU01@@Z
+ ??4_Container_base12@std@@QAEAAU01@ABU01@@Z
+ ??4_Init_locks@std@@QAEAAV01@ABV01@@Z
+ ??4_Num_base@std@@QAEAAU01@ABU01@@Z
+ ??4_Num_float_base@std@@QAEAAU01@ABU01@@Z
+ ??4_Num_int_base@std@@QAEAAU01@ABU01@@Z
+ ??4_Timevec@std@@QAEAAV01@ABV01@@Z
+ ??4_UShinit@std@@QAEAAV01@ABV01@@Z
+ ??4_Winit@std@@QAEAAV01@ABV01@@Z
+ ??4ios_base@std@@QAEAAV01@ABV01@@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAF@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAG@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAH@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAI@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAJ@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAK@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAM@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAN@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAO@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AAPAX@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_J@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_K@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@AA_N@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@DU?$char_traits@D@std@@@1@AAV21@@Z@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??5?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV01@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAF@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAG@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAH@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAI@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAJ@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAK@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAM@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAN@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAO@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AAPAX@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_J@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_K@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@AA_N@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@GU?$char_traits@G@std@@@1@AAV21@@Z@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??5?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAF@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAG@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAH@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAI@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAJ@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAK@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAM@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAN@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAO@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AAPAX@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_J@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_K@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@AA_N@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@_WU?$char_traits@_W@std@@@1@AAV21@@Z@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??5?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@F@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@G@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@I@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@J@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@K@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@M@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@N@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@O@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@DU?$char_traits@D@std@@@1@AAV21@@Z@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PAV?$basic_streambuf@DU?$char_traits@D@std@@@1@@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@PBX@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_J@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_K@Z
+ ??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@_N@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@F@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@G@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@H@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@I@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@J@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@K@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@M@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@N@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@O@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@GU?$char_traits@G@std@@@1@AAV21@@Z@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PAV?$basic_streambuf@GU?$char_traits@G@std@@@1@@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@PBX@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_J@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_K@Z
+ ??6?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV01@_N@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@F@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@G@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@H@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@I@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@J@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@K@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@M@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@N@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@O@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAV?$basic_ios@_WU?$char_traits@_W@std@@@1@AAV21@@Z@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@P6AAAVios_base@1@AAV21@@Z@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PAV?$basic_streambuf@_WU?$char_traits@_W@std@@@1@@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@PBX@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_J@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_K@Z
+ ??6?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV01@_N@Z
+ ??7ios_base@std@@QBE_NXZ
+ ??Bid@locale@std@@QAEIXZ
+ ??Bios_base@std@@QBEPAXXZ
+ ??_7?$basic_ios@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_ios@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_ios@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_iostream@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_iostream@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_iostream@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_istream@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_istream@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_istream@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_ostream@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_ostream@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_ostream@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$basic_streambuf@DU?$char_traits@D@std@@@std@@6B@
+ ??_7?$basic_streambuf@GU?$char_traits@G@std@@@std@@6B@
+ ??_7?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@6B@
+ ??_7?$codecvt@DDH@std@@6B@
+ ??_7?$codecvt@GDH@std@@6B@
+ ??_7?$codecvt@_WDH@std@@6B@
+ ??_7?$ctype@D@std@@6B@
+ ??_7?$ctype@G@std@@6B@
+ ??_7?$ctype@_W@std@@6B@
+ ??_7?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@6B@
+ ??_7?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@6B@
+ ??_7?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@6B@
+ ??_7_Locimp@locale@std@@6B@
+ ??_7codecvt_base@std@@6B@
+ ??_7ctype_base@std@@6B@
+ ??_7ios_base@std@@6B@
+ ??_7time_base@std@@6B@
+ ??_8?$basic_iostream@DU?$char_traits@D@std@@@std@@7B?$basic_istream@DU?$char_traits@D@std@@@1@@
+ ??_8?$basic_iostream@DU?$char_traits@D@std@@@std@@7B?$basic_ostream@DU?$char_traits@D@std@@@1@@
+ ??_8?$basic_iostream@GU?$char_traits@G@std@@@std@@7B?$basic_istream@GU?$char_traits@G@std@@@1@@
+ ??_8?$basic_iostream@GU?$char_traits@G@std@@@std@@7B?$basic_ostream@GU?$char_traits@G@std@@@1@@
+ ??_8?$basic_iostream@_WU?$char_traits@_W@std@@@std@@7B?$basic_istream@_WU?$char_traits@_W@std@@@1@@
+ ??_8?$basic_iostream@_WU?$char_traits@_W@std@@@std@@7B?$basic_ostream@_WU?$char_traits@_W@std@@@1@@
+ ??_8?$basic_istream@DU?$char_traits@D@std@@@std@@7B@
+ ??_8?$basic_istream@GU?$char_traits@G@std@@@std@@7B@
+ ??_8?$basic_istream@_WU?$char_traits@_W@std@@@std@@7B@
+ ??_8?$basic_ostream@DU?$char_traits@D@std@@@std@@7B@
+ ??_8?$basic_ostream@GU?$char_traits@G@std@@@std@@7B@
+ ??_8?$basic_ostream@_WU?$char_traits@_W@std@@@std@@7B@
+ ??_D?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ??_D?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ??_D?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ??_D?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ??_D?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ??_D?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ??_D?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ??_D?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ??_D?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ??_F?$codecvt@DDH@std@@QAEXXZ
+ ??_F?$codecvt@GDH@std@@QAEXXZ
+ ??_F?$codecvt@_WDH@std@@QAEXXZ
+ ??_F?$ctype@D@std@@QAEXXZ
+ ??_F?$ctype@G@std@@QAEXXZ
+ ??_F?$ctype@_W@std@@QAEXXZ
+ ??_F?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QAEXXZ
+ ??_F?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QAEXXZ
+ ??_F_Locimp@locale@std@@QAEXXZ
+ ??_F_Locinfo@std@@QAEXXZ
+ ??_F_Timevec@std@@QAEXXZ
+ ??_Fcodecvt_base@std@@QAEXXZ
+ ??_Fctype_base@std@@QAEXXZ
+ ??_Ffacet@locale@std@@QAEXXZ
+ ??_Fid@locale@std@@QAEXXZ
+ ??_Ftime_base@std@@QAEXXZ
+ ?NFS_Allocate@details@Concurrency@@YAPAXIIPAX@Z
+ ?NFS_Free@details@Concurrency@@YAXPAX@Z
+ ?NFS_GetLineSize@details@Concurrency@@YAIXZ
+ ?_Addcats@_Locinfo@std@@QAEAAV12@HPBD@Z
+ ?_Addfac@_Locimp@locale@std@@AAEXPAVfacet@23@I@Z
+ ?_Addstd@ios_base@std@@SAXPAV12@@Z
+ ?_Advance@_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAEXXZ
+ ?_Assign@_Concurrent_queue_iterator_base_v4@details@Concurrency@@IAEXABV123@@Z
+ ?_Atexit@@YAXP6AXXZ@Z
+ ?_BADOFF@std@@3_JB
+ ?_C_str@?$_Yarn@D@std@@QBEPBDXZ
+ ?_Callfns@ios_base@std@@AAEXW4event@12@@Z
+ ?_Clocptr@_Locimp@locale@std@@0PAV123@A
+ ?_Decref@facet@locale@std@@QAEPAV123@XZ
+ ?_Donarrow@?$ctype@G@std@@IBEDGD@Z
+ ?_Donarrow@?$ctype@_W@std@@IBED_WD@Z
+ ?_Dowiden@?$ctype@G@std@@IBEGD@Z
+ ?_Dowiden@?$ctype@_W@std@@IBE_WD@Z
+ ?_Empty@?$_Yarn@D@std@@QBE_NXZ
+ ?_Ffmt@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAPADPADDH@Z
+ ?_Ffmt@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAPADPADDH@Z
+ ?_Ffmt@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAPADPADDH@Z
+ ?_Findarr@ios_base@std@@AAEAAU_Iosarray@12@H@Z
+ ?_Fiopen@std@@YAPAU_iobuf@@PBDHH@Z
+ ?_Fiopen@std@@YAPAU_iobuf@@PBGHH@Z
+ ?_Fiopen@std@@YAPAU_iobuf@@PB_WHH@Z
+ ?_Fput@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBDIIII@Z
+ ?_Fput@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBDIIII@Z
+ ?_Fput@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBDIIII@Z
+ ?_GetCombinableSize@details@Concurrency@@YAIXZ
+ ?_GetCurrentThreadId@details@Concurrency@@YAKXZ
+ ?_Getcat@?$codecvt@DDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$codecvt@GDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$codecvt@_WDH@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$ctype@D@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$ctype@G@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$ctype@_W@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@SAIPAPBVfacet@locale@2@PBV42@@Z
+ ?_Getcat@facet@locale@std@@SAIPAPBV123@PBV23@@Z
+ ?_Getcoll@_Locinfo@std@@QBE?AU_Collvec@@XZ
+ ?_Getctype@_Locinfo@std@@QBE?AU_Ctypevec@@XZ
+ ?_Getcvt@_Locinfo@std@@QBE?AU_Cvtvec@@XZ
+ ?_Getdateorder@_Locinfo@std@@QBEHXZ
+ ?_Getdays@_Locinfo@std@@QBEPBDXZ
+ ?_Getfalse@_Locinfo@std@@QBEPBDXZ
+ ?_Getffld@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffld@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffld@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffldx@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffldx@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getffldx@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1AAVios_base@2@PAH@Z
+ ?_Getfmt@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z
+ ?_Getfmt@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z
+ ?_Getfmt@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD@Z
+ ?_Getgloballocale@locale@std@@CAPAV_Locimp@12@XZ
+ ?_Getifld@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@1HABVlocale@2@@Z
+ ?_Getifld@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@1HABVlocale@2@@Z
+ ?_Getifld@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHPADAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@1HABVlocale@2@@Z
+ ?_Getint@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@0HHAAH@Z
+ ?_Getint@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@0HHAAH@Z
+ ?_Getint@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAHAAV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@0HHAAH@Z
+ ?_Getlconv@_Locinfo@std@@QBEPBUlconv@@XZ
+ ?_Getmonths@_Locinfo@std@@QBEPBDXZ
+ ?_Getname@_Locinfo@std@@QBEPBDXZ
+ ?_Getpfirst@_Container_base12@std@@QBEPAPAU_Iterator_base12@2@XZ
+ ?_Getptr@_Timevec@std@@QBEPAXXZ
+ ?_Gettnames@_Locinfo@std@@QBE?AV_Timevec@2@XZ
+ ?_Gettrue@_Locinfo@std@@QBEPBDXZ
+ ?_Gnavail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBE_JXZ
+ ?_Gnavail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBE_JXZ
+ ?_Gnavail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBE_JXZ
+ ?_Gndec@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Gndec@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Gndec@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Gninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Gninc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Gninc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Gnpreinc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Gnpreinc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Gnpreinc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Hexdig@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAHDDDD@Z
+ ?_Hexdig@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAHGGGG@Z
+ ?_Hexdig@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAH_W000@Z
+ ?_Id_cnt@id@locale@std@@0HA
+ ?_Ifmt@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABAPADPADPBDH@Z
+ ?_Ifmt@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABAPADPADPBDH@Z
+ ?_Ifmt@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABAPADPADPBDH@Z
+ ?_Incref@facet@locale@std@@QAEXXZ
+ ?_Index@ios_base@std@@0HA
+ ?_Init@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAPAD0PAH001@Z
+ ?_Init@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXXZ
+ ?_Init@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAPAG0PAH001@Z
+ ?_Init@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXXZ
+ ?_Init@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPAPA_W0PAH001@Z
+ ?_Init@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXXZ
+ ?_Init@?$codecvt@DDH@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$codecvt@GDH@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$codecvt@_WDH@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$ctype@D@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$ctype@G@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$ctype@_W@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@IAEXABV_Locinfo@2@@Z
+ ?_Init@ios_base@std@@IAEXXZ
+ ?_Init@locale@std@@CAPAV_Locimp@12@XZ
+ ?_Init_cnt@Init@ios_base@std@@0HA
+ ?_Init_cnt@_UShinit@std@@0HA
+ ?_Init_cnt@_Winit@std@@0HA
+ ?_Init_cnt_func@Init@ios_base@std@@CAAAHXZ
+ ?_Init_ctor@Init@ios_base@std@@CAXPAV123@@Z
+ ?_Init_dtor@Init@ios_base@std@@CAXPAV123@@Z
+ ?_Init_locks_ctor@_Init_locks@std@@CAXPAV12@@Z
+ ?_Init_locks_dtor@_Init_locks@std@@CAXPAV12@@Z
+ ?_Internal_assign@_Concurrent_vector_base_v4@details@Concurrency@@IAEXABV123@IP6AXPAXI@ZP6AX1PBXI@Z4@Z
+ ?_Internal_capacity@_Concurrent_vector_base_v4@details@Concurrency@@IBEIXZ
+ ?_Internal_clear@_Concurrent_vector_base_v4@details@Concurrency@@IAEIP6AXPAXI@Z@Z
+ ?_Internal_compact@_Concurrent_vector_base_v4@details@Concurrency@@IAEPAXIPAXP6AX0I@ZP6AX0PBXI@Z@Z
+ ?_Internal_copy@_Concurrent_vector_base_v4@details@Concurrency@@IAEXABV123@IP6AXPAXPBXI@Z@Z
+ ?_Internal_empty@_Concurrent_queue_base_v4@details@Concurrency@@IBE_NXZ
+ ?_Internal_finish_clear@_Concurrent_queue_base_v4@details@Concurrency@@IAEXXZ
+ ?_Internal_grow_by@_Concurrent_vector_base_v4@details@Concurrency@@IAEIIIP6AXPAXPBXI@Z1@Z
+ ?_Internal_grow_to_at_least_with_result@_Concurrent_vector_base_v4@details@Concurrency@@IAEIIIP6AXPAXPBXI@Z1@Z
+ ?_Internal_pop_if_present@_Concurrent_queue_base_v4@details@Concurrency@@IAE_NPAX@Z
+ ?_Internal_push@_Concurrent_queue_base_v4@details@Concurrency@@IAEXPBX@Z
+ ?_Internal_push_back@_Concurrent_vector_base_v4@details@Concurrency@@IAEPAXIAAI@Z
+ ?_Internal_reserve@_Concurrent_vector_base_v4@details@Concurrency@@IAEXIII@Z
+ ?_Internal_resize@_Concurrent_vector_base_v4@details@Concurrency@@IAEXIIIP6AXPAXI@ZP6AX0PBXI@Z2@Z
+ ?_Internal_size@_Concurrent_queue_base_v4@details@Concurrency@@IBEIXZ
+ ?_Internal_swap@_Concurrent_vector_base_v4@details@Concurrency@@IAEXAAV123@@Z
+ ?_Internal_throw_exception@_Concurrent_queue_base_v4@details@Concurrency@@IBEXXZ
+ ?_Internal_throw_exception@_Concurrent_vector_base_v4@details@Concurrency@@IBEXI@Z
+ ?_Ios_base_dtor@ios_base@std@@CAXPAV12@@Z
+ ?_Ipfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_N_N@Z
+ ?_Ipfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_N_N@Z
+ ?_Ipfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_N_N@Z
+ ?_Iput@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPADI@Z
+ ?_Iput@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPADI@Z
+ ?_Iput@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPADI@Z
+ ?_Locimp_Addfac@_Locimp@locale@std@@CAXPAV123@PAVfacet@23@I@Z
+ ?_Locimp_ctor@_Locimp@locale@std@@CAXPAV123@ABV123@@Z
+ ?_Locimp_dtor@_Locimp@locale@std@@CAXPAV123@@Z
+ ?_Locinfo_Addcats@_Locinfo@std@@SAAAV12@PAV12@HPBD@Z
+ ?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@HPBD@Z
+ ?_Locinfo_ctor@_Locinfo@std@@SAXPAV12@PBD@Z
+ ?_Locinfo_dtor@_Locinfo@std@@SAXPAV12@@Z
+ ?_Lock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAEXXZ
+ ?_Lock@?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAEXXZ
+ ?_Lock@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAEXXZ
+ ?_Lock@_Mutex@std@@QAEXXZ
+ ?_Lockit_ctor@_Lockit@std@@CAXPAV12@@Z
+ ?_Lockit_ctor@_Lockit@std@@CAXPAV12@H@Z
+ ?_Lockit_ctor@_Lockit@std@@SAXH@Z
+ ?_Lockit_dtor@_Lockit@std@@CAXPAV12@@Z
+ ?_Lockit_dtor@_Lockit@std@@SAXH@Z
+ ?_MP_Add@tr1@std@@YAXQA_K_K@Z
+ ?_MP_Get@tr1@std@@YA_KQA_K@Z
+ ?_MP_Mul@tr1@std@@YAXQA_K_K1@Z
+ ?_MP_Rem@tr1@std@@YAXQA_K_K@Z
+ ?_Makeloc@_Locimp@locale@std@@CAPAV123@ABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Makeushloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Makewloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Makexloc@_Locimp@locale@std@@CAXABV_Locinfo@3@HPAV123@PBV23@@Z
+ ?_Mtx_delete@threads@stdext@@YAXPAX@Z
+ ?_Mtx_lock@threads@stdext@@YAXPAX@Z
+ ?_Mtx_new@threads@stdext@@YAXAAPAX@Z
+ ?_Mtx_unlock@threads@stdext@@YAXPAX@Z
+ ?_Mutex_Lock@_Mutex@std@@CAXPAV12@@Z
+ ?_Mutex_Unlock@_Mutex@std@@CAXPAV12@@Z
+ ?_Mutex_ctor@_Mutex@std@@CAXPAV12@@Z
+ ?_Mutex_dtor@_Mutex@std@@CAXPAV12@@Z
+ ?_Nomemory@std@@YAXXZ
+ ?_Orphan_all@_Container_base0@std@@QAEXXZ
+ ?_Orphan_all@_Container_base12@std@@QAEXXZ
+ ?_Osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?_Osfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?_Osfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?_Pnavail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBE_JXZ
+ ?_Pnavail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBE_JXZ
+ ?_Pnavail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBE_JXZ
+ ?_Pninc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEPADXZ
+ ?_Pninc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEPAGXZ
+ ?_Pninc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEPA_WXZ
+ ?_Ptr_cerr@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_cin@std@@3PAV?$basic_istream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_clog@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_cout@std@@3PAV?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?_Ptr_wcerr@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wcerr@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?_Ptr_wcin@std@@3PAV?$basic_istream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wcin@std@@3PAV?$basic_istream@_WU?$char_traits@_W@std@@@1@A
+ ?_Ptr_wclog@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wclog@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?_Ptr_wcout@std@@3PAV?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?_Ptr_wcout@std@@3PAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?_Put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDI@Z
+ ?_Put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBGI@Z
+ ?_Put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PB_WI@Z
+ ?_Putc@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDI@Z
+ ?_Putc@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBDI@Z
+ ?_Putc@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PBDI@Z
+ ?_Putgrouped@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@PBDID@Z
+ ?_Putgrouped@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@PBDIG@Z
+ ?_Putgrouped@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@PBDI_W@Z
+ ?_Raise_handler@std@@3P6AXABVexception@stdext@@@ZA
+ ?_Random_device@tr1@std@@YAIXZ
+ ?_Rep@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@DI@Z
+ ?_Rep@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@GI@Z
+ ?_Rep@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@ABA?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@_WI@Z
+ ?_Rng_abort@tr1@std@@YAXPBD@Z
+ ?_Segment_index_of@_Concurrent_vector_base_v4@details@Concurrency@@KAII@Z
+ ?_Setgloballocale@locale@std@@CAXPAX@Z
+ ?_Swap_all@_Container_base0@std@@QAEXAAU12@@Z
+ ?_Swap_all@_Container_base12@std@@QAEXAAU12@@Z
+ ?_Sync@ios_base@std@@0_NA
+ ?_Tidy@?$_Yarn@D@std@@AAEXXZ
+ ?_Tidy@?$ctype@D@std@@IAEXXZ
+ ?_Tidy@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@AAEXXZ
+ ?_Tidy@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@AAEXXZ
+ ?_Tidy@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@AAEXXZ
+ ?_Tidy@ios_base@std@@AAEXXZ
+ ?_Unlock@?$basic_streambuf@DU?$char_traits@D@std@@@std@@UAEXXZ
+ ?_Unlock@?$basic_streambuf@GU?$char_traits@G@std@@@std@@UAEXXZ
+ ?_Unlock@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@UAEXXZ
+ ?_Unlock@_Mutex@std@@QAEXXZ
+ ?_XLgamma@tr1@std@@YAMM@Z
+ ?_XLgamma@tr1@std@@YANN@Z
+ ?_XLgamma@tr1@std@@YAOO@Z
+ ?_Xbad@tr1@std@@YAXW4error_type@regex_constants@12@@Z
+ ?_Xfunc@tr1@std@@YAXXZ
+ ?_Xinvalid_argument@std@@YAXPBD@Z
+ ?_Xlength_error@std@@YAXPBD@Z
+ ?_Xmem@tr1@std@@YAXXZ
+ ?_Xout_of_range@std@@YAXPBD@Z
+ ?_Xoverflow_error@std@@YAXPBD@Z
+ ?_Xruntime_error@std@@YAXPBD@Z
+ ?always_noconv@codecvt_base@std@@QBE_NXZ
+ ?bad@ios_base@std@@QBE_NXZ
+ ?c_str@?$_Yarn@D@std@@QBEPBDXZ
+ ?cancel@agent@Concurrency@@QAE_NXZ
+ ?cerr@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?cin@std@@3V?$basic_istream@DU?$char_traits@D@std@@@1@A
+ ?classic@locale@std@@SAABV12@XZ
+ ?classic_table@?$ctype@D@std@@SAPBFXZ
+ ?clear@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z
+ ?clear@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXI@Z
+ ?clear@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXH_N@Z
+ ?clear@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXI@Z
+ ?clear@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXH_N@Z
+ ?clear@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXI@Z
+ ?clear@ios_base@std@@QAEXH@Z
+ ?clear@ios_base@std@@QAEXH_N@Z
+ ?clear@ios_base@std@@QAEXI@Z
+ ?clog@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?copyfmt@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEAAV12@ABV12@@Z
+ ?copyfmt@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEAAV12@ABV12@@Z
+ ?copyfmt@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEAAV12@ABV12@@Z
+ ?copyfmt@ios_base@std@@QAEAAV12@ABV12@@Z
+ ?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
+ ?date_order@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ
+ ?date_order@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ
+ ?date_order@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AW4dateorder@time_base@2@XZ
+ ?denorm_min@?$numeric_limits@C@std@@SACXZ
+ ?denorm_min@?$numeric_limits@D@std@@SADXZ
+ ?denorm_min@?$numeric_limits@E@std@@SAEXZ
+ ?denorm_min@?$numeric_limits@F@std@@SAFXZ
+ ?denorm_min@?$numeric_limits@G@std@@SAGXZ
+ ?denorm_min@?$numeric_limits@H@std@@SAHXZ
+ ?denorm_min@?$numeric_limits@I@std@@SAIXZ
+ ?denorm_min@?$numeric_limits@J@std@@SAJXZ
+ ?denorm_min@?$numeric_limits@K@std@@SAKXZ
+ ?denorm_min@?$numeric_limits@M@std@@SAMXZ
+ ?denorm_min@?$numeric_limits@N@std@@SANXZ
+ ?denorm_min@?$numeric_limits@O@std@@SAOXZ
+ ?denorm_min@?$numeric_limits@_J@std@@SA_JXZ
+ ?denorm_min@?$numeric_limits@_K@std@@SA_KXZ
+ ?denorm_min@?$numeric_limits@_N@std@@SA_NXZ
+ ?denorm_min@?$numeric_limits@_W@std@@SA_WXZ
+ ?digits10@?$numeric_limits@C@std@@2HB
+ ?digits10@?$numeric_limits@D@std@@2HB
+ ?digits10@?$numeric_limits@E@std@@2HB
+ ?digits10@?$numeric_limits@F@std@@2HB
+ ?digits10@?$numeric_limits@G@std@@2HB
+ ?digits10@?$numeric_limits@H@std@@2HB
+ ?digits10@?$numeric_limits@I@std@@2HB
+ ?digits10@?$numeric_limits@J@std@@2HB
+ ?digits10@?$numeric_limits@K@std@@2HB
+ ?digits10@?$numeric_limits@M@std@@2HB
+ ?digits10@?$numeric_limits@N@std@@2HB
+ ?digits10@?$numeric_limits@O@std@@2HB
+ ?digits10@?$numeric_limits@_J@std@@2HB
+ ?digits10@?$numeric_limits@_K@std@@2HB
+ ?digits10@?$numeric_limits@_N@std@@2HB
+ ?digits10@?$numeric_limits@_W@std@@2HB
+ ?digits10@_Num_base@std@@2HB
+ ?digits@?$numeric_limits@C@std@@2HB
+ ?digits@?$numeric_limits@D@std@@2HB
+ ?digits@?$numeric_limits@E@std@@2HB
+ ?digits@?$numeric_limits@F@std@@2HB
+ ?digits@?$numeric_limits@G@std@@2HB
+ ?digits@?$numeric_limits@H@std@@2HB
+ ?digits@?$numeric_limits@I@std@@2HB
+ ?digits@?$numeric_limits@J@std@@2HB
+ ?digits@?$numeric_limits@K@std@@2HB
+ ?digits@?$numeric_limits@M@std@@2HB
+ ?digits@?$numeric_limits@N@std@@2HB
+ ?digits@?$numeric_limits@O@std@@2HB
+ ?digits@?$numeric_limits@_J@std@@2HB
+ ?digits@?$numeric_limits@_K@std@@2HB
+ ?digits@?$numeric_limits@_N@std@@2HB
+ ?digits@?$numeric_limits@_W@std@@2HB
+ ?digits@_Num_base@std@@2HB
+ ?do_always_noconv@?$codecvt@GDH@std@@MBE_NXZ
+ ?do_always_noconv@?$codecvt@_WDH@std@@MBE_NXZ
+ ?do_always_noconv@codecvt_base@std@@MBE_NXZ
+ ?do_date_order@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ
+ ?do_date_order@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ
+ ?do_date_order@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AW4dateorder@time_base@2@XZ
+ ?do_encoding@codecvt_base@std@@MBEHXZ
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?do_get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?do_get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?do_get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?do_get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?do_get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?do_get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?do_get_date@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_date@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_date@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_monthname@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_monthname@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_monthname@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_time@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_time@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_time@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_weekday@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_weekday@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_weekday@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_year@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_year@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_get_year@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?do_in@?$codecvt@DDH@std@@MBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?do_in@?$codecvt@GDH@std@@MBEHAAHPBD1AAPBDPAG3AAPAG@Z
+ ?do_in@?$codecvt@_WDH@std@@MBEHAAHPBD1AAPBDPA_W3AAPA_W@Z
+ ?do_is@?$ctype@G@std@@MBEPBGPBG0PAF@Z
+ ?do_is@?$ctype@G@std@@MBE_NFG@Z
+ ?do_is@?$ctype@_W@std@@MBEPB_WPB_W0PAF@Z
+ ?do_is@?$ctype@_W@std@@MBE_NF_W@Z
+ ?do_length@?$codecvt@DDH@std@@MBEHABHPBD1I@Z
+ ?do_length@?$codecvt@GDH@std@@MBEHABHPBD1I@Z
+ ?do_length@?$codecvt@_WDH@std@@MBEHABHPBD1I@Z
+ ?do_max_length@?$codecvt@GDH@std@@MBEHXZ
+ ?do_max_length@?$codecvt@_WDH@std@@MBEHXZ
+ ?do_max_length@codecvt_base@std@@MBEHXZ
+ ?do_narrow@?$ctype@D@std@@MBEDDD@Z
+ ?do_narrow@?$ctype@D@std@@MBEPBDPBD0DPAD@Z
+ ?do_narrow@?$ctype@G@std@@MBEDGD@Z
+ ?do_narrow@?$ctype@G@std@@MBEPBGPBG0DPAD@Z
+ ?do_narrow@?$ctype@_W@std@@MBED_WD@Z
+ ?do_narrow@?$ctype@_W@std@@MBEPB_WPB_W0DPAD@Z
+ ?do_out@?$codecvt@DDH@std@@MBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?do_out@?$codecvt@GDH@std@@MBEHAAHPBG1AAPBGPAD3AAPAD@Z
+ ?do_out@?$codecvt@_WDH@std@@MBEHAAHPB_W1AAPB_WPAD3AAPAD@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DJ@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DK@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DN@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DO@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBX@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_J@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_K@Z
+ ?do_put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_N@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GJ@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GK@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GN@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GO@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBX@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_J@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_K@Z
+ ?do_put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_N@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WJ@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WK@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WN@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WO@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBX@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_J@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_K@Z
+ ?do_put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_N@Z
+ ?do_put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@DD@Z
+ ?do_put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@DD@Z
+ ?do_put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@MBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@DD@Z
+ ?do_scan_is@?$ctype@G@std@@MBEPBGFPBG0@Z
+ ?do_scan_is@?$ctype@_W@std@@MBEPB_WFPB_W0@Z
+ ?do_scan_not@?$ctype@G@std@@MBEPBGFPBG0@Z
+ ?do_scan_not@?$ctype@_W@std@@MBEPB_WFPB_W0@Z
+ ?do_tolower@?$ctype@D@std@@MBEDD@Z
+ ?do_tolower@?$ctype@D@std@@MBEPBDPADPBD@Z
+ ?do_tolower@?$ctype@G@std@@MBEGG@Z
+ ?do_tolower@?$ctype@G@std@@MBEPBGPAGPBG@Z
+ ?do_tolower@?$ctype@_W@std@@MBEPB_WPA_WPB_W@Z
+ ?do_tolower@?$ctype@_W@std@@MBE_W_W@Z
+ ?do_toupper@?$ctype@D@std@@MBEDD@Z
+ ?do_toupper@?$ctype@D@std@@MBEPBDPADPBD@Z
+ ?do_toupper@?$ctype@G@std@@MBEGG@Z
+ ?do_toupper@?$ctype@G@std@@MBEPBGPAGPBG@Z
+ ?do_toupper@?$ctype@_W@std@@MBEPB_WPA_WPB_W@Z
+ ?do_toupper@?$ctype@_W@std@@MBE_W_W@Z
+ ?do_unshift@?$codecvt@DDH@std@@MBEHAAHPAD1AAPAD@Z
+ ?do_unshift@?$codecvt@GDH@std@@MBEHAAHPAD1AAPAD@Z
+ ?do_unshift@?$codecvt@_WDH@std@@MBEHAAHPAD1AAPAD@Z
+ ?do_widen@?$ctype@D@std@@MBEDD@Z
+ ?do_widen@?$ctype@D@std@@MBEPBDPBD0PAD@Z
+ ?do_widen@?$ctype@G@std@@MBEGD@Z
+ ?do_widen@?$ctype@G@std@@MBEPBDPBD0PAG@Z
+ ?do_widen@?$ctype@_W@std@@MBEPBDPBD0PA_W@Z
+ ?do_widen@?$ctype@_W@std@@MBE_WD@Z
+ ?done@agent@Concurrency@@IAE_NXZ
+ ?eback@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?eback@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?eback@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?egptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?egptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?egptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?empty@?$_Yarn@D@std@@QBE_NXZ
+ ?empty@locale@std@@SA?AV12@XZ
+ ?encoding@codecvt_base@std@@QBEHXZ
+ ?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?endl@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?endl@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?ends@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?ends@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?ends@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?eof@ios_base@std@@QBE_NXZ
+ ?epptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?epptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?epptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?epsilon@?$numeric_limits@C@std@@SACXZ
+ ?epsilon@?$numeric_limits@D@std@@SADXZ
+ ?epsilon@?$numeric_limits@E@std@@SAEXZ
+ ?epsilon@?$numeric_limits@F@std@@SAFXZ
+ ?epsilon@?$numeric_limits@G@std@@SAGXZ
+ ?epsilon@?$numeric_limits@H@std@@SAHXZ
+ ?epsilon@?$numeric_limits@I@std@@SAIXZ
+ ?epsilon@?$numeric_limits@J@std@@SAJXZ
+ ?epsilon@?$numeric_limits@K@std@@SAKXZ
+ ?epsilon@?$numeric_limits@M@std@@SAMXZ
+ ?epsilon@?$numeric_limits@N@std@@SANXZ
+ ?epsilon@?$numeric_limits@O@std@@SAOXZ
+ ?epsilon@?$numeric_limits@_J@std@@SA_JXZ
+ ?epsilon@?$numeric_limits@_K@std@@SA_KXZ
+ ?epsilon@?$numeric_limits@_N@std@@SA_NXZ
+ ?epsilon@?$numeric_limits@_W@std@@SA_WXZ
+ ?exceptions@ios_base@std@@QAEXH@Z
+ ?exceptions@ios_base@std@@QAEXI@Z
+ ?exceptions@ios_base@std@@QBEHXZ
+ ?fail@ios_base@std@@QBE_NXZ
+ ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEDD@Z
+ ?fill@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDXZ
+ ?fill@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEGG@Z
+ ?fill@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEGXZ
+ ?fill@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE_W_W@Z
+ ?fill@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBE_WXZ
+ ?flags@ios_base@std@@QAEHH@Z
+ ?flags@ios_base@std@@QBEHXZ
+ ?flush@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ
+ ?flush@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@XZ
+ ?flush@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@XZ
+ ?flush@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?flush@std@@YAAAV?$basic_ostream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?flush@std@@YAAAV?$basic_ostream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?gbump@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXH@Z
+ ?gbump@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXH@Z
+ ?gbump@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXH@Z
+ ?gcount@?$basic_istream@DU?$char_traits@D@std@@@std@@QBE_JXZ
+ ?gcount@?$basic_istream@GU?$char_traits@G@std@@@std@@QBE_JXZ
+ ?gcount@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QBE_JXZ
+ ?generic_category@std@@YAABVerror_category@1@XZ
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAD@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAV?$basic_streambuf@DU?$char_traits@D@std@@@2@@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@AAV?$basic_streambuf@DU?$char_traits@D@std@@@2@D@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_JD@Z
+ ?get@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAG@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAV?$basic_streambuf@GU?$char_traits@G@std@@@2@@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@AAV?$basic_streambuf@GU?$char_traits@G@std@@@2@G@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_JG@Z
+ ?get@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@_W@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@AA_W@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J_W@Z
+ ?get@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?get@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?get@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAG@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAI@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAJ@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAK@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAM@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAN@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAO@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAAPAX@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_J@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_K@Z
+ ?get@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHAA_N@Z
+ ?get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?get@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBD4@Z
+ ?get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?get@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PBG4@Z
+ ?get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@DD@Z
+ ?get@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@PB_W4@Z
+ ?get_date@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_date@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_date@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_monthname@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_monthname@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_monthname@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_time@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_time@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_time@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_weekday@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_weekday@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_weekday@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_year@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@DU?$char_traits@D@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_year@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@GU?$char_traits@G@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?get_year@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@0AAVios_base@2@AAHPAUtm@@@Z
+ ?getline@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z
+ ?getline@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_JD@Z
+ ?getline@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z
+ ?getline@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_JG@Z
+ ?getline@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z
+ ?getline@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J_W@Z
+ ?getloc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QBE?AVlocale@2@XZ
+ ?getloc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QBE?AVlocale@2@XZ
+ ?getloc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QBE?AVlocale@2@XZ
+ ?getloc@ios_base@std@@QBE?AVlocale@2@XZ
+ ?global@locale@std@@SA?AV12@ABV12@@Z
+ ?good@ios_base@std@@QBE_NXZ
+ ?gptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?gptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?gptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?has_denorm@_Num_base@std@@2W4float_denorm_style@2@B
+ ?has_denorm@_Num_float_base@std@@2W4float_denorm_style@2@B
+ ?has_denorm_loss@_Num_base@std@@2_NB
+ ?has_denorm_loss@_Num_float_base@std@@2_NB
+ ?has_infinity@_Num_base@std@@2_NB
+ ?has_infinity@_Num_float_base@std@@2_NB
+ ?has_quiet_NaN@_Num_base@std@@2_NB
+ ?has_quiet_NaN@_Num_float_base@std@@2_NB
+ ?has_signaling_NaN@_Num_base@std@@2_NB
+ ?has_signaling_NaN@_Num_float_base@std@@2_NB
+ ?id@?$codecvt@DDH@std@@2V0locale@2@A
+ ?id@?$codecvt@GDH@std@@2V0locale@2@A
+ ?id@?$codecvt@_WDH@std@@2V0locale@2@A
+ ?id@?$collate@D@std@@2V0locale@2@A
+ ?id@?$collate@G@std@@2V0locale@2@A
+ ?id@?$collate@_W@std@@2V0locale@2@A
+ ?id@?$ctype@D@std@@2V0locale@2@A
+ ?id@?$ctype@G@std@@2V0locale@2@A
+ ?id@?$ctype@_W@std@@2V0locale@2@A
+ ?id@?$messages@D@std@@2V0locale@2@A
+ ?id@?$messages@G@std@@2V0locale@2@A
+ ?id@?$messages@_W@std@@2V0locale@2@A
+ ?id@?$money_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$money_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$moneypunct@D$00@std@@2V0locale@2@A
+ ?id@?$moneypunct@D$0A@@std@@2V0locale@2@A
+ ?id@?$moneypunct@G$00@std@@2V0locale@2@A
+ ?id@?$moneypunct@G$0A@@std@@2V0locale@2@A
+ ?id@?$moneypunct@_W$00@std@@2V0locale@2@A
+ ?id@?$moneypunct@_W$0A@@std@@2V0locale@2@A
+ ?id@?$num_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$numpunct@D@std@@2V0locale@2@A
+ ?id@?$numpunct@G@std@@2V0locale@2@A
+ ?id@?$numpunct@_W@std@@2V0locale@2@A
+ ?id@?$time_get@DV?$istreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_get@GV?$istreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_get@_WV?$istreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@2V0locale@2@A
+ ?id@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@2V0locale@2@A
+ ?ignore@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z
+ ?ignore@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JG@Z
+ ?ignore@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JG@Z
+ ?imbue@?$basic_ios@DU?$char_traits@D@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?imbue@?$basic_ios@GU?$char_traits@G@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?imbue@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?imbue@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEXABVlocale@2@@Z
+ ?imbue@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEXABVlocale@2@@Z
+ ?imbue@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEXABVlocale@2@@Z
+ ?imbue@ios_base@std@@QAE?AVlocale@2@ABV32@@Z
+ ?in@?$codecvt@DDH@std@@QBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?in@?$codecvt@GDH@std@@QBEHAAHPBD1AAPBDPAG3AAPAG@Z
+ ?in@?$codecvt@_WDH@std@@QBEHAAHPBD1AAPBDPA_W3AAPA_W@Z
+ ?in_avail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JXZ
+ ?in_avail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JXZ
+ ?in_avail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JXZ
+ ?infinity@?$numeric_limits@C@std@@SACXZ
+ ?infinity@?$numeric_limits@D@std@@SADXZ
+ ?infinity@?$numeric_limits@E@std@@SAEXZ
+ ?infinity@?$numeric_limits@F@std@@SAFXZ
+ ?infinity@?$numeric_limits@G@std@@SAGXZ
+ ?infinity@?$numeric_limits@H@std@@SAHXZ
+ ?infinity@?$numeric_limits@I@std@@SAIXZ
+ ?infinity@?$numeric_limits@J@std@@SAJXZ
+ ?infinity@?$numeric_limits@K@std@@SAKXZ
+ ?infinity@?$numeric_limits@M@std@@SAMXZ
+ ?infinity@?$numeric_limits@N@std@@SANXZ
+ ?infinity@?$numeric_limits@O@std@@SAOXZ
+ ?infinity@?$numeric_limits@_J@std@@SA_JXZ
+ ?infinity@?$numeric_limits@_K@std@@SA_KXZ
+ ?infinity@?$numeric_limits@_N@std@@SA_NXZ
+ ?infinity@?$numeric_limits@_W@std@@SA_WXZ
+ ?init@?$basic_ios@DU?$char_traits@D@std@@@std@@IAEXPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@_N@Z
+ ?init@?$basic_ios@GU?$char_traits@G@std@@@std@@IAEXPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@_N@Z
+ ?init@?$basic_ios@_WU?$char_traits@_W@std@@@std@@IAEXPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@_N@Z
+ ?intl@?$moneypunct@D$00@std@@2_NB
+ ?intl@?$moneypunct@D$0A@@std@@2_NB
+ ?intl@?$moneypunct@G$00@std@@2_NB
+ ?intl@?$moneypunct@G$0A@@std@@2_NB
+ ?intl@?$moneypunct@_W$00@std@@2_NB
+ ?intl@?$moneypunct@_W$0A@@std@@2_NB
+ ?iostream_category@std@@YAABVerror_category@1@XZ
+ ?ipfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_N_N@Z
+ ?ipfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_N_N@Z
+ ?ipfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_N_N@Z
+ ?is@?$ctype@D@std@@QBEPBDPBD0PAF@Z
+ ?is@?$ctype@D@std@@QBE_NFD@Z
+ ?is@?$ctype@G@std@@QBEPBGPBG0PAF@Z
+ ?is@?$ctype@G@std@@QBE_NFG@Z
+ ?is@?$ctype@_W@std@@QBEPB_WPB_W0PAF@Z
+ ?is@?$ctype@_W@std@@QBE_NF_W@Z
+ ?is_bounded@_Num_base@std@@2_NB
+ ?is_bounded@_Num_float_base@std@@2_NB
+ ?is_bounded@_Num_int_base@std@@2_NB
+ ?is_current_task_group_canceling@Concurrency@@YA_NXZ
+ ?is_exact@_Num_base@std@@2_NB
+ ?is_exact@_Num_float_base@std@@2_NB
+ ?is_exact@_Num_int_base@std@@2_NB
+ ?is_iec559@_Num_base@std@@2_NB
+ ?is_iec559@_Num_float_base@std@@2_NB
+ ?is_integer@_Num_base@std@@2_NB
+ ?is_integer@_Num_float_base@std@@2_NB
+ ?is_integer@_Num_int_base@std@@2_NB
+ ?is_modulo@?$numeric_limits@_N@std@@2_NB
+ ?is_modulo@_Num_base@std@@2_NB
+ ?is_modulo@_Num_float_base@std@@2_NB
+ ?is_modulo@_Num_int_base@std@@2_NB
+ ?is_signed@?$numeric_limits@C@std@@2_NB
+ ?is_signed@?$numeric_limits@D@std@@2_NB
+ ?is_signed@?$numeric_limits@E@std@@2_NB
+ ?is_signed@?$numeric_limits@F@std@@2_NB
+ ?is_signed@?$numeric_limits@G@std@@2_NB
+ ?is_signed@?$numeric_limits@H@std@@2_NB
+ ?is_signed@?$numeric_limits@I@std@@2_NB
+ ?is_signed@?$numeric_limits@J@std@@2_NB
+ ?is_signed@?$numeric_limits@K@std@@2_NB
+ ?is_signed@?$numeric_limits@_J@std@@2_NB
+ ?is_signed@?$numeric_limits@_K@std@@2_NB
+ ?is_signed@?$numeric_limits@_N@std@@2_NB
+ ?is_signed@?$numeric_limits@_W@std@@2_NB
+ ?is_signed@_Num_base@std@@2_NB
+ ?is_signed@_Num_float_base@std@@2_NB
+ ?is_specialized@_Num_base@std@@2_NB
+ ?is_specialized@_Num_float_base@std@@2_NB
+ ?is_specialized@_Num_int_base@std@@2_NB
+ ?isfx@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?isfx@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?isfx@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?iword@ios_base@std@@QAEAAJH@Z
+ ?length@?$codecvt@DDH@std@@QBEHABHPBD1I@Z
+ ?length@?$codecvt@GDH@std@@QBEHABHPBD1I@Z
+ ?length@?$codecvt@_WDH@std@@QBEHABHPBD1I@Z
+ ?lowest@?$numeric_limits@C@std@@SACXZ
+ ?lowest@?$numeric_limits@D@std@@SADXZ
+ ?lowest@?$numeric_limits@E@std@@SAEXZ
+ ?lowest@?$numeric_limits@F@std@@SAFXZ
+ ?lowest@?$numeric_limits@G@std@@SAGXZ
+ ?lowest@?$numeric_limits@H@std@@SAHXZ
+ ?lowest@?$numeric_limits@I@std@@SAIXZ
+ ?lowest@?$numeric_limits@J@std@@SAJXZ
+ ?lowest@?$numeric_limits@K@std@@SAKXZ
+ ?lowest@?$numeric_limits@M@std@@SAMXZ
+ ?lowest@?$numeric_limits@N@std@@SANXZ
+ ?lowest@?$numeric_limits@O@std@@SAOXZ
+ ?lowest@?$numeric_limits@_J@std@@SA_JXZ
+ ?lowest@?$numeric_limits@_K@std@@SA_KXZ
+ ?lowest@?$numeric_limits@_N@std@@SA_NXZ
+ ?lowest@?$numeric_limits@_W@std@@SA_WXZ
+ ?max@?$numeric_limits@C@std@@SACXZ
+ ?max@?$numeric_limits@D@std@@SADXZ
+ ?max@?$numeric_limits@E@std@@SAEXZ
+ ?max@?$numeric_limits@F@std@@SAFXZ
+ ?max@?$numeric_limits@G@std@@SAGXZ
+ ?max@?$numeric_limits@H@std@@SAHXZ
+ ?max@?$numeric_limits@I@std@@SAIXZ
+ ?max@?$numeric_limits@J@std@@SAJXZ
+ ?max@?$numeric_limits@K@std@@SAKXZ
+ ?max@?$numeric_limits@M@std@@SAMXZ
+ ?max@?$numeric_limits@N@std@@SANXZ
+ ?max@?$numeric_limits@O@std@@SAOXZ
+ ?max@?$numeric_limits@_J@std@@SA_JXZ
+ ?max@?$numeric_limits@_K@std@@SA_KXZ
+ ?max@?$numeric_limits@_N@std@@SA_NXZ
+ ?max@?$numeric_limits@_W@std@@SA_WXZ
+ ?max_digits10@?$numeric_limits@C@std@@2HB
+ ?max_digits10@?$numeric_limits@D@std@@2HB
+ ?max_digits10@?$numeric_limits@E@std@@2HB
+ ?max_digits10@?$numeric_limits@F@std@@2HB
+ ?max_digits10@?$numeric_limits@G@std@@2HB
+ ?max_digits10@?$numeric_limits@H@std@@2HB
+ ?max_digits10@?$numeric_limits@I@std@@2HB
+ ?max_digits10@?$numeric_limits@J@std@@2HB
+ ?max_digits10@?$numeric_limits@K@std@@2HB
+ ?max_digits10@?$numeric_limits@M@std@@2HB
+ ?max_digits10@?$numeric_limits@N@std@@2HB
+ ?max_digits10@?$numeric_limits@O@std@@2HB
+ ?max_digits10@?$numeric_limits@_J@std@@2HB
+ ?max_digits10@?$numeric_limits@_K@std@@2HB
+ ?max_digits10@?$numeric_limits@_N@std@@2HB
+ ?max_digits10@?$numeric_limits@_W@std@@2HB
+ ?max_digits10@_Num_base@std@@2HB
+ ?max_exponent10@?$numeric_limits@M@std@@2HB
+ ?max_exponent10@?$numeric_limits@N@std@@2HB
+ ?max_exponent10@?$numeric_limits@O@std@@2HB
+ ?max_exponent10@_Num_base@std@@2HB
+ ?max_exponent@?$numeric_limits@M@std@@2HB
+ ?max_exponent@?$numeric_limits@N@std@@2HB
+ ?max_exponent@?$numeric_limits@O@std@@2HB
+ ?max_exponent@_Num_base@std@@2HB
+ ?max_length@codecvt_base@std@@QBEHXZ
+ ?min@?$numeric_limits@C@std@@SACXZ
+ ?min@?$numeric_limits@D@std@@SADXZ
+ ?min@?$numeric_limits@E@std@@SAEXZ
+ ?min@?$numeric_limits@F@std@@SAFXZ
+ ?min@?$numeric_limits@G@std@@SAGXZ
+ ?min@?$numeric_limits@H@std@@SAHXZ
+ ?min@?$numeric_limits@I@std@@SAIXZ
+ ?min@?$numeric_limits@J@std@@SAJXZ
+ ?min@?$numeric_limits@K@std@@SAKXZ
+ ?min@?$numeric_limits@M@std@@SAMXZ
+ ?min@?$numeric_limits@N@std@@SANXZ
+ ?min@?$numeric_limits@O@std@@SAOXZ
+ ?min@?$numeric_limits@_J@std@@SA_JXZ
+ ?min@?$numeric_limits@_K@std@@SA_KXZ
+ ?min@?$numeric_limits@_N@std@@SA_NXZ
+ ?min@?$numeric_limits@_W@std@@SA_WXZ
+ ?min_exponent10@?$numeric_limits@M@std@@2HB
+ ?min_exponent10@?$numeric_limits@N@std@@2HB
+ ?min_exponent10@?$numeric_limits@O@std@@2HB
+ ?min_exponent10@_Num_base@std@@2HB
+ ?min_exponent@?$numeric_limits@M@std@@2HB
+ ?min_exponent@?$numeric_limits@N@std@@2HB
+ ?min_exponent@?$numeric_limits@O@std@@2HB
+ ?min_exponent@_Num_base@std@@2HB
+ ?move@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEX$$QAV12@@Z
+ ?move@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEX$$QAV12@@Z
+ ?move@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEX$$QAV12@@Z
+ ?narrow@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDDD@Z
+ ?narrow@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEDGD@Z
+ ?narrow@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBED_WD@Z
+ ?narrow@?$ctype@D@std@@QBEDDD@Z
+ ?narrow@?$ctype@D@std@@QBEPBDPBD0DPAD@Z
+ ?narrow@?$ctype@G@std@@QBEDGD@Z
+ ?narrow@?$ctype@G@std@@QBEPBGPBG0DPAD@Z
+ ?narrow@?$ctype@_W@std@@QBED_WD@Z
+ ?narrow@?$ctype@_W@std@@QBEPB_WPB_W0DPAD@Z
+ ?opfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE_NXZ
+ ?opfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE_NXZ
+ ?opfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE_NXZ
+ ?osfx@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?osfx@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?osfx@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?out@?$codecvt@DDH@std@@QBEHAAHPBD1AAPBDPAD3AAPAD@Z
+ ?out@?$codecvt@GDH@std@@QBEHAAHPBG1AAPBGPAD3AAPAD@Z
+ ?out@?$codecvt@_WDH@std@@QBEHAAHPB_W1AAPB_WPAD3AAPAD@Z
+ ?overflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHH@Z
+ ?overflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGG@Z
+ ?overflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGG@Z
+ ?pbackfail@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHH@Z
+ ?pbackfail@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGG@Z
+ ?pbackfail@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGG@Z
+ ?pbase@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?pbase@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?pbase@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?pbump@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXH@Z
+ ?pbump@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXH@Z
+ ?pbump@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXH@Z
+ ?peek@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?peek@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?peek@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?pptr@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IBEPADXZ
+ ?pptr@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IBEPAGXZ
+ ?pptr@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IBEPA_WXZ
+ ?precision@ios_base@std@@QAE_J_J@Z
+ ?precision@ios_base@std@@QBE_JXZ
+ ?pubimbue@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?pubimbue@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?pubimbue@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AVlocale@2@ABV32@@Z
+ ?pubseekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z
+ ?pubseekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z
+ ?pubseekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z
+ ?pubseekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z
+ ?pubseekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@_JHH@Z
+ ?pubseekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@_JII@Z
+ ?pubseekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z
+ ?pubseekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z
+ ?pubseekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z
+ ?pubseekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z
+ ?pubseekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@V32@H@Z
+ ?pubseekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@V32@I@Z
+ ?pubsetbuf@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEPAV12@PAD_J@Z
+ ?pubsetbuf@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEPAV12@PAG_J@Z
+ ?pubsetbuf@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEPAV12@PA_W_J@Z
+ ?pubsync@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?pubsync@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEHXZ
+ ?pubsync@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEHXZ
+ ?put@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z
+ ?put@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@G@Z
+ ?put@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_W@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DJ@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DK@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DN@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DO@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBX@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_J@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_K@Z
+ ?put@?$num_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@D_N@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GJ@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GK@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GN@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GO@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBX@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_J@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_K@Z
+ ?put@?$num_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@G_N@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WJ@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WK@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WN@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WO@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBX@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_J@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_K@Z
+ ?put@?$num_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_W_N@Z
+ ?put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@DD@Z
+ ?put@?$time_put@DV?$ostreambuf_iterator@DU?$char_traits@D@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@DU?$char_traits@D@std@@@2@V32@AAVios_base@2@DPBUtm@@PBD3@Z
+ ?put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@DD@Z
+ ?put@?$time_put@GV?$ostreambuf_iterator@GU?$char_traits@G@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@GU?$char_traits@G@std@@@2@V32@AAVios_base@2@GPBUtm@@PBG3@Z
+ ?put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@DD@Z
+ ?put@?$time_put@_WV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@std@@@std@@QBE?AV?$ostreambuf_iterator@_WU?$char_traits@_W@std@@@2@V32@AAVios_base@2@_WPBUtm@@PB_W4@Z
+ ?putback@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@D@Z
+ ?putback@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@G@Z
+ ?putback@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_W@Z
+ ?pword@ios_base@std@@QAEAAPAXH@Z
+ ?quiet_NaN@?$numeric_limits@C@std@@SACXZ
+ ?quiet_NaN@?$numeric_limits@D@std@@SADXZ
+ ?quiet_NaN@?$numeric_limits@E@std@@SAEXZ
+ ?quiet_NaN@?$numeric_limits@F@std@@SAFXZ
+ ?quiet_NaN@?$numeric_limits@G@std@@SAGXZ
+ ?quiet_NaN@?$numeric_limits@H@std@@SAHXZ
+ ?quiet_NaN@?$numeric_limits@I@std@@SAIXZ
+ ?quiet_NaN@?$numeric_limits@J@std@@SAJXZ
+ ?quiet_NaN@?$numeric_limits@K@std@@SAKXZ
+ ?quiet_NaN@?$numeric_limits@M@std@@SAMXZ
+ ?quiet_NaN@?$numeric_limits@N@std@@SANXZ
+ ?quiet_NaN@?$numeric_limits@O@std@@SAOXZ
+ ?quiet_NaN@?$numeric_limits@_J@std@@SA_JXZ
+ ?quiet_NaN@?$numeric_limits@_K@std@@SA_KXZ
+ ?quiet_NaN@?$numeric_limits@_N@std@@SA_NXZ
+ ?quiet_NaN@?$numeric_limits@_W@std@@SA_WXZ
+ ?radix@_Num_base@std@@2HB
+ ?radix@_Num_float_base@std@@2HB
+ ?radix@_Num_int_base@std@@2HB
+ ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@PAV32@@Z
+ ?rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@XZ
+ ?rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@PAV32@@Z
+ ?rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@XZ
+ ?rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@PAV32@@Z
+ ?rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBEPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@XZ
+ ?rdstate@ios_base@std@@QBEHXZ
+ ?read@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@PAD_J@Z
+ ?read@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@PAG_J@Z
+ ?read@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PA_W_J@Z
+ ?readsome@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE_JPAD_J@Z
+ ?readsome@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE_JPAG_J@Z
+ ?readsome@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE_JPA_W_J@Z
+ ?register_callback@ios_base@std@@QAEXP6AXW4event@12@AAV12@H@ZH@Z
+ ?resetiosflags@std@@YA?AU?$_Smanip@H@1@H@Z
+ ?round_error@?$numeric_limits@C@std@@SACXZ
+ ?round_error@?$numeric_limits@D@std@@SADXZ
+ ?round_error@?$numeric_limits@E@std@@SAEXZ
+ ?round_error@?$numeric_limits@F@std@@SAFXZ
+ ?round_error@?$numeric_limits@G@std@@SAGXZ
+ ?round_error@?$numeric_limits@H@std@@SAHXZ
+ ?round_error@?$numeric_limits@I@std@@SAIXZ
+ ?round_error@?$numeric_limits@J@std@@SAJXZ
+ ?round_error@?$numeric_limits@K@std@@SAKXZ
+ ?round_error@?$numeric_limits@M@std@@SAMXZ
+ ?round_error@?$numeric_limits@N@std@@SANXZ
+ ?round_error@?$numeric_limits@O@std@@SAOXZ
+ ?round_error@?$numeric_limits@_J@std@@SA_JXZ
+ ?round_error@?$numeric_limits@_K@std@@SA_KXZ
+ ?round_error@?$numeric_limits@_N@std@@SA_NXZ
+ ?round_error@?$numeric_limits@_W@std@@SA_WXZ
+ ?round_style@_Num_base@std@@2W4float_round_style@2@B
+ ?round_style@_Num_float_base@std@@2W4float_round_style@2@B
+ ?sbumpc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sbumpc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?sbumpc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?scan_is@?$ctype@D@std@@QBEPBDFPBD0@Z
+ ?scan_is@?$ctype@G@std@@QBEPBGFPBG0@Z
+ ?scan_is@?$ctype@_W@std@@QBEPB_WFPB_W0@Z
+ ?scan_not@?$ctype@D@std@@QBEPBDFPBD0@Z
+ ?scan_not@?$ctype@G@std@@QBEPBGFPBG0@Z
+ ?scan_not@?$ctype@_W@std@@QBEPB_WFPB_W0@Z
+ ?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z
+ ?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JH@Z
+ ?seekg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JH@Z
+ ?seekoff@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z
+ ?seekoff@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z
+ ?seekoff@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE?AV?$fpos@H@2@_JHH@Z
+ ?seekp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@_JH@Z
+ ?seekp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@_JH@Z
+ ?seekp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@V?$fpos@H@2@@Z
+ ?seekp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@_JH@Z
+ ?seekpos@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z
+ ?seekpos@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z
+ ?seekpos@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE?AV?$fpos@H@2@V32@H@Z
+ ?set_new_handler@std@@YAP6AXXZH@Z
+ ?set_new_handler@std@@YAP6AXXZP6AXXZ@Z
+ ?set_rdbuf@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXPAV?$basic_streambuf@DU?$char_traits@D@std@@@2@@Z
+ ?set_rdbuf@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXPAV?$basic_streambuf@GU?$char_traits@G@std@@@2@@Z
+ ?set_rdbuf@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXPAV?$basic_streambuf@_WU?$char_traits@_W@std@@@2@@Z
+ ?setbase@std@@YA?AU?$_Smanip@H@1@H@Z
+ ?setbuf@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEPAV12@PAD_J@Z
+ ?setbuf@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEPAV12@PAG_J@Z
+ ?setbuf@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEPAV12@PA_W_J@Z
+ ?setf@ios_base@std@@QAEHH@Z
+ ?setf@ios_base@std@@QAEHHH@Z
+ ?setg@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD00@Z
+ ?setg@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG00@Z
+ ?setg@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W00@Z
+ ?setiosflags@std@@YA?AU?$_Smanip@H@1@H@Z
+ ?setp@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD00@Z
+ ?setp@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXPAD0@Z
+ ?setp@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG00@Z
+ ?setp@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXPAG0@Z
+ ?setp@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W00@Z
+ ?setp@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXPA_W0@Z
+ ?setprecision@std@@YA?AU?$_Smanip@_J@1@_J@Z
+ ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXH_N@Z
+ ?setstate@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXI@Z
+ ?setstate@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXH_N@Z
+ ?setstate@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXI@Z
+ ?setstate@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXH_N@Z
+ ?setstate@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXI@Z
+ ?setstate@ios_base@std@@QAEXH@Z
+ ?setstate@ios_base@std@@QAEXH_N@Z
+ ?setstate@ios_base@std@@QAEXI@Z
+ ?setw@std@@YA?AU?$_Smanip@_J@1@_J@Z
+ ?sgetc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sgetc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?sgetc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?sgetn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPAD_J@Z
+ ?sgetn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JPAG_J@Z
+ ?sgetn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JPA_W_J@Z
+ ?showmanyc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JXZ
+ ?showmanyc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JXZ
+ ?showmanyc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JXZ
+ ?signaling_NaN@?$numeric_limits@C@std@@SACXZ
+ ?signaling_NaN@?$numeric_limits@D@std@@SADXZ
+ ?signaling_NaN@?$numeric_limits@E@std@@SAEXZ
+ ?signaling_NaN@?$numeric_limits@F@std@@SAFXZ
+ ?signaling_NaN@?$numeric_limits@G@std@@SAGXZ
+ ?signaling_NaN@?$numeric_limits@H@std@@SAHXZ
+ ?signaling_NaN@?$numeric_limits@I@std@@SAIXZ
+ ?signaling_NaN@?$numeric_limits@J@std@@SAJXZ
+ ?signaling_NaN@?$numeric_limits@K@std@@SAKXZ
+ ?signaling_NaN@?$numeric_limits@M@std@@SAMXZ
+ ?signaling_NaN@?$numeric_limits@N@std@@SANXZ
+ ?signaling_NaN@?$numeric_limits@O@std@@SAOXZ
+ ?signaling_NaN@?$numeric_limits@_J@std@@SA_JXZ
+ ?signaling_NaN@?$numeric_limits@_K@std@@SA_KXZ
+ ?signaling_NaN@?$numeric_limits@_N@std@@SA_NXZ
+ ?signaling_NaN@?$numeric_limits@_W@std@@SA_WXZ
+ ?snextc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?snextc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?snextc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?sputbackc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z
+ ?sputbackc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGG@Z
+ ?sputbackc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEG_W@Z
+ ?sputc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHD@Z
+ ?sputc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGG@Z
+ ?sputc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEG_W@Z
+ ?sputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAE_JPBD_J@Z
+ ?sputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAE_JPBG_J@Z
+ ?sputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAE_JPB_W_J@Z
+ ?start@agent@Concurrency@@QAE_NXZ
+ ?status@agent@Concurrency@@QAE?AW4agent_status@2@XZ
+ ?status_port@agent@Concurrency@@QAEPAV?$ISource@W4agent_status@Concurrency@@@2@XZ
+ ?stossc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEXXZ
+ ?stossc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEXXZ
+ ?stossc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEXXZ
+ ?sungetc@?$basic_streambuf@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sungetc@?$basic_streambuf@GU?$char_traits@G@std@@@std@@QAEGXZ
+ ?sungetc@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@QAEGXZ
+ ?swap@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_iostream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_iostream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_iostream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEXAAV12@@Z
+ ?swap@?$basic_streambuf@DU?$char_traits@D@std@@@std@@IAEXAAV12@@Z
+ ?swap@?$basic_streambuf@GU?$char_traits@G@std@@@std@@IAEXAAV12@@Z
+ ?swap@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@IAEXAAV12@@Z
+ ?swap@ios_base@std@@QAEXAAV12@@Z
+ ?sync@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEHXZ
+ ?sync@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEHXZ
+ ?sync@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEHXZ
+ ?sync@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ
+ ?sync@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEHXZ
+ ?sync@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEHXZ
+ ?sync_with_stdio@ios_base@std@@SA_N_N@Z
+ ?system_category@std@@YAABVerror_category@1@XZ
+ ?table@?$ctype@D@std@@QBEPBFXZ
+ ?table_size@?$ctype@D@std@@2IB
+ ?tellg@?$basic_istream@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellg@?$basic_istream@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellg@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellp@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellp@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tellp@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAE?AV?$fpos@H@2@XZ
+ ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QAEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@PAV32@@Z
+ ?tie@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEPAV?$basic_ostream@DU?$char_traits@D@std@@@2@XZ
+ ?tie@?$basic_ios@GU?$char_traits@G@std@@@std@@QAEPAV?$basic_ostream@GU?$char_traits@G@std@@@2@PAV32@@Z
+ ?tie@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEPAV?$basic_ostream@GU?$char_traits@G@std@@@2@XZ
+ ?tie@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QAEPAV?$basic_ostream@_WU?$char_traits@_W@std@@@2@PAV32@@Z
+ ?tie@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBEPAV?$basic_ostream@_WU?$char_traits@_W@std@@@2@XZ
+ ?tinyness_before@_Num_base@std@@2_NB
+ ?tinyness_before@_Num_float_base@std@@2_NB
+ ?tolower@?$ctype@D@std@@QBEDD@Z
+ ?tolower@?$ctype@D@std@@QBEPBDPADPBD@Z
+ ?tolower@?$ctype@G@std@@QBEGG@Z
+ ?tolower@?$ctype@G@std@@QBEPBGPAGPBG@Z
+ ?tolower@?$ctype@_W@std@@QBEPB_WPA_WPB_W@Z
+ ?tolower@?$ctype@_W@std@@QBE_W_W@Z
+ ?toupper@?$ctype@D@std@@QBEDD@Z
+ ?toupper@?$ctype@D@std@@QBEPBDPADPBD@Z
+ ?toupper@?$ctype@G@std@@QBEGG@Z
+ ?toupper@?$ctype@G@std@@QBEPBGPAGPBG@Z
+ ?toupper@?$ctype@_W@std@@QBEPB_WPA_WPB_W@Z
+ ?toupper@?$ctype@_W@std@@QBE_W_W@Z
+ ?traps@_Num_base@std@@2_NB
+ ?traps@_Num_float_base@std@@2_NB
+ ?uflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ
+ ?uflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGXZ
+ ?uflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGXZ
+ ?uncaught_exception@std@@YA_NXZ
+ ?underflow@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAEHXZ
+ ?underflow@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAEGXZ
+ ?underflow@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAEGXZ
+ ?unget@?$basic_istream@DU?$char_traits@D@std@@@std@@QAEAAV12@XZ
+ ?unget@?$basic_istream@GU?$char_traits@G@std@@@std@@QAEAAV12@XZ
+ ?unget@?$basic_istream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@XZ
+ ?unsetf@ios_base@std@@QAEXH@Z
+ ?unshift@?$codecvt@DDH@std@@QBEHAAHPAD1AAPAD@Z
+ ?unshift@?$codecvt@GDH@std@@QBEHAAHPAD1AAPAD@Z
+ ?unshift@?$codecvt@_WDH@std@@QBEHAAHPAD1AAPAD@Z
+ ?wait@agent@Concurrency@@SA?AW4agent_status@2@PAV12@I@Z
+ ?wait_for_all@agent@Concurrency@@SAXIPAPAV12@PAW4agent_status@2@I@Z
+ ?wait_for_one@agent@Concurrency@@SAXIPAPAV12@AAW4agent_status@2@AAII@Z
+ ?wcerr@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?wcerr@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?wcin@std@@3V?$basic_istream@GU?$char_traits@G@std@@@1@A
+ ?wcin@std@@3V?$basic_istream@_WU?$char_traits@_W@std@@@1@A
+ ?wclog@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?wclog@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?wcout@std@@3V?$basic_ostream@GU?$char_traits@G@std@@@1@A
+ ?wcout@std@@3V?$basic_ostream@_WU?$char_traits@_W@std@@@1@A
+ ?widen@?$basic_ios@DU?$char_traits@D@std@@@std@@QBEDD@Z
+ ?widen@?$basic_ios@GU?$char_traits@G@std@@@std@@QBEGD@Z
+ ?widen@?$basic_ios@_WU?$char_traits@_W@std@@@std@@QBE_WD@Z
+ ?widen@?$ctype@D@std@@QBEDD@Z
+ ?widen@?$ctype@D@std@@QBEPBDPBD0PAD@Z
+ ?widen@?$ctype@G@std@@QBEGD@Z
+ ?widen@?$ctype@G@std@@QBEPBDPBD0PAG@Z
+ ?widen@?$ctype@_W@std@@QBEPBDPBD0PA_W@Z
+ ?widen@?$ctype@_W@std@@QBE_WD@Z
+ ?width@ios_base@std@@QAE_J_J@Z
+ ?width@ios_base@std@@QBE_JXZ
+ ?write@?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV12@PBD_J@Z
+ ?write@?$basic_ostream@GU?$char_traits@G@std@@@std@@QAEAAV12@PBG_J@Z
+ ?write@?$basic_ostream@_WU?$char_traits@_W@std@@@std@@QAEAAV12@PB_W_J@Z
+ ?ws@std@@YAAAV?$basic_istream@DU?$char_traits@D@std@@@1@AAV21@@Z
+ ?ws@std@@YAAAV?$basic_istream@GU?$char_traits@G@std@@@1@AAV21@@Z
+ ?ws@std@@YAAAV?$basic_istream@_WU?$char_traits@_W@std@@@1@AAV21@@Z
+ ?xalloc@ios_base@std@@SAHXZ
+ ?xsgetn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JPAD_J@Z
+ ?xsgetn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JPAG_J@Z
+ ?xsgetn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPA_W_J@Z
+ ?xsputn@?$basic_streambuf@DU?$char_traits@D@std@@@std@@MAE_JPBD_J@Z
+ ?xsputn@?$basic_streambuf@GU?$char_traits@G@std@@@std@@MAE_JPBG_J@Z
+ ?xsputn@?$basic_streambuf@_WU?$char_traits@_W@std@@@std@@MAE_JPB_W_J@Z
+ _Cosh
+ _Denorm
+ _Dnorm
+ _Dscale
+ _Dtest
+ _Eps
+ _Exp
+ _FCosh
+ _FDenorm
+ _FDnorm
+ _FDscale
+ _FDtest
+ _FEps
+ _FExp
+ _FInf
+ _FNan
+ _FRteps
+ _FSinh
+ _FSnan
+ _FXbig
+ _GetLocaleForCP
+ _Getcoll
+ _Getctype
+ _Getcvt
+ _Getdateorder
+ _Getwctype
+ _Getwctypes
+ _Hugeval
+ _Inf
+ _LCosh
+ _LDenorm
+ _LDscale
+ _LDtest
+ _LEps
+ _LExp
+ _LInf
+ _LNan
+ _LPoly
+ _LRteps
+ _LSinh
+ _LSnan
+ _LXbig
+ _LZero
+ _Mbrtowc
+ _Mtxdst
+ _Mtxinit
+ _Mtxlock
+ _Mtxunlock
+ _Nan
+ _Once
+ _Poly
+ _Rteps
+ _Sinh
+ _Snan
+ _Stod
+ _Stodx
+ _Stof
+ _Stofx
+ _Stold
+ _Stoldx
+ _Stoll
+ _Stollx
+ _Stolx
+ _Stoul
+ _Stoull
+ _Stoullx
+ _Stoulx
+ _Strcoll
+ _Strxfrm
+ _Tolower
+ _Toupper
+ _Towlower
+ _Towupper
+ _Wcrtomb
+ _Wcscoll
+ _Wcsxfrm
+ _Xbig
+ __Wcrtomb_lk
+ towctrans
+ wctrans
+ wctype
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def
new file mode 100644
index 00000000..6317e658
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-msvcr100-win32.def
@@ -0,0 +1,1640 @@
+
+ ;
+ _except_handler4
+ ;_heap_init
+ ;__crtExitProcess
+ ;_NMSG_WRITE
+ ;_FF_MSGBANNER
+ ;_cinit
+ ;_setenvp
+ ;__crtGetEnvironmentStringsA
+ ;_ioinit
+ ;_mtinit
+
+ _initterm=my_initterm
+ __dllonexit=my_dllonexit
+ __getmainargs=my_getmainargs
+ __setusermatherr=my_setusermatherr
+
+ _onexit DATA ; the one is crazy. atonexit.obj in msvcrt.lib which calls the __imp___onexit directly.
+
+ ; MSVCR100.DLL exports
+ ??0?$_SpinWait@$0A@@details@Concurrency@@QAE@P6AXXZ@Z
+ ??0SchedulerPolicy@Concurrency@@QAA@IZZ
+ ??0SchedulerPolicy@Concurrency@@QAE@ABV01@@Z
+ ??0SchedulerPolicy@Concurrency@@QAE@XZ
+ ??0_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??0_NonReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??0_ReaderWriterLock@details@Concurrency@@QAE@XZ
+ ??0_ReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??0_ReentrantLock@details@Concurrency@@QAE@XZ
+ ??0_ReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??0_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z
+ ??0_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@AAV123@@Z
+ ??0_SpinLock@details@Concurrency@@QAE@ACJ@Z
+ ??0_TaskCollection@details@Concurrency@@QAE@XZ
+ ??0_Timer@details@Concurrency@@IAE@I_N@Z
+ ??0__non_rtti_object@std@@QAE@ABV01@@Z
+ ??0__non_rtti_object@std@@QAE@PBD@Z
+ ;fixme-not-in-libcmt.lib; ??0bad_cast@std@@AAE@PBQBD@Z
+ ??0bad_cast@std@@QAE@ABV01@@Z
+ ??0bad_cast@std@@QAE@PBD@Z
+ ??0bad_target@Concurrency@@QAE@PBD@Z
+ ??0bad_target@Concurrency@@QAE@XZ
+ ??0bad_typeid@std@@QAE@ABV01@@Z
+ ??0bad_typeid@std@@QAE@PBD@Z
+ ??0context_self_unblock@Concurrency@@QAE@PBD@Z
+ ??0context_self_unblock@Concurrency@@QAE@XZ
+ ??0context_unblock_unbalanced@Concurrency@@QAE@PBD@Z
+ ??0context_unblock_unbalanced@Concurrency@@QAE@XZ
+ ??0critical_section@Concurrency@@QAE@XZ
+ ??0default_scheduler_exists@Concurrency@@QAE@PBD@Z
+ ??0default_scheduler_exists@Concurrency@@QAE@XZ
+ ??0event@Concurrency@@QAE@XZ
+ ??0exception@std@@QAE@ABQBD@Z
+ ??0exception@std@@QAE@ABQBDH@Z
+ ??0exception@std@@QAE@ABV01@@Z
+ ??0exception@std@@QAE@XZ
+ ??0improper_lock@Concurrency@@QAE@PBD@Z
+ ??0improper_lock@Concurrency@@QAE@XZ
+ ??0improper_scheduler_attach@Concurrency@@QAE@PBD@Z
+ ??0improper_scheduler_attach@Concurrency@@QAE@XZ
+ ??0improper_scheduler_detach@Concurrency@@QAE@PBD@Z
+ ??0improper_scheduler_detach@Concurrency@@QAE@XZ
+ ??0improper_scheduler_reference@Concurrency@@QAE@PBD@Z
+ ??0improper_scheduler_reference@Concurrency@@QAE@XZ
+ ??0invalid_link_target@Concurrency@@QAE@PBD@Z
+ ??0invalid_link_target@Concurrency@@QAE@XZ
+ ??0invalid_multiple_scheduling@Concurrency@@QAE@PBD@Z
+ ??0invalid_multiple_scheduling@Concurrency@@QAE@XZ
+ ??0invalid_operation@Concurrency@@QAE@PBD@Z
+ ??0invalid_operation@Concurrency@@QAE@XZ
+ ??0invalid_oversubscribe_operation@Concurrency@@QAE@PBD@Z
+ ??0invalid_oversubscribe_operation@Concurrency@@QAE@XZ
+ ??0invalid_scheduler_policy_key@Concurrency@@QAE@PBD@Z
+ ??0invalid_scheduler_policy_key@Concurrency@@QAE@XZ
+ ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@PBD@Z
+ ??0invalid_scheduler_policy_thread_specification@Concurrency@@QAE@XZ
+ ??0invalid_scheduler_policy_value@Concurrency@@QAE@PBD@Z
+ ??0invalid_scheduler_policy_value@Concurrency@@QAE@XZ
+ ??0message_not_found@Concurrency@@QAE@PBD@Z
+ ??0message_not_found@Concurrency@@QAE@XZ
+ ??0missing_wait@Concurrency@@QAE@PBD@Z
+ ??0missing_wait@Concurrency@@QAE@XZ
+ ??0nested_scheduler_missing_detach@Concurrency@@QAE@PBD@Z
+ ??0nested_scheduler_missing_detach@Concurrency@@QAE@XZ
+ ??0operation_timed_out@Concurrency@@QAE@PBD@Z
+ ??0operation_timed_out@Concurrency@@QAE@XZ
+ ??0reader_writer_lock@Concurrency@@QAE@XZ
+ ??0scheduler_not_attached@Concurrency@@QAE@PBD@Z
+ ??0scheduler_not_attached@Concurrency@@QAE@XZ
+ ??0scheduler_resource_allocation_error@Concurrency@@QAE@J@Z
+ ??0scheduler_resource_allocation_error@Concurrency@@QAE@PBDJ@Z
+ ??0scoped_lock@critical_section@Concurrency@@QAE@AAV12@@Z
+ ??0scoped_lock@reader_writer_lock@Concurrency@@QAE@AAV12@@Z
+ ??0scoped_lock_read@reader_writer_lock@Concurrency@@QAE@AAV12@@Z
+ ??0task_canceled@details@Concurrency@@QAE@PBD@Z
+ ??0task_canceled@details@Concurrency@@QAE@XZ
+ ??0unsupported_os@Concurrency@@QAE@PBD@Z
+ ??0unsupported_os@Concurrency@@QAE@XZ
+ ??1SchedulerPolicy@Concurrency@@QAE@XZ
+ ??1_NonReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??1_ReentrantBlockingLock@details@Concurrency@@QAE@XZ
+ ??1_Scoped_lock@_NonReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??1_Scoped_lock@_ReentrantPPLLock@details@Concurrency@@QAE@XZ
+ ??1_SpinLock@details@Concurrency@@QAE@XZ
+ ??1_TaskCollection@details@Concurrency@@QAE@XZ
+ ??1_Timer@details@Concurrency@@IAE@XZ
+ ??1__non_rtti_object@std@@UAE@XZ
+ ??1bad_cast@std@@UAE@XZ
+ ??1bad_typeid@std@@UAE@XZ
+ ??1critical_section@Concurrency@@QAE@XZ
+ ??1event@Concurrency@@QAE@XZ
+ ??1exception@std@@UAE@XZ
+ ??1reader_writer_lock@Concurrency@@QAE@XZ
+ ??1scoped_lock@critical_section@Concurrency@@QAE@XZ
+ ??1scoped_lock@reader_writer_lock@Concurrency@@QAE@XZ
+ ??1scoped_lock_read@reader_writer_lock@Concurrency@@QAE@XZ
+ ??1type_info@@UAE@XZ
+ ??2@YAPAXI@Z
+ ??2@YAPAXIHPBDH@Z
+ ??3@YAXPAX@Z
+ ;fixme-not-in-libcmt.lib; ??4?$_SpinWait@$00@details@Concurrency@@QAEAAV012@ABV012@@Z
+ ;fixme-not-in-libcmt.lib; ??4?$_SpinWait@$0A@@details@Concurrency@@QAEAAV012@ABV012@@Z
+ ??4SchedulerPolicy@Concurrency@@QAEAAV01@ABV01@@Z
+ ??4__non_rtti_object@std@@QAEAAV01@ABV01@@Z
+ ??4bad_cast@std@@QAEAAV01@ABV01@@Z
+ ??4bad_typeid@std@@QAEAAV01@ABV01@@Z
+ ??4exception@std@@QAEAAV01@ABV01@@Z
+ ??8type_info@@QBE_NABV0@@Z
+ ??9type_info@@QBE_NABV0@@Z
+ ??_7__non_rtti_object@std@@6B@
+ ??_7bad_cast@std@@6B@
+ ??_7bad_typeid@std@@6B@
+ ;fixme-not-in-libcmt.lib; ??_7exception@@6B@
+ ??_7exception@std@@6B@
+ ;fixme-not-in-libcmt.lib; ??_F?$_SpinWait@$00@details@Concurrency@@QAEXXZ
+ ;fixme-not-in-libcmt.lib; ??_F?$_SpinWait@$0A@@details@Concurrency@@QAEXXZ
+ ??_Fbad_cast@std@@QAEXXZ
+ ??_Fbad_typeid@std@@QAEXXZ
+ ??_U@YAPAXI@Z
+ ??_U@YAPAXIHPBDH@Z
+ ??_V@YAXPAX@Z
+ ?Alloc@Concurrency@@YAPAXI@Z
+ ?Block@Context@Concurrency@@SAXXZ
+ ?Create@CurrentScheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z
+ ?Create@Scheduler@Concurrency@@SAPAV12@ABVSchedulerPolicy@2@@Z
+ ?CreateResourceManager@Concurrency@@YAPAUIResourceManager@1@XZ
+ ?CreateScheduleGroup@CurrentScheduler@Concurrency@@SAPAVScheduleGroup@2@XZ
+ ?CurrentContext@Context@Concurrency@@SAPAV12@XZ
+ ?Detach@CurrentScheduler@Concurrency@@SAXXZ
+ ?DisableTracing@Concurrency@@YAJXZ
+ ?EnableTracing@Concurrency@@YAJXZ
+ ?Free@Concurrency@@YAXPAX@Z
+ ?Get@CurrentScheduler@Concurrency@@SAPAVScheduler@2@XZ
+ ?GetExecutionContextId@Concurrency@@YAIXZ
+ ?GetNumberOfVirtualProcessors@CurrentScheduler@Concurrency@@SAIXZ
+ ?GetOSVersion@Concurrency@@YA?AW4OSVersion@IResourceManager@1@XZ
+ ?GetPolicy@CurrentScheduler@Concurrency@@SA?AVSchedulerPolicy@2@XZ
+ ?GetPolicyValue@SchedulerPolicy@Concurrency@@QBEIW4PolicyElementKey@2@@Z
+ ?GetProcessorCount@Concurrency@@YAIXZ
+ ?GetProcessorNodeCount@Concurrency@@YAIXZ
+ ?GetSchedulerId@Concurrency@@YAIXZ
+ ?GetSharedTimerQueue@details@Concurrency@@YAPAXXZ
+ ?Id@Context@Concurrency@@SAIXZ
+ ?Id@CurrentScheduler@Concurrency@@SAIXZ
+ ?IsCurrentTaskCollectionCanceling@Context@Concurrency@@SA_NXZ
+ ?Log2@details@Concurrency@@YAKI@Z
+ ?Oversubscribe@Context@Concurrency@@SAX_N@Z
+ ?RegisterShutdownEvent@CurrentScheduler@Concurrency@@SAXPAX@Z
+ ?ResetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXXZ
+ ?ScheduleGroupId@Context@Concurrency@@SAIXZ
+ ?ScheduleTask@CurrentScheduler@Concurrency@@SAXP6AXPAX@Z0@Z
+ ?SetConcurrencyLimits@SchedulerPolicy@Concurrency@@QAEXII@Z
+ ?SetDefaultSchedulerPolicy@Scheduler@Concurrency@@SAXABVSchedulerPolicy@2@@Z
+ ?SetPolicyValue@SchedulerPolicy@Concurrency@@QAEIW4PolicyElementKey@2@I@Z
+ ?VirtualProcessorId@Context@Concurrency@@SAIXZ
+ ?Yield@Context@Concurrency@@SAXXZ
+ ?_Abort@_StructuredTaskCollection@details@Concurrency@@AAEXXZ
+ ?_Acquire@_NonReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Acquire@_NonReentrantPPLLock@details@Concurrency@@QAEXPAX@Z
+ ?_Acquire@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Acquire@_ReentrantLock@details@Concurrency@@QAEXXZ
+ ?_Acquire@_ReentrantPPLLock@details@Concurrency@@QAEXPAX@Z
+ ?_AcquireRead@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_AcquireWrite@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_Cancel@_StructuredTaskCollection@details@Concurrency@@QAEXXZ
+ ?_Cancel@_TaskCollection@details@Concurrency@@QAEXXZ
+ ?_CheckTaskCollection@_UnrealizedChore@details@Concurrency@@IAEXXZ
+ ?_ConcRT_Assert@details@Concurrency@@YAXPBD0H@Z
+ ?_ConcRT_CoreAssert@details@Concurrency@@YAXPBD0H@Z
+ ?_ConcRT_DumpMessage@details@Concurrency@@YAXPB_WZZ
+ ?_ConcRT_Trace@details@Concurrency@@YAXHPB_WZZ
+ ?_Copy_str@exception@std@@AAEXPBD@Z
+ ?_DoYield@?$_SpinWait@$00@details@Concurrency@@IAEXXZ
+ ?_DoYield@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ
+ ?_IsCanceling@_StructuredTaskCollection@details@Concurrency@@QAE_NXZ
+ ?_IsCanceling@_TaskCollection@details@Concurrency@@QAE_NXZ
+ ?_Name_base@type_info@@CAPBDPBV1@PAU__type_info_node@@@Z
+ ?_Name_base_internal@type_info@@CAPBDPBV1@PAU__type_info_node@@@Z
+ ?_NumberOfSpins@?$_SpinWait@$00@details@Concurrency@@IAEKXZ
+ ?_NumberOfSpins@?$_SpinWait@$0A@@details@Concurrency@@IAEKXZ
+ ?_Release@_NonReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Release@_NonReentrantPPLLock@details@Concurrency@@QAEXXZ
+ ?_Release@_ReentrantBlockingLock@details@Concurrency@@QAEXXZ
+ ?_Release@_ReentrantLock@details@Concurrency@@QAEXXZ
+ ?_Release@_ReentrantPPLLock@details@Concurrency@@QAEXXZ
+ ?_ReleaseRead@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_ReleaseWrite@_ReaderWriterLock@details@Concurrency@@QAEXXZ
+ ?_Reset@?$_SpinWait@$00@details@Concurrency@@IAEXXZ
+ ?_Reset@?$_SpinWait@$0A@@details@Concurrency@@IAEXXZ
+ ?_RunAndWait@_StructuredTaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z
+ ?_RunAndWait@_TaskCollection@details@Concurrency@@QAG?AW4_TaskCollectionStatus@23@PAV_UnrealizedChore@23@@Z
+ ?_Schedule@_StructuredTaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z
+ ?_Schedule@_TaskCollection@details@Concurrency@@QAEXPAV_UnrealizedChore@23@@Z
+ ?_SetSpinCount@?$_SpinWait@$00@details@Concurrency@@QAEXI@Z
+ ?_SetSpinCount@?$_SpinWait@$0A@@details@Concurrency@@QAEXI@Z
+ ?_ShouldSpinAgain@?$_SpinWait@$00@details@Concurrency@@IAE_NXZ
+ ?_ShouldSpinAgain@?$_SpinWait@$0A@@details@Concurrency@@IAE_NXZ
+ ?_SpinOnce@?$_SpinWait@$00@details@Concurrency@@QAE_NXZ
+ ?_SpinOnce@?$_SpinWait@$0A@@details@Concurrency@@QAE_NXZ
+ ?_SpinYield@Context@Concurrency@@SAXXZ
+ ?_Start@_Timer@details@Concurrency@@IAEXXZ
+ ?_Stop@_Timer@details@Concurrency@@IAEXXZ
+ ?_Tidy@exception@std@@AAEXXZ
+ ?_Trace_ppl_function@Concurrency@@YAXABU_GUID@@EW4ConcRT_EventType@1@@Z
+ ?_TryAcquire@_NonReentrantBlockingLock@details@Concurrency@@QAE_NXZ
+ ?_TryAcquire@_ReentrantBlockingLock@details@Concurrency@@QAE_NXZ
+ ?_TryAcquire@_ReentrantLock@details@Concurrency@@QAE_NXZ
+ ?_TryAcquireWrite@_ReaderWriterLock@details@Concurrency@@QAE_NXZ
+ ?_Type_info_dtor@type_info@@CAXPAV1@@Z
+ ?_Type_info_dtor_internal@type_info@@CAXPAV1@@Z
+ ?_UnderlyingYield@details@Concurrency@@YAXXZ
+ ?_ValidateExecute@@YAHP6GHXZ@Z
+ ?_ValidateRead@@YAHPBXI@Z
+ ?_ValidateWrite@@YAHPAXI@Z
+ ?_Value@_SpinCount@details@Concurrency@@SAIXZ
+ ?__ExceptionPtrAssign@@YAXPAXPBX@Z
+ ?__ExceptionPtrCompare@@YA_NPBX0@Z
+ ?__ExceptionPtrCopy@@YAXPAXPBX@Z
+ ?__ExceptionPtrCopyException@@YAXPAXPBX1@Z
+ ?__ExceptionPtrCreate@@YAXPAX@Z
+ ?__ExceptionPtrCurrentException@@YAXPAX@Z
+ ?__ExceptionPtrDestroy@@YAXPAX@Z
+ ?__ExceptionPtrRethrow@@YAXPBX@Z
+ __uncaught_exception
+ ?_inconsistency@@YAXXZ
+ ?_invalid_parameter@@YAXPBG00II@Z
+ ?_is_exception_typeof@@YAHABVtype_info@@PAU_EXCEPTION_POINTERS@@@Z
+ ?_name_internal_method@type_info@@QBEPBDPAU__type_info_node@@@Z
+ ?_open@@YAHPBDHH@Z
+ ?_query_new_handler@@YAP6AHI@ZXZ
+ ?_query_new_mode@@YAHXZ
+ ?_set_new_handler@@YAP6AHI@ZH@Z
+ ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z
+ ?_set_new_mode@@YAHH@Z
+ ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZH@Z
+ ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z
+ ?_sopen@@YAHPBDHHH@Z
+ ?_type_info_dtor_internal_method@type_info@@QAEXXZ
+ ?_wopen@@YAHPB_WHH@Z
+ ?_wsopen@@YAHPB_WHHH@Z
+ ?before@type_info@@QBEHABV1@@Z
+ ?get_error_code@scheduler_resource_allocation_error@Concurrency@@QBEJXZ
+ ?lock@critical_section@Concurrency@@QAEXXZ
+ ?lock@reader_writer_lock@Concurrency@@QAEXXZ
+ ?lock_read@reader_writer_lock@Concurrency@@QAEXXZ
+ ?name@type_info@@QBEPBDPAU__type_info_node@@@Z
+ ?native_handle@critical_section@Concurrency@@QAEAAV12@XZ
+ ?raw_name@type_info@@QBEPBDXZ
+ ?reset@event@Concurrency@@QAEXXZ
+ ?set@event@Concurrency@@QAEXXZ
+ ?set_new_handler@@YAP6AXXZP6AXXZ@Z
+ ?set_terminate@@YAP6AXXZH@Z
+ ?set_terminate@@YAP6AXXZP6AXXZ@Z
+ ?set_unexpected@@YAP6AXXZH@Z
+ ?set_unexpected@@YAP6AXXZP6AXXZ@Z
+ ?swprintf@@YAHPAGIPBGZZ
+ ?swprintf@@YAHPA_WIPB_WZZ
+ ?terminate@@YAXXZ ;fixme-causes-trouble-with-version-in-libcmt.lib;
+ ?try_lock@critical_section@Concurrency@@QAE_NXZ
+ ?try_lock@reader_writer_lock@Concurrency@@QAE_NXZ
+ ?try_lock_read@reader_writer_lock@Concurrency@@QAE_NXZ
+ ?unexpected@@YAXXZ
+ ?unlock@critical_section@Concurrency@@QAEXXZ
+ ?unlock@reader_writer_lock@Concurrency@@QAEXXZ
+ ?vswprintf@@YAHPA_WIPB_WPAD@Z
+ ?wait@Concurrency@@YAXI@Z
+ ?wait@event@Concurrency@@QAEII@Z
+ ?wait_for_multiple@event@Concurrency@@SAIPAPAV12@I_NI@Z
+ ?what@exception@std@@UBEPBDXZ
+ $I10_OUTPUT
+ _CIacos
+ _CIasin
+ _CIatan
+ _CIatan2
+ _CIcos
+ _CIcosh
+ _CIexp
+ _CIfmod
+ _CIlog
+ _CIlog10
+ _CIpow
+ _CIsin
+ _CIsinh
+ _CIsqrt
+ _CItan
+ _CItanh
+ _CRT_RTC_INIT
+ _CRT_RTC_INITW
+ _CreateFrameInfo
+ _CxxThrowException
+ _EH_prolog
+ _FindAndUnlinkFrame
+ _Getdays
+ _Getmonths
+ _Gettnames
+ _HUGE
+ _IsExceptionObjectToBeDestroyed
+ _NLG_Dispatch2
+ _NLG_Return
+ _NLG_Return2
+ _Strftime
+ _XcptFilter
+ __AdjustPointer
+ __BuildCatchObject
+ __BuildCatchObjectHelper
+ __CppXcptFilter
+ __CxxCallUnwindDelDtor
+ __CxxCallUnwindDtor
+ __CxxCallUnwindStdDelDtor
+ __CxxCallUnwindVecDtor
+ __CxxDetectRethrow
+ __CxxExceptionFilter
+ __CxxFrameHandler
+ __CxxFrameHandler2
+ __CxxFrameHandler3
+ __CxxLongjmpUnwind
+ __CxxQueryExceptionSize
+ __CxxRegisterExceptionObject
+ __CxxUnregisterExceptionObject
+ __DestructExceptionObject
+ __FrameUnwindFilter
+ __RTCastToVoid
+ __RTDynamicCast
+ __RTtypeid
+ __STRINGTOLD
+ __STRINGTOLD_L
+ __TypeMatch
+ ___lc_codepage_func
+ ___lc_collate_cp_func
+ ___lc_handle_func
+ ___mb_cur_max_func
+ ___mb_cur_max_l_func
+ ___setlc_active_func
+ ___unguarded_readlc_active_add_func
+ __argc
+ __argv
+ __badioinfo
+ __clean_type_info_names_internal
+ __control87_2
+ __create_locale
+ __crtCompareStringA
+ __crtCompareStringW
+ __crtLCMapStringA
+ __crtLCMapStringW
+ __daylight
+ __doserrno
+ __dstbias
+ ___fls_getvalue@4
+ ___fls_setvalue@8
+ __fpecode
+ __free_locale
+ __get_current_locale
+ __get_flsindex
+ ;fixme?; __get_tlsindex
+ __initenv
+ __iob_func
+ __isascii
+ __iscsym
+ __iscsymf
+ __iswcsym
+ __iswcsymf
+ __lconv
+ __lconv_init
+ __libm_sse2_acos
+ __libm_sse2_acosf
+ __libm_sse2_asin
+ __libm_sse2_asinf
+ __libm_sse2_atan
+ __libm_sse2_atan2
+ __libm_sse2_atanf
+ __libm_sse2_cos
+ __libm_sse2_cosf
+ __libm_sse2_exp
+ __libm_sse2_expf
+ __libm_sse2_log
+ __libm_sse2_log10
+ __libm_sse2_log10f
+ __libm_sse2_logf
+ __libm_sse2_pow
+ __libm_sse2_powf
+ __libm_sse2_sin
+ __libm_sse2_sinf
+ __libm_sse2_tan
+ __libm_sse2_tanf
+ __mb_cur_max
+ ;fixme?; __p___argc
+ ;fixme?; __p___argv
+ ;fixme?; __p___initenv
+ ;fixme?; __p___mb_cur_max
+ ;fixme?; __p___wargv
+ ;fixme?; __p___winitenv
+ ;fixme?; __p__acmdln
+ ;fixme?; __p__commode
+ ;fixme?; __p__daylight
+ ;fixme?; __p__dstbias
+ ;fixme?; __p__environ
+ ;fixme?; __p__fmode
+ ;fixme?; __p__iob
+ ;fixme?; __p__mbcasemap
+ ;fixme?; __p__mbctype
+ ;fixme?; __p__pctype
+ ;fixme?; __p__pgmptr
+ ;fixme?; __p__pwctype
+ ;fixme?; __p__timezone
+ ;fixme?; __p__tzname
+ ;fixme?; __p__wcmdln
+ ;fixme?; __p__wenviron
+ ;fixme?; __p__wpgmptr
+
+ __pctype_func
+ __pioinfo
+ __pwctype_func
+ __pxcptinfoptrs
+ __report_gsfailure
+ __set_app_type
+ __set_flsgetvalue
+ __setlc_active
+ ;fixme?; __setusermatherr
+ __strncnt
+ __swprintf_l
+ __sys_errlist
+ __sys_nerr
+ __threadhandle
+ __threadid
+ __timezone
+ __toascii
+ __tzname
+ __unDName
+ __unDNameEx
+ __unDNameHelper
+ __unguarded_readlc_active
+ __vswprintf_l
+ __wargv
+ __wcserror
+ __wcserror_s
+ __wcsncnt
+ ;fixme?; __wgetmainargs
+ __winitenv
+ _abnormal_termination
+ _abs64
+ _access
+ _access_s
+ _acmdln
+ _aligned_free
+ _aligned_malloc
+ _aligned_msize
+ _aligned_offset_malloc
+ _aligned_offset_realloc
+ _aligned_offset_recalloc
+ _aligned_realloc
+ _aligned_recalloc
+ _amsg_exit
+ _assert
+ _atodbl
+ _atodbl_l
+ _atof_l
+ _atoflt
+ _atoflt_l
+ _atoi64
+ _atoi64_l
+ _atoi_l
+ _atol_l
+ _atoldbl
+ _atoldbl_l
+ _beep
+ _beginthread
+ _beginthreadex
+ _byteswap_uint64
+ _byteswap_ulong
+ _byteswap_ushort
+ _c_exit
+ _cabs
+ _callnewh
+ _calloc_crt
+ _cexit
+ _cgets
+ _cgets_s
+ _cgetws
+ _cgetws_s
+ _chdir
+ _chdrive
+ _chgsign
+ _chkesp
+ _chmod
+ _chsize
+ _chsize_s
+ _clearfp
+ _close
+ _commit
+ ;fixme?; _commode
+ _configthreadlocale
+ _control87
+ _controlfp
+ _controlfp_s
+ _copysign
+ _cprintf
+ _cprintf_l
+ _cprintf_p
+ _cprintf_p_l
+ _cprintf_s
+ _cprintf_s_l
+ _cputs
+ _cputws
+ _creat
+ _create_locale
+ _crt_debugger_hook
+ _cscanf
+ _cscanf_l
+ _cscanf_s
+ _cscanf_s_l
+ _ctime32
+ _ctime32_s
+ _ctime64
+ _ctime64_s
+ _cwait
+ _cwprintf
+ _cwprintf_l
+ _cwprintf_p
+ _cwprintf_p_l
+ _cwprintf_s
+ _cwprintf_s_l
+ _cwscanf
+ _cwscanf_l
+ _cwscanf_s
+ _cwscanf_s_l
+ _daylight
+ _difftime32
+ _difftime64
+ _dosmaperr
+ _dstbias
+ _dup
+ _dup2
+ _dupenv_s
+ _ecvt
+ _ecvt_s
+ _encoded_null
+ _endthread
+ _endthreadex
+ _environ
+ _eof
+ _errno
+ _except_handler2
+ _except_handler3
+ ;fixme?; _except_handler4_common
+ _execl
+ _execle
+ _execlp
+ _execlpe
+ _execv
+ _execve
+ _execvp
+ _execvpe
+ _exit
+ _expand
+ _fclose_nolock
+ _fcloseall
+ _fcvt
+ _fcvt_s
+ _fdopen
+ _fflush_nolock
+ _fgetchar
+ _fgetwc_nolock
+ _fgetwchar
+ _filbuf
+ _filelength
+ _filelengthi64
+ _fileno
+ _findclose
+ _findfirst32
+ _findfirst32i64
+ _findfirst64
+ _findfirst64i32
+ _findnext32
+ _findnext32i64
+ _findnext64
+ _findnext64i32
+ _finite
+ _flsbuf
+ _flushall
+ ;fixme?; _fmode
+ _fpclass
+ _fpieee_flt
+ _fpreset
+ _fprintf_l
+ _fprintf_p
+ _fprintf_p_l
+ _fprintf_s_l
+ _fputchar
+ _fputwc_nolock
+ _fputwchar
+ _fread_nolock
+ _fread_nolock_s
+ _free_locale
+ _freea
+ _freea_s
+ _freefls
+ _fscanf_l
+ _fscanf_s_l
+ _fseek_nolock
+ _fseeki64
+ _fseeki64_nolock
+ _fsopen
+ _fstat32
+ _fstat32i64
+ _fstat64
+ _fstat64i32
+ _ftell_nolock
+ _ftelli64
+ _ftelli64_nolock
+ _ftime32
+ _ftime32_s
+ _ftime64
+ _ftime64_s
+ _ftol
+ _fullpath
+ _futime32
+ _futime64
+ _fwprintf_l
+ _fwprintf_p
+ _fwprintf_p_l
+ _fwprintf_s_l
+ _fwrite_nolock
+ _fwscanf_l
+ _fwscanf_s_l
+ _gcvt
+ _gcvt_s
+ _get_current_locale
+ _get_daylight
+ _get_doserrno
+ _get_dstbias
+ _get_errno
+ _get_fmode
+ _get_heap_handle
+ _get_invalid_parameter_handler
+ _get_osfhandle
+ _get_output_format
+ _get_pgmptr
+ _get_printf_count_output
+ _get_purecall_handler
+ _get_terminate
+ _get_timezone
+ _get_tzname
+ _get_unexpected
+ _get_wpgmptr
+ _getc_nolock
+ _getch
+ _getch_nolock
+ _getche
+ _getche_nolock
+ _getcwd
+ _getdcwd
+ _getdcwd_nolock
+ _getdiskfree
+ _getdllprocaddr
+ _getdrive
+ _getdrives
+ _getmaxstdio
+ _getmbcp
+ _getpid
+ _getptd
+ _getsystime
+ _getw
+ _getwch
+ _getwch_nolock
+ _getwche
+ _getwche_nolock
+ _getws
+ _getws_s
+ _global_unwind2
+ _gmtime32
+ _gmtime32_s
+ _gmtime64
+ _gmtime64_s
+ _heapadd
+ _heapchk
+ _heapmin
+ _heapset
+ _heapused
+ _heapwalk
+ _hypot
+ _hypotf
+ _i64toa
+ _i64toa_s
+ _i64tow
+ _i64tow_s
+ _initptd
+ ;fixme?; _initterm
+ _initterm_e
+ _inp
+ _inpd
+ _inpw
+ _invalid_parameter
+ _invalid_parameter_noinfo
+ _invalid_parameter_noinfo_noreturn
+ _invoke_watson
+ _iob
+ _isalnum_l
+ _isalpha_l
+ _isatty
+ _iscntrl_l
+ _isctype
+ _isctype_l
+ _isdigit_l
+ _isgraph_l
+ _isleadbyte_l
+ _islower_l
+ _ismbbalnum
+ _ismbbalnum_l
+ _ismbbalpha
+ _ismbbalpha_l
+ _ismbbgraph
+ _ismbbgraph_l
+ _ismbbkalnum
+ _ismbbkalnum_l
+ _ismbbkana
+ _ismbbkana_l
+ _ismbbkprint
+ _ismbbkprint_l
+ _ismbbkpunct
+ _ismbbkpunct_l
+ _ismbblead
+ _ismbblead_l
+ _ismbbprint
+ _ismbbprint_l
+ _ismbbpunct
+ _ismbbpunct_l
+ _ismbbtrail
+ _ismbbtrail_l
+ _ismbcalnum
+ _ismbcalnum_l
+ _ismbcalpha
+ _ismbcalpha_l
+ _ismbcdigit
+ _ismbcdigit_l
+ _ismbcgraph
+ _ismbcgraph_l
+ _ismbchira
+ _ismbchira_l
+ _ismbckata
+ _ismbckata_l
+ _ismbcl0
+ _ismbcl0_l
+ _ismbcl1
+ _ismbcl1_l
+ _ismbcl2
+ _ismbcl2_l
+ _ismbclegal
+ _ismbclegal_l
+ _ismbclower
+ _ismbclower_l
+ _ismbcprint
+ _ismbcprint_l
+ _ismbcpunct
+ _ismbcpunct_l
+ _ismbcspace
+ _ismbcspace_l
+ _ismbcsymbol
+ _ismbcsymbol_l
+ _ismbcupper
+ _ismbcupper_l
+ _ismbslead
+ _ismbslead_l
+ _ismbstrail
+ _ismbstrail_l
+ _isnan
+ _isprint_l
+ _ispunct_l
+ _isspace_l
+ _isupper_l
+ _iswalnum_l
+ _iswalpha_l
+ _iswcntrl_l
+ _iswcsym_l
+ _iswcsymf_l
+ _iswctype_l
+ _iswdigit_l
+ _iswgraph_l
+ _iswlower_l
+ _iswprint_l
+ _iswpunct_l
+ _iswspace_l
+ _iswupper_l
+ _iswxdigit_l
+ _isxdigit_l
+ _itoa
+ _itoa_s
+ _itow
+ _itow_s
+ _j0
+ _j1
+ _jn
+ _kbhit
+ _lfind
+ _lfind_s
+ _loaddll
+ _local_unwind2
+ _local_unwind4
+ _localtime32
+ _localtime32_s
+ _localtime64
+ _localtime64_s
+ _lock
+ _lock_file
+ _locking
+ _logb
+ _longjmpex
+ _lrotl
+ _lrotr
+ _lsearch
+ _lsearch_s
+ _lseek
+ _lseeki64
+ _ltoa
+ _ltoa_s
+ _ltow
+ _ltow_s
+ _makepath
+ _makepath_s
+ _malloc_crt
+ _mbbtombc
+ _mbbtombc_l
+ _mbbtype
+ _mbbtype_l
+ _mbcasemap
+ _mbccpy
+ _mbccpy_l
+ _mbccpy_s
+ _mbccpy_s_l
+ _mbcjistojms
+ _mbcjistojms_l
+ _mbcjmstojis
+ _mbcjmstojis_l
+ _mbclen
+ _mbclen_l
+ _mbctohira
+ _mbctohira_l
+ _mbctokata
+ _mbctokata_l
+ _mbctolower
+ _mbctolower_l
+ _mbctombb
+ _mbctombb_l
+ _mbctoupper
+ _mbctoupper_l
+ _mbctype
+ _mblen_l
+ _mbsbtype
+ _mbsbtype_l
+ _mbscat_s
+ _mbscat_s_l
+ _mbschr
+ _mbschr_l
+ _mbscmp
+ _mbscmp_l
+ _mbscoll
+ _mbscoll_l
+ _mbscpy_s
+ _mbscpy_s_l
+ _mbscspn
+ _mbscspn_l
+ _mbsdec
+ _mbsdec_l
+ _mbsicmp
+ _mbsicmp_l
+ _mbsicoll
+ _mbsicoll_l
+ _mbsinc
+ _mbsinc_l
+ _mbslen
+ _mbslen_l
+ _mbslwr
+ _mbslwr_l
+ _mbslwr_s
+ _mbslwr_s_l
+ _mbsnbcat
+ _mbsnbcat_l
+ _mbsnbcat_s
+ _mbsnbcat_s_l
+ _mbsnbcmp
+ _mbsnbcmp_l
+ _mbsnbcnt
+ _mbsnbcnt_l
+ _mbsnbcoll
+ _mbsnbcoll_l
+ _mbsnbcpy
+ _mbsnbcpy_l
+ _mbsnbcpy_s
+ _mbsnbcpy_s_l
+ _mbsnbicmp
+ _mbsnbicmp_l
+ _mbsnbicoll
+ _mbsnbicoll_l
+ _mbsnbset
+ _mbsnbset_l
+ _mbsnbset_s
+ _mbsnbset_s_l
+ _mbsncat
+ _mbsncat_l
+ _mbsncat_s
+ _mbsncat_s_l
+ _mbsnccnt
+ _mbsnccnt_l
+ _mbsncmp
+ _mbsncmp_l
+ _mbsncoll
+ _mbsncoll_l
+ _mbsncpy
+ _mbsncpy_l
+ _mbsncpy_s
+ _mbsncpy_s_l
+ _mbsnextc
+ _mbsnextc_l
+ _mbsnicmp
+ _mbsnicmp_l
+ _mbsnicoll
+ _mbsnicoll_l
+ _mbsninc
+ _mbsninc_l
+ _mbsnlen
+ _mbsnlen_l
+ _mbsnset
+ _mbsnset_l
+ _mbsnset_s
+ _mbsnset_s_l
+ _mbspbrk
+ _mbspbrk_l
+ _mbsrchr
+ _mbsrchr_l
+ _mbsrev
+ _mbsrev_l
+ _mbsset
+ _mbsset_l
+ _mbsset_s
+ _mbsset_s_l
+ _mbsspn
+ _mbsspn_l
+ _mbsspnp
+ _mbsspnp_l
+ _mbsstr
+ _mbsstr_l
+ _mbstok
+ _mbstok_l
+ _mbstok_s
+ _mbstok_s_l
+ _mbstowcs_l
+ _mbstowcs_s_l
+ _mbstrlen
+ _mbstrlen_l
+ _mbstrnlen
+ _mbstrnlen_l
+ _mbsupr
+ _mbsupr_l
+ _mbsupr_s
+ _mbsupr_s_l
+ _mbtowc_l
+ _memccpy
+ _memicmp
+ _memicmp_l
+ _mkdir
+ _mkgmtime32
+ _mkgmtime64
+ _mktemp
+ _mktemp_s
+ _mktime32
+ _mktime64
+ _msize
+ _nextafter
+ _open
+ _open_osfhandle
+ _outp
+ _outpd
+ _outpw
+ _pclose
+ _pctype
+ _pgmptr
+ _pipe
+ _popen
+ _printf_l
+ _printf_p
+ _printf_p_l
+ _printf_s_l
+ _purecall
+ _putch
+ _putch_nolock
+ _putenv
+ _putenv_s
+ _putw
+ _putwch
+ _putwch_nolock
+ _putws
+ _pwctype
+ _read
+ _realloc_crt
+ _recalloc
+ _recalloc_crt
+ _resetstkoflw
+ _rmdir
+ _rmtmp
+ _rotl
+ _rotl64
+ _rotr
+ _rotr64
+ _scalb
+ _scanf_l
+ _scanf_s_l
+ _scprintf
+ _scprintf_l
+ _scprintf_p
+ _scprintf_p_l
+ _scwprintf
+ _scwprintf_l
+ _scwprintf_p
+ _scwprintf_p_l
+ _searchenv
+ _searchenv_s
+ _seh_longjmp_unwind4
+ _seh_longjmp_unwind
+ _set_SSE2_enable
+ _set_abort_behavior
+ _set_controlfp
+ _set_doserrno
+ _set_errno
+ _set_error_mode
+ _set_fmode
+ _set_invalid_parameter_handler
+ _set_malloc_crt_max_wait
+ _set_output_format
+ _set_printf_count_output
+ _set_purecall_handler
+ _seterrormode
+ _setjmp
+ _setjmp3
+ _setmaxstdio
+ _setmbcp
+ _setmode
+ _setsystime
+ _sleep
+ _snprintf
+ _snprintf_c
+ _snprintf_c_l
+ _snprintf_l
+ _snprintf_s
+ _snprintf_s_l
+ _snscanf
+ _snscanf_l
+ _snscanf_s
+ _snscanf_s_l
+ _snwprintf
+ _snwprintf_l
+ _snwprintf_s
+ _snwprintf_s_l
+ _snwscanf
+ _snwscanf_l
+ _snwscanf_s
+ _snwscanf_s_l
+ _sopen
+ _sopen_s
+ _spawnl
+ _spawnle
+ _spawnlp
+ _spawnlpe
+ _spawnv
+ _spawnve
+ _spawnvp
+ _spawnvpe
+ _splitpath
+ _splitpath_s
+ _sprintf_l
+ _sprintf_p
+ _sprintf_p_l
+ _sprintf_s_l
+ _sscanf_l
+ _sscanf_s_l
+ _stat32
+ _stat32i64
+ _stat64
+ _stat64i32
+ _statusfp
+ _statusfp2
+ _strcoll_l
+ _strdate
+ _strdate_s
+ _strdup
+ _strerror
+ _strerror_s
+ _strftime_l
+ _stricmp
+ _stricmp_l
+ _stricoll
+ _stricoll_l
+ _strlwr
+ _strlwr_l
+ _strlwr_s
+ _strlwr_s_l
+ _strncoll
+ _strncoll_l
+ _strnicmp
+ _strnicmp_l
+ _strnicoll
+ _strnicoll_l
+ _strnset
+ _strnset_s
+ _strrev
+ _strset
+ _strset_s
+ _strtime
+ _strtime_s
+ _strtod_l
+ _strtoi64
+ _strtoi64_l
+ _strtol_l
+ _strtoui64
+ _strtoui64_l
+ _strtoul_l
+ _strupr
+ _strupr_l
+ _strupr_s
+ _strupr_s_l
+ _strxfrm_l
+ _swab
+ _swprintf
+ _swprintf_c
+ _swprintf_c_l
+ _swprintf_p
+ _swprintf_p_l
+ _swprintf_s_l
+ _swscanf_l
+ _swscanf_s_l
+ _sys_errlist
+ _sys_nerr
+ _tell
+ _telli64
+ _tempnam
+ _time32
+ _time64
+ _timezone
+ _tolower
+ _tolower_l
+ _toupper
+ _toupper_l
+ _towlower_l
+ _towupper_l
+ _tzname
+ _tzset
+ _ui64toa
+ _ui64toa_s
+ _ui64tow
+ _ui64tow_s
+ _ultoa
+ _ultoa_s
+ _ultow
+ _ultow_s
+ _umask
+ _umask_s
+ _ungetc_nolock
+ _ungetch
+ _ungetch_nolock
+ _ungetwc_nolock
+ _ungetwch
+ _ungetwch_nolock
+ _unlink
+ _unloaddll
+ _unlock
+ _unlock_file
+ _utime32
+ _utime64
+ _vcprintf
+ _vcprintf_l
+ _vcprintf_p
+ _vcprintf_p_l
+ _vcprintf_s
+ _vcprintf_s_l
+ _vcwprintf
+ _vcwprintf_l
+ _vcwprintf_p
+ _vcwprintf_p_l
+ _vcwprintf_s
+ _vcwprintf_s_l
+ _vfprintf_l
+ _vfprintf_p
+ _vfprintf_p_l
+ _vfprintf_s_l
+ _vfwprintf_l
+ _vfwprintf_p
+ _vfwprintf_p_l
+ _vfwprintf_s_l
+ _vprintf_l
+ _vprintf_p
+ _vprintf_p_l
+ _vprintf_s_l
+ _vscprintf
+ _vscprintf_l
+ _vscprintf_p
+ _vscprintf_p_l
+ _vscwprintf
+ _vscwprintf_l
+ _vscwprintf_p
+ _vscwprintf_p_l
+ _vsnprintf
+ _vsnprintf_c
+ _vsnprintf_c_l
+ _vsnprintf_l
+ _vsnprintf_s
+ _vsnprintf_s_l
+ _vsnwprintf
+ _vsnwprintf_l
+ _vsnwprintf_s
+ _vsnwprintf_s_l
+ _vsprintf_l
+ _vsprintf_p
+ _vsprintf_p_l
+ _vsprintf_s_l
+ _vswprintf
+ _vswprintf_c
+ _vswprintf_c_l
+ _vswprintf_l
+ _vswprintf_p
+ _vswprintf_p_l
+ _vswprintf_s_l
+ _vwprintf_l
+ _vwprintf_p
+ _vwprintf_p_l
+ _vwprintf_s_l
+ _waccess
+ _waccess_s
+ _wasctime
+ _wasctime_s
+ _wassert
+ _wchdir
+ _wchmod
+ ;fixme?; _wcmdln
+ _wcreat
+ _wcscoll_l
+ _wcsdup
+ _wcserror
+ _wcserror_s
+ _wcsftime_l
+ _wcsicmp
+ _wcsicmp_l
+ _wcsicoll
+ _wcsicoll_l
+ _wcslwr
+ _wcslwr_l
+ _wcslwr_s
+ _wcslwr_s_l
+ _wcsncoll
+ _wcsncoll_l
+ _wcsnicmp
+ _wcsnicmp_l
+ _wcsnicoll
+ _wcsnicoll_l
+ _wcsnset
+ _wcsnset_s
+ _wcsrev
+ _wcsset
+ _wcsset_s
+ _wcstod_l
+ _wcstoi64
+ _wcstoi64_l
+ _wcstol_l
+ _wcstombs_l
+ _wcstombs_s_l
+ _wcstoui64
+ _wcstoui64_l
+ _wcstoul_l
+ _wcsupr
+ _wcsupr_l
+ _wcsupr_s
+ _wcsupr_s_l
+ _wcsxfrm_l
+ _wctime32
+ _wctime32_s
+ _wctime64
+ _wctime64_s
+ _wctomb_l
+ _wctomb_s_l
+ _wctype
+ _wdupenv_s
+ _wenviron
+ _wexecl
+ _wexecle
+ _wexeclp
+ _wexeclpe
+ _wexecv
+ _wexecve
+ _wexecvp
+ _wexecvpe
+ _wfdopen
+ _wfindfirst32
+ _wfindfirst32i64
+ _wfindfirst64
+ _wfindfirst64i32
+ _wfindnext32
+ _wfindnext32i64
+ _wfindnext64
+ _wfindnext64i32
+ _wfopen
+ _wfopen_s
+ _wfreopen
+ _wfreopen_s
+ _wfsopen
+ _wfullpath
+ _wgetcwd
+ _wgetdcwd
+ _wgetdcwd_nolock
+ _wgetenv
+ _wgetenv_s
+ _wmakepath
+ _wmakepath_s
+ _wmkdir
+ _wmktemp
+ _wmktemp_s
+ _wopen
+ _wperror
+ _wpgmptr
+ _wpopen
+ _wprintf_l
+ _wprintf_p
+ _wprintf_p_l
+ _wprintf_s_l
+ _wputenv
+ _wputenv_s
+ _wremove
+ _wrename
+ _write
+ _wrmdir
+ _wscanf_l
+ _wscanf_s_l
+ _wsearchenv
+ _wsearchenv_s
+ _wsetlocale
+ _wsopen
+ _wsopen_s
+ _wspawnl
+ _wspawnle
+ _wspawnlp
+ _wspawnlpe
+ _wspawnv
+ _wspawnve
+ _wspawnvp
+ _wspawnvpe
+ _wsplitpath
+ _wsplitpath_s
+ _wstat32
+ _wstat32i64
+ _wstat64
+ _wstat64i32
+ _wstrdate
+ _wstrdate_s
+ _wstrtime
+ _wstrtime_s
+ _wsystem
+ _wtempnam
+ _wtmpnam
+ _wtmpnam_s
+ _wtof
+ _wtof_l
+ _wtoi
+ _wtoi64
+ _wtoi64_l
+ _wtoi_l
+ _wtol
+ _wtol_l
+ _wunlink
+ _wutime32
+ _wutime64
+ _y0
+ _y1
+ _yn
+ abort
+ abs
+ acos
+ asctime
+ asctime_s
+ asin
+ atan
+ atan2
+ atexit
+ atof
+ atoi
+ atol
+ bsearch
+ bsearch_s
+ btowc
+ calloc
+ ceil
+ clearerr
+ clearerr_s
+ clock
+ cos
+ cosh
+ div
+ exit
+ exp
+ fabs
+ fclose
+ feof
+ ferror
+ fflush
+ fgetc
+ fgetpos
+ fgets
+ fgetwc
+ fgetws
+ floor
+ fmod
+ fopen
+ fopen_s
+ fprintf
+ fprintf_s
+ fputc
+ fputs
+ fputwc
+ fputws
+ fread
+ fread_s
+ free
+ freopen
+ freopen_s
+ frexp
+ fscanf
+ fscanf_s
+ fseek
+ fsetpos
+ ftell
+ fwprintf
+ fwprintf_s
+ fwrite
+ fwscanf
+ fwscanf_s
+ getc
+ getchar
+ getenv
+ getenv_s
+ gets
+ gets_s
+ getwc
+ getwchar
+ is_wctype
+ isalnum
+ isalpha
+ iscntrl
+ isdigit
+ isgraph
+ isleadbyte
+ islower
+ isprint
+ ispunct
+ isspace
+ isupper
+ iswalnum
+ iswalpha
+ iswascii
+ iswcntrl
+ iswctype
+ iswdigit
+ iswgraph
+ iswlower
+ iswprint
+ iswpunct
+ iswspace
+ iswupper
+ iswxdigit
+ isxdigit
+ labs
+ ldexp
+ ldiv
+ llabs
+ lldiv
+ localeconv
+ log
+ log10
+ longjmp
+ malloc
+ mblen
+ mbrlen
+ mbrtowc
+ mbsrtowcs
+ mbsrtowcs_s
+ mbstowcs
+ mbstowcs_s
+ mbtowc
+ memchr
+ memcmp
+ memcpy
+ memcpy_s
+ memmove
+ memmove_s
+ memset
+ modf
+ perror
+ pow
+ printf
+ printf_s
+ putc
+ putchar
+ puts
+ putwc
+ putwchar
+ qsort
+ qsort_s
+ raise
+ rand
+ rand_s
+ realloc
+ remove
+ rename
+ rewind
+ scanf
+ scanf_s
+ setbuf
+ setlocale
+ setvbuf
+ signal
+ sin
+ sinh
+ sprintf
+ sprintf_s
+ sqrt
+ srand
+ sscanf
+ sscanf_s
+ strcat
+ strcat_s
+ strchr
+ strcmp
+ strcoll
+ strcpy
+ strcpy_s
+ strcspn
+ strerror
+ strerror_s
+ strftime
+ strlen
+ strncat
+ strncat_s
+ strncmp
+ strncpy
+ strncpy_s
+ strnlen
+ strpbrk
+ strrchr
+ strspn
+ strstr
+ strtod
+ strtok
+ strtok_s
+ strtol
+ strtoul
+ strxfrm
+ swprintf_s
+ swscanf
+ swscanf_s
+ system
+ tan
+ tanh
+ tmpfile
+ tmpfile_s
+ tmpnam
+ tmpnam_s
+ tolower
+ toupper
+ towlower
+ towupper
+ ungetc
+ ungetwc
+ vfprintf
+ vfprintf_s
+ vfwprintf
+ vfwprintf_s
+ vprintf
+ vprintf_s
+ vsprintf
+ vsprintf_s
+ vswprintf_s
+ vwprintf
+ vwprintf_s
+ wcrtomb
+ wcrtomb_s
+ wcscat
+ wcscat_s
+ wcschr
+ wcscmp
+ wcscoll
+ wcscpy
+ wcscpy_s
+ wcscspn
+ wcsftime
+ wcslen
+ wcsncat
+ wcsncat_s
+ wcsncmp
+ wcsncpy
+ wcsncpy_s
+ wcsnlen
+ wcspbrk
+ wcsrchr
+ wcsrtombs
+ wcsrtombs_s
+ wcsspn
+ wcsstr
+ wcstod
+ wcstok
+ wcstok_s
+ wcstol
+ wcstombs
+ wcstombs_s
+ wcstoul
+ wcsxfrm
+ wctob
+ wctomb
+ wctomb_s
+ wmemcpy_s
+ wmemmove_s
+ wprintf
+ wprintf_s
+ wscanf
+ wscanf_s
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def b/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def
new file mode 100644
index 00000000..8787224f
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-openssl-1.1plus.def
@@ -0,0 +1,395 @@
+; $Id: VBoxRT-openssl-1.1plus.def $
+;; @file
+; IPRT - Windows OpenSSL exports we use outside VBoxRT (keep them few!).
+;
+; This file is appended to the architecture specific .def file.
+;
+
+;
+; Copyright (C) 2009-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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+ ; ConsoleImpl.cpp uses this when VBOX_OPENSSL_FIPS is enabled.
+ FIPS_mode
+
+ ; VBoxVRDP.dll - secure.cpp
+ BIO_free
+ BIO_new_file
+ BN_bin2bn
+ BN_bn2bin
+ BN_CTX_free
+ BN_CTX_new
+ BN_free
+ BN_mod_exp
+ BN_new
+ BN_num_bits
+ BN_set_word
+ EVP_PKEY_get0_RSA
+ i2d_X509
+ MD5_Final
+ MD5_Init
+ MD5_Update
+ OBJ_obj2nid
+ PEM_read_bio_PrivateKey
+ PEM_read_bio_X509
+ RAND_bytes
+ RC4
+ RC4_set_key
+ RSA_free
+ RSA_generate_key_ex
+ RSA_get0_key
+ RSA_new
+ SHA1_Final
+ SHA1_Init
+ SHA1_Update
+ X509_free
+ X509_get_X509_PUBKEY
+ X509_PUBKEY_get0_param
+
+ ; VBoxVRDP.dll - tcp_vrdp.cpp
+ BIO_new_socket
+ BIO_test_flags
+ OPENSSL_init_ssl
+ SSL_accept
+ SSL_CTX_free
+ SSL_CTX_load_verify_locations
+ SSL_CTX_new
+ SSL_CTX_set_verify
+ SSL_CTX_use_certificate_file
+ SSL_CTX_use_PrivateKey_file
+ SSL_free
+ SSL_get_certificate
+ SSL_new
+ SSL_pending
+ SSL_read
+ SSL_set_bio
+ SSL_set_read_ahead
+ SSL_write
+ TLSv1_server_method
+ X509_get_issuer_name
+ X509_NAME_oneline
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDKeyStore.cpp:
+ EVP_aes_128_xts
+ EVP_aes_256_xts
+ EVP_CIPHER_CTX_free
+ EVP_CIPHER_CTX_new
+ EVP_DecryptFinal
+ EVP_DecryptInit
+ EVP_DecryptUpdate
+ EVP_EncryptFinal
+ EVP_EncryptInit
+ EVP_EncryptUpdate
+ EVP_MD_size
+ EVP_sha1
+ EVP_sha256
+ EVP_sha512
+ PKCS5_PBKDF2_HMAC
+ ;exported above: RAND_bytes
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDFilterCrypt.cpp:
+ ;exported above: EVP_aes_128_xts
+ ;exported above: EVP_aes_256_xts
+ ;exported above: EVP_CIPHER_CTX_free
+ ;exported above: EVP_CIPHER_CTX_new
+ ;exported above: EVP_DecryptFinal
+ ;exported above: EVP_DecryptInit
+ ;exported above: EVP_DecryptUpdate
+ ;exported above: EVP_EncryptFinal
+ ;exported above: EVP_EncryptInit
+ ;exported above: EVP_EncryptUpdate
+ ;exported above: RAND_bytes
+
+ ; vboxwebsrv needs SSL support.
+ ASN1_STRING_data
+ ASN1_STRING_get0_data
+ ASN1_STRING_to_UTF8
+ ;exported above: BIO_free
+ ;exported above: BIO_new_file
+ ;exported above: BIO_new_socket
+ BIO_read
+ BIO_write
+ CRYPTO_free
+ DH_check
+ DH_free
+ DH_generate_parameters
+ DH_generate_parameters_ex
+ DH_new
+ ERR_clear_error
+ ERR_error_string
+ ERR_error_string_n
+ ERR_get_error
+ ERR_peek_error
+ GENERAL_NAME_free
+ i2v_GENERAL_NAMES
+ OpenSSL_version_num
+ OPENSSL_init_crypto
+ ;exported above: OPENSSL_init_ssl
+ OPENSSL_sk_num
+ OPENSSL_sk_pop_free
+ OPENSSL_sk_value
+ PEM_read_bio_DHparams
+ RAND_load_file
+ RAND_pseudo_bytes
+ RAND_seed
+ RAND_status
+ ;exported above: RSA_free
+ RSA_generate_key
+ ;exported above: SSL_accept
+ SSL_clear
+ SSL_connect
+ SSL_ctrl
+ SSL_CTX_ctrl
+ ;exported above: SSL_CTX_free
+ SSL_CTX_get_cert_store
+ ;exported above: SSL_CTX_load_verify_locations
+ ;exported above: SSL_CTX_new
+ SSL_CTX_set_client_CA_list
+ SSL_CTX_set_default_passwd_cb
+ SSL_CTX_set_default_passwd_cb_userdata
+ SSL_CTX_set_default_verify_paths
+ SSL_CTX_set_options
+ SSL_CTX_set_session_id_context
+ ;exported above: SSL_CTX_set_verify
+ SSL_CTX_set_verify_depth
+ SSL_CTX_use_certificate_chain_file
+ ;exported above: SSL_CTX_use_PrivateKey_file
+ ;exported above: SSL_free
+ SSL_get_error
+ SSL_get_peer_certificate
+ SSL_get_verify_result
+ SSL_get1_session
+ SSL_is_init_finished
+ SSL_load_client_CA_file
+ ;exported above: SSL_new
+ SSL_peek
+ ;exported above: SSL_read
+ SSL_SESSION_free
+ ;exported above: SSL_set_bio
+ SSL_set_session
+ SSL_shutdown
+ SSL_want
+ ;exported above: SSL_write
+ TLS_method
+ ;exported above: X509_free
+ X509_get_ext_d2i
+ ;exported above: X509_get_issuer_name
+ X509_get_subject_name
+ X509_load_crl_file
+ X509_LOOKUP_file
+ X509_NAME_ENTRY_get_data
+ X509_NAME_get_entry
+ X509_NAME_get_index_by_NID
+ ;exported above: X509_NAME_oneline
+ X509_STORE_add_lookup
+ X509_STORE_CTX_get_current_cert
+ X509_STORE_CTX_get_error
+ X509_STORE_CTX_get_error_depth
+ X509_STORE_CTX_set_error
+ X509_STORE_set1_param
+ X509_STORE_set_flags
+ X509_verify_cert_error_string
+ X509_VERIFY_PARAM_free
+ X509_VERIFY_PARAM_new
+ X509_VERIFY_PARAM_set_flags
+ X509V3_conf_free
+
+ ; tstRTBigNum.cpp
+ BN_div
+ BN_mul
+ BN_mod_exp_simple
+ BN_ucmp
+
+ ; VBox-libtpms
+ BN_set_flags
+ BN_clear_free
+ BN_cmp
+ BN_dup
+ BN_sub
+ BN_add
+ BN_copy
+ RAND_add
+ BN_is_zero
+ BN_is_one
+ BN_value_one
+ BN_CTX_start
+ BN_CTX_get
+ BN_CTX_end
+ BN_mod_add
+ BN_mod_mul
+ BN_lshift
+ BN_mask_bits
+ BN_rshift
+ BN_mod_inverse
+ RSA_size
+ RSA_set0_key
+ RSA_get0_factors
+ RSA_public_encrypt
+ RSA_private_encrypt
+ RSA_private_decrypt
+ RSA_sign
+ RSA_verify
+ RSA_padding_add_PKCS1_type_1
+ RSA_padding_add_PKCS1_type_2
+ RSA_padding_check_PKCS1_type_1
+ RSA_padding_check_PKCS1_type_2
+ RSA_padding_add_PKCS1_OAEP
+ RSA_padding_check_PKCS1_OAEP
+ ERR_get_error_line_data
+ AES_set_encrypt_key
+ AES_set_decrypt_key
+ AES_decrypt
+ AES_encrypt
+ AES_cbc_encrypt
+ AES_ofb128_encrypt
+ SHA256_Init
+ SHA256_Update
+ SHA256_Final
+ SHA384_Init
+ SHA384_Update
+ SHA384_Final
+ SHA512_Init
+ SHA512_Update
+ SHA512_Final
+ EC_GROUP_free
+ EC_GROUP_set_generator
+ EC_GROUP_new_curve_GFp
+ EC_POINT_new
+ EC_POINT_free
+ EC_POINT_clear_free
+ EC_POINT_set_affine_coordinates
+ EC_POINT_get_affine_coordinates
+ EC_POINT_add
+ EC_POINTs_mul
+ EC_POINT_mul
+ Camellia_set_key
+ Camellia_encrypt
+ Camellia_decrypt
+ DES_ecb3_encrypt
+ DES_set_key_unchecked
+
+ ; VBox-libssh
+ BIO_ctrl
+ BIO_new
+ BIO_new_mem_buf
+ BIO_s_mem
+ BN_bn2hex
+ BN_is_bit_set
+ d2i_DSA_SIG
+ d2i_ECDSA_SIG
+ DH_compute_key
+ DH_generate_key
+ DH_get0_key
+ DH_get0_pqg
+ DH_set0_key
+ DH_set0_pqg
+ DH_size
+ DSA_free
+ DSA_generate_key
+ DSA_generate_parameters_ex
+ DSA_get0_key
+ DSA_get0_pqg
+ DSA_new
+ DSA_set0_key
+ DSA_set0_pqg
+ DSA_SIG_free
+ DSA_SIG_get0
+ DSA_SIG_new
+ DSA_SIG_set0
+ DSA_size
+ EC_GROUP_cmp
+ EC_GROUP_get_curve_name
+ EC_GROUP_get_degree
+ EC_KEY_dup
+ EC_KEY_free
+ EC_KEY_generate_key
+ EC_KEY_get0_group
+ EC_KEY_get0_private_key
+ EC_KEY_get0_public_key
+ EC_KEY_new_by_curve_name
+ EC_KEY_set_asn1_flag
+ EC_KEY_set_private_key
+ EC_KEY_set_public_key
+ EC_POINT_cmp
+ EC_POINT_oct2point
+ EC_POINT_point2oct
+ ECDH_compute_key
+ ECDSA_SIG_free
+ ECDSA_SIG_get0
+ ECDSA_SIG_new
+ ECDSA_SIG_set0
+ EVP_aes_128_cbc
+ EVP_aes_128_ctr
+ EVP_aes_128_gcm
+ EVP_aes_192_cbc
+ EVP_aes_192_ctr
+ EVP_aes_256_cbc
+ EVP_aes_256_ctr
+ EVP_aes_256_gcm
+ EVP_CIPHER_CTX_ctrl
+ EVP_CIPHER_CTX_reset
+ EVP_CIPHER_CTX_set_padding
+ EVP_DecryptInit_ex
+ EVP_DigestFinal
+ EVP_DigestInit
+ EVP_DigestInit_ex
+ EVP_DigestSignFinal
+ EVP_DigestSignInit
+ EVP_DigestUpdate
+ EVP_DigestVerifyFinal
+ EVP_DigestVerifyInit
+ EVP_EncryptInit_ex
+ EVP_MD_CTX_free
+ EVP_MD_CTX_new
+ EVP_MD_CTX_reset
+ EVP_md5
+ EVP_PKEY_base_id
+ EVP_PKEY_free
+ EVP_PKEY_get1_DSA
+ EVP_PKEY_get1_EC_KEY
+ EVP_PKEY_get1_RSA
+ EVP_PKEY_new
+ EVP_PKEY_set1_DSA
+ EVP_PKEY_set1_EC_KEY
+ EVP_PKEY_set1_RSA
+ EVP_PKEY_size
+ EVP_sha384
+ HMAC_CTX_free
+ HMAC_CTX_new
+ HMAC_Final
+ HMAC_Init_ex
+ HMAC_Update
+ i2d_DSA_SIG
+ i2d_ECDSA_SIG
+ OpenSSL_version
+ PEM_write_bio_PrivateKey
+ RSA_get0_crt_params
+ RSA_set0_crt_params
+ RSA_set0_factors
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def b/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def
new file mode 100644
index 00000000..ea9407a8
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-openssl-3.0.def
@@ -0,0 +1,430 @@
+; $Id: VBoxRT-openssl-3.0.def $
+;; @file
+; IPRT - Windows OpenSSL exports we use outside VBoxRT (keep them few!).
+;
+; This file is appended to the architecture specific .def file.
+;
+
+;
+; Copyright (C) 2009-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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+ ; VBoxVRDP.dll - secure.cpp
+ BIO_free
+ BIO_new_file
+ BN_bin2bn
+ BN_bn2bin
+ BN_CTX_free
+ BN_CTX_new
+ BN_free
+ BN_mod_exp
+ BN_new
+ BN_num_bits
+ BN_set_word
+ EVP_PKEY_get0_RSA
+ i2d_X509
+ MD5_Final
+ MD5_Init
+ MD5_Update
+ OBJ_obj2nid
+ PEM_read_bio_PrivateKey
+ PEM_read_bio_X509
+ RAND_bytes
+ RC4
+ RC4_set_key
+ RSA_free
+ RSA_generate_key_ex
+ RSA_get0_key
+ RSA_new
+ SHA1_Final
+ SHA1_Init
+ SHA1_Update
+ X509_free
+ X509_get_X509_PUBKEY
+ X509_PUBKEY_get0_param
+
+ ; VBoxVRDP.dll - tcp_vrdp.cpp
+ BIO_new_socket
+ BIO_test_flags
+ OPENSSL_init_ssl
+ SSL_accept
+ SSL_CTX_free
+ SSL_CTX_load_verify_locations
+ SSL_CTX_new
+ SSL_CTX_set_verify
+ SSL_CTX_use_certificate_file
+ SSL_CTX_use_PrivateKey_file
+ SSL_free
+ SSL_get_certificate
+ SSL_new
+ SSL_pending
+ SSL_read
+ SSL_set_bio
+ SSL_set_read_ahead
+ SSL_write
+ TLSv1_server_method
+ X509_get_issuer_name
+ X509_NAME_oneline
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDKeyStore.cpp:
+ EVP_aes_128_xts
+ EVP_aes_256_xts
+ EVP_CIPHER_CTX_free
+ EVP_CIPHER_CTX_new
+ EVP_DecryptFinal
+ EVP_DecryptInit
+ EVP_DecryptUpdate
+ EVP_EncryptFinal
+ EVP_EncryptInit
+ EVP_EncryptUpdate
+ EVP_MD_get_size
+ EVP_sha1
+ EVP_sha256
+ EVP_sha512
+ PKCS5_PBKDF2_HMAC
+ ;exported above: RAND_bytes
+
+ ; VDPluginCrypt.dll (if it wanted to use IPRT) - VDFilterCrypt.cpp:
+ ;exported above: EVP_aes_128_xts
+ ;exported above: EVP_aes_256_xts
+ ;exported above: EVP_CIPHER_CTX_free
+ ;exported above: EVP_CIPHER_CTX_new
+ ;exported above: EVP_DecryptFinal
+ ;exported above: EVP_DecryptInit
+ ;exported above: EVP_DecryptUpdate
+ ;exported above: EVP_EncryptFinal
+ ;exported above: EVP_EncryptInit
+ ;exported above: EVP_EncryptUpdate
+ ;exported above: RAND_bytes
+
+ ; vboxwebsrv needs SSL support.
+ ASN1_STRING_get0_data
+ ASN1_STRING_to_UTF8
+ ;exported above: BIO_free
+ ;exported above: BIO_new_file
+ ;exported above: BIO_new_socket
+ BIO_read
+ BIO_write
+ CRYPTO_free
+ DH_check
+ DH_free
+ DH_generate_parameters
+ DH_generate_parameters_ex
+ DH_new
+ ERR_clear_error
+ ERR_error_string
+ ERR_error_string_n
+ ERR_get_error
+ ERR_peek_error
+ GENERAL_NAME_free
+ i2v_GENERAL_NAMES
+ OpenSSL_version_num
+ OPENSSL_init_crypto
+ ;exported above: OPENSSL_init_ssl
+ OPENSSL_sk_num
+ OPENSSL_sk_pop_free
+ OPENSSL_sk_value
+ PEM_read_bio_DHparams
+ RAND_load_file
+ RAND_seed
+ RAND_status
+ ;exported above: RSA_free
+ RSA_generate_key
+ ;exported above: SSL_accept
+ SSL_clear
+ SSL_connect
+ SSL_ctrl
+ SSL_CTX_ctrl
+ ;exported above: SSL_CTX_free
+ SSL_CTX_get_cert_store
+ ;exported above: SSL_CTX_load_verify_locations
+ ;exported above: SSL_CTX_new
+ SSL_CTX_set_client_CA_list
+ SSL_CTX_set_default_passwd_cb
+ SSL_CTX_set_default_passwd_cb_userdata
+ SSL_CTX_set_default_verify_paths
+ SSL_CTX_set_options
+ SSL_CTX_set_session_id_context
+ ;exported above: SSL_CTX_set_verify
+ SSL_CTX_set_verify_depth
+ SSL_CTX_use_certificate_chain_file
+ ;exported above: SSL_CTX_use_PrivateKey_file
+ ;exported above: SSL_free
+ SSL_get_error
+ SSL_get1_peer_certificate
+ SSL_get_verify_result
+ SSL_get1_session
+ SSL_is_init_finished
+ SSL_load_client_CA_file
+ ;exported above: SSL_new
+ SSL_peek
+ ;exported above: SSL_read
+ SSL_SESSION_free
+ ;exported above: SSL_set_bio
+ SSL_set_session
+ SSL_shutdown
+ SSL_want
+ ;exported above: SSL_write
+ TLS_method
+ ;exported above: X509_free
+ X509_get_ext_d2i
+ ;exported above: X509_get_issuer_name
+ X509_get_subject_name
+ X509_load_crl_file
+ X509_LOOKUP_file
+ X509_NAME_ENTRY_get_data
+ X509_NAME_get_entry
+ X509_NAME_get_index_by_NID
+ ;exported above: X509_NAME_oneline
+ X509_STORE_add_lookup
+ X509_STORE_CTX_get_current_cert
+ X509_STORE_CTX_get_error
+ X509_STORE_CTX_get_error_depth
+ X509_STORE_CTX_set_error
+ X509_STORE_set1_param
+ X509_STORE_set_flags
+ X509_verify_cert_error_string
+ X509_VERIFY_PARAM_free
+ X509_VERIFY_PARAM_new
+ X509_VERIFY_PARAM_set_flags
+ X509V3_conf_free
+
+ ; tstRTBigNum.cpp
+ BN_div
+ BN_mul
+ BN_mod_exp_simple
+ BN_ucmp
+
+ ; VBox-libtpms
+ BN_set_flags
+ BN_clear_free
+ BN_cmp
+ BN_dup
+ BN_sub
+ BN_add
+ BN_copy
+ RAND_add
+ BN_is_zero
+ BN_is_one
+ BN_value_one
+ BN_CTX_start
+ BN_CTX_get
+ BN_CTX_end
+ BN_mod_add
+ BN_mod_mul
+ BN_lshift
+ BN_mask_bits
+ BN_rshift
+ BN_mod_inverse
+ RSA_size
+ RSA_set0_key
+ RSA_get0_factors
+ RSA_public_encrypt
+ RSA_private_encrypt
+ RSA_private_decrypt
+ RSA_sign
+ RSA_verify
+ RSA_padding_add_PKCS1_type_1
+ RSA_padding_add_PKCS1_type_2
+ RSA_padding_check_PKCS1_type_1
+ RSA_padding_check_PKCS1_type_2
+ RSA_padding_add_PKCS1_OAEP
+ RSA_padding_check_PKCS1_OAEP
+ ERR_get_error_line_data
+ AES_set_encrypt_key
+ AES_set_decrypt_key
+ AES_decrypt
+ AES_encrypt
+ AES_cbc_encrypt
+ AES_ofb128_encrypt
+ SHA256_Init
+ SHA256_Update
+ SHA256_Final
+ SHA384_Init
+ SHA384_Update
+ SHA384_Final
+ SHA512_Init
+ SHA512_Update
+ SHA512_Final
+ EC_GROUP_free
+ EC_GROUP_set_generator
+ EC_GROUP_new_curve_GFp
+ EC_POINT_new
+ EC_POINT_free
+ EC_POINT_clear_free
+ EC_POINT_set_affine_coordinates
+ EC_POINT_get_affine_coordinates
+ EC_POINT_add
+ EC_POINTs_mul
+ EC_POINT_mul
+ Camellia_set_key
+ Camellia_encrypt
+ Camellia_decrypt
+ DES_ecb3_encrypt
+ DES_set_key_unchecked
+
+ ; VBox-libssh
+ BIO_ctrl
+ BIO_new
+ BIO_new_mem_buf
+ BIO_s_mem
+ BN_bn2hex
+ BN_is_bit_set
+ d2i_DSA_SIG
+ d2i_ECDSA_SIG
+ DH_compute_key
+ DH_generate_key
+ DH_get0_key
+ DH_get0_pqg
+ DH_set0_key
+ DH_set0_pqg
+ DH_size
+ DSA_free
+ DSA_generate_key
+ DSA_generate_parameters_ex
+ DSA_get0_key
+ DSA_get0_pqg
+ DSA_new
+ DSA_set0_key
+ DSA_set0_pqg
+ DSA_SIG_free
+ DSA_SIG_get0
+ DSA_SIG_new
+ DSA_SIG_set0
+ DSA_size
+ EC_GROUP_cmp
+ EC_GROUP_get_curve_name
+ EC_GROUP_get_degree
+ EC_KEY_dup
+ EC_KEY_free
+ EC_KEY_generate_key
+ EC_KEY_get0_group
+ EC_KEY_get0_private_key
+ EC_KEY_get0_public_key
+ EC_KEY_new_by_curve_name
+ EC_KEY_set_asn1_flag
+ EC_KEY_set_private_key
+ EC_KEY_set_public_key
+ EC_POINT_cmp
+ EC_POINT_oct2point
+ EC_POINT_point2oct
+ ECDH_compute_key
+ ECDSA_SIG_free
+ ECDSA_SIG_get0
+ ECDSA_SIG_new
+ ECDSA_SIG_set0
+ EVP_aes_128_cbc
+ EVP_aes_128_ctr
+ EVP_aes_128_gcm
+ EVP_aes_192_cbc
+ EVP_aes_192_ctr
+ EVP_aes_256_cbc
+ EVP_aes_256_ctr
+ EVP_aes_256_gcm
+ EVP_CIPHER_CTX_ctrl
+ EVP_CIPHER_CTX_reset
+ EVP_CIPHER_CTX_set_padding
+ EVP_DecryptInit_ex
+ EVP_DigestFinal
+ EVP_DigestInit
+ EVP_DigestInit_ex
+ EVP_DigestSignFinal
+ EVP_DigestSignInit
+ EVP_DigestSignUpdate
+ EVP_DigestUpdate
+ EVP_DigestVerifyFinal
+ EVP_DigestVerifyInit
+ EVP_DigestVerifyUpdate
+ EVP_EncryptInit_ex
+ EVP_MD_CTX_free
+ EVP_MD_CTX_new
+ EVP_MD_CTX_reset
+ EVP_md5
+ EVP_PKEY_get_base_id
+ EVP_PKEY_free
+ EVP_PKEY_get1_DSA
+ EVP_PKEY_get1_EC_KEY
+ EVP_PKEY_get1_RSA
+ EVP_PKEY_new
+ EVP_PKEY_set1_DSA
+ EVP_PKEY_set1_EC_KEY
+ EVP_PKEY_set1_RSA
+ EVP_PKEY_get_size
+ EVP_sha384
+ HMAC_CTX_free
+ HMAC_CTX_new
+ HMAC_Final
+ HMAC_Init_ex
+ HMAC_Update
+ i2d_DSA_SIG
+ i2d_ECDSA_SIG
+ OpenSSL_version
+ PEM_write_bio_PrivateKey
+ RSA_get0_crt_params
+ RSA_set0_crt_params
+ RSA_set0_factors
+ ; since 0.10.5 also:
+ EC_KEY_up_ref
+ ENGINE_by_id
+ ENGINE_free
+ ENGINE_init
+ ENGINE_load_builtin_engines
+ EVP_default_properties_is_fips_enabled
+ EVP_PKEY_CTX_free
+ EVP_PKEY_CTX_new_from_name
+ EVP_PKEY_CTX_new_from_pkey
+ EVP_PKEY_CTX_set_params
+ EVP_PKEY_derive
+ EVP_PKEY_derive_init
+ EVP_PKEY_derive_set_peer
+ EVP_PKEY_eq
+ EVP_PKEY_fromdata
+ EVP_PKEY_fromdata_init
+ EVP_PKEY_generate
+ EVP_PKEY_get_bits
+ EVP_PKEY_get_bn_param
+ EVP_PKEY_keygen_init
+ EVP_PKEY_new_mac_key
+ EVP_PKEY_paramgen_init
+ EVP_PKEY_set_bn_param
+ EVP_PKEY_todata
+ EVP_PKEY_up_ref
+ OSSL_PARAM_BLD_free
+ OSSL_PARAM_BLD_new
+ OSSL_PARAM_BLD_push_BN
+ OSSL_PARAM_BLD_to_param
+ OSSL_PARAM_construct_end
+ OSSL_PARAM_construct_int
+ OSSL_PARAM_construct_uint
+ OSSL_PARAM_free
+ OSSL_PARAM_get_BN
+ OSSL_PARAM_locate_const
+ OSSL_PARAM_merge
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-win32.def b/src/VBox/Runtime/r3/win/VBoxRT-win32.def
new file mode 100644
index 00000000..261b3ce1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-win32.def
@@ -0,0 +1,62 @@
+; $Id: VBoxRT-win32.def $
+;; @file
+; IPRT - Win32 ASM exports.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+EXPORTS
+ ASMMultU64ByU32DivByU32
+
+ RTTimeNanoTSLegacySyncInvarNoDelta
+ RTTimeNanoTSLFenceSyncInvarNoDelta
+ RTTimeNanoTSLegacyAsyncUseApicId
+ RTTimeNanoTSLegacyAsyncUseRdtscp
+ RTTimeNanoTSLegacyAsyncUseIdtrLim
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim
+ RTTimeNanoTSLFenceAsyncUseApicId
+ RTTimeNanoTSLFenceAsyncUseRdtscp
+ RTTimeNanoTSLFenceAsyncUseIdtrLim
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim
+
+ RTStrMemFind32
+
+ ; Export aliases for noexcept affected methods.
+ ?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@_EPAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@ZPAX@Z ; before-noexcept ; (public: void __thiscall RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse *,void const *,unsigned int,unsigned int,unsigned __int64,unsigned __int64) noexcept,void *))
+ ?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@ZPAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QAEXP6AHPAV1@PBXII_K2@_EPAX@Z ; after-noexcept ; (public: void __thiscall RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse *,void const *,unsigned int,unsigned int,unsigned __int64,unsigned __int64),void *))
+ ?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@_E12@Z=?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@Z12@Z ; before-noexcept ; (public: void __thiscall RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter *,void *,unsigned int,unsigned __int64,unsigned int *) noexcept,void *,unsigned __int64))
+ ?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@Z12@Z=?setProducerCallback@RTCRestBinaryParameter@@QAEXP6AHPAV1@PAXI_KPAI@_E12@Z ; after-noexcept ; (public: void __thiscall RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter *,void *,unsigned int,unsigned __int64,unsigned int *),void *,unsigned __int64))
+
diff --git a/src/VBox/Runtime/r3/win/VBoxRT-win64.def b/src/VBox/Runtime/r3/win/VBoxRT-win64.def
new file mode 100644
index 00000000..e3a83833
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/VBoxRT-win64.def
@@ -0,0 +1,74 @@
+; $Id: VBoxRT-win64.def $
+;; @file
+; IPRT - Win64 ASM exports.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+EXPORTS
+ ASMAtomicBitClear
+ ASMAtomicBitToggle
+ ASMAtomicBitTestAndToggle
+ ASMBitFirstClear
+ ASMBitFirstSet
+ ASMAtomicReadU64
+ ASMAtomicXchgU8
+ ASMAtomicXchgU16
+ ASMGetFlags
+ ASMProbeReadByte
+ ASMSetFlags
+ ASMMultU64ByU32DivByU32
+ ASMNopPause
+
+ RTTimeNanoTSLegacySyncInvarNoDelta
+ RTTimeNanoTSLFenceSyncInvarNoDelta
+ RTTimeNanoTSLegacyAsyncUseApicId
+ RTTimeNanoTSLegacyAsyncUseRdtscp
+ RTTimeNanoTSLegacyAsyncUseIdtrLim
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLegacySyncInvarWithDeltaUseIdtrLim
+ RTTimeNanoTSLFenceAsyncUseApicId
+ RTTimeNanoTSLFenceAsyncUseRdtscp
+ RTTimeNanoTSLFenceAsyncUseIdtrLim
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseApicId
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseRdtscp
+ RTTimeNanoTSLFenceSyncInvarWithDeltaUseIdtrLim
+
+ RTStrMemFind32
+
+ ; Export aliases for noexcept affected methods.
+ ?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@_EPEAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@ZPEAX@Z ; before-noexcept ; (public: void __cdecl RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse * __ptr64,void const * __ptr64,unsigned __int64,unsigned int,unsigned __int64,unsigned __int64) noexcept,void * __ptr64) __ptr64
+ ?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@ZPEAX@Z=?setConsumerCallback@RTCRestBinaryResponse@@QEAAXP6AHPEAV1@PEBX_KI22@_EPEAX@Z ; after-noexcept ; (public: void __cdecl RTCRestBinaryResponse::setConsumerCallback(int (__cdecl*)(class RTCRestBinaryResponse * __ptr64,void const * __ptr64,unsigned __int64,unsigned int,unsigned __int64,unsigned __int64),void * __ptr64) __ptr64)
+ ?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@_E12@Z=?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@Z12@Z ; before-noexcept ; (public: void __cdecl RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter * __ptr64,void * __ptr64,unsigned __int64,unsigned __int64,unsigned __int64 * __ptr64) noexcept,void * __ptr64,unsigned __int64) __ptr64)
+ ?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@Z12@Z=?setProducerCallback@RTCRestBinaryParameter@@QEAAXP6AHPEAV1@PEAX_K2PEA_K@_E12@Z ; after-noexcept ; (public: void __cdecl RTCRestBinaryParameter::setProducerCallback(int (__cdecl*)(class RTCRestBinaryParameter * __ptr64,void * __ptr64,unsigned __int64,unsigned __int64,unsigned __int64 * __ptr64),void * __ptr64,unsigned __int64) __ptr64)
+
diff --git a/src/VBox/Runtime/r3/win/alloc-win.cpp b/src/VBox/Runtime/r3/win/alloc-win.cpp
new file mode 100644
index 00000000..a9ebe7f0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/alloc-win.cpp
@@ -0,0 +1,210 @@
+/* $Id: alloc-win.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef IPRT_NO_CRT
+# define USE_VIRTUAL_ALLOC
+#endif
+#define LOG_GROUP RTLOGGROUP_MEM
+#include <iprt/win/windows.h>
+
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+
+#ifndef USE_VIRTUAL_ALLOC
+# include <malloc.h>
+#endif
+
+
+RTDECL(void *) RTMemPageAllocTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(pszTag);
+
+#ifdef USE_VIRTUAL_ALLOC
+ void *pv = VirtualAlloc(NULL, RT_ALIGN_Z(cb, PAGE_SIZE), MEM_COMMIT, PAGE_READWRITE);
+#else
+ void *pv = _aligned_malloc(RT_ALIGN_Z(cb, PAGE_SIZE), PAGE_SIZE);
+#endif
+ AssertMsg(pv, ("cb=%d lasterr=%d\n", cb, GetLastError()));
+ return pv;
+}
+
+
+RTDECL(void *) RTMemPageAllocExTag(size_t cb, uint32_t fFlags, const char *pszTag) RT_NO_THROW_DEF
+{
+ size_t const cbAligned = RT_ALIGN_Z(cb, PAGE_SIZE);
+ RT_NOREF_PV(pszTag);
+ AssertReturn(!(fFlags & ~RTMEMPAGEALLOC_F_VALID_MASK), NULL);
+
+#ifdef USE_VIRTUAL_ALLOC
+ void *pv = VirtualAlloc(NULL, cbAligned, MEM_COMMIT, PAGE_READWRITE);
+#else
+ void *pv = _aligned_malloc(cbAligned, PAGE_SIZE);
+#endif
+ AssertMsgReturn(pv, ("cb=%d lasterr=%d\n", cb, GetLastError()), NULL);
+
+ if (fFlags & RTMEMPAGEALLOC_F_ADVISE_LOCKED)
+ {
+ /** @todo check why we get ERROR_WORKING_SET_QUOTA here. */
+ BOOL const fOkay = VirtualLock(pv, cbAligned);
+ AssertMsg(fOkay || GetLastError() == ERROR_WORKING_SET_QUOTA, ("pv=%p cb=%d lasterr=%d\n", pv, cb, GetLastError()));
+ NOREF(fOkay);
+ }
+
+ if (fFlags & RTMEMPAGEALLOC_F_ZERO)
+ RT_BZERO(pv, cbAligned);
+
+ return pv;
+}
+
+
+RTDECL(void *) RTMemPageAllocZTag(size_t cb, const char *pszTag) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(pszTag);
+
+#ifdef USE_VIRTUAL_ALLOC
+ void *pv = VirtualAlloc(NULL, RT_ALIGN_Z(cb, PAGE_SIZE), MEM_COMMIT, PAGE_READWRITE);
+#else
+ void *pv = _aligned_malloc(RT_ALIGN_Z(cb, PAGE_SIZE), PAGE_SIZE);
+#endif
+ if (pv)
+ {
+ memset(pv, 0, RT_ALIGN_Z(cb, PAGE_SIZE));
+ return pv;
+ }
+ AssertMsgFailed(("cb=%d lasterr=%d\n", cb, GetLastError()));
+ return NULL;
+}
+
+
+RTDECL(void) RTMemPageFree(void *pv, size_t cb) RT_NO_THROW_DEF
+{
+ RT_NOREF_PV(cb);
+
+ if (pv)
+ {
+#ifdef USE_VIRTUAL_ALLOC
+ if (!VirtualFree(pv, 0, MEM_RELEASE))
+ AssertMsgFailed(("pv=%p lasterr=%d\n", pv, GetLastError()));
+#else
+ _aligned_free(pv);
+#endif
+ }
+}
+
+
+RTDECL(int) RTMemProtect(void *pv, size_t cb, unsigned fProtect) RT_NO_THROW_DEF
+{
+ /*
+ * Validate input.
+ */
+ if (cb == 0)
+ {
+ AssertMsgFailed(("!cb\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (fProtect & ~(RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC))
+ {
+ AssertMsgFailed(("fProtect=%#x\n", fProtect));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Convert the flags.
+ */
+ int fProt;
+ Assert(!RTMEM_PROT_NONE);
+ switch (fProtect & (RTMEM_PROT_NONE | RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC))
+ {
+ case RTMEM_PROT_NONE:
+ fProt = PAGE_NOACCESS;
+ break;
+
+ case RTMEM_PROT_READ:
+ fProt = PAGE_READONLY;
+ break;
+
+ case RTMEM_PROT_READ | RTMEM_PROT_WRITE:
+ fProt = PAGE_READWRITE;
+ break;
+
+ case RTMEM_PROT_READ | RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ case RTMEM_PROT_READ | RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ case RTMEM_PROT_WRITE:
+ fProt = PAGE_READWRITE;
+ break;
+
+ case RTMEM_PROT_WRITE | RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ case RTMEM_PROT_EXEC:
+ fProt = PAGE_EXECUTE_READWRITE;
+ break;
+
+ /* If the compiler had any brains it would warn about this case. */
+ default:
+ AssertMsgFailed(("fProtect=%#x\n", fProtect));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ /*
+ * Align the request.
+ */
+ cb += (uintptr_t)pv & PAGE_OFFSET_MASK;
+ pv = (void *)((uintptr_t)pv & ~(uintptr_t)PAGE_OFFSET_MASK);
+
+ /*
+ * Change the page attributes.
+ */
+ DWORD fFlags = 0;
+ if (VirtualProtect(pv, cb, fProt, &fFlags))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
diff --git a/src/VBox/Runtime/r3/win/allocex-win.cpp b/src/VBox/Runtime/r3/win/allocex-win.cpp
new file mode 100644
index 00000000..dca6d053
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/allocex-win.cpp
@@ -0,0 +1,133 @@
+/* $Id: allocex-win.cpp $ */
+/** @file
+ * IPRT - Memory Allocation, Extended Alloc Workers, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RTMEM_NO_WRAP_TO_EF_APIS
+#include <iprt/mem.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include "../allocex.h"
+
+#include <iprt/win/windows.h>
+
+
+static int rtMemAllocExInRange(size_t cbAlloc, uint32_t fFlags, void **ppv, uintptr_t uAddr, uintptr_t uAddrLast)
+{
+ /*
+ * Try with every possible address hint since the possible range is very limited.
+ */
+ DWORD fPageProt = (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ while (uAddr <= uAddrLast)
+ {
+ MEMORY_BASIC_INFORMATION MemInfo;
+ SIZE_T cbRange = VirtualQuery((void *)uAddr, &MemInfo, sizeof(MemInfo));
+ AssertReturn(cbRange == sizeof(MemInfo), VERR_NOT_SUPPORTED);
+ Assert(MemInfo.RegionSize > 0);
+
+ if ( MemInfo.State == MEM_FREE
+ && MemInfo.RegionSize >= cbAlloc)
+ {
+ void *pv = VirtualAlloc((void *)uAddr, cbAlloc, MEM_RESERVE | MEM_COMMIT, fPageProt);
+ if ((uintptr_t)pv == uAddr)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+ AssertStmt(!pv, VirtualFree(pv, cbAlloc, MEM_RELEASE));
+ }
+
+ /* skip ahead */
+ uintptr_t uAddrNext = (uintptr_t)MemInfo.BaseAddress + MemInfo.RegionSize;
+ if (uAddrNext <= uAddr)
+ break;
+ uAddr += uAddrNext;
+ }
+
+ return VERR_NO_MEMORY;
+}
+
+
+DECLHIDDEN(int) rtMemAllocEx16BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ cbAlloc = RT_ALIGN_Z(cbAlloc, PAGE_SIZE);
+ AssertReturn(cbAlloc <= _64K - PAGE_SIZE, VERR_NO_MEMORY);
+
+ /* Seems this doesn't work on W7/64... */
+ return rtMemAllocExInRange(cbAlloc, fFlags, ppv, PAGE_SIZE, _64K - cbAlloc);
+}
+
+
+DECLHIDDEN(int) rtMemAllocEx32BitReach(size_t cbAlloc, uint32_t fFlags, void **ppv)
+{
+ cbAlloc = RT_ALIGN_Z(cbAlloc, PAGE_SIZE);
+ AssertReturn(cbAlloc <= _2G+_1G, VERR_NO_MEMORY);
+
+ /*
+ * Just try first.
+ */
+ DWORD fPageProt = (fFlags & RTMEMALLOCEX_FLAGS_EXEC ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE);
+ void *pv = VirtualAlloc(NULL, cbAlloc, MEM_RESERVE | MEM_COMMIT, fPageProt);
+ if (!pv)
+ return VERR_NO_MEMORY;
+ if ((uintptr_t)pv + cbAlloc - 1 < _4G)
+ {
+ *ppv = pv;
+ return VINF_SUCCESS;
+ }
+ VirtualFree(pv, cbAlloc, MEM_RELEASE);
+
+ /*
+ * No luck, do address scan based allocation.
+ */
+ return rtMemAllocExInRange(cbAlloc, fFlags, ppv, _64K, _4G - cbAlloc);
+}
+
+
+DECLHIDDEN(void) rtMemFreeExYyBitReach(void *pv, size_t cb, uint32_t fFlags)
+{
+ RT_NOREF_PV(fFlags);
+
+ BOOL fRc = VirtualFree(pv, cb, MEM_RELEASE);
+ Assert(fRc); RT_NOREF_PV(fRc);
+}
+
diff --git a/src/VBox/Runtime/r3/win/dir-win.cpp b/src/VBox/Runtime/r3/win/dir-win.cpp
new file mode 100644
index 00000000..f0a193ff
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/dir-win.cpp
@@ -0,0 +1,169 @@
+/* $Id: dir-win.cpp $ */
+/** @file
+ * IPRT - Directory, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/win/windows.h>
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include "internal/fs.h"
+#include "internal/path.h"
+
+
+
+RTDECL(int) RTDirCreate(const char *pszPath, RTFMODE fMode, uint32_t fCreate)
+{
+ /*
+ * Validate the file mode.
+ */
+ int rc;
+ fMode = rtFsModeNormalize(fMode, pszPath, 0, RTFS_TYPE_DIRECTORY);
+ if (rtFsModeIsValidPermissions(fMode))
+ {
+ /*
+ * Convert to UTF-16.
+ */
+ PRTUTF16 pwszString;
+ rc = RTPathWinFromUtf8(&pwszString, pszPath, 0 /*fFlags*/);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the directory.
+ */
+ if (CreateDirectoryW((LPCWSTR)pwszString, NULL))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ /*
+ * Turn off indexing of directory through Windows Indexing Service
+ */
+ /** @todo This FILE_ATTRIBUTE_NOT_CONTENT_INDEXED hack (for .VDI files,
+ * really) may cause failures on samba shares. That really sweet and
+ * need to be addressed differently. We shouldn't be doing this
+ * unless the caller actually asks for it, must less returning failure,
+ * for crying out loud! This is only important a couple of places in
+ * main, if important is the right way to put it... */
+ if ( RT_SUCCESS(rc)
+ && !(fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET))
+ {
+ if ( SetFileAttributesW((LPCWSTR)pwszString, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
+ || (fCreate & RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL) )
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ RTPathWinFree(pwszString);
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("Invalid file mode! %RTfmode\n", fMode));
+ rc = VERR_INVALID_FMODE;
+ }
+
+ LogFlow(("RTDirCreate(%p:{%s}, %RTfmode): returns %Rrc\n", pszPath, pszPath, fMode, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirRemove(const char *pszPath)
+{
+ /*
+ * Convert to UTF-16.
+ */
+ PRTUTF16 pwszString;
+ int rc = RTPathWinFromUtf8(&pwszString, pszPath, 0 /*fFlags*/);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Remove the directory.
+ */
+ if (RemoveDirectoryW((LPCWSTR)pwszString))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTPathWinFree(pwszString);
+ }
+
+ LogFlow(("RTDirRemove(%p:{%s}): returns %Rrc\n", pszPath, pszPath, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTDirFlush(const char *pszPath)
+{
+ RT_NOREF_PV(pszPath);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTDirRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Call the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst,
+ fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0,
+ RTFS_TYPE_DIRECTORY);
+
+ LogFlow(("RTDirRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/direnum-win.cpp b/src/VBox/Runtime/r3/win/direnum-win.cpp
new file mode 100644
index 00000000..80d36864
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/direnum-win.cpp
@@ -0,0 +1,404 @@
+/* $Id: direnum-win.cpp $ */
+/** @file
+ * IPRT - Directory Enumeration, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#include <iprt/win/windows.h>
+
+#include <iprt/dir.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include "internal/fs.h"
+#include "internal/dir.h"
+
+
+size_t rtDirNativeGetStructSize(const char *pszPath)
+{
+ NOREF(pszPath);
+ return sizeof(RTDIRINTERNAL);
+}
+
+
+int rtDirNativeOpen(PRTDIRINTERNAL pDir, uintptr_t hRelativeDir, void *pvNativeRelative))
+{
+ RT_NOREF(hRelativeDir, pvNativeRelative);
+
+ /*
+ * Setup the search expression.
+ *
+ * pszPathBuf is pointing to the return 4K return buffer for the RTPathReal()
+ * call in rtDirOpenCommon(), so all we gota do is check that we don't overflow
+ * it when adding the wildcard expression.
+ */
+/** @todo the pszPathBuf argument was removed in order to support paths longer than RTPATH_MAX. Rewrite this code. */
+ size_t cbExpr;
+ const char *pszExpr;
+ if (pDir->enmFilter == RTDIRFILTER_WINNT)
+ {
+ pszExpr = pDir->pszFilter;
+ cbExpr = pDir->cchFilter + 1;
+ }
+ else
+ {
+ pszExpr = "*";
+ cbExpr = sizeof("*");
+ }
+ if (pDir->cchPath + cbExpr > RTPATH_MAX)
+ return VERR_FILENAME_TOO_LONG;
+ memcpy(pszPathBuf + pDir->cchPath, pszExpr, cbExpr);
+
+
+ /*
+ * Attempt opening the search.
+ */
+ PRTUTF16 pwszName;
+ int rc = RTPathWinFromUtf8(pwszPathBuf, &pwszName, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ pDir->hDir = FindFirstFileW((LPCWSTR)pwszName, &pDir->Data);
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ pDir->fDataUnread = true;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ /* Theoretical case of an empty directory or more normal case of no matches. */
+ if ( dwErr == ERROR_FILE_NOT_FOUND
+ || dwErr == ERROR_NO_MORE_FILES /* ???*/)
+ pDir->fDataUnread = false;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ RTPathWinFree(pwszName);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTDirClose(PRTDIRINTERNAL pDir)
+{
+ /*
+ * Validate input.
+ */
+ if (!pDir)
+ return VERR_INVALID_PARAMETER;
+ if (pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Close the handle.
+ */
+ pDir->u32Magic++;
+ if (pDir->hDir != INVALID_HANDLE_VALUE)
+ {
+ BOOL fRc = FindClose(pDir->hDir);
+ Assert(fRc);
+ pDir->hDir = INVALID_HANDLE_VALUE;
+ }
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+ RTMemFree(pDir);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirRead(RTDIR hDir, PRTDIRENTRY pDirEntry, size_t *pcbDirEntry)
+{
+ PPRTDIRINTERNAL pDir = hDir;
+
+ /*
+ * Validate input.
+ */
+ if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pDirEntry)
+ {
+ AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
+ return VERR_INVALID_PARAMETER;
+ }
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRY, szName[2]))
+ {
+ AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRY, szName[2])));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+
+ BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
+ if (!fRc)
+ {
+ int iErr = GetLastError();
+ if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromWin32(iErr);
+ }
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRY, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDir->fDataUnread = false;
+ pDirEntry->INodeId = 0; /** @todo we can use the fileid here if we must (see GetFileInformationByHandle). */
+ pDirEntry->enmType = pDir->Data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
+ ? RTDIRENTRYTYPE_DIRECTORY : RTDIRENTRYTYPE_FILE;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTDirReadEx(RTDIR hDir, PRTDIRENTRYEX pDirEntry, size_t *pcbDirEntry, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ PPRTDIRINTERNAL pDir = hDir;
+ /** @todo Symlinks: Find[First|Next]FileW will return info about
+ the link, so RTPATH_F_FOLLOW_LINK is not handled correctly. */
+ /*
+ * Validate input.
+ */
+ if (!pDir || pDir->u32Magic != RTDIR_MAGIC)
+ {
+ AssertMsgFailed(("Invalid pDir=%p\n", pDir));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pDirEntry)
+ {
+ AssertMsgFailed(("Invalid pDirEntry=%p\n", pDirEntry));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
+ || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
+ {
+ AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
+ return VERR_INVALID_PARAMETER;
+ }
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ size_t cbDirEntry = sizeof(*pDirEntry);
+ if (pcbDirEntry)
+ {
+ cbDirEntry = *pcbDirEntry;
+ if (cbDirEntry < RT_UOFFSETOF(RTDIRENTRYEX, szName[2]))
+ {
+ AssertMsgFailed(("Invalid *pcbDirEntry=%zu (min %zu)\n", *pcbDirEntry, RT_UOFFSETOF(RTDIRENTRYEX, szName[2])));
+ return VERR_INVALID_PARAMETER;
+ }
+ }
+
+ /*
+ * Fetch data?
+ */
+ if (!pDir->fDataUnread)
+ {
+ RTStrFree(pDir->pszName);
+ pDir->pszName = NULL;
+
+ BOOL fRc = FindNextFileW(pDir->hDir, &pDir->Data);
+ if (!fRc)
+ {
+ int iErr = GetLastError();
+ if (pDir->hDir == INVALID_HANDLE_VALUE || iErr == ERROR_NO_MORE_FILES)
+ return VERR_NO_MORE_FILES;
+ return RTErrConvertFromWin32(iErr);
+ }
+ }
+
+ /*
+ * Convert the filename to UTF-8.
+ */
+ if (!pDir->pszName)
+ {
+ int rc = RTUtf16ToUtf8((PCRTUTF16)pDir->Data.cFileName, &pDir->pszName);
+ if (RT_FAILURE(rc))
+ {
+ pDir->pszName = NULL;
+ return rc;
+ }
+ pDir->cchName = strlen(pDir->pszName);
+ }
+
+ /*
+ * Check if we've got enough space to return the data.
+ */
+ const char *pszName = pDir->pszName;
+ const size_t cchName = pDir->cchName;
+ const size_t cbRequired = RT_UOFFSETOF(RTDIRENTRYEX, szName[1]) + cchName;
+ if (pcbDirEntry)
+ *pcbDirEntry = cbRequired;
+ if (cbRequired > cbDirEntry)
+ return VERR_BUFFER_OVERFLOW;
+
+ /*
+ * Setup the returned data.
+ */
+ pDir->fDataUnread = false;
+ pDirEntry->cbName = (uint16_t)cchName;
+ Assert(pDirEntry->cbName == cchName);
+ memcpy(pDirEntry->szName, pszName, cchName + 1);
+ if (pDir->Data.cAlternateFileName[0])
+ {
+ /* copy and calc length */
+ PCRTUTF16 pwszSrc = (PCRTUTF16)pDir->Data.cAlternateFileName;
+ PRTUTF16 pwszDst = pDirEntry->wszShortName;
+ uint32_t off = 0;
+ while (off < RT_ELEMENTS(pDirEntry->wszShortName) - 1U && pwszSrc[off])
+ {
+ pwszDst[off] = pwszSrc[off];
+ off++;
+ }
+ pDirEntry->cwcShortName = (uint16_t)off;
+
+ /* zero the rest */
+ do
+ pwszDst[off++] = '\0';
+ while (off < RT_ELEMENTS(pDirEntry->wszShortName));
+ }
+ else
+ {
+ memset(pDirEntry->wszShortName, 0, sizeof(pDirEntry->wszShortName));
+ pDirEntry->cwcShortName = 0;
+ }
+
+ pDirEntry->Info.cbObject = ((uint64_t)pDir->Data.nFileSizeHigh << 32)
+ | (uint64_t)pDir->Data.nFileSizeLow;
+ pDirEntry->Info.cbAllocated = pDirEntry->Info.cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(pDir->Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pDirEntry->Info.BirthTime, *(uint64_t *)&pDir->Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.AccessTime, *(uint64_t *)&pDir->Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pDirEntry->Info.ModificationTime, *(uint64_t *)&pDir->Data.ftLastWriteTime);
+ pDirEntry->Info.ChangeTime = pDirEntry->Info.ModificationTime;
+
+ pDirEntry->Info.Attr.fMode = rtFsModeFromDos((pDir->Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszName, cchName, pDir->Data.dwReserved0, 0);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_EASIZE:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pDirEntry->Info.Attr.u.EASize.cb = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pDirEntry->Info.Attr.u.Unix.uid = ~0U;
+ pDirEntry->Info.Attr.u.Unix.gid = ~0U;
+ pDirEntry->Info.Attr.u.Unix.cHardlinks = 1;
+ pDirEntry->Info.Attr.u.Unix.INodeIdDevice = 0; /** @todo Use the volume serial number (see GetFileInformationByHandle). */
+ pDirEntry->Info.Attr.u.Unix.INodeId = 0; /** @todo Use the fileid (see GetFileInformationByHandle). */
+ pDirEntry->Info.Attr.u.Unix.fFlags = 0;
+ pDirEntry->Info.Attr.u.Unix.GenerationId = 0;
+ pDirEntry->Info.Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_NOTHING:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pDirEntry->Info.Attr.u.UnixOwner.uid = ~0U;
+ pDirEntry->Info.Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pDirEntry->Info.Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pDirEntry->Info.Attr.u.UnixGroup.gid = ~0U;
+ pDirEntry->Info.Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Runtime/r3/win/dllmain-win.cpp b/src/VBox/Runtime/r3/win/dllmain-win.cpp
new file mode 100644
index 00000000..b52d059b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/dllmain-win.cpp
@@ -0,0 +1,100 @@
+/* $Id: dllmain-win.cpp $ */
+/** @file
+ * IPRT - Win32 DllMain (Ring-3).
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/windows.h>
+#include <iprt/thread.h>
+#include <iprt/param.h>
+#include "internal/thread.h"
+
+
+
+/**
+ * Increases the load count on the IPRT DLL so it won't unload.
+ *
+ * This is a separate function so as to not overflow the stack of threads with
+ * very little of it.
+ *
+ * @param hModule The IPRT DLL module handle.
+ */
+DECL_NO_INLINE(static, void) EnsureNoUnload(HMODULE hModule)
+{
+ WCHAR wszName[RTPATH_MAX];
+ SetLastError(NO_ERROR);
+ if ( GetModuleFileNameW(hModule, wszName, RT_ELEMENTS(wszName)) > 0
+ && GetLastError() == NO_ERROR)
+ {
+ int cExtraLoads = 32;
+ while (cExtraLoads-- > 0)
+ LoadLibraryW(wszName);
+ }
+}
+
+
+/**
+ * The Dll main entry point.
+ */
+BOOL __stdcall DllMain(HANDLE hModule, DWORD dwReason, PVOID pvReserved)
+{
+ RT_NOREF_PV(pvReserved);
+
+ switch (dwReason)
+ {
+ /*
+ * When attaching to a process, we'd like to make sure IPRT stays put
+ * and doesn't get unloaded.
+ */
+ case DLL_PROCESS_ATTACH:
+ EnsureNoUnload((HMODULE)hModule);
+ break;
+
+ case DLL_PROCESS_DETACH:
+ case DLL_THREAD_ATTACH:
+ default:
+ /* ignore */
+ break;
+
+ case DLL_THREAD_DETACH:
+ rtThreadWinTlsDestruction();
+ rtThreadNativeDetach();
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src/VBox/Runtime/r3/win/env-win.cpp b/src/VBox/Runtime/r3/win/env-win.cpp
new file mode 100644
index 00000000..89577098
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/env-win.cpp
@@ -0,0 +1,520 @@
+/* $Id: env-win.cpp $ */
+/** @file
+ * IPRT - Environment, Posix.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/env.h>
+
+#ifdef IPRT_NO_CRT
+# include <iprt/asm.h>
+#endif
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/string.h>
+#include <iprt/mem.h>
+#include <iprt/utf16.h>
+
+#ifndef IPRT_NO_CRT
+# include <stdlib.h>
+# include <errno.h>
+#endif
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+#ifdef IPRT_NO_CRT
+static uint32_t volatile g_idxGetEnvBufs = 0;
+static char *g_apszGetEnvBufs[64]; /* leak */
+#endif
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue);
+
+
+#if defined(RT_ARCH_X86) && defined(RT_OS_WINDOWS)
+/**
+ * This is a workaround for NT 3.x not setting the last error.
+ */
+static DWORD rtEnvNt31CheckEmpty(PCRTUTF16 pwszVar)
+{
+ /* Check the version first: */
+ DWORD dwVersion = GetVersion();
+ if (RT_BYTE1(dwVersion) != 3)
+ return 0;
+
+ /* When called with an empty buffer, we should get 1 if empty value
+ and 0 if not found: */
+ DWORD cwcNeeded = GetEnvironmentVariableW(pwszVar, NULL, 0);
+ return cwcNeeded == 0 ? ERROR_ENVVAR_NOT_FOUND : NO_ERROR;
+}
+#endif
+
+
+RTDECL(bool) RTEnvExistsBad(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvGetBad(pszVar) != NULL;
+#else
+ return RTEnvExistsUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(bool) RTEnvExist(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvExistsBad(pszVar);
+#else
+ return RTEnvExistsUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(bool) RTEnvExistsUtf8(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, false);
+
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ AssertRCReturn(rc, false);
+
+#ifndef IPRT_NO_CRT
+ bool fRet = _wgetenv(pwszVar) != NULL;
+#else
+ DWORD dwRet = GetEnvironmentVariableW(pwszVar, NULL, 0);
+ bool fRet = dwRet != 0;
+#endif
+
+ RTUtf16Free(pwszVar);
+ return fRet;
+}
+
+
+RTDECL(const char *) RTEnvGetBad(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, NULL);
+#ifndef IPRT_NO_CRT
+ return getenv(pszVar);
+#else
+ /*
+ * Query the value into heap buffer which we give a lifetime of 64
+ * RTEnvGetBad calls.
+ */
+ char *pszValue = RTEnvDup(pszVar);
+ if (pszValue)
+ {
+ RTMEM_MAY_LEAK(pszValue); /* Quite possible we'll leak this, but the leak is limited to 64 values. */
+
+ uint32_t idx = ASMAtomicIncU32(&g_idxGetEnvBufs) % RT_ELEMENTS(g_apszGetEnvBufs);
+ char *pszOld = (char *)ASMAtomicXchgPtr((void * volatile *)&g_apszGetEnvBufs[idx], pszValue);
+ RTStrFree(pszOld);
+ }
+ return pszValue;
+#endif
+}
+
+
+RTDECL(const char *) RTEnvGet(const char *pszVar)
+{
+ return RTEnvGetBad(pszVar);
+}
+
+RTDECL(int) RTEnvGetUtf8(const char *pszVar, char *pszValue, size_t cbValue, size_t *pcchActual)
+{
+ AssertPtrReturn(pszVar, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pszValue, VERR_INVALID_POINTER);
+ AssertReturn(pszValue || !cbValue, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pcchActual, VERR_INVALID_POINTER);
+ AssertReturn(pcchActual || (pszValue && cbValue), VERR_INVALID_PARAMETER);
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ if (pcchActual)
+ *pcchActual = 0;
+
+ /*
+ * Convert the name to UTF-16.
+ */
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Query the variable. First try with a medium sized stack buffer (too
+ * small for your typical PATH, but large enough for most other things).
+ */
+ RTUTF16 wszValue[512];
+ uint32_t cwcValueBuf = RT_ELEMENTS(wszValue);
+ PRTUTF16 pwszValue = wszValue;
+ PRTUTF16 pwszValueFree = NULL;
+
+ for (unsigned iTry = 0;; iTry++)
+ {
+ /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW nor ERROR_ENVVAR_NOT_FOUND.
+ Note! Assume that the CRT transparently updates the process
+ environment and that we don't need to use _wgetenv_s here. */
+ SetLastError(NO_ERROR);
+ DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
+ DWORD dwErr = GetLastError();
+
+ if (cwcValueRet < cwcValueBuf)
+ {
+#ifdef RT_ARCH_X86
+ if (cwcValueRet == 0 && dwErr == NO_ERROR)
+ dwErr = rtEnvNt31CheckEmpty(pwszVar);
+#endif
+ if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
+ {
+ if (cbValue)
+ rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszValue, cbValue, pcchActual);
+ else
+ rc = RTUtf16CalcUtf8LenEx(pwszValue, cwcValueRet, pcchActual);
+ }
+ else
+ {
+ Assert(cwcValueRet == 0);
+ Assert(dwErr != NO_ERROR);
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ break;
+ }
+
+ /*
+ * Insufficient buffer, so increase it. The first re-try will use the
+ * returned size, further re-tries will max out with a multiple of the
+ * stack buffer till we reaches 32KB chars (128 loops).
+ */
+ Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
+ RTMemTmpFree(pwszValueFree);
+ AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
+
+ cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
+ pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
+ AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
+ }
+
+ RTMemTmpFree(pwszValueFree);
+ RTUtf16Free(pwszVar);
+ return rc;
+}
+
+
+RTDECL(char *) RTEnvDup(const char *pszVar)
+{
+ AssertPtrReturn(pszVar, NULL);
+
+ /*
+ * Convert the name to UTF-16.
+ */
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ AssertRCReturn(rc, NULL);
+
+ /*
+ * Query the variable. First try with a medium sized stack buffer (too
+ * small for your typical PATH, but large enough for most other things).
+ */
+ char *pszRet = NULL;
+ RTUTF16 wszValue[512];
+ uint32_t cwcValueBuf = RT_ELEMENTS(wszValue);
+ PRTUTF16 pwszValue = wszValue;
+ PRTUTF16 pwszValueFree = NULL;
+
+ for (unsigned iTry = 0;; iTry++)
+ {
+ /* This API is weird, it didn't always set ERROR_BUFFER_OVERFLOW nor ERROR_ENVVAR_NOT_FOUND.
+ Note! Assume that the CRT transparently updates the process
+ environment and that we don't need to use _wgetenv_s here. */
+ SetLastError(NO_ERROR);
+ DWORD const cwcValueRet = GetEnvironmentVariableW(pwszVar, pwszValue, cwcValueBuf);
+ DWORD dwErr = GetLastError();
+
+ if (cwcValueRet < cwcValueBuf)
+ {
+#ifdef RT_ARCH_X86
+ if (cwcValueRet == 0 && dwErr == NO_ERROR)
+ dwErr = rtEnvNt31CheckEmpty(pwszVar);
+#endif
+ if (cwcValueRet > 0 || dwErr == NO_ERROR) /* In case of empty values we have to see if last error was set or not. */
+ {
+ rc = RTUtf16ToUtf8Ex(pwszValue, cwcValueRet, &pszRet, 0, NULL);
+ if (RT_FAILURE(rc))
+ pszRet = NULL;
+ }
+ else
+ {
+ Assert(cwcValueRet == 0);
+ Assert(dwErr != NO_ERROR);
+ }
+ break;
+ }
+
+ /*
+ * Insufficient buffer, so increase it. The first re-try will use the
+ * returned size, further re-tries will max out with a multiple of the
+ * stack buffer till we reaches 32KB chars (128 loops).
+ */
+ Assert(dwErr == NO_ERROR || dwErr == ERROR_BUFFER_OVERFLOW);
+ RTMemTmpFree(pwszValueFree);
+ AssertBreakStmt(cwcValueBuf < _32K, rc = VERR_INTERNAL_ERROR_3 /* not a good one */);
+
+ cwcValueBuf = RT_MAX(cwcValueRet + iTry, RT_ELEMENTS(wszValue) * iTry);
+ pwszValueFree = pwszValue = (PRTUTF16)RTMemTmpAlloc(cwcValueBuf * sizeof(RTUTF16));
+ AssertBreakStmt(pwszValue, rc = VERR_NO_TMP_MEMORY);
+ }
+
+ RTMemTmpFree(pwszValueFree);
+ RTUtf16Free(pwszVar);
+ return pszRet;
+}
+
+
+RTDECL(int) RTEnvPutBad(const char *pszVarEqualValue)
+{
+#ifndef IPRT_NO_CRT
+ /** @todo putenv is a source memory leaks. deal with this on a per system basis. */
+ if (!putenv((char *)pszVarEqualValue))
+ return 0;
+ return RTErrConvertFromErrno(errno);
+#else
+ return RTEnvPutUtf8(pszVarEqualValue);
+#endif
+}
+
+
+RTDECL(int) RTEnvPut(const char *pszVarEqualValue)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvPutBad(pszVarEqualValue);
+#else
+ return RTEnvPutUtf8(pszVarEqualValue);
+#endif
+}
+
+
+RTDECL(int) RTEnvPutUtf8(const char *pszVarEqualValue)
+{
+ PRTUTF16 pwszVarEqualValue;
+ int rc = RTStrToUtf16(pszVarEqualValue, &pwszVarEqualValue);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IPRT_NO_CRT
+ if (!_wputenv(pwszVarEqualValue))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(errno);
+#else
+ PRTUTF16 pwszValue = RTUtf16Chr(pwszVarEqualValue, '=');
+ if (pwszValue)
+ {
+ *pwszValue++ = '\0';
+
+ SetLastError(*pwszValue ? ERROR_OUTOFMEMORY : ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
+ if (SetEnvironmentVariableW(pwszVarEqualValue, *pwszValue ? pwszValue : NULL))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_ENVVAR_NOT_FOUND)
+ {
+ Assert(!*pwszValue);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ Assert(*pwszValue);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ }
+ else
+ rc = VERR_INVALID_PARAMETER;
+#endif
+ RTUtf16Free(pwszVarEqualValue);
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTEnvSetBad(const char *pszVar, const char *pszValue)
+{
+#ifndef IPRT_NO_CRT
+ AssertMsgReturn(strchr(pszVar, '=') == NULL, ("'%s'\n", pszVar), VERR_ENV_INVALID_VAR_NAME);
+ int rc;
+ if (!RTEnvExist(pszVar))
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ else
+ {
+ errno_t rcErrno = _putenv_s(pszVar, *pszValue ? pszValue : " " /* wrong, but will be treated as unset otherwise */);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+ }
+ return rc;
+#else
+ return RTEnvSetUtf8(pszVar, pszValue);
+#endif
+}
+
+
+RTDECL(int) RTEnvSet(const char *pszVar, const char *pszValue)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvSetBad(pszVar, pszValue);
+#else
+ return RTEnvSetUtf8(pszVar, pszValue);
+#endif
+}
+
+
+/**
+ * Worker common to RTEnvSetUtf8() and rtEnvSetExWorker().
+ */
+int rtEnvSetUtf8Worker(const char *pchVar, size_t cchVar, const char *pszValue)
+{
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16Ex(pchVar, cchVar, &pwszVar, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszValue;
+ rc = RTStrToUtf16(pszValue, &pwszValue);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IPRT_NO_CRT
+ errno_t rcErrno = _wputenv_s(pwszVar,
+ *pwszValue ? pwszValue : L" " /* wrong, but will be treated as unset otherwise */);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+#else
+ SetLastError(ERROR_OUTOFMEMORY); /* The API did not always set the last error. */
+ if (SetEnvironmentVariableW(pwszVar, pwszValue))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+#endif
+ RTUtf16Free(pwszValue);
+ }
+ RTUtf16Free(pwszVar);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTEnvSetUtf8(const char *pszVar, const char *pszValue)
+{
+ size_t cchVar = strlen(pszVar);
+ AssertReturn(memchr(pszVar, '=', cchVar) == NULL, VERR_ENV_INVALID_VAR_NAME);
+ return rtEnvSetUtf8Worker(pszVar, cchVar, pszValue);
+}
+
+
+RTDECL(int) RTEnvUnsetBad(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+ int rc;
+ if (!RTEnvExist(pszVar))
+ rc = VINF_ENV_VAR_NOT_FOUND;
+ else
+ {
+ errno_t rcErrno = _putenv_s(pszVar, NULL);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+ }
+ return rc;
+#else
+ return RTEnvUnsetUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(int) RTEnvUnset(const char *pszVar)
+{
+#ifndef IPRT_NO_CRT
+ return RTEnvUnsetBad(pszVar);
+#else
+ return RTEnvUnsetUtf8(pszVar);
+#endif
+}
+
+
+RTDECL(int) RTEnvUnsetUtf8(const char *pszVar)
+{
+ AssertReturn(strchr(pszVar, '=') == NULL, VERR_ENV_INVALID_VAR_NAME);
+
+ PRTUTF16 pwszVar;
+ int rc = RTStrToUtf16(pszVar, &pwszVar);
+ if (RT_SUCCESS(rc))
+ {
+#ifndef IPRT_NO_CRT
+ if (_wgetenv(pwszVar))
+ {
+ errno_t rcErrno = _wputenv_s(pwszVar, NULL);
+ if (rcErrno == 0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromErrno(rcErrno);
+ }
+ else
+ rc = VINF_ENV_VAR_NOT_FOUND;
+#else
+ SetLastError(ERROR_ENVVAR_NOT_FOUND); /* The API did not always set the last error. */
+ if (SetEnvironmentVariableW(pwszVar, NULL))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = dwErr == ERROR_ENVVAR_NOT_FOUND ? VINF_ENV_VAR_NOT_FOUND : RTErrConvertFromWin32(dwErr);
+ }
+#endif
+ RTUtf16Free(pwszVar);
+ }
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/errvars-win.cpp b/src/VBox/Runtime/r3/win/errvars-win.cpp
new file mode 100644
index 00000000..6d8b4814
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/errvars-win.cpp
@@ -0,0 +1,108 @@
+/* $Id: errvars-win.cpp $ */
+/** @file
+ * IPRT - Save and Restore Error Variables, Windows Ring-3.
+ */
+
+/*
+ * Copyright (C) 2011-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/win/winsock2.h>
+#ifndef IPRT_NO_CRT
+# include <errno.h>
+#endif
+
+#include <iprt/errcore.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+
+RTDECL(PRTERRVARS) RTErrVarsSave(PRTERRVARS pVars)
+{
+ pVars->ai32Vars[0] = RTERRVARS_MAGIC;
+ pVars->ai32Vars[1] = GetLastError();
+ pVars->ai32Vars[2] = g_pfnWSAGetLastError ? g_pfnWSAGetLastError() : WSANOTINITIALISED;
+#ifndef IPRT_NO_CRT
+ pVars->ai32Vars[3] = errno;
+#endif
+ return pVars;
+}
+
+
+RTDECL(void) RTErrVarsRestore(PCRTERRVARS pVars)
+{
+ AssertReturnVoid(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+#ifndef IPRT_NO_CRT
+ errno = pVars->ai32Vars[3];
+#endif
+ if ( pVars->ai32Vars[2] != WSANOTINITIALISED
+ && g_pfnWSASetLastError)
+ g_pfnWSASetLastError(pVars->ai32Vars[2]);
+ SetLastError(pVars->ai32Vars[1]);
+}
+
+
+RTDECL(bool) RTErrVarsAreEqual(PCRTERRVARS pVars1, PCRTERRVARS pVars2)
+{
+ Assert(pVars1->ai32Vars[0] == RTERRVARS_MAGIC);
+ Assert(pVars2->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars1->ai32Vars[0] == pVars2->ai32Vars[0]
+ && pVars1->ai32Vars[1] == pVars2->ai32Vars[1]
+ && pVars1->ai32Vars[2] == pVars2->ai32Vars[2]
+#ifndef IPRT_NO_CRT
+ && pVars1->ai32Vars[3] == pVars2->ai32Vars[3]
+#endif
+ ;
+}
+
+
+RTDECL(bool) RTErrVarsHaveChanged(PCRTERRVARS pVars)
+{
+ Assert(pVars->ai32Vars[0] == RTERRVARS_MAGIC);
+
+ return pVars->ai32Vars[0] != RTERRVARS_MAGIC
+ || (uint32_t)pVars->ai32Vars[1] != GetLastError()
+ || pVars->ai32Vars[2] != (g_pfnWSAGetLastError ? g_pfnWSAGetLastError() : WSANOTINITIALISED)
+#ifndef IPRT_NO_CRT
+ || pVars->ai32Vars[3] != errno
+#endif
+ ;
+
+}
+
diff --git a/src/VBox/Runtime/r3/win/fileaio-win.cpp b/src/VBox/Runtime/r3/win/fileaio-win.cpp
new file mode 100644
index 00000000..a3fb934d
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fileaio-win.cpp
@@ -0,0 +1,554 @@
+/* $Id: fileaio-win.cpp $ */
+/** @file
+ * IPRT - File async I/O, native implementation for the Windows host platform.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+
+#include <iprt/asm.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include "internal/fileaio.h"
+
+#include <iprt/win/windows.h>
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Transfer direction.
+ */
+typedef enum TRANSFERDIRECTION
+{
+ TRANSFERDIRECTION_INVALID = 0,
+ /** Read. */
+ TRANSFERDIRECTION_READ,
+ /** Write. */
+ TRANSFERDIRECTION_WRITE,
+ /** The usual 32-bit hack. */
+ TRANSFERDIRECTION_32BIT_HACK = 0x7fffffff
+} TRANSFERDIRECTION;
+
+/**
+ * Async I/O completion context state.
+ */
+typedef struct RTFILEAIOCTXINTERNAL
+{
+ /** handle to I/O completion port. */
+ HANDLE hIoCompletionPort;
+ /** Current number of requests pending. */
+ volatile int32_t cRequests;
+ /** Flag whether the thread was woken up. */
+ volatile bool fWokenUp;
+ /** Flag whether the thread is currently waiting. */
+ volatile bool fWaiting;
+ /** Flags given during creation. */
+ uint32_t fFlags;
+ /** Magic value (RTFILEAIOCTX_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOCTXINTERNAL;
+/** Pointer to an internal context structure. */
+typedef RTFILEAIOCTXINTERNAL *PRTFILEAIOCTXINTERNAL;
+
+/**
+ * Async I/O request state.
+ */
+typedef struct RTFILEAIOREQINTERNAL
+{
+ /** Overlapped structure. */
+ OVERLAPPED Overlapped;
+ /** Current state the request is in. */
+ RTFILEAIOREQSTATE enmState;
+ /** The file handle. */
+ HANDLE hFile;
+ /** Kind of transfer Read/Write. */
+ TRANSFERDIRECTION enmTransferDirection;
+ /** Number of bytes to transfer. */
+ size_t cbTransfer;
+ /** Pointer to the buffer. */
+ void *pvBuf;
+ /** Opaque user data. */
+ void *pvUser;
+ /** Flag whether the request completed. */
+ bool fCompleted;
+ /** Number of bytes transferred successfully. */
+ size_t cbTransfered;
+ /** Error code of the completed request. */
+ int Rc;
+ /** Completion context we are assigned to. */
+ PRTFILEAIOCTXINTERNAL pCtxInt;
+ /** Magic value (RTFILEAIOREQ_MAGIC). */
+ uint32_t u32Magic;
+} RTFILEAIOREQINTERNAL;
+/** Pointer to an internal request structure. */
+typedef RTFILEAIOREQINTERNAL *PRTFILEAIOREQINTERNAL;
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Id for the wakeup event. */
+#define AIO_CONTEXT_WAKEUP_EVENT 1
+/** Converts a pointer to an OVERLAPPED structure to a internal request. */
+#define OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped) ( (PRTFILEAIOREQINTERNAL)((uintptr_t)(pOverlapped) - RT_UOFFSETOF(RTFILEAIOREQINTERNAL, Overlapped)) )
+
+RTR3DECL(int) RTFileAioGetLimits(PRTFILEAIOLIMITS pAioLimits)
+{
+ AssertPtrReturn(pAioLimits, VERR_INVALID_POINTER);
+
+ /* No limits known. */
+ pAioLimits->cReqsOutstandingMax = RTFILEAIO_UNLIMITED_REQS;
+ pAioLimits->cbBufferAlignment = 0;
+
+ return VINF_SUCCESS;
+}
+
+RTR3DECL(int) RTFileAioReqCreate(PRTFILEAIOREQ phReq)
+{
+ AssertPtrReturn(phReq, VERR_INVALID_POINTER);
+
+ PRTFILEAIOREQINTERNAL pReqInt = (PRTFILEAIOREQINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOREQINTERNAL));
+ if (RT_UNLIKELY(!pReqInt))
+ return VERR_NO_MEMORY;
+
+ pReqInt->pCtxInt = NULL;
+ pReqInt->fCompleted = false;
+ pReqInt->u32Magic = RTFILEAIOREQ_MAGIC;
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ *phReq = (RTFILEAIOREQ)pReqInt;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqDestroy(RTFILEAIOREQ hReq)
+{
+ /*
+ * Validate the handle and ignore nil.
+ */
+ if (hReq == NIL_RTFILEAIOREQ)
+ return VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+
+ /*
+ * Trash the magic and free it.
+ */
+ ASMAtomicUoWriteU32(&pReqInt->u32Magic, ~RTFILEAIOREQ_MAGIC);
+ RTMemFree(pReqInt);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Worker setting up the request.
+ */
+DECLINLINE(int) rtFileAioReqPrepareTransfer(RTFILEAIOREQ hReq, RTFILE hFile,
+ TRANSFERDIRECTION enmTransferDirection,
+ RTFOFF off, void *pvBuf, size_t cbTransfer,
+ void *pvUser)
+{
+ /*
+ * Validate the input.
+ */
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ Assert(hFile != NIL_RTFILE);
+ AssertPtr(pvBuf);
+ Assert(off >= 0);
+ Assert(cbTransfer > 0);
+
+ pReqInt->enmTransferDirection = enmTransferDirection;
+ pReqInt->hFile = (HANDLE)RTFileToNative(hFile);
+ pReqInt->Overlapped.Offset = (DWORD)(off & 0xffffffff);
+ pReqInt->Overlapped.OffsetHigh = (DWORD)(off >> 32);
+ pReqInt->cbTransfer = cbTransfer;
+ pReqInt->pvBuf = pvBuf;
+ pReqInt->pvUser = pvUser;
+ pReqInt->fCompleted = false;
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioReqPrepareRead(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void *pvBuf, size_t cbRead, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_READ,
+ off, pvBuf, cbRead, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareWrite(RTFILEAIOREQ hReq, RTFILE hFile, RTFOFF off,
+ void const *pvBuf, size_t cbWrite, void *pvUser)
+{
+ return rtFileAioReqPrepareTransfer(hReq, hFile, TRANSFERDIRECTION_WRITE,
+ off, (void *)pvBuf, cbWrite, pvUser);
+}
+
+RTDECL(int) RTFileAioReqPrepareFlush(RTFILEAIOREQ hReq, RTFILE hFile, void *pvUser)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ AssertReturn(hFile != NIL_RTFILE, VERR_INVALID_HANDLE);
+ RT_NOREF_PV(pvUser);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(void *) RTFileAioReqGetUser(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN_RC(pReqInt, NULL);
+
+ return pReqInt->pvUser;
+}
+
+RTDECL(int) RTFileAioReqCancel(RTFILEAIOREQ hReq)
+{
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ /**
+ * @todo r=aeichner It is not possible to cancel specific
+ * requests on Windows before Vista.
+ * CancelIo cancels all requests for a file issued by the
+ * calling thread and CancelIoEx which does what we need
+ * is only available from Vista and up.
+ * The solution is to return VERR_FILE_AIO_IN_PROGRESS
+ * if the request didn't completed yet (checked above).
+ * Shouldn't be a big issue because a request is normally
+ * only canceled if it exceeds a timeout which is quite huge.
+ */
+ return VERR_FILE_AIO_COMPLETED;
+}
+
+RTDECL(int) RTFileAioReqGetRC(RTFILEAIOREQ hReq, size_t *pcbTransfered)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOREQINTERNAL pReqInt = hReq;
+ RTFILEAIOREQ_VALID_RETURN(pReqInt);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, SUBMITTED, VERR_FILE_AIO_IN_PROGRESS);
+ RTFILEAIOREQ_NOT_STATE_RETURN_RC(pReqInt, PREPARED, VERR_FILE_AIO_NOT_SUBMITTED);
+
+ rc = pReqInt->Rc;
+ if (pcbTransfered && RT_SUCCESS(rc))
+ *pcbTransfered = pReqInt->cbTransfered;
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxCreate(PRTFILEAIOCTX phAioCtx, uint32_t cAioReqsMax, uint32_t fFlags)
+{
+ AssertPtrReturn(phAioCtx, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTFILEAIOCTX_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ RT_NOREF_PV(cAioReqsMax);
+
+ if ( g_pfnCreateIoCompletionPort
+ && g_pfnGetQueuedCompletionStatus
+ && g_pfnPostQueuedCompletionStatus)
+ {
+ PRTFILEAIOCTXINTERNAL pCtxInt = (PRTFILEAIOCTXINTERNAL)RTMemAllocZ(sizeof(RTFILEAIOCTXINTERNAL));
+ if (RT_UNLIKELY(!pCtxInt))
+ return VERR_NO_MEMORY;
+
+ pCtxInt->hIoCompletionPort = g_pfnCreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
+ if (RT_UNLIKELY(!pCtxInt->hIoCompletionPort))
+ {
+ RTMemFree(pCtxInt);
+ return VERR_NO_MEMORY;
+ }
+
+ pCtxInt->fFlags = fFlags;
+ pCtxInt->u32Magic = RTFILEAIOCTX_MAGIC;
+
+ *phAioCtx = (RTFILEAIOCTX)pCtxInt;
+
+ return VINF_SUCCESS;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(int) RTFileAioCtxDestroy(RTFILEAIOCTX hAioCtx)
+{
+ /* Validate the handle and ignore nil. */
+ if (hAioCtx == NIL_RTFILEAIOCTX)
+ return VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ /* Cannot destroy a busy context. */
+ if (RT_UNLIKELY(pCtxInt->cRequests))
+ return VERR_FILE_AIO_BUSY;
+
+ CloseHandle(pCtxInt->hIoCompletionPort);
+ ASMAtomicUoWriteU32(&pCtxInt->u32Magic, RTFILEAIOCTX_MAGIC_DEAD);
+ RTMemFree(pCtxInt);
+
+ return VINF_SUCCESS;
+}
+
+RTDECL(int) RTFileAioCtxAssociateWithFile(RTFILEAIOCTX hAioCtx, RTFILE hFile)
+{
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ if ( g_pfnCreateIoCompletionPort
+ && g_pfnGetQueuedCompletionStatus
+ && g_pfnPostQueuedCompletionStatus)
+ {
+ int rc = VINF_SUCCESS;
+ HANDLE hTemp = g_pfnCreateIoCompletionPort((HANDLE)RTFileToNative(hFile), pCtxInt->hIoCompletionPort, 0, 1);
+ if (hTemp != pCtxInt->hIoCompletionPort)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+RTDECL(uint32_t) RTFileAioCtxGetMaxReqCount(RTFILEAIOCTX hAioCtx)
+{
+ RT_NOREF_PV(hAioCtx);
+ return RTFILEAIO_UNLIMITED_REQS;
+}
+
+RTDECL(int) RTFileAioCtxSubmit(RTFILEAIOCTX hAioCtx, PRTFILEAIOREQ pahReqs, size_t cReqs)
+{
+ /*
+ * Parameter validation.
+ */
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertReturn(cReqs > 0, VERR_INVALID_PARAMETER);
+ Assert(cReqs <= INT32_MAX);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ size_t i;
+
+ for (i = 0; i < cReqs; i++)
+ {
+ PRTFILEAIOREQINTERNAL pReqInt = pahReqs[i];
+ BOOL fSucceeded;
+
+ Assert(pReqInt->cbTransfer == (DWORD)pReqInt->cbTransfer);
+ if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_READ)
+ {
+ fSucceeded = ReadFile(pReqInt->hFile, pReqInt->pvBuf,
+ (DWORD)pReqInt->cbTransfer, NULL,
+ &pReqInt->Overlapped);
+ }
+ else if (pReqInt->enmTransferDirection == TRANSFERDIRECTION_WRITE)
+ {
+ fSucceeded = WriteFile(pReqInt->hFile, pReqInt->pvBuf,
+ (DWORD)pReqInt->cbTransfer, NULL,
+ &pReqInt->Overlapped);
+ }
+ else
+ {
+ fSucceeded = false;
+ AssertMsgFailed(("Invalid transfer direction\n"));
+ }
+
+ if (RT_UNLIKELY(!fSucceeded && GetLastError() != ERROR_IO_PENDING))
+ {
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ pReqInt->Rc = rc;
+ break;
+ }
+ RTFILEAIOREQ_SET_STATE(pReqInt, SUBMITTED);
+ }
+
+ ASMAtomicAddS32(&pCtxInt->cRequests, (int32_t)i);
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWait(RTFILEAIOCTX hAioCtx, size_t cMinReqs, RTMSINTERVAL cMillies,
+ PRTFILEAIOREQ pahReqs, size_t cReqs, uint32_t *pcReqs)
+{
+ /*
+ * Validate the parameters, making sure to always set pcReqs.
+ */
+ AssertPtrReturn(pcReqs, VERR_INVALID_POINTER);
+ *pcReqs = 0; /* always set */
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+ AssertPtrReturn(pahReqs, VERR_INVALID_POINTER);
+ AssertReturn(cReqs != 0, VERR_INVALID_PARAMETER);
+ AssertReturn(cReqs >= cMinReqs, VERR_OUT_OF_RANGE);
+
+ /*
+ * Can't wait if there are no requests around.
+ */
+ if ( RT_UNLIKELY(ASMAtomicUoReadS32(&pCtxInt->cRequests) == 0)
+ && !(pCtxInt->fFlags & RTFILEAIOCTX_FLAGS_WAIT_WITHOUT_PENDING_REQUESTS))
+ return VERR_FILE_AIO_NO_REQUEST;
+
+ /* Wait for at least one. */
+ if (!cMinReqs)
+ cMinReqs = 1;
+
+ /*
+ * Loop until we're woken up, hit an error (incl timeout), or
+ * have collected the desired number of requests.
+ */
+ int rc = VINF_SUCCESS;
+ int cRequestsCompleted = 0;
+ while ( !pCtxInt->fWokenUp
+ && cMinReqs > 0)
+ {
+ uint64_t StartNanoTS = 0;
+ DWORD dwTimeout = cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies;
+ DWORD cbTransfered;
+ LPOVERLAPPED pOverlapped;
+ ULONG_PTR lCompletionKey;
+ BOOL fSucceeded;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ StartNanoTS = RTTimeNanoTS();
+
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, true);
+ fSucceeded = g_pfnGetQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
+ &cbTransfered,
+ &lCompletionKey,
+ &pOverlapped,
+ dwTimeout);
+ ASMAtomicXchgBool(&pCtxInt->fWaiting, false);
+ if ( !fSucceeded
+ && !pOverlapped)
+ {
+ /* The call failed to dequeue a completion packet, includes VERR_TIMEOUT */
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+
+ /* Check if we got woken up. */
+ if (lCompletionKey == AIO_CONTEXT_WAKEUP_EVENT)
+ {
+ Assert(fSucceeded && !pOverlapped);
+ break;
+ }
+
+ /* A request completed. */
+ PRTFILEAIOREQINTERNAL pReqInt = OVERLAPPED_2_RTFILEAIOREQINTERNAL(pOverlapped);
+ AssertPtr(pReqInt);
+ Assert(pReqInt->u32Magic == RTFILEAIOREQ_MAGIC);
+
+ /* Mark the request as finished. */
+ RTFILEAIOREQ_SET_STATE(pReqInt, COMPLETED);
+
+ pReqInt->cbTransfered = cbTransfered;
+ if (fSucceeded)
+ pReqInt->Rc = VINF_SUCCESS;
+ else
+ {
+ DWORD errCode = GetLastError();
+ pReqInt->Rc = RTErrConvertFromWin32(errCode);
+ if (pReqInt->Rc == VERR_UNRESOLVED_ERROR)
+ LogRel(("AIO/win: Request %#p returned rc=%Rrc (native %u\n)", pReqInt, pReqInt->Rc, errCode));
+ }
+
+ pahReqs[cRequestsCompleted++] = (RTFILEAIOREQ)pReqInt;
+
+ /* Update counter. */
+ cMinReqs--;
+
+ if (cMillies != RT_INDEFINITE_WAIT)
+ {
+ /* Recalculate timeout. */
+ uint64_t NanoTS = RTTimeNanoTS();
+ uint64_t cMilliesElapsed = (NanoTS - StartNanoTS) / 1000000;
+ if (cMilliesElapsed < cMillies)
+ cMillies -= cMilliesElapsed;
+ else
+ cMillies = 0;
+ }
+ }
+
+ /*
+ * Update the context state and set the return value.
+ */
+ *pcReqs = cRequestsCompleted;
+ ASMAtomicSubS32(&pCtxInt->cRequests, cRequestsCompleted);
+
+ /*
+ * Clear the wakeup flag and set rc.
+ */
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, false);
+
+ if ( fWokenUp
+ && RT_SUCCESS(rc))
+ rc = VERR_INTERRUPTED;
+
+ return rc;
+}
+
+RTDECL(int) RTFileAioCtxWakeup(RTFILEAIOCTX hAioCtx)
+{
+ int rc = VINF_SUCCESS;
+ PRTFILEAIOCTXINTERNAL pCtxInt = hAioCtx;
+ RTFILEAIOCTX_VALID_RETURN(pCtxInt);
+
+ bool fWokenUp = ASMAtomicXchgBool(&pCtxInt->fWokenUp, true);
+ bool fWaiting = ASMAtomicReadBool(&pCtxInt->fWaiting);
+
+ if ( !fWokenUp
+ && fWaiting)
+ {
+ BOOL fSucceeded = g_pfnPostQueuedCompletionStatus(pCtxInt->hIoCompletionPort,
+ 0, AIO_CONTEXT_WAKEUP_EVENT,
+ NULL);
+
+ if (!fSucceeded)
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/fileio-win.cpp b/src/VBox/Runtime/r3/win/fileio-win.cpp
new file mode 100644
index 00000000..3feba3a2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fileio-win.cpp
@@ -0,0 +1,1549 @@
+/* $Id: fileio-win.cpp $ */
+/** @file
+ * IPRT - File I/O, native implementation for the Windows host platform.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DIR
+#ifndef _WIN32_WINNT
+# define _WIN32_WINNT 0x0500
+#endif
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/file.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/utf16.h>
+#include "internal/file.h"
+#include "internal/fs.h"
+#include "internal/path.h"
+#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+typedef BOOL WINAPI FNVERIFYCONSOLEIOHANDLE(HANDLE);
+typedef FNVERIFYCONSOLEIOHANDLE *PFNVERIFYCONSOLEIOHANDLE; /* No, nobody fell on the keyboard, really! */
+
+
+/**
+ * This is wrapper around the ugly SetFilePointer api.
+ *
+ * It's equivalent to SetFilePointerEx which we so unfortunately cannot use because of
+ * it not being present in NT4 GA.
+ *
+ * @returns Success indicator. Extended error information obtainable using GetLastError().
+ * @param hFile Filehandle.
+ * @param offSeek Offset to seek.
+ * @param poffNew Where to store the new file offset. NULL allowed.
+ * @param uMethod Seek method. (The windows one!)
+ */
+DECLINLINE(bool) MySetFilePointer(RTFILE hFile, uint64_t offSeek, uint64_t *poffNew, unsigned uMethod)
+{
+ bool fRc;
+ LARGE_INTEGER off;
+
+ off.QuadPart = offSeek;
+#if 1
+ if (off.LowPart != INVALID_SET_FILE_POINTER)
+ {
+ off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
+ fRc = off.LowPart != INVALID_SET_FILE_POINTER;
+ }
+ else
+ {
+ SetLastError(NO_ERROR);
+ off.LowPart = SetFilePointer((HANDLE)RTFileToNative(hFile), off.LowPart, &off.HighPart, uMethod);
+ fRc = GetLastError() == NO_ERROR;
+ }
+#else
+ fRc = SetFilePointerEx((HANDLE)RTFileToNative(hFile), off, &off, uMethod);
+#endif
+ if (fRc && poffNew)
+ *poffNew = off.QuadPart;
+ return fRc;
+}
+
+
+/**
+ * Helper for checking if a VERR_DISK_FULL isn't a VERR_FILE_TOO_BIG.
+ * @returns VERR_DISK_FULL or VERR_FILE_TOO_BIG.
+ */
+static int rtFileWinCheckIfDiskReallyFull(RTFILE hFile, uint64_t cbDesired)
+{
+ /*
+ * Windows doesn't appear to have a way to query the file size limit of a
+ * file system, so we have to deduce the limit from the file system driver name.
+ * This means it will only work for known file systems.
+ */
+ if (cbDesired >= _2G - 1)
+ {
+ uint64_t cbMaxFile = UINT64_MAX;
+ RTFSTYPE enmFsType;
+ int rc = rtNtQueryFsType((HANDLE)RTFileToNative(hFile), &enmFsType);
+ if (RT_SUCCESS(rc))
+ switch (enmFsType)
+ {
+ case RTFSTYPE_NTFS:
+ case RTFSTYPE_EXFAT:
+ case RTFSTYPE_UDF:
+ cbMaxFile = UINT64_C(0xffffffffffffffff); /* (May be limited by IFS.) */
+ break;
+
+ case RTFSTYPE_ISO9660:
+ cbMaxFile = 8 *_1T;
+ break;
+
+ case RTFSTYPE_FAT:
+ cbMaxFile = _4G;
+ break;
+
+ case RTFSTYPE_HPFS:
+ cbMaxFile = _2G;
+ break;
+
+ default:
+ break;
+ }
+ if (cbDesired >= cbMaxFile)
+ return VERR_FILE_TOO_BIG;
+ }
+ return VERR_DISK_FULL;
+}
+
+
+RTR3DECL(int) RTFileFromNative(PRTFILE pFile, RTHCINTPTR uNative)
+{
+ HANDLE h = (HANDLE)uNative;
+ AssertCompile(sizeof(h) == sizeof(uNative));
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ AssertMsgFailed(("%p\n", uNative));
+ *pFile = NIL_RTFILE;
+ return VERR_INVALID_HANDLE;
+ }
+ *pFile = (RTFILE)h;
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(RTHCINTPTR) RTFileToNative(RTFILE hFile)
+{
+ AssertReturn(hFile != NIL_RTFILE, (RTHCINTPTR)INVALID_HANDLE_VALUE);
+ return (RTHCINTPTR)hFile;
+}
+
+
+RTR3DECL(int) RTFileOpen(PRTFILE pFile, const char *pszFilename, uint64_t fOpen)
+{
+ return RTFileOpenEx(pszFilename, fOpen, pFile, NULL);
+}
+
+
+RTDECL(int) RTFileOpenEx(const char *pszFilename, uint64_t fOpen, PRTFILE phFile, PRTFILEACTION penmActionTaken)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(phFile, VERR_INVALID_PARAMETER);
+ *phFile = NIL_RTFILE;
+ if (penmActionTaken)
+ *penmActionTaken = RTFILEACTION_INVALID;
+ AssertReturn(pszFilename, VERR_INVALID_PARAMETER);
+
+ /*
+ * Merge forced open flags and validate them.
+ */
+ int rc = rtFileRecalcAndValidateFlags(&fOpen);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Determine disposition, access, share mode, creation flags, and security attributes
+ * for the CreateFile API call.
+ */
+ DWORD dwCreationDisposition;
+ switch (fOpen & RTFILE_O_ACTION_MASK)
+ {
+ case RTFILE_O_OPEN:
+ dwCreationDisposition = fOpen & RTFILE_O_TRUNCATE ? TRUNCATE_EXISTING : OPEN_EXISTING;
+ break;
+ case RTFILE_O_OPEN_CREATE:
+ dwCreationDisposition = OPEN_ALWAYS;
+ break;
+ case RTFILE_O_CREATE:
+ dwCreationDisposition = CREATE_NEW;
+ break;
+ case RTFILE_O_CREATE_REPLACE:
+ dwCreationDisposition = CREATE_ALWAYS;
+ break;
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+
+ DWORD dwDesiredAccess;
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ:
+ dwDesiredAccess = FILE_GENERIC_READ; /* RTFILE_O_APPEND is ignored. */
+ break;
+ case RTFILE_O_WRITE:
+ dwDesiredAccess = fOpen & RTFILE_O_APPEND
+ ? FILE_GENERIC_WRITE & ~FILE_WRITE_DATA
+ : FILE_GENERIC_WRITE;
+ break;
+ case RTFILE_O_READWRITE:
+ dwDesiredAccess = fOpen & RTFILE_O_APPEND
+ ? FILE_GENERIC_READ | (FILE_GENERIC_WRITE & ~FILE_WRITE_DATA)
+ : FILE_GENERIC_READ | FILE_GENERIC_WRITE;
+ break;
+ case RTFILE_O_ATTR_ONLY:
+ if (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
+ {
+ dwDesiredAccess = 0;
+ break;
+ }
+ RT_FALL_THRU();
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+ if (dwCreationDisposition == TRUNCATE_EXISTING)
+ /* Required for truncating the file (see MSDN), it is *NOT* part of FILE_GENERIC_WRITE. */
+ dwDesiredAccess |= GENERIC_WRITE;
+
+ /* RTFileSetMode needs following rights as well. */
+ switch (fOpen & RTFILE_O_ACCESS_ATTR_MASK)
+ {
+ case RTFILE_O_ACCESS_ATTR_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_ACCESS_ATTR_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_ACCESS_ATTR_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ default:
+ /* Attributes access is the same as the file access. */
+ switch (fOpen & RTFILE_O_ACCESS_MASK)
+ {
+ case RTFILE_O_READ: dwDesiredAccess |= FILE_READ_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_WRITE: dwDesiredAccess |= FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ case RTFILE_O_READWRITE: dwDesiredAccess |= FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES | SYNCHRONIZE; break;
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+ }
+
+ DWORD dwShareMode;
+ switch (fOpen & RTFILE_O_DENY_MASK)
+ {
+ case RTFILE_O_DENY_NONE: dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_READ; break;
+ case RTFILE_O_DENY_READWRITE: dwShareMode = 0; break;
+
+ case RTFILE_O_DENY_NOT_DELETE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READ: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_WRITE; break;
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_WRITE: dwShareMode = FILE_SHARE_DELETE | FILE_SHARE_READ; break;
+ case RTFILE_O_DENY_NOT_DELETE | RTFILE_O_DENY_READWRITE:dwShareMode = FILE_SHARE_DELETE; break;
+ default:
+ AssertMsgFailedReturn(("Impossible fOpen=%#llx\n", fOpen), VERR_INVALID_FLAGS);
+ }
+
+ SECURITY_ATTRIBUTES SecurityAttributes;
+ PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
+ if (fOpen & RTFILE_O_INHERIT)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ DWORD dwFlagsAndAttributes;
+ dwFlagsAndAttributes = !(fOpen & RTFILE_O_TEMP_AUTO_DELETE) ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_TEMPORARY;
+ if (fOpen & RTFILE_O_TEMP_AUTO_DELETE)
+ fOpen |= FILE_FLAG_DELETE_ON_CLOSE;
+ if (fOpen & RTFILE_O_WRITE_THROUGH)
+ dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH;
+ if (fOpen & RTFILE_O_ASYNC_IO)
+ dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED;
+ if (fOpen & RTFILE_O_NO_CACHE)
+ {
+ dwFlagsAndAttributes |= FILE_FLAG_NO_BUFFERING;
+ dwDesiredAccess &= ~FILE_APPEND_DATA;
+ }
+
+ /*
+ * Open/Create the file.
+ */
+ PRTUTF16 pwszFilename;
+ rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = CreateFileW(pwszFilename,
+ dwDesiredAccess,
+ dwShareMode,
+ pSecurityAttributes,
+ dwCreationDisposition,
+ dwFlagsAndAttributes,
+ NULL);
+ DWORD const dwErr = GetLastError();
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Calculate the action taken value.
+ */
+ RTFILEACTION enmActionTaken;
+ switch (dwCreationDisposition)
+ {
+ case CREATE_NEW:
+ enmActionTaken = RTFILEACTION_CREATED;
+ break;
+ case CREATE_ALWAYS:
+ AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr));
+ enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_REPLACED : RTFILEACTION_CREATED;
+ break;
+ case OPEN_EXISTING:
+ enmActionTaken = RTFILEACTION_OPENED;
+ break;
+ case OPEN_ALWAYS:
+ AssertMsg(dwErr == ERROR_ALREADY_EXISTS || dwErr == NO_ERROR, ("%u\n", dwErr));
+ enmActionTaken = dwErr == ERROR_ALREADY_EXISTS ? RTFILEACTION_OPENED : RTFILEACTION_CREATED;
+ break;
+ case TRUNCATE_EXISTING:
+ enmActionTaken = RTFILEACTION_TRUNCATED;
+ break;
+ default:
+ AssertMsgFailed(("%d %#x\n", dwCreationDisposition, dwCreationDisposition));
+ enmActionTaken = RTFILEACTION_INVALID;
+ break;
+ }
+
+ /*
+ * Turn off indexing of directory through Windows Indexing Service if
+ * we created a new file or replaced an existing one.
+ */
+ if ( (fOpen & RTFILE_O_NOT_CONTENT_INDEXED)
+ && ( enmActionTaken == RTFILEACTION_CREATED
+ || enmActionTaken == RTFILEACTION_REPLACED) )
+ {
+ /** @todo there must be a way to do this via the handle! */
+ if (!SetFileAttributesW(pwszFilename, FILE_ATTRIBUTE_NOT_CONTENT_INDEXED))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ /*
+ * If RTFILEACTION_OPENED, we may need to truncate the file.
+ */
+ else if ( (fOpen & (RTFILE_O_TRUNCATE | RTFILE_O_ACTION_MASK)) == (RTFILE_O_TRUNCATE | RTFILE_O_OPEN_CREATE)
+ && enmActionTaken == RTFILEACTION_OPENED)
+ {
+ if (SetEndOfFile(hFile))
+ enmActionTaken = RTFILEACTION_TRUNCATED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ if (penmActionTaken)
+ *penmActionTaken = enmActionTaken;
+ if (RT_SUCCESS(rc))
+ {
+ *phFile = (RTFILE)hFile;
+ Assert((HANDLE)*phFile == hFile);
+ RTPathWinFree(pwszFilename);
+ return VINF_SUCCESS;
+ }
+
+ CloseHandle(hFile);
+ }
+ else
+ {
+ if ( penmActionTaken
+ && dwCreationDisposition == CREATE_NEW
+ && dwErr == ERROR_FILE_EXISTS)
+ *penmActionTaken = RTFILEACTION_ALREADY_EXISTS;
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ RTPathWinFree(pwszFilename);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileOpenBitBucket(PRTFILE phFile, uint64_t fAccess)
+{
+ AssertReturn( fAccess == RTFILE_O_READ
+ || fAccess == RTFILE_O_WRITE
+ || fAccess == RTFILE_O_READWRITE,
+ VERR_INVALID_PARAMETER);
+ return RTFileOpen(phFile, "NUL", fAccess | RTFILE_O_DENY_NONE | RTFILE_O_OPEN);
+}
+
+
+RTDECL(int) RTFileDup(RTFILE hFileSrc, uint64_t fFlags, PRTFILE phFileNew)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phFileNew, VERR_INVALID_POINTER);
+ *phFileNew = NIL_RTFILE;
+ AssertPtrReturn(phFileNew, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~(uint64_t)RTFILE_O_INHERIT), VERR_INVALID_FLAGS);
+
+ /*
+ * Do the job.
+ */
+ HANDLE hNew = INVALID_HANDLE_VALUE;
+ if (DuplicateHandle(GetCurrentProcess(), (HANDLE)RTFileToNative(hFileSrc),
+ GetCurrentProcess(), &hNew, 0, RT_BOOL(fFlags & RTFILE_O_INHERIT), DUPLICATE_SAME_ACCESS))
+ {
+ *phFileNew = (RTFILE)hNew;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTFileClose(RTFILE hFile)
+{
+ if (hFile == NIL_RTFILE)
+ return VINF_SUCCESS;
+ if (CloseHandle((HANDLE)RTFileToNative(hFile)))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTFILE rtFileGetStandard(RTHANDLESTD enmStdHandle)
+{
+ DWORD dwStdHandle;
+ switch (enmStdHandle)
+ {
+ case RTHANDLESTD_INPUT: dwStdHandle = STD_INPUT_HANDLE; break;
+ case RTHANDLESTD_OUTPUT: dwStdHandle = STD_OUTPUT_HANDLE; break;
+ case RTHANDLESTD_ERROR: dwStdHandle = STD_ERROR_HANDLE; break;
+ default:
+ AssertFailedReturn(NIL_RTFILE);
+ }
+
+ HANDLE hNative = GetStdHandle(dwStdHandle);
+ if (hNative == INVALID_HANDLE_VALUE)
+ return NIL_RTFILE;
+
+ RTFILE hFile = (RTFILE)(uintptr_t)hNative;
+ AssertReturn((HANDLE)(uintptr_t)hFile == hNative, NIL_RTFILE);
+ return hFile;
+}
+
+
+RTR3DECL(int) RTFileSeek(RTFILE hFile, int64_t offSeek, unsigned uMethod, uint64_t *poffActual)
+{
+ static ULONG aulSeekRecode[] =
+ {
+ FILE_BEGIN,
+ FILE_CURRENT,
+ FILE_END,
+ };
+
+ /*
+ * Validate input.
+ */
+ if (uMethod > RTFILE_SEEK_END)
+ {
+ AssertMsgFailed(("Invalid uMethod=%d\n", uMethod));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Execute the seek.
+ */
+ if (MySetFilePointer(hFile, offSeek, poffActual, aulSeekRecode[uMethod]))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTFileRead(RTFILE hFile, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ if (cbToRead <= 0)
+ {
+ if (pcbRead)
+ *pcbRead = 0;
+ return VINF_SUCCESS;
+ }
+ ULONG cbToReadAdj = (ULONG)cbToRead;
+ AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
+
+ ULONG cbRead = 0;
+ if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, NULL))
+ {
+ if (pcbRead)
+ /* Caller can handle partial reads. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects everything to be read. */
+ while (cbToReadAdj > cbRead)
+ {
+ ULONG cbReadPart = 0;
+ if (!ReadFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbRead, cbToReadAdj - cbRead, &cbReadPart, NULL))
+ return RTErrConvertFromWin32(GetLastError());
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ cbRead += cbReadPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If it's a console, we might bump into out of memory conditions in the
+ * ReadConsole call.
+ */
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ ULONG cbChunk = cbToReadAdj / 2;
+ if (cbChunk > 16*_1K)
+ cbChunk = 16*_1K;
+ else
+ cbChunk = RT_ALIGN_32(cbChunk, 256);
+
+ cbRead = 0;
+ while (cbToReadAdj > cbRead)
+ {
+ ULONG cbToReadNow = RT_MIN(cbChunk, cbToReadAdj - cbRead);
+ ULONG cbReadPart = 0;
+ if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadNow, &cbReadPart, NULL))
+ {
+ /* If we failed because the buffer is too big, shrink it and
+ try again. */
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
+ && cbChunk > 8)
+ {
+ cbChunk /= 2;
+ continue;
+ }
+ return RTErrConvertFromWin32(dwErr);
+ }
+ cbRead += cbReadPart;
+
+ /* Return if the caller can handle partial reads, otherwise try
+ fill the buffer all the way up. */
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ break;
+ }
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ }
+ return VINF_SUCCESS;
+ }
+
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTFileReadAt(RTFILE hFile, RTFOFF off, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ ULONG cbToReadAdj = (ULONG)cbToRead;
+ AssertReturn(cbToReadAdj == cbToRead, VERR_NUMBER_TOO_BIG);
+
+ OVERLAPPED Overlapped;
+ Overlapped.Offset = (uint32_t)off;
+ Overlapped.OffsetHigh = (uint32_t)(off >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbRead = 0;
+ if (ReadFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToReadAdj, &cbRead, &Overlapped))
+ {
+ if (pcbRead)
+ /* Caller can handle partial reads. */
+ *pcbRead = cbRead;
+ else
+ {
+ /* Caller expects everything to be read. */
+ while (cbToReadAdj > cbRead)
+ {
+ Overlapped.Offset = (uint32_t)(off + cbRead);
+ Overlapped.OffsetHigh = (uint32_t)((off + cbRead) >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbReadPart = 0;
+ if (!ReadFile((HANDLE)RTFileToNative(hFile), (char *)pvBuf + cbRead, cbToReadAdj - cbRead,
+ &cbReadPart, &Overlapped))
+ return RTErrConvertFromWin32(GetLastError());
+ if (cbReadPart == 0)
+ return VERR_EOF;
+ cbRead += cbReadPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /* We will get an EOF error when using overlapped I/O. So, make sure we don't
+ return it when pcbhRead is not NULL. */
+ DWORD dwErr = GetLastError();
+ if (pcbRead && dwErr == ERROR_HANDLE_EOF)
+ {
+ *pcbRead = 0;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTR3DECL(int) RTFileWrite(RTFILE hFile, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ if (cbToWrite <= 0)
+ return VINF_SUCCESS;
+ ULONG const cbToWriteAdj = (ULONG)cbToWrite;
+ AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
+
+ ULONG cbWritten = 0;
+ if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, NULL))
+ {
+ if (pcbWritten)
+ /* Caller can handle partial writes. */
+ *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
+ else
+ {
+ /* Caller expects everything to be written. */
+ while (cbWritten < cbToWriteAdj)
+ {
+ ULONG cbWrittenPart = 0;
+ if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
+ cbToWriteAdj - cbWritten, &cbWrittenPart, NULL))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj - cbWritten);
+ return rc;
+ }
+ if (cbWrittenPart == 0)
+ return VERR_WRITE_ERROR;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * If it's a console, we might bump into out of memory conditions in the
+ * WriteConsole call.
+ */
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_NOT_ENOUGH_MEMORY)
+ {
+ ULONG cbChunk = cbToWriteAdj / 2;
+ if (cbChunk > _32K)
+ cbChunk = _32K;
+ else
+ cbChunk = RT_ALIGN_32(cbChunk, 256);
+
+ cbWritten = 0;
+ while (cbWritten < cbToWriteAdj)
+ {
+ ULONG cbToWriteNow = RT_MIN(cbChunk, cbToWriteAdj - cbWritten);
+ ULONG cbWrittenPart = 0;
+ if (!WriteFile((HANDLE)RTFileToNative(hFile), (const char *)pvBuf + cbWritten, cbToWriteNow, &cbWrittenPart, NULL))
+ {
+ /* If we failed because the buffer is too big, shrink it and
+ try again. */
+ dwErr = GetLastError();
+ if ( dwErr == ERROR_NOT_ENOUGH_MEMORY
+ && cbChunk > 8)
+ {
+ cbChunk /= 2;
+ continue;
+ }
+ int rc = RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteNow);
+ return rc;
+ }
+ cbWritten += cbWrittenPart;
+
+ /* Return if the caller can handle partial writes, otherwise try
+ write out everything. */
+ if (pcbWritten)
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
+ break;
+ }
+ if (cbWrittenPart == 0)
+ return VERR_WRITE_ERROR;
+ }
+ return VINF_SUCCESS;
+ }
+
+ int rc = RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, RTFileTell(hFile) + cbToWriteAdj);
+ return rc;
+}
+
+
+RTDECL(int) RTFileWriteAt(RTFILE hFile, RTFOFF off, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ ULONG const cbToWriteAdj = (ULONG)cbToWrite;
+ AssertReturn(cbToWriteAdj == cbToWrite, VERR_NUMBER_TOO_BIG);
+
+ OVERLAPPED Overlapped;
+ Overlapped.Offset = (uint32_t)off;
+ Overlapped.OffsetHigh = (uint32_t)(off >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbWritten = 0;
+ if (WriteFile((HANDLE)RTFileToNative(hFile), pvBuf, cbToWriteAdj, &cbWritten, &Overlapped))
+ {
+ if (pcbWritten)
+ /* Caller can handle partial writes. */
+ *pcbWritten = RT_MIN(cbWritten, cbToWriteAdj); /* paranoia^3 */
+ else
+ {
+ /* Caller expects everything to be written. */
+ while (cbWritten < cbToWriteAdj)
+ {
+ Overlapped.Offset = (uint32_t)(off + cbWritten);
+ Overlapped.OffsetHigh = (uint32_t)((off + cbWritten) >> 32);
+ Overlapped.hEvent = NULL;
+ Overlapped.Internal = 0;
+ Overlapped.InternalHigh = 0;
+
+ ULONG cbWrittenPart = 0;
+ if (!WriteFile((HANDLE)RTFileToNative(hFile), (char*)pvBuf + cbWritten,
+ cbToWriteAdj - cbWritten, &cbWrittenPart, &Overlapped))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj);
+ return rc;
+ }
+ if (cbWrittenPart == 0)
+ return VERR_WRITE_ERROR;
+ cbWritten += cbWrittenPart;
+ }
+ }
+ return VINF_SUCCESS;
+ }
+
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_DISK_FULL)
+ rc = rtFileWinCheckIfDiskReallyFull(hFile, off + cbToWriteAdj);
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileFlush(RTFILE hFile)
+{
+ if (!FlushFileBuffers((HANDLE)RTFileToNative(hFile)))
+ {
+ int rc = GetLastError();
+ Log(("FlushFileBuffers failed with %d\n", rc));
+ return RTErrConvertFromWin32(rc);
+ }
+ return VINF_SUCCESS;
+}
+
+#if 1
+
+/**
+ * Checks the the two handles refers to the same file.
+ *
+ * @returns true if the same file, false if different ones or invalid handles.
+ * @param hFile1 Handle \#1.
+ * @param hFile2 Handle \#2.
+ */
+static bool rtFileIsSame(HANDLE hFile1, HANDLE hFile2)
+{
+ /*
+ * We retry in case CreationTime or the Object ID is being modified and there
+ * aren't any IndexNumber (file ID) on this kind of file system.
+ */
+ for (uint32_t iTries = 0; iTries < 3; iTries++)
+ {
+ /*
+ * Fetch data to compare (being a little lazy here).
+ */
+ struct
+ {
+ HANDLE hFile;
+ NTSTATUS rcObjId;
+ FILE_OBJECTID_INFORMATION ObjId;
+ FILE_ALL_INFORMATION All;
+ FILE_FS_VOLUME_INFORMATION Vol;
+ } auData[2];
+ auData[0].hFile = hFile1;
+ auData[1].hFile = hFile2;
+
+ for (uintptr_t i = 0; i < RT_ELEMENTS(auData); i++)
+ {
+ RT_ZERO(auData[i].ObjId);
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ auData[i].rcObjId = NtQueryInformationFile(auData[i].hFile, &Ios, &auData[i].ObjId, sizeof(auData[i].ObjId),
+ FileObjectIdInformation);
+
+ RT_ZERO(auData[i].All);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ NTSTATUS rcNt = NtQueryInformationFile(auData[i].hFile, &Ios, &auData[i].All, sizeof(auData[i].All),
+ FileAllInformation);
+ AssertReturn(rcNt == STATUS_BUFFER_OVERFLOW /* insufficient space for name info */ || NT_SUCCESS(rcNt), false);
+
+ union
+ {
+ FILE_FS_VOLUME_INFORMATION Info;
+ uint8_t abBuf[sizeof(FILE_FS_VOLUME_INFORMATION) + 4096];
+ } uVol;
+ RT_ZERO(uVol.Info);
+ RTNT_IO_STATUS_BLOCK_REINIT(&Ios);
+ rcNt = NtQueryVolumeInformationFile(auData[i].hFile, &Ios, &uVol, sizeof(uVol), FileFsVolumeInformation);
+ if (NT_SUCCESS(rcNt))
+ auData[i].Vol = uVol.Info;
+ else
+ RT_ZERO(auData[i].Vol);
+ }
+
+ /*
+ * Compare it.
+ */
+ if ( auData[0].All.StandardInformation.Directory
+ == auData[1].All.StandardInformation.Directory)
+ { /* likely */ }
+ else
+ break;
+
+ if ( (auData[0].All.BasicInformation.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT))
+ == (auData[1].All.BasicInformation.FileAttributes & (FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)))
+ { /* likely */ }
+ else
+ break;
+
+ if ( auData[0].Vol.VolumeSerialNumber
+ == auData[1].Vol.VolumeSerialNumber)
+ { /* likely */ }
+ else
+ break;
+
+ if ( auData[0].All.InternalInformation.IndexNumber.QuadPart
+ == auData[1].All.InternalInformation.IndexNumber.QuadPart)
+ { /* likely */ }
+ else
+ break;
+
+ if ( !NT_SUCCESS(auData[0].rcObjId)
+ || memcmp(&auData[0].ObjId, &auData[1].ObjId, RT_UOFFSETOF(FILE_OBJECTID_INFORMATION, ExtendedInfo)) == 0)
+ {
+ if ( auData[0].All.BasicInformation.CreationTime.QuadPart
+ == auData[1].All.BasicInformation.CreationTime.QuadPart)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+/**
+ * If @a hFile is opened in append mode, try return a handle with
+ * FILE_WRITE_DATA permissions.
+ *
+ * @returns Duplicate handle.
+ * @param hFile The NT handle to check & duplicate.
+ *
+ * @todo It would be much easier to implement this by not dropping the
+ * FILE_WRITE_DATA access and instead have the RTFileWrite APIs
+ * enforce the appending. That will require keeping additional
+ * information along side the handle (instance structure). However, on
+ * windows you can grant append permissions w/o giving people access to
+ * overwrite existing data, so the RTFileOpenEx code would have to deal
+ * with those kinds of STATUS_ACCESS_DENIED too then.
+ */
+static HANDLE rtFileReOpenAppendOnlyWithFullWriteAccess(HANDLE hFile)
+{
+ OBJECT_BASIC_INFORMATION BasicInfo = {0};
+ ULONG cbActual = 0;
+ NTSTATUS rcNt = NtQueryObject(hFile, ObjectBasicInformation, &BasicInfo, sizeof(BasicInfo), &cbActual);
+ if (NT_SUCCESS(rcNt))
+ {
+ if ((BasicInfo.GrantedAccess & (FILE_APPEND_DATA | FILE_WRITE_DATA)) == FILE_APPEND_DATA)
+ {
+ /*
+ * We cannot use NtDuplicateObject here as it is not possible to
+ * upgrade the access on files, only making it more strict. So,
+ * query the path and re-open it (we could do by file/object/whatever
+ * id too, but that may not work with all file systems).
+ */
+ for (uint32_t i = 0; i < 16; i++)
+ {
+ UNICODE_STRING NtName;
+ int rc = RTNtPathFromHandle(&NtName, hFile, 0);
+ AssertRCReturn(rc, INVALID_HANDLE_VALUE);
+
+ HANDLE hDupFile = RTNT_INVALID_HANDLE_VALUE;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, &NtName, BasicInfo.Attributes & ~OBJ_INHERIT, NULL, NULL);
+
+ rcNt = NtCreateFile(&hDupFile,
+ BasicInfo.GrantedAccess | FILE_WRITE_DATA,
+ &ObjAttr,
+ &Ios,
+ NULL /* AllocationSize*/,
+ FILE_ATTRIBUTE_NORMAL,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ FILE_OPEN,
+ FILE_OPEN_FOR_BACKUP_INTENT /*??*/,
+ NULL /*EaBuffer*/,
+ 0 /*EaLength*/);
+ RTUtf16Free(NtName.Buffer);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Check that we've opened the same file.
+ */
+ if (rtFileIsSame(hFile, hDupFile))
+ return hDupFile;
+ NtClose(hDupFile);
+ }
+ }
+ AssertFailed();
+ }
+ }
+ return INVALID_HANDLE_VALUE;
+}
+
+#endif
+
+RTR3DECL(int) RTFileSetSize(RTFILE hFile, uint64_t cbSize)
+{
+#if 1
+ HANDLE hNtFile = (HANDLE)RTFileToNative(hFile);
+ HANDLE hDupFile = INVALID_HANDLE_VALUE;
+ union
+ {
+ FILE_END_OF_FILE_INFORMATION Eof;
+ FILE_ALLOCATION_INFORMATION Alloc;
+ } uInfo;
+
+ /*
+ * Change the EOF marker.
+ *
+ * HACK ALERT! If the file was opened in RTFILE_O_APPEND mode, we will have
+ * to re-open it with FILE_WRITE_DATA access to get the job done.
+ * This how ftruncate on a unixy system would work but not how
+ * it is done on Windows where appending is a separate permission
+ * rather than just a write modifier, making this hack totally wrong.
+ */
+ /** @todo The right way to fix this is either to add a RTFileSetSizeEx function
+ * for specifically requesting the unixy behaviour, or add an additional
+ * flag to RTFileOpen[Ex] to request the unixy append behaviour there.
+ * The latter would require saving the open flags in a instance data
+ * structure, which is a bit of a risky move, though something we should
+ * do in 6.2 (or later).
+ *
+ * Note! Because handle interitance, it is not realyan option to
+ * always use FILE_WRITE_DATA and implement the RTFILE_O_APPEND
+ * bits in RTFileWrite and friends. Besides, it's not like
+ * RTFILE_O_APPEND is so clearly defined anyway - see
+ * RTFileWriteAt.
+ */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ uInfo.Eof.EndOfFile.QuadPart = cbSize;
+ NTSTATUS rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation);
+ if (rcNt == STATUS_ACCESS_DENIED)
+ {
+ hDupFile = rtFileReOpenAppendOnlyWithFullWriteAccess(hNtFile);
+ if (hDupFile != INVALID_HANDLE_VALUE)
+ {
+ hNtFile = hDupFile;
+ uInfo.Eof.EndOfFile.QuadPart = cbSize;
+ rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Eof), FileEndOfFileInformation);
+ }
+ }
+
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Change the allocation.
+ */
+ uInfo.Alloc.AllocationSize.QuadPart = cbSize;
+ rcNt = NtSetInformationFile(hNtFile, &Ios, &uInfo.Eof, sizeof(uInfo.Alloc), FileAllocationInformation);
+ }
+
+ /*
+ * Close the temporary file handle:
+ */
+ if (hDupFile != INVALID_HANDLE_VALUE)
+ NtClose(hDupFile);
+
+ if (RT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+
+#else /* this version of the code will fail to truncate files when RTFILE_O_APPEND is in effect, which isn't what we want... */
+ /*
+ * Get current file pointer.
+ */
+ int rc;
+ uint64_t offCurrent;
+ if (MySetFilePointer(hFile, 0, &offCurrent, FILE_CURRENT))
+ {
+ /*
+ * Set new file pointer.
+ */
+ if (MySetFilePointer(hFile, cbSize, NULL, FILE_BEGIN))
+ {
+ /* set file pointer */
+ if (SetEndOfFile((HANDLE)RTFileToNative(hFile)))
+ {
+ /*
+ * Restore file pointer and return.
+ * If the old pointer was beyond the new file end, ignore failure.
+ */
+ if ( MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN)
+ || offCurrent > cbSize)
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Failed, try restoring the file pointer.
+ */
+ rc = GetLastError();
+ MySetFilePointer(hFile, offCurrent, NULL, FILE_BEGIN);
+
+ if (rc == ERROR_DISK_FULL)
+ return rtFileWinCheckIfDiskReallyFull(hFile, cbSize);
+ }
+ else
+ rc = GetLastError();
+ }
+ else
+ rc = GetLastError();
+
+ return RTErrConvertFromWin32(rc);
+#endif
+}
+
+
+RTR3DECL(int) RTFileQuerySize(RTFILE hFile, uint64_t *pcbSize)
+{
+ /*
+ * GetFileSize works for most handles.
+ */
+ ULARGE_INTEGER Size;
+ Size.LowPart = GetFileSize((HANDLE)RTFileToNative(hFile), &Size.HighPart);
+ if (Size.LowPart != INVALID_FILE_SIZE)
+ {
+ *pcbSize = Size.QuadPart;
+ return VINF_SUCCESS;
+ }
+ int rc = RTErrConvertFromWin32(GetLastError());
+
+ /*
+ * Could it be a volume or a disk?
+ */
+ DISK_GEOMETRY DriveGeo;
+ DWORD cbDriveGeo;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0,
+ &DriveGeo, sizeof(DriveGeo), &cbDriveGeo, NULL))
+ {
+ if ( DriveGeo.MediaType == FixedMedia
+ || DriveGeo.MediaType == RemovableMedia)
+ {
+ *pcbSize = DriveGeo.Cylinders.QuadPart
+ * DriveGeo.TracksPerCylinder
+ * DriveGeo.SectorsPerTrack
+ * DriveGeo.BytesPerSector;
+
+ GET_LENGTH_INFORMATION DiskLenInfo;
+ DWORD Ignored;
+ if (DeviceIoControl((HANDLE)RTFileToNative(hFile),
+ IOCTL_DISK_GET_LENGTH_INFO, NULL, 0,
+ &DiskLenInfo, sizeof(DiskLenInfo), &Ignored, (LPOVERLAPPED)NULL))
+ {
+ /* IOCTL_DISK_GET_LENGTH_INFO is supported -- override cbSize. */
+ *pcbSize = DiskLenInfo.Length.QuadPart;
+ }
+ return VINF_SUCCESS;
+ }
+ }
+
+ /*
+ * Return the GetFileSize result if not a volume/disk.
+ */
+ return rc;
+}
+
+
+RTR3DECL(int) RTFileQueryMaxSizeEx(RTFILE hFile, PRTFOFF pcbMax)
+{
+ /** @todo r=bird:
+ * We might have to make this code OS version specific... In the worse
+ * case, we'll have to try GetVolumeInformationByHandle on vista and fall
+ * back on NtQueryVolumeInformationFile(,,,, FileFsAttributeInformation)
+ * else where, and check for known file system names. (For LAN shares we'll
+ * have to figure out the remote file system.) */
+ RT_NOREF_PV(hFile); RT_NOREF_PV(pcbMax);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTR3DECL(bool) RTFileIsValid(RTFILE hFile)
+{
+ if (hFile != NIL_RTFILE)
+ {
+ DWORD dwType = GetFileType((HANDLE)RTFileToNative(hFile));
+ switch (dwType)
+ {
+ case FILE_TYPE_CHAR:
+ case FILE_TYPE_DISK:
+ case FILE_TYPE_PIPE:
+ case FILE_TYPE_REMOTE:
+ return true;
+
+ case FILE_TYPE_UNKNOWN:
+ if (GetLastError() == NO_ERROR)
+ return true;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+
+#define LOW_DWORD(u64) ((DWORD)u64)
+#define HIGH_DWORD(u64) (((DWORD *)&u64)[1])
+
+RTR3DECL(int) RTFileLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Prepare flags. */
+ Assert(RTFILE_LOCK_WRITE);
+ DWORD dwFlags = (fLock & RTFILE_LOCK_WRITE) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
+ Assert(RTFILE_LOCK_WAIT);
+ if (!(fLock & RTFILE_LOCK_WAIT))
+ dwFlags |= LOCKFILE_FAIL_IMMEDIATELY;
+
+ /* Windows structure. */
+ OVERLAPPED Overlapped;
+ memset(&Overlapped, 0, sizeof(Overlapped));
+ Overlapped.Offset = LOW_DWORD(offLock);
+ Overlapped.OffsetHigh = HIGH_DWORD(offLock);
+
+ /* Note: according to Microsoft, LockFileEx API call is available starting from NT 3.5 */
+ if (LockFileEx((HANDLE)RTFileToNative(hFile), dwFlags, 0, LOW_DWORD(cbLock), HIGH_DWORD(cbLock), &Overlapped))
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTFileChangeLock(RTFILE hFile, unsigned fLock, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ /* Check arguments. */
+ if (fLock & ~RTFILE_LOCK_MASK)
+ {
+ AssertMsgFailed(("Invalid fLock=%08X\n", fLock));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /* Remove old lock. */
+ int rc = RTFileUnlock(hFile, offLock, cbLock);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* Set new lock. */
+ rc = RTFileLock(hFile, fLock, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /* Try to restore old lock. */
+ unsigned fLockOld = (fLock & RTFILE_LOCK_WRITE) ? fLock & ~RTFILE_LOCK_WRITE : fLock | RTFILE_LOCK_WRITE;
+ rc = RTFileLock(hFile, fLockOld, offLock, cbLock);
+ if (RT_SUCCESS(rc))
+ return VERR_FILE_LOCK_VIOLATION;
+ else
+ return VERR_FILE_LOCK_LOST;
+}
+
+
+RTR3DECL(int) RTFileUnlock(RTFILE hFile, int64_t offLock, uint64_t cbLock)
+{
+ Assert(offLock >= 0);
+
+ if (UnlockFile((HANDLE)RTFileToNative(hFile),
+ LOW_DWORD(offLock), HIGH_DWORD(offLock),
+ LOW_DWORD(cbLock), HIGH_DWORD(cbLock)))
+ return VINF_SUCCESS;
+
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+
+RTR3DECL(int) RTFileQueryInfo(RTFILE hFile, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ /*
+ * Validate input.
+ */
+ if (hFile == NIL_RTFILE)
+ {
+ AssertMsgFailed(("Invalid hFile=%RTfile\n", hFile));
+ return VERR_INVALID_PARAMETER;
+ }
+ if (!pObjInfo)
+ {
+ AssertMsgFailed(("Invalid pObjInfo=%p\n", pObjInfo));
+ return VERR_INVALID_PARAMETER;
+ }
+ if ( enmAdditionalAttribs < RTFSOBJATTRADD_NOTHING
+ || enmAdditionalAttribs > RTFSOBJATTRADD_LAST)
+ {
+ AssertMsgFailed(("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ /*
+ * Query file info.
+ */
+ HANDLE hHandle = (HANDLE)RTFileToNative(hFile);
+#if 1
+ uint64_t auBuf[168 / sizeof(uint64_t)]; /* Missing FILE_ALL_INFORMATION here. */
+ int rc = rtPathNtQueryInfoFromHandle(hFile, auBuf, sizeof(auBuf), pObjInfo, enmAdditionalAttribs, NULL, 0);
+ if (RT_SUCCESS(rc))
+ return rc;
+
+ /*
+ * Console I/O handles make trouble here. On older windows versions they
+ * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
+ * more recent ones they cause different errors to appear.
+ *
+ * Thus, we must ignore the latter and doubly verify invalid handle claims.
+ * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
+ * GetFileType should it not be there.
+ */
+ if ( rc == VERR_INVALID_HANDLE
+ || rc == VERR_ACCESS_DENIED
+ || rc == VERR_UNEXPECTED_FS_OBJ_TYPE)
+ {
+ static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
+ static bool volatile s_fInitialized = false;
+ PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
+ if (s_fInitialized)
+ pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
+ else
+ {
+ pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
+ ASMAtomicWriteBool(&s_fInitialized, true);
+ }
+ if ( pfnVerifyConsoleIoHandle
+ ? !pfnVerifyConsoleIoHandle(hHandle)
+ : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
+ return VERR_INVALID_HANDLE;
+ }
+ /*
+ * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console
+ * I/O handles and null device handles. We must ignore these just like the
+ * above invalid handle error.
+ */
+ else if (rc != VERR_INVALID_FUNCTION && rc != VERR_IO_BAD_COMMAND)
+ return rc;
+
+ RT_ZERO(*pObjInfo);
+ pObjInfo->Attr.enmAdditional = enmAdditionalAttribs;
+ pObjInfo->Attr.fMode = rtFsModeFromDos(RTFS_DOS_NT_DEVICE, "", 0, 0, 0);
+ return VINF_SUCCESS;
+#else
+
+ BY_HANDLE_FILE_INFORMATION Data;
+ if (!GetFileInformationByHandle(hHandle, &Data))
+ {
+ /*
+ * Console I/O handles make trouble here. On older windows versions they
+ * end up with ERROR_INVALID_HANDLE when handed to the above API, while on
+ * more recent ones they cause different errors to appear.
+ *
+ * Thus, we must ignore the latter and doubly verify invalid handle claims.
+ * We use the undocumented VerifyConsoleIoHandle to do this, falling back on
+ * GetFileType should it not be there.
+ */
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_INVALID_HANDLE)
+ {
+ static PFNVERIFYCONSOLEIOHANDLE s_pfnVerifyConsoleIoHandle = NULL;
+ static bool volatile s_fInitialized = false;
+ PFNVERIFYCONSOLEIOHANDLE pfnVerifyConsoleIoHandle;
+ if (s_fInitialized)
+ pfnVerifyConsoleIoHandle = s_pfnVerifyConsoleIoHandle;
+ else
+ {
+ pfnVerifyConsoleIoHandle = (PFNVERIFYCONSOLEIOHANDLE)RTLdrGetSystemSymbol("kernel32.dll", "VerifyConsoleIoHandle");
+ ASMAtomicWriteBool(&s_fInitialized, true);
+ }
+ if ( pfnVerifyConsoleIoHandle
+ ? !pfnVerifyConsoleIoHandle(hHandle)
+ : GetFileType(hHandle) == FILE_TYPE_UNKNOWN && GetLastError() != NO_ERROR)
+ return VERR_INVALID_HANDLE;
+ }
+ /*
+ * On Windows 10 and (hopefully) 8.1 we get ERROR_INVALID_FUNCTION with console I/O
+ * handles. We must ignore these just like the above invalid handle error.
+ */
+ else if (dwErr != ERROR_INVALID_FUNCTION)
+ return RTErrConvertFromWin32(dwErr);
+
+ RT_ZERO(Data);
+ Data.dwFileAttributes = RTFS_DOS_NT_DEVICE;
+ }
+
+ /*
+ * Setup the returned data.
+ */
+ pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
+ | (uint64_t)Data.nFileSizeLow;
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
+ pObjInfo->ChangeTime = pObjInfo->ModificationTime;
+
+ pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT, "", 0,
+ RTFSMODE_SYMLINK_REPARSE_TAG /* (symlink or not, doesn't usually matter here) */);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = Data.nNumberOfLinks ? Data.nNumberOfLinks : 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = Data.dwVolumeSerialNumber;
+ pObjInfo->Attr.u.Unix.INodeId = RT_MAKE_U64(Data.nFileIndexLow, Data.nFileIndexHigh);
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = ~0U;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = ~0U;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+#endif
+}
+
+
+RTR3DECL(int) RTFileSetTimes(RTFILE hFile, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ RT_NOREF_PV(pChangeTime); /* Not exposed thru the windows API we're using. */
+
+ if (!pAccessTime && !pModificationTime && !pBirthTime)
+ return VINF_SUCCESS; /* NOP */
+
+ FILETIME CreationTimeFT;
+ PFILETIME pCreationTimeFT = NULL;
+ if (pBirthTime)
+ pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
+
+ FILETIME LastAccessTimeFT;
+ PFILETIME pLastAccessTimeFT = NULL;
+ if (pAccessTime)
+ pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
+
+ FILETIME LastWriteTimeFT;
+ PFILETIME pLastWriteTimeFT = NULL;
+ if (pModificationTime)
+ pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
+
+ int rc = VINF_SUCCESS;
+ if (!SetFileTime((HANDLE)RTFileToNative(hFile), pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFileSetTimes(%RTfile, %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
+ hFile, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
+ }
+ return rc;
+}
+
+
+#if 0 /* RTFileSetMode is implemented by RTFileSetMode-r3-nt.cpp */
+/* This comes from a source file with a different set of system headers (DDK)
+ * so it can't be declared in a common header, like internal/file.h.
+ */
+extern int rtFileNativeSetAttributes(HANDLE FileHandle, ULONG FileAttributes);
+
+
+RTR3DECL(int) RTFileSetMode(RTFILE hFile, RTFMODE fMode)
+{
+ /*
+ * Normalize the mode and call the API.
+ */
+ fMode = rtFsModeNormalize(fMode, NULL, 0);
+ if (!rtFsModeIsValid(fMode))
+ return VERR_INVALID_PARAMETER;
+
+ ULONG FileAttributes = (fMode & RTFS_DOS_MASK) >> RTFS_DOS_SHIFT;
+ int Err = rtFileNativeSetAttributes((HANDLE)hFile, FileAttributes);
+ if (Err != ERROR_SUCCESS)
+ {
+ int rc = RTErrConvertFromWin32(Err);
+ Log(("RTFileSetMode(%RTfile, %RTfmode): rtFileNativeSetAttributes (0x%08X) failed with err %d (%Rrc)\n",
+ hFile, fMode, FileAttributes, Err, rc));
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+#endif
+
+
+/* RTFileQueryFsSizes is implemented by ../nt/RTFileQueryFsSizes-nt.cpp */
+
+
+RTR3DECL(int) RTFileDelete(const char *pszFilename)
+{
+ PRTUTF16 pwszFilename;
+ int rc = RTPathWinFromUtf8(&pwszFilename, pszFilename, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ if (!DeleteFileW(pwszFilename))
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszFilename);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTFileRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Hand it on to the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst,
+ fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0,
+ RTFS_TYPE_FILE);
+
+ LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+
+}
+
+
+RTDECL(int) RTFileMove(const char *pszSrc, const char *pszDst, unsigned fMove)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(!(fMove & ~RTFILEMOVE_FLAGS_REPLACE), ("%#x\n", fMove), VERR_INVALID_PARAMETER);
+
+ /*
+ * Hand it on to the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst,
+ fMove & RTFILEMOVE_FLAGS_REPLACE
+ ? MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING
+ : MOVEFILE_COPY_ALLOWED,
+ RTFS_TYPE_FILE);
+
+ LogFlow(("RTFileMove(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n",
+ pszSrc, pszSrc, pszDst, pszDst, fMove, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/fs-win.cpp b/src/VBox/Runtime/r3/win/fs-win.cpp
new file mode 100644
index 00000000..4210ffed
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/fs-win.cpp
@@ -0,0 +1,440 @@
+/* $Id: fs-win.cpp $ */
+/** @file
+ * IPRT - File System, Win32.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include <iprt/win/windows.h>
+
+#include <iprt/fs.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/param.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include "internal/fs.h"
+
+/* from ntdef.h */
+typedef LONG NTSTATUS;
+
+/* from ntddk.h */
+typedef struct _IO_STATUS_BLOCK {
+ union {
+ NTSTATUS Status;
+ PVOID Pointer;
+ };
+ ULONG_PTR Information;
+} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
+
+typedef enum _FSINFOCLASS {
+ FileFsAttributeInformation = 5,
+} FS_INFORMATION_CLASS, *PFS_INFORMATION_CLASS;
+
+/* from ntifs.h */
+
+typedef struct _FILE_FS_ATTRIBUTE_INFORMATION {
+ ULONG FileSystemAttributes;
+ LONG MaximumComponentNameLength;
+ ULONG FileSystemNameLength;
+ WCHAR FileSystemName[1];
+} FILE_FS_ATTRIBUTE_INFORMATION, *PFILE_FS_ATTRIBUTE_INFORMATION;
+
+extern "C" NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE, PIO_STATUS_BLOCK, PVOID, ULONG, FS_INFORMATION_CLASS);
+
+/**
+ * Checks quickly if this is an correct root specification.
+ * Root specs ends with a slash of some kind.
+ *
+ * @returns indicator.
+ * @param pszFsPath Path to check.
+ */
+static bool rtFsIsRoot(const char *pszFsPath)
+{
+ /*
+ * UNC has exactly two slashes..
+ *
+ * Anything else starting with slashe(s) requires
+ * expansion and will have to take the long road.
+ */
+ if (RTPATH_IS_SLASH(pszFsPath[0]))
+ {
+ if ( !RTPATH_IS_SLASH(pszFsPath[1])
+ || RTPATH_IS_SLASH(pszFsPath[2]))
+ return false;
+
+ /* end of machine name */
+ const char *pszSlash = strpbrk(pszFsPath + 2, "\\/");
+ if (!pszSlash)
+ return false;
+
+ /* end of service name. */
+ pszSlash = strpbrk(pszSlash + 1, "\\/");
+ if (!pszSlash)
+ return false;
+
+ return pszSlash[1] == '\0';
+ }
+
+ /*
+ * Ok the other alternative is driver letter.
+ */
+ return pszFsPath[0] >= 'A' && pszFsPath[0] <= 'Z'
+ && pszFsPath[1] == ':'
+ && RTPATH_IS_SLASH(pszFsPath[2])
+ && !pszFsPath[3];
+}
+
+
+
+/**
+ * Finds the root of the specified volume.
+ *
+ * @returns iprt status code.
+ * @param pszFsPath Path within the filesystem. Verified as one byte or more.
+ * @param ppwszFsRoot Where to store the returned string. Free with rtFsFreeRoot(),
+ */
+static int rtFsGetRoot(const char *pszFsPath, PRTUTF16 *ppwszFsRoot)
+{
+ /*
+ * Do straight forward stuff first,
+ */
+ if (rtFsIsRoot(pszFsPath))
+ return RTStrToUtf16(pszFsPath, ppwszFsRoot);
+
+ /*
+ * Expand and add slash (if required).
+ */
+ char szFullPath[RTPATH_MAX];
+ int rc = RTPathAbs(pszFsPath, szFullPath, sizeof(szFullPath));
+ if (RT_FAILURE(rc))
+ return rc;
+ size_t cb = strlen(szFullPath);
+ if (!RTPATH_IS_SLASH(szFullPath[cb - 1]))
+ {
+ AssertReturn(cb + 1 < RTPATH_MAX, VERR_FILENAME_TOO_LONG);
+ szFullPath[cb] = '\\';
+ szFullPath[++cb] = '\0';
+ }
+
+ /*
+ * Convert the path.
+ */
+ rc = RTStrToUtf16(szFullPath, ppwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc == VERR_BUFFER_OVERFLOW ? VERR_FILENAME_TOO_LONG : rc;
+
+ /*
+ * Walk the path until our proper API is happy or there is no more path left.
+ */
+ PRTUTF16 pwszStart = *ppwszFsRoot;
+ if (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0))
+ {
+ PRTUTF16 pwszEnd = pwszStart + RTUtf16Len(pwszStart);
+ PRTUTF16 pwszMin = pwszStart + 2;
+ do
+ {
+ /* Strip off the last path component. */
+ while (pwszEnd-- > pwszMin)
+ if (RTPATH_IS_SLASH(*pwszEnd))
+ break;
+ AssertReturn(pwszEnd >= pwszMin, VERR_INTERNAL_ERROR); /* leaks, but that's irrelevant for an internal error. */
+ pwszEnd[1] = '\0';
+ } while (!GetVolumeInformationW(pwszStart, NULL, 0, NULL, NULL, 0, NULL, 0));
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Frees string returned by rtFsGetRoot().
+ */
+static void rtFsFreeRoot(PRTUTF16 pwszFsRoot)
+{
+ RTUtf16Free(pwszFsRoot);
+}
+
+
+RTR3DECL(int) RTFsQuerySizes(const char *pszFsPath, RTFOFF *pcbTotal, RTFOFF *pcbFree,
+ uint32_t *pcbBlock, uint32_t *pcbSector)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ PRTUTF16 pwszFsRoot;
+ int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Free and total.
+ */
+ if (pcbTotal || pcbFree)
+ {
+ ULARGE_INTEGER cbTotal;
+ ULARGE_INTEGER cbFree;
+ if (GetDiskFreeSpaceExW(pwszFsRoot, &cbFree, &cbTotal, NULL))
+ {
+ if (pcbTotal)
+ *pcbTotal = cbTotal.QuadPart;
+ if (pcbFree)
+ *pcbFree = cbFree.QuadPart;
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+ }
+
+ /*
+ * Block and sector size.
+ */
+ if ( RT_SUCCESS(rc)
+ && (pcbBlock || pcbSector))
+ {
+ DWORD dwDummy1, dwDummy2;
+ DWORD cbSector;
+ DWORD cSectorsPerCluster;
+ if (GetDiskFreeSpaceW(pwszFsRoot, &cSectorsPerCluster, &cbSector, &dwDummy1, &dwDummy2))
+ {
+ if (pcbBlock)
+ *pcbBlock = cbSector * cSectorsPerCluster;
+ if (pcbSector)
+ *pcbSector = cbSector;
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetDiskFreeSpace failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+ }
+
+ rtFsFreeRoot(pwszFsRoot);
+ return rc;
+}
+
+
+/**
+ * Query the serial number of a filesystem.
+ *
+ * @returns iprt status code.
+ * @param pszFsPath Path within the mounted filesystem.
+ * @param pu32Serial Where to store the serial number.
+ */
+RTR3DECL(int) RTFsQuerySerial(const char *pszFsPath, uint32_t *pu32Serial)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pu32Serial, VERR_INVALID_POINTER);
+ PRTUTF16 pwszFsRoot;
+ int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do work.
+ */
+ DWORD dwMaxName;
+ DWORD dwFlags;
+ DWORD dwSerial;
+ if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
+ *pu32Serial = dwSerial;
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetDiskFreeSpaceEx failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+
+ rtFsFreeRoot(pwszFsRoot);
+ return rc;
+}
+
+
+/**
+ * Query the properties of a mounted filesystem.
+ *
+ * @returns iprt status code.
+ * @param pszFsPath Path within the mounted filesystem.
+ * @param pProperties Where to store the properties.
+ */
+RTR3DECL(int) RTFsQueryProperties(const char *pszFsPath, PRTFSPROPERTIES pProperties)
+{
+ /*
+ * Validate & get valid root path.
+ */
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath != '\0', VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pProperties, VERR_INVALID_POINTER);
+ PRTUTF16 pwszFsRoot;
+ int rc = rtFsGetRoot(pszFsPath, &pwszFsRoot);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Do work.
+ */
+ DWORD dwMaxName;
+ DWORD dwFlags;
+ DWORD dwSerial;
+ if (GetVolumeInformationW(pwszFsRoot, NULL, 0, &dwSerial, &dwMaxName, &dwFlags, NULL, 0))
+ {
+ memset(pProperties, 0, sizeof(*pProperties));
+ pProperties->cbMaxComponent = dwMaxName;
+ pProperties->fFileCompression = !!(dwFlags & FILE_FILE_COMPRESSION);
+ pProperties->fCompressed = !!(dwFlags & FILE_VOLUME_IS_COMPRESSED);
+ pProperties->fReadOnly = !!(dwFlags & FILE_READ_ONLY_VOLUME);
+ pProperties->fSupportsUnicode = !!(dwFlags & FILE_UNICODE_ON_DISK);
+ pProperties->fCaseSensitive = false; /* win32 is case preserving only */
+ /** @todo r=bird: What about FILE_CASE_SENSITIVE_SEARCH ? Is this set for NTFS
+ * as well perchance? If so, better mention it instead of just setting
+ * fCaseSensitive to false. */
+ pProperties->fRemote = false; /* no idea yet */
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTFsQuerySizes(%s,): GetVolumeInformation failed with lasterr %d (%Rrc)\n",
+ pszFsPath, Err, rc));
+ }
+
+ rtFsFreeRoot(pwszFsRoot);
+ return rc;
+}
+
+
+RTR3DECL(bool) RTFsIsCaseSensitive(const char *pszFsPath)
+{
+ return false;
+}
+
+
+/**
+ * Internal helper for comparing a WCHAR string with a char string.
+ *
+ * @returns @c true if equal, @c false if not.
+ * @param pwsz1 The first string.
+ * @param cch1 The length of the first string, in bytes.
+ * @param psz2 The second string.
+ * @param cch2 The length of the second string.
+ */
+static bool rtFsWinAreEqual(WCHAR const *pwsz1, size_t cch1, const char *psz2, size_t cch2)
+{
+ if (cch1 != cch2 * 2)
+ return false;
+ while (cch2-- > 0)
+ {
+ unsigned ch1 = *pwsz1++;
+ unsigned ch2 = (unsigned char)*psz2++;
+ if (ch1 != ch2)
+ return false;
+ }
+ return true;
+}
+
+
+RTR3DECL(int) RTFsQueryType(const char *pszFsPath, PRTFSTYPE penmType)
+{
+ *penmType = RTFSTYPE_UNKNOWN;
+
+ AssertPtrReturn(pszFsPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszFsPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path and try open it.
+ */
+ PRTUTF16 pwszFsPath;
+ int rc = RTPathWinFromUtf8(&pwszFsPath, pszFsPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile = CreateFileW(pwszFsPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Use the NT api directly to get the file system name.
+ */
+ char abBuf[8192];
+ IO_STATUS_BLOCK Ios;
+ NTSTATUS rcNt = NtQueryVolumeInformationFile(hFile, &Ios,
+ abBuf, sizeof(abBuf),
+ FileFsAttributeInformation);
+ if (rcNt >= 0)
+ {
+ PFILE_FS_ATTRIBUTE_INFORMATION pFsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION)abBuf;
+#define IS_FS(szName) \
+ rtFsWinAreEqual(pFsAttrInfo->FileSystemName, pFsAttrInfo->FileSystemNameLength, szName, sizeof(szName) - 1)
+ if (IS_FS("NTFS"))
+ *penmType = RTFSTYPE_NTFS;
+ else if (IS_FS("FAT"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("FAT32"))
+ *penmType = RTFSTYPE_FAT;
+ else if (IS_FS("EXFAT"))
+ *penmType = RTFSTYPE_EXFAT;
+ else if (IS_FS("VBoxSharedFolderFS"))
+ *penmType = RTFSTYPE_VBOXSHF;
+#undef IS_FS
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ CloseHandle(hFile);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszFsPath);
+ }
+ return rc;
+}
diff --git a/src/VBox/Runtime/r3/win/init-win.cpp b/src/VBox/Runtime/r3/win/init-win.cpp
new file mode 100644
index 00000000..92cd2665
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/init-win.cpp
@@ -0,0 +1,919 @@
+/* $Id: init-win.cpp $ */
+/** @file
+ * IPRT - Init Ring-3, Windows Specific Code.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/nt/nt-and-windows.h>
+#ifndef LOAD_LIBRARY_SEARCH_APPLICATION_DIR
+# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x200
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 0x800
+#endif
+
+#include "internal-r3-win.h"
+#include <iprt/initterm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/param.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include "../init.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef VOID (WINAPI *PFNGETCURRENTTHREADSTACKLIMITS)(PULONG_PTR puLow, PULONG_PTR puHigh);
+typedef LPTOP_LEVEL_EXCEPTION_FILTER (WINAPI * PFNSETUNHANDLEDEXCEPTIONFILTER)(LPTOP_LEVEL_EXCEPTION_FILTER);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Windows DLL loader protection level. */
+DECL_HIDDEN_DATA(RTR3WINLDRPROT) g_enmWinLdrProt = RTR3WINLDRPROT_NONE;
+/** Our simplified windows version. */
+DECL_HIDDEN_DATA(RTWINOSTYPE) g_enmWinVer = kRTWinOSType_UNKNOWN;
+/** Extended windows version information. */
+DECL_HIDDEN_DATA(OSVERSIONINFOEXW) g_WinOsInfoEx;
+
+/** The native kernel32.dll handle. */
+DECL_HIDDEN_DATA(HMODULE) g_hModKernel32 = NULL;
+/** GetSystemWindowsDirectoryW or GetWindowsDirectoryW (NT4). */
+DECL_HIDDEN_DATA(PFNGETWINSYSDIR) g_pfnGetSystemWindowsDirectoryW = NULL;
+/** The GetCurrentThreadStackLimits API. */
+static PFNGETCURRENTTHREADSTACKLIMITS g_pfnGetCurrentThreadStackLimits = NULL;
+/** The previous unhandled exception filter. */
+static LPTOP_LEVEL_EXCEPTION_FILTER g_pfnUnhandledXcptFilter = NULL;
+/** SystemTimeToTzSpecificLocalTime. */
+DECL_HIDDEN_DATA(decltype(SystemTimeToTzSpecificLocalTime) *) g_pfnSystemTimeToTzSpecificLocalTime = NULL;
+/** CreateWaitableTimerEx . */
+DECL_HIDDEN_DATA(PFNCREATEWAITABLETIMEREX) g_pfnCreateWaitableTimerExW = NULL;
+DECL_HIDDEN_DATA(decltype(GetHandleInformation) *) g_pfnGetHandleInformation = NULL;
+DECL_HIDDEN_DATA(decltype(SetHandleInformation) *) g_pfnSetHandleInformation = NULL;
+DECL_HIDDEN_DATA(decltype(IsDebuggerPresent) *) g_pfnIsDebuggerPresent = NULL;
+DECL_HIDDEN_DATA(decltype(GetSystemTimeAsFileTime) *) g_pfnGetSystemTimeAsFileTime = NULL;
+DECL_HIDDEN_DATA(decltype(GetProcessAffinityMask) *) g_pfnGetProcessAffinityMask = NULL;
+DECL_HIDDEN_DATA(decltype(SetThreadAffinityMask) *) g_pfnSetThreadAffinityMask = NULL;
+DECL_HIDDEN_DATA(decltype(CreateIoCompletionPort) *) g_pfnCreateIoCompletionPort = NULL;
+DECL_HIDDEN_DATA(decltype(GetQueuedCompletionStatus) *) g_pfnGetQueuedCompletionStatus = NULL;
+DECL_HIDDEN_DATA(decltype(PostQueuedCompletionStatus) *) g_pfnPostQueuedCompletionStatus = NULL;
+DECL_HIDDEN_DATA(decltype(IsProcessorFeaturePresent) *) g_pfnIsProcessorFeaturePresent = NULL;
+DECL_HIDDEN_DATA(decltype(SetUnhandledExceptionFilter) *) g_pfnSetUnhandledExceptionFilter = NULL;
+DECL_HIDDEN_DATA(decltype(UnhandledExceptionFilter) *) g_pfnUnhandledExceptionFilter = NULL;
+
+/** The native ntdll.dll handle. */
+DECL_HIDDEN_DATA(HMODULE) g_hModNtDll = NULL;
+/** NtQueryFullAttributesFile */
+DECL_HIDDEN_DATA(PFNNTQUERYFULLATTRIBUTESFILE) g_pfnNtQueryFullAttributesFile = NULL;
+/** NtDuplicateToken (NT 3.51). */
+DECL_HIDDEN_DATA(PFNNTDUPLICATETOKEN) g_pfnNtDuplicateToken = NULL;
+/** NtAlertThread (NT 3.51). */
+DECL_HIDDEN_DATA(decltype(NtAlertThread) *) g_pfnNtAlertThread = NULL;
+
+/** Either ws2_32.dll (NT4+) or wsock32.dll (NT3.x). */
+DECL_HIDDEN_DATA(HMODULE) g_hModWinSock = NULL;
+/** Set if we're dealing with old winsock. */
+DECL_HIDDEN_DATA(bool) g_fOldWinSock = false;
+/** WSAStartup */
+DECL_HIDDEN_DATA(PFNWSASTARTUP) g_pfnWSAStartup = NULL;
+/** WSACleanup */
+DECL_HIDDEN_DATA(PFNWSACLEANUP) g_pfnWSACleanup = NULL;
+/** Pointner to WSAGetLastError (for RTErrVarsSave). */
+DECL_HIDDEN_DATA(PFNWSAGETLASTERROR) g_pfnWSAGetLastError = NULL;
+/** Pointner to WSASetLastError (for RTErrVarsRestore). */
+DECL_HIDDEN_DATA(PFNWSASETLASTERROR) g_pfnWSASetLastError = NULL;
+/** WSACreateEvent */
+DECL_HIDDEN_DATA(PFNWSACREATEEVENT) g_pfnWSACreateEvent = NULL;
+/** WSACloseEvent */
+DECL_HIDDEN_DATA(PFNWSACLOSEEVENT) g_pfnWSACloseEvent = NULL;
+/** WSASetEvent */
+DECL_HIDDEN_DATA(PFNWSASETEVENT) g_pfnWSASetEvent = NULL;
+/** WSAEventSelect */
+DECL_HIDDEN_DATA(PFNWSAEVENTSELECT) g_pfnWSAEventSelect = NULL;
+/** WSAEnumNetworkEvents */
+DECL_HIDDEN_DATA(PFNWSAENUMNETWORKEVENTS) g_pfnWSAEnumNetworkEvents = NULL;
+/** WSASocketW */
+DECL_HIDDEN_DATA(PFNWSASOCKETW) g_pfnWSASocketW = NULL;
+/** WSASend */
+DECL_HIDDEN_DATA(PFNWSASEND) g_pfnWSASend = NULL;
+/** socket */
+DECL_HIDDEN_DATA(PFNWINSOCKSOCKET) g_pfnsocket = NULL;
+/** closesocket */
+DECL_HIDDEN_DATA(PFNWINSOCKCLOSESOCKET) g_pfnclosesocket = NULL;
+/** recv */
+DECL_HIDDEN_DATA(PFNWINSOCKRECV) g_pfnrecv = NULL;
+/** send */
+DECL_HIDDEN_DATA(PFNWINSOCKSEND) g_pfnsend = NULL;
+/** recvfrom */
+DECL_HIDDEN_DATA(PFNWINSOCKRECVFROM) g_pfnrecvfrom = NULL;
+/** sendto */
+DECL_HIDDEN_DATA(PFNWINSOCKSENDTO) g_pfnsendto = NULL;
+/** bind */
+DECL_HIDDEN_DATA(PFNWINSOCKBIND) g_pfnbind = NULL;
+/** listen */
+DECL_HIDDEN_DATA(PFNWINSOCKLISTEN) g_pfnlisten = NULL;
+/** accept */
+DECL_HIDDEN_DATA(PFNWINSOCKACCEPT) g_pfnaccept = NULL;
+/** connect */
+DECL_HIDDEN_DATA(PFNWINSOCKCONNECT) g_pfnconnect = NULL;
+/** shutdown */
+DECL_HIDDEN_DATA(PFNWINSOCKSHUTDOWN) g_pfnshutdown = NULL;
+/** getsockopt */
+DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKOPT) g_pfngetsockopt = NULL;
+/** setsockopt */
+DECL_HIDDEN_DATA(PFNWINSOCKSETSOCKOPT) g_pfnsetsockopt = NULL;
+/** ioctlsocket */
+DECL_HIDDEN_DATA(PFNWINSOCKIOCTLSOCKET) g_pfnioctlsocket = NULL;
+/** getpeername */
+DECL_HIDDEN_DATA(PFNWINSOCKGETPEERNAME) g_pfngetpeername = NULL;
+/** getsockname */
+DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKNAME) g_pfngetsockname = NULL;
+/** __WSAFDIsSet */
+DECL_HIDDEN_DATA(PFNWINSOCK__WSAFDISSET) g_pfn__WSAFDIsSet = NULL;
+/** select */
+DECL_HIDDEN_DATA(PFNWINSOCKSELECT) g_pfnselect = NULL;
+/** gethostbyname */
+DECL_HIDDEN_DATA(PFNWINSOCKGETHOSTBYNAME) g_pfngethostbyname = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static LONG CALLBACK rtR3WinUnhandledXcptFilter(PEXCEPTION_POINTERS);
+
+
+/**
+ * Translates OSVERSIONINOFEX into a Windows OS type.
+ *
+ * @returns The Windows OS type.
+ * @param pOSInfoEx The OS info returned by Windows.
+ *
+ * @remarks This table has been assembled from Usenet postings, personal
+ * observations, and reading other people's code. Please feel
+ * free to add to it or correct it.
+ * <pre>
+ dwPlatFormID dwMajorVersion dwMinorVersion dwBuildNumber
+95 1 4 0 950
+95 SP1 1 4 0 >950 && <=1080
+95 OSR2 1 4 <10 >1080
+98 1 4 10 1998
+98 SP1 1 4 10 >1998 && <2183
+98 SE 1 4 10 >=2183
+ME 1 4 90 3000
+
+NT 3.51 2 3 51 1057
+NT 4 2 4 0 1381
+2000 2 5 0 2195
+XP 2 5 1 2600
+2003 2 5 2 3790
+Vista 2 6 0
+
+CE 1.0 3 1 0
+CE 2.0 3 2 0
+CE 2.1 3 2 1
+CE 3.0 3 3 0
+</pre>
+ */
+static RTWINOSTYPE rtR3InitWinSimplifiedVersion(OSVERSIONINFOEXW const *pOSInfoEx)
+{
+ RTWINOSTYPE enmVer = kRTWinOSType_UNKNOWN;
+ BYTE const bProductType = pOSInfoEx->wProductType;
+ DWORD const dwPlatformId = pOSInfoEx->dwPlatformId;
+ DWORD const dwMinorVersion = pOSInfoEx->dwMinorVersion;
+ DWORD const dwMajorVersion = pOSInfoEx->dwMajorVersion;
+ DWORD const dwBuildNumber = pOSInfoEx->dwBuildNumber & 0xFFFF; /* Win 9x needs this. */
+
+ if ( dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
+ && dwMajorVersion == 4)
+ {
+ if ( dwMinorVersion < 10
+ && dwBuildNumber == 950)
+ enmVer = kRTWinOSType_95;
+ else if ( dwMinorVersion < 10
+ && dwBuildNumber > 950
+ && dwBuildNumber <= 1080)
+ enmVer = kRTWinOSType_95SP1;
+ else if ( dwMinorVersion < 10
+ && dwBuildNumber > 1080)
+ enmVer = kRTWinOSType_95OSR2;
+ else if ( dwMinorVersion == 10
+ && dwBuildNumber == 1998)
+ enmVer = kRTWinOSType_98;
+ else if ( dwMinorVersion == 10
+ && dwBuildNumber > 1998
+ && dwBuildNumber < 2183)
+ enmVer = kRTWinOSType_98SP1;
+ else if ( dwMinorVersion == 10
+ && dwBuildNumber >= 2183)
+ enmVer = kRTWinOSType_98SE;
+ else if (dwMinorVersion == 90)
+ enmVer = kRTWinOSType_ME;
+ }
+ else if (dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ if (dwMajorVersion == 3)
+ {
+ if ( dwMinorVersion < 50)
+ enmVer = kRTWinOSType_NT310;
+ else if (dwMinorVersion == 50)
+ enmVer = kRTWinOSType_NT350;
+ else
+ enmVer = kRTWinOSType_NT351;
+ }
+ else if (dwMajorVersion == 4)
+ enmVer = kRTWinOSType_NT4;
+ else if (dwMajorVersion == 5)
+ {
+ if (dwMinorVersion == 0)
+ enmVer = kRTWinOSType_2K;
+ else if (dwMinorVersion == 1)
+ enmVer = kRTWinOSType_XP;
+ else
+ enmVer = kRTWinOSType_2003;
+ }
+ else if (dwMajorVersion == 6)
+ {
+ if (dwMinorVersion == 0)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2008 : kRTWinOSType_VISTA;
+ else if (dwMinorVersion == 1)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2008R2 : kRTWinOSType_7;
+ else if (dwMinorVersion == 2)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2012 : kRTWinOSType_8;
+ else if (dwMinorVersion == 3)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2012R2 : kRTWinOSType_81;
+ else if (dwMinorVersion == 4)
+ enmVer = bProductType != VER_NT_WORKSTATION ? kRTWinOSType_2016 : kRTWinOSType_10;
+ else
+ enmVer = kRTWinOSType_NT_UNKNOWN;
+ }
+ else if (dwMajorVersion == 10)
+ {
+ if (dwMinorVersion == 0)
+ {
+ /* The version detection for server 2019, server 2022 and windows 11
+ are by build number. Stupid, stupid, Microsoft. */
+ if (bProductType == VER_NT_WORKSTATION)
+ enmVer = dwBuildNumber >= 22000 ? kRTWinOSType_11 : kRTWinOSType_10;
+ else
+ enmVer = dwBuildNumber >= 20348 ? kRTWinOSType_2022
+ : dwBuildNumber >= 17763 ? kRTWinOSType_2019 : kRTWinOSType_2016;
+ }
+ else
+ enmVer = kRTWinOSType_NT_UNKNOWN;
+ }
+ else
+ enmVer = kRTWinOSType_NT_UNKNOWN;
+ }
+
+ return enmVer;
+}
+
+
+/**
+ * Initializes the global variables related to windows version.
+ */
+static void rtR3InitWindowsVersion(void)
+{
+ Assert(g_hModNtDll != NULL);
+
+ /*
+ * ASSUMES OSVERSIONINFOEX starts with the exact same layout as OSVERSIONINFO (safe).
+ */
+ AssertCompileMembersSameSizeAndOffset(OSVERSIONINFOEX, szCSDVersion, OSVERSIONINFO, szCSDVersion);
+ AssertCompileMemberOffset(OSVERSIONINFOEX, wServicePackMajor, sizeof(OSVERSIONINFO));
+
+ /*
+ * Use the NT version of RtlGetVersion (since w2k) so we don't get fooled
+ * by the standard compatibility shims. (Sandboxes may still fool us.)
+ *
+ * Note! This API was added in windows 2000 together with the extended
+ * version info structure (OSVERSIONINFOEXW), so there is no need
+ * to retry with the smaller version (OSVERSIONINFOW).
+ */
+ RT_ZERO(g_WinOsInfoEx);
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+ LONG (__stdcall *pfnRtlGetVersion)(OSVERSIONINFOEXW *);
+ *(FARPROC *)&pfnRtlGetVersion = GetProcAddress(g_hModNtDll, "RtlGetVersion");
+ LONG rcNt = -1;
+ if (pfnRtlGetVersion)
+ rcNt = pfnRtlGetVersion(&g_WinOsInfoEx);
+ if (rcNt != 0)
+ {
+ /*
+ * Couldn't find it or it failed, try the windows version of the API.
+ * The GetVersionExW API was added in NT 3.51, however only the small
+ * structure version existed till windows 2000. We'll try the larger
+ * structure version first, anyway, just in case.
+ */
+ RT_ZERO(g_WinOsInfoEx);
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
+
+ BOOL (__stdcall *pfnGetVersionExW)(OSVERSIONINFOW *);
+ *(FARPROC *)&pfnGetVersionExW = GetProcAddress(g_hModKernel32, "GetVersionExW");
+
+ if (!pfnGetVersionExW || !pfnGetVersionExW((POSVERSIONINFOW)&g_WinOsInfoEx))
+ {
+ /*
+ * If that didn't work either, just get the basic version bits.
+ */
+ RT_ZERO(g_WinOsInfoEx);
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
+ if (pfnGetVersionExW && pfnGetVersionExW((POSVERSIONINFOW)&g_WinOsInfoEx))
+ Assert(g_WinOsInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT || g_WinOsInfoEx.dwMajorVersion < 5);
+ else
+ {
+ /*
+ * Okay, nothing worked, so use GetVersion.
+ *
+ * This should only happen if we're on NT 3.1 or NT 3.50.
+ * It should never happen for 64-bit builds.
+ */
+#ifdef RT_ARCH_X86
+ RT_ZERO(g_WinOsInfoEx);
+ DWORD const dwVersion = GetVersion();
+
+ /* Common fields: */
+ g_WinOsInfoEx.dwMajorVersion = dwVersion & 0xff;
+ g_WinOsInfoEx.dwMinorVersion = (dwVersion >> 8) & 0xff;
+ if (!(dwVersion & RT_BIT_32(31)))
+ g_WinOsInfoEx.dwBuildNumber = dwVersion >> 16;
+ else
+ g_WinOsInfoEx.dwBuildNumber = 511;
+ g_WinOsInfoEx.dwPlatformId = VER_PLATFORM_WIN32_NT;
+ /** @todo get CSD from registry. */
+#else
+ AssertBreakpoint();
+ RT_ZERO(g_WinOsInfoEx);
+#endif
+ }
+
+#ifdef RT_ARCH_X86
+ /*
+ * Fill in some of the extended info too.
+ */
+ g_WinOsInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW); /* Pretend. */
+ g_WinOsInfoEx.wProductType = VER_NT_WORKSTATION;
+ NT_PRODUCT_TYPE enmProdType = NtProductWinNt;
+ if (RtlGetNtProductType(&enmProdType))
+ g_WinOsInfoEx.wProductType = (BYTE)enmProdType;
+ /** @todo parse the CSD string to figure that version. */
+#endif
+ }
+ }
+
+ if (g_WinOsInfoEx.dwOSVersionInfoSize)
+ g_enmWinVer = rtR3InitWinSimplifiedVersion(&g_WinOsInfoEx);
+}
+
+
+/**
+ * Resolves the winsock error APIs.
+ */
+static void rtR3InitWinSockApis(void)
+{
+ /*
+ * Try get ws2_32.dll, then try load it, then finally fall back to the old
+ * wsock32.dll. We use RTLdrLoadSystem to the loading as it has all the fancy
+ * logic for safely doing that.
+ */
+ g_hModWinSock = GetModuleHandleW(L"ws2_32.dll");
+ if (g_hModWinSock == NULL)
+ {
+ RTLDRMOD hLdrMod;
+ int rc = RTLdrLoadSystem("ws2_32.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_FAILURE(rc))
+ {
+ rc = RTLdrLoadSystem("wsock32.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("rc=%Rrc\n", rc));
+ return;
+ }
+ g_fOldWinSock = true;
+ }
+ g_hModWinSock = (HMODULE)RTLdrGetNativeHandle(hLdrMod);
+ RTLdrClose(hLdrMod);
+ }
+
+ g_pfnWSAStartup = (decltype(g_pfnWSAStartup)) GetProcAddress(g_hModWinSock, "WSAStartup");
+ g_pfnWSACleanup = (decltype(g_pfnWSACleanup)) GetProcAddress(g_hModWinSock, "WSACleanup");
+ g_pfnWSAGetLastError = (decltype(g_pfnWSAGetLastError)) GetProcAddress(g_hModWinSock, "WSAGetLastError");
+ g_pfnWSASetLastError = (decltype(g_pfnWSASetLastError)) GetProcAddress(g_hModWinSock, "WSASetLastError");
+ g_pfnWSACreateEvent = (decltype(g_pfnWSACreateEvent)) GetProcAddress(g_hModWinSock, "WSACreateEvent");
+ g_pfnWSACloseEvent = (decltype(g_pfnWSACloseEvent)) GetProcAddress(g_hModWinSock, "WSACloseEvent");
+ g_pfnWSASetEvent = (decltype(g_pfnWSASetEvent)) GetProcAddress(g_hModWinSock, "WSASetEvent");
+ g_pfnWSAEventSelect = (decltype(g_pfnWSAEventSelect)) GetProcAddress(g_hModWinSock, "WSAEventSelect");
+ g_pfnWSAEnumNetworkEvents = (decltype(g_pfnWSAEnumNetworkEvents))GetProcAddress(g_hModWinSock,"WSAEnumNetworkEvents");
+ g_pfnWSASocketW = (decltype(g_pfnWSASocketW)) GetProcAddress(g_hModWinSock, "WSASocketW");
+ g_pfnWSASend = (decltype(g_pfnWSASend)) GetProcAddress(g_hModWinSock, "WSASend");
+ g_pfnsocket = (decltype(g_pfnsocket)) GetProcAddress(g_hModWinSock, "socket");
+ g_pfnclosesocket = (decltype(g_pfnclosesocket)) GetProcAddress(g_hModWinSock, "closesocket");
+ g_pfnrecv = (decltype(g_pfnrecv)) GetProcAddress(g_hModWinSock, "recv");
+ g_pfnsend = (decltype(g_pfnsend)) GetProcAddress(g_hModWinSock, "send");
+ g_pfnrecvfrom = (decltype(g_pfnrecvfrom)) GetProcAddress(g_hModWinSock, "recvfrom");
+ g_pfnsendto = (decltype(g_pfnsendto)) GetProcAddress(g_hModWinSock, "sendto");
+ g_pfnbind = (decltype(g_pfnbind)) GetProcAddress(g_hModWinSock, "bind");
+ g_pfnlisten = (decltype(g_pfnlisten)) GetProcAddress(g_hModWinSock, "listen");
+ g_pfnaccept = (decltype(g_pfnaccept)) GetProcAddress(g_hModWinSock, "accept");
+ g_pfnconnect = (decltype(g_pfnconnect)) GetProcAddress(g_hModWinSock, "connect");
+ g_pfnshutdown = (decltype(g_pfnshutdown)) GetProcAddress(g_hModWinSock, "shutdown");
+ g_pfngetsockopt = (decltype(g_pfngetsockopt)) GetProcAddress(g_hModWinSock, "getsockopt");
+ g_pfnsetsockopt = (decltype(g_pfnsetsockopt)) GetProcAddress(g_hModWinSock, "setsockopt");
+ g_pfnioctlsocket = (decltype(g_pfnioctlsocket)) GetProcAddress(g_hModWinSock, "ioctlsocket");
+ g_pfngetpeername = (decltype(g_pfngetpeername)) GetProcAddress(g_hModWinSock, "getpeername");
+ g_pfngetsockname = (decltype(g_pfngetsockname)) GetProcAddress(g_hModWinSock, "getsockname");
+ g_pfn__WSAFDIsSet = (decltype(g_pfn__WSAFDIsSet)) GetProcAddress(g_hModWinSock, "__WSAFDIsSet");
+ g_pfnselect = (decltype(g_pfnselect)) GetProcAddress(g_hModWinSock, "select");
+ g_pfngethostbyname = (decltype(g_pfngethostbyname)) GetProcAddress(g_hModWinSock, "gethostbyname");
+
+ Assert(g_pfnWSAStartup);
+ Assert(g_pfnWSACleanup);
+ Assert(g_pfnWSAGetLastError);
+ Assert(g_pfnWSASetLastError);
+ Assert(g_pfnWSACreateEvent || g_fOldWinSock);
+ Assert(g_pfnWSACloseEvent || g_fOldWinSock);
+ Assert(g_pfnWSASetEvent || g_fOldWinSock);
+ Assert(g_pfnWSAEventSelect || g_fOldWinSock);
+ Assert(g_pfnWSAEnumNetworkEvents || g_fOldWinSock);
+ Assert(g_pfnWSASocketW || g_fOldWinSock);
+ Assert(g_pfnWSASend || g_fOldWinSock);
+ Assert(g_pfnsocket);
+ Assert(g_pfnclosesocket);
+ Assert(g_pfnrecv);
+ Assert(g_pfnsend);
+ Assert(g_pfnrecvfrom);
+ Assert(g_pfnsendto);
+ Assert(g_pfnbind);
+ Assert(g_pfnlisten);
+ Assert(g_pfnaccept);
+ Assert(g_pfnconnect);
+ Assert(g_pfnshutdown);
+ Assert(g_pfngetsockopt);
+ Assert(g_pfnsetsockopt);
+ Assert(g_pfnioctlsocket);
+ Assert(g_pfngetpeername);
+ Assert(g_pfngetsockname);
+ Assert(g_pfn__WSAFDIsSet);
+ Assert(g_pfnselect);
+ Assert(g_pfngethostbyname);
+}
+
+
+static int rtR3InitNativeObtrusiveWorker(uint32_t fFlags)
+{
+ /*
+ * Disable error popups.
+ */
+ UINT fOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
+ SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | fOldErrMode);
+
+ /*
+ * Restrict DLL searching for the process on windows versions which allow
+ * us to do so.
+ * - The first trick works on XP SP1+ and disables the searching of the
+ * current directory.
+ * - The second trick is W7 w/ KB2533623 and W8+, it restrict the DLL
+ * searching to the application directory (except when
+ * RTR3INIT_FLAGS_STANDALONE_APP is given) and the System32 directory.
+ */
+ int rc = VINF_SUCCESS;
+
+ typedef BOOL (WINAPI *PFNSETDLLDIRECTORY)(LPCWSTR);
+ PFNSETDLLDIRECTORY pfnSetDllDir = (PFNSETDLLDIRECTORY)GetProcAddress(g_hModKernel32, "SetDllDirectoryW");
+ if (pfnSetDllDir)
+ {
+ if (pfnSetDllDir(L""))
+ g_enmWinLdrProt = RTR3WINLDRPROT_NO_CWD;
+ else
+ rc = VERR_INTERNAL_ERROR_3;
+ }
+
+ /** @bugref{6861} Observed GUI issues on Vista (32-bit and 64-bit) when using
+ * SetDefaultDllDirectories.
+ * @bugref{8194} Try use SetDefaultDllDirectories on Vista for standalone apps
+ * despite potential GUI issues. */
+ if ( g_enmWinVer > kRTWinOSType_VISTA
+ || (fFlags & RTR3INIT_FLAGS_STANDALONE_APP))
+ {
+ typedef BOOL(WINAPI *PFNSETDEFAULTDLLDIRECTORIES)(DWORD);
+ PFNSETDEFAULTDLLDIRECTORIES pfnSetDefDllDirs;
+ pfnSetDefDllDirs = (PFNSETDEFAULTDLLDIRECTORIES)GetProcAddress(g_hModKernel32, "SetDefaultDllDirectories");
+ if (pfnSetDefDllDirs)
+ {
+ DWORD fDllDirs = LOAD_LIBRARY_SEARCH_SYSTEM32;
+ if (!(fFlags & RTR3INIT_FLAGS_STANDALONE_APP))
+ fDllDirs |= LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
+ if (pfnSetDefDllDirs(fDllDirs))
+ g_enmWinLdrProt = fDllDirs & LOAD_LIBRARY_SEARCH_APPLICATION_DIR ? RTR3WINLDRPROT_SAFE : RTR3WINLDRPROT_SAFER;
+ else if (RT_SUCCESS(rc))
+ rc = VERR_INTERNAL_ERROR_4;
+ }
+ }
+
+ /*
+ * Register an unhandled exception callback if we can.
+ */
+ g_pfnGetCurrentThreadStackLimits = (PFNGETCURRENTTHREADSTACKLIMITS)GetProcAddress(g_hModKernel32, "GetCurrentThreadStackLimits");
+ g_pfnSetUnhandledExceptionFilter = (decltype(SetUnhandledExceptionFilter) *)GetProcAddress(g_hModKernel32, "SetUnhandledExceptionFilter");
+ g_pfnUnhandledExceptionFilter = (decltype(UnhandledExceptionFilter) *) GetProcAddress(g_hModKernel32, "UnhandledExceptionFilter");
+ if (g_pfnSetUnhandledExceptionFilter && !g_pfnUnhandledXcptFilter)
+ {
+ g_pfnUnhandledXcptFilter = g_pfnSetUnhandledExceptionFilter(rtR3WinUnhandledXcptFilter);
+ AssertStmt(g_pfnUnhandledXcptFilter != rtR3WinUnhandledXcptFilter, g_pfnUnhandledXcptFilter = NULL);
+ }
+
+ return rc;
+}
+
+
+DECLHIDDEN(int) rtR3InitNativeFirst(uint32_t fFlags)
+{
+ /*
+ * Make sure we've got the handles of the two main Windows NT dlls.
+ */
+ g_hModKernel32 = GetModuleHandleW(L"kernel32.dll");
+ if (g_hModKernel32 == NULL)
+ return VERR_INTERNAL_ERROR_2;
+ g_hModNtDll = GetModuleHandleW(L"ntdll.dll");
+ if (g_hModNtDll == NULL)
+ return VERR_INTERNAL_ERROR_2;
+
+ rtR3InitWindowsVersion();
+
+ int rc = VINF_SUCCESS;
+ if (!(fFlags & RTR3INIT_FLAGS_UNOBTRUSIVE))
+ rc = rtR3InitNativeObtrusiveWorker(fFlags);
+
+ /*
+ * Resolve some kernel32.dll APIs we may need but aren't necessarily
+ * present in older windows versions.
+ */
+ g_pfnGetSystemWindowsDirectoryW = (PFNGETWINSYSDIR)GetProcAddress(g_hModKernel32, "GetSystemWindowsDirectoryW");
+ if (g_pfnGetSystemWindowsDirectoryW)
+ g_pfnGetSystemWindowsDirectoryW = (PFNGETWINSYSDIR)GetProcAddress(g_hModKernel32, "GetWindowsDirectoryW");
+ g_pfnSystemTimeToTzSpecificLocalTime = (decltype(SystemTimeToTzSpecificLocalTime) *)GetProcAddress(g_hModKernel32, "SystemTimeToTzSpecificLocalTime");
+ g_pfnCreateWaitableTimerExW = (PFNCREATEWAITABLETIMEREX) GetProcAddress(g_hModKernel32, "CreateWaitableTimerExW");
+ g_pfnGetHandleInformation = (decltype(GetHandleInformation) *) GetProcAddress(g_hModKernel32, "GetHandleInformation");
+ g_pfnSetHandleInformation = (decltype(SetHandleInformation) *) GetProcAddress(g_hModKernel32, "SetHandleInformation");
+ g_pfnIsDebuggerPresent = (decltype(IsDebuggerPresent) *) GetProcAddress(g_hModKernel32, "IsDebuggerPresent");
+ g_pfnGetSystemTimeAsFileTime = (decltype(GetSystemTimeAsFileTime) *) GetProcAddress(g_hModKernel32, "GetSystemTimeAsFileTime");
+ g_pfnGetProcessAffinityMask = (decltype(GetProcessAffinityMask) *) GetProcAddress(g_hModKernel32, "GetProcessAffinityMask");
+ g_pfnSetThreadAffinityMask = (decltype(SetThreadAffinityMask) *) GetProcAddress(g_hModKernel32, "SetThreadAffinityMask");
+ g_pfnCreateIoCompletionPort = (decltype(CreateIoCompletionPort) *) GetProcAddress(g_hModKernel32, "CreateIoCompletionPort");
+ g_pfnGetQueuedCompletionStatus = (decltype(GetQueuedCompletionStatus) *) GetProcAddress(g_hModKernel32, "GetQueuedCompletionStatus");
+ g_pfnPostQueuedCompletionStatus = (decltype(PostQueuedCompletionStatus) *)GetProcAddress(g_hModKernel32, "PostQueuedCompletionStatus");
+ g_pfnIsProcessorFeaturePresent = (decltype(IsProcessorFeaturePresent) *) GetProcAddress(g_hModKernel32, "IsProcessorFeaturePresent");
+
+ Assert(g_pfnGetHandleInformation || g_enmWinVer < kRTWinOSType_NT351);
+ Assert(g_pfnSetHandleInformation || g_enmWinVer < kRTWinOSType_NT351);
+ Assert(g_pfnIsDebuggerPresent || g_enmWinVer < kRTWinOSType_NT4);
+ Assert(g_pfnGetSystemTimeAsFileTime || g_enmWinVer < kRTWinOSType_NT4);
+ Assert(g_pfnGetProcessAffinityMask || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnSetThreadAffinityMask || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnCreateIoCompletionPort || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnGetQueuedCompletionStatus || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnPostQueuedCompletionStatus || g_enmWinVer < kRTWinOSType_NT350);
+ Assert(g_pfnIsProcessorFeaturePresent || g_enmWinVer < kRTWinOSType_NT4);
+
+ /*
+ * Resolve some ntdll.dll APIs that weren't there in early NT versions.
+ */
+ g_pfnNtQueryFullAttributesFile = (PFNNTQUERYFULLATTRIBUTESFILE)GetProcAddress(g_hModNtDll, "NtQueryFullAttributesFile");
+ g_pfnNtDuplicateToken = (PFNNTDUPLICATETOKEN)GetProcAddress( g_hModNtDll, "NtDuplicateToken");
+ g_pfnNtAlertThread = (decltype(NtAlertThread) *)GetProcAddress( g_hModNtDll, "NtAlertThread");
+
+ /*
+ * Resolve the winsock error getter and setter so assertions can save those too.
+ */
+ rtR3InitWinSockApis();
+
+ return rc;
+}
+
+
+DECLHIDDEN(void) rtR3InitNativeObtrusive(uint32_t fFlags)
+{
+ rtR3InitNativeObtrusiveWorker(fFlags);
+}
+
+
+DECLHIDDEN(int) rtR3InitNativeFinal(uint32_t fFlags)
+{
+ /* Nothing to do here. */
+ RT_NOREF_PV(fFlags);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unhandled exception filter callback.
+ *
+ * Will try log stuff.
+ */
+static LONG CALLBACK rtR3WinUnhandledXcptFilter(PEXCEPTION_POINTERS pPtrs)
+{
+ /*
+ * Try get the logger and log exception details.
+ *
+ * Note! We'll be using RTLogLoggerWeak for now, though we should probably add
+ * a less deadlock prone API here and gives up pretty fast if it
+ * cannot get the lock...
+ */
+ PRTLOGGER pLogger = RTLogRelGetDefaultInstanceWeak();
+ if (!pLogger)
+ pLogger = RTLogGetDefaultInstanceWeak();
+ if (pLogger)
+ {
+ RTLogLoggerWeak(pLogger, NULL, "\n!!! rtR3WinUnhandledXcptFilter caught an exception on thread %p in %u !!!\n",
+ RTThreadNativeSelf(), RTProcSelf());
+
+ /*
+ * Dump the exception record.
+ */
+ uintptr_t uXcptPC = 0;
+ PEXCEPTION_RECORD pXcptRec = RT_VALID_PTR(pPtrs) && RT_VALID_PTR(pPtrs->ExceptionRecord) ? pPtrs->ExceptionRecord : NULL;
+ if (pXcptRec)
+ {
+ RTLogLoggerWeak(pLogger, NULL, "\nExceptionCode=%#010x ExceptionFlags=%#010x ExceptionAddress=%p\n",
+ pXcptRec->ExceptionCode, pXcptRec->ExceptionFlags, pXcptRec->ExceptionAddress);
+ for (uint32_t i = 0; i < RT_MIN(pXcptRec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS); i++)
+ RTLogLoggerWeak(pLogger, NULL, "ExceptionInformation[%d]=%p\n", i, pXcptRec->ExceptionInformation[i]);
+ uXcptPC = (uintptr_t)pXcptRec->ExceptionAddress;
+
+ /* Nested? Display one level only. */
+ PEXCEPTION_RECORD pNestedRec = pXcptRec->ExceptionRecord;
+ if (RT_VALID_PTR(pNestedRec))
+ {
+ RTLogLoggerWeak(pLogger, NULL, "Nested: ExceptionCode=%#010x ExceptionFlags=%#010x ExceptionAddress=%p (nested %p)\n",
+ pNestedRec->ExceptionCode, pNestedRec->ExceptionFlags, pNestedRec->ExceptionAddress,
+ pNestedRec->ExceptionRecord);
+ for (uint32_t i = 0; i < RT_MIN(pNestedRec->NumberParameters, EXCEPTION_MAXIMUM_PARAMETERS); i++)
+ RTLogLoggerWeak(pLogger, NULL, "Nested: ExceptionInformation[%d]=%p\n", i, pNestedRec->ExceptionInformation[i]);
+ uXcptPC = (uintptr_t)pNestedRec->ExceptionAddress;
+ }
+ }
+
+ /*
+ * Dump the context record.
+ */
+ volatile char szMarker[] = "stackmarker";
+ uintptr_t uXcptSP = (uintptr_t)&szMarker[0];
+ PCONTEXT pXcptCtx = RT_VALID_PTR(pPtrs) && RT_VALID_PTR(pPtrs->ContextRecord) ? pPtrs->ContextRecord : NULL;
+ if (pXcptCtx)
+ {
+#ifdef RT_ARCH_AMD64
+ RTLogLoggerWeak(pLogger, NULL, "\ncs:rip=%04x:%016RX64\n", pXcptCtx->SegCs, pXcptCtx->Rip);
+ RTLogLoggerWeak(pLogger, NULL, "ss:rsp=%04x:%016RX64 rbp=%016RX64\n", pXcptCtx->SegSs, pXcptCtx->Rsp, pXcptCtx->Rbp);
+ RTLogLoggerWeak(pLogger, NULL, "rax=%016RX64 rcx=%016RX64 rdx=%016RX64 rbx=%016RX64\n",
+ pXcptCtx->Rax, pXcptCtx->Rcx, pXcptCtx->Rdx, pXcptCtx->Rbx);
+ RTLogLoggerWeak(pLogger, NULL, "rsi=%016RX64 rdi=%016RX64 rsp=%016RX64 rbp=%016RX64\n",
+ pXcptCtx->Rsi, pXcptCtx->Rdi, pXcptCtx->Rsp, pXcptCtx->Rbp);
+ RTLogLoggerWeak(pLogger, NULL, "r8 =%016RX64 r9 =%016RX64 r10=%016RX64 r11=%016RX64\n",
+ pXcptCtx->R8, pXcptCtx->R9, pXcptCtx->R10, pXcptCtx->R11);
+ RTLogLoggerWeak(pLogger, NULL, "r12=%016RX64 r13=%016RX64 r14=%016RX64 r15=%016RX64\n",
+ pXcptCtx->R12, pXcptCtx->R13, pXcptCtx->R14, pXcptCtx->R15);
+ RTLogLoggerWeak(pLogger, NULL, "ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n",
+ pXcptCtx->SegDs, pXcptCtx->SegEs, pXcptCtx->SegFs, pXcptCtx->SegGs, pXcptCtx->EFlags);
+ RTLogLoggerWeak(pLogger, NULL, "p1home=%016RX64 p2home=%016RX64 pe3home=%016RX64\n",
+ pXcptCtx->P1Home, pXcptCtx->P2Home, pXcptCtx->P3Home);
+ RTLogLoggerWeak(pLogger, NULL, "p4home=%016RX64 p5home=%016RX64 pe6home=%016RX64\n",
+ pXcptCtx->P4Home, pXcptCtx->P5Home, pXcptCtx->P6Home);
+ RTLogLoggerWeak(pLogger, NULL, " LastBranchToRip=%016RX64 LastBranchFromRip=%016RX64\n",
+ pXcptCtx->LastBranchToRip, pXcptCtx->LastBranchFromRip);
+ RTLogLoggerWeak(pLogger, NULL, "LastExceptionToRip=%016RX64 LastExceptionFromRip=%016RX64\n",
+ pXcptCtx->LastExceptionToRip, pXcptCtx->LastExceptionFromRip);
+ uXcptSP = pXcptCtx->Rsp;
+ uXcptPC = pXcptCtx->Rip;
+
+#elif defined(RT_ARCH_X86)
+ RTLogLoggerWeak(pLogger, NULL, "\ncs:eip=%04x:%08RX32\n", pXcptCtx->SegCs, pXcptCtx->Eip);
+ RTLogLoggerWeak(pLogger, NULL, "ss:esp=%04x:%08RX32 ebp=%08RX32\n", pXcptCtx->SegSs, pXcptCtx->Esp, pXcptCtx->Ebp);
+ RTLogLoggerWeak(pLogger, NULL, "eax=%08RX32 ecx=%08RX32 edx=%08RX32 ebx=%08RX32\n",
+ pXcptCtx->Eax, pXcptCtx->Ecx, pXcptCtx->Edx, pXcptCtx->Ebx);
+ RTLogLoggerWeak(pLogger, NULL, "esi=%08RX32 edi=%08RX32 esp=%08RX32 ebp=%08RX32\n",
+ pXcptCtx->Esi, pXcptCtx->Edi, pXcptCtx->Esp, pXcptCtx->Ebp);
+ RTLogLoggerWeak(pLogger, NULL, "ds=%04x es=%04x fs=%04x gs=%04x eflags=%08x\n",
+ pXcptCtx->SegDs, pXcptCtx->SegEs, pXcptCtx->SegFs, pXcptCtx->SegGs, pXcptCtx->EFlags);
+ uXcptSP = pXcptCtx->Esp;
+ uXcptPC = pXcptCtx->Eip;
+#endif
+ }
+
+ /*
+ * Dump stack.
+ */
+ uintptr_t uStack = (uintptr_t)(void *)&szMarker[0];
+ uStack -= uStack & 15;
+
+ size_t cbToDump = PAGE_SIZE - (uStack & PAGE_OFFSET_MASK);
+ if (cbToDump < 512)
+ cbToDump += PAGE_SIZE;
+ size_t cbToXcpt = uXcptSP - uStack;
+ while (cbToXcpt > cbToDump && cbToXcpt <= _16K)
+ cbToDump += PAGE_SIZE;
+ ULONG_PTR uLow = (uintptr_t)&szMarker[0];
+ ULONG_PTR uHigh = (uintptr_t)&szMarker[0];
+ if (g_pfnGetCurrentThreadStackLimits)
+ {
+ g_pfnGetCurrentThreadStackLimits(&uLow, &uHigh);
+ size_t cbToTop = RT_MAX(uLow, uHigh) - uStack;
+ if (cbToTop < _1M)
+ cbToDump = cbToTop;
+ }
+
+ RTLogLoggerWeak(pLogger, NULL, "\nStack %p, dumping %#x bytes (low=%p, high=%p)\n", uStack, cbToDump, uLow, uHigh);
+ RTLogLoggerWeak(pLogger, NULL, "%.*RhxD\n", cbToDump, uStack);
+
+ /*
+ * Try figure the thread name.
+ *
+ * Note! This involves the thread db lock, so it may deadlock, which
+ * is why it's at the end.
+ */
+ RTLogLoggerWeak(pLogger, NULL, "Thread ID: %p\n", RTThreadNativeSelf());
+ RTLogLoggerWeak(pLogger, NULL, "Thread name: %s\n", RTThreadSelfName());
+ RTLogLoggerWeak(pLogger, NULL, "Thread IPRT: %p\n", RTThreadSelf());
+
+ /*
+ * Try dump the load information.
+ */
+ PPEB pPeb = RTNtCurrentPeb();
+ if (RT_VALID_PTR(pPeb))
+ {
+ PPEB_LDR_DATA pLdrData = pPeb->Ldr;
+ if (RT_VALID_PTR(pLdrData))
+ {
+ PLDR_DATA_TABLE_ENTRY pFound = NULL;
+ LIST_ENTRY * const pList = &pLdrData->InMemoryOrderModuleList;
+ LIST_ENTRY *pListEntry = pList->Flink;
+ uint32_t cLoops = 0;
+ RTLogLoggerWeak(pLogger, NULL,
+ "\nLoaded Modules:\n"
+ "%-*s[*] Timestamp Path\n", sizeof(void *) * 4 + 2 - 1, "Address range"
+ );
+ while (pListEntry != pList && RT_VALID_PTR(pListEntry) && cLoops < 1024)
+ {
+ PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+ uint32_t const cbLength = (uint32_t)(uintptr_t)pLdrEntry->Reserved3[1];
+ char chInd = ' ';
+ if (uXcptPC - (uintptr_t)pLdrEntry->DllBase < cbLength)
+ {
+ chInd = '*';
+ pFound = pLdrEntry;
+ }
+
+ if ( RT_VALID_PTR(pLdrEntry->FullDllName.Buffer)
+ && pLdrEntry->FullDllName.Length > 0
+ && pLdrEntry->FullDllName.Length < _8K
+ && (pLdrEntry->FullDllName.Length & 1) == 0
+ && pLdrEntry->FullDllName.Length <= pLdrEntry->FullDllName.MaximumLength)
+ RTLogLoggerWeak(pLogger, NULL, "%p..%p%c %08RX32 %.*ls\n",
+ pLdrEntry->DllBase, (uintptr_t)pLdrEntry->DllBase + cbLength - 1, chInd,
+ pLdrEntry->TimeDateStamp, pLdrEntry->FullDllName.Length / sizeof(RTUTF16),
+ pLdrEntry->FullDllName.Buffer);
+ else
+ RTLogLoggerWeak(pLogger, NULL, "%p..%p%c %08RX32 <bad or missing: %p LB %#x max %#x\n",
+ pLdrEntry->DllBase, (uintptr_t)pLdrEntry->DllBase + cbLength - 1, chInd,
+ pLdrEntry->TimeDateStamp, pLdrEntry->FullDllName.Buffer, pLdrEntry->FullDllName.Length,
+ pLdrEntry->FullDllName.MaximumLength);
+
+ /* advance */
+ pListEntry = pListEntry->Flink;
+ cLoops++;
+ }
+
+ /*
+ * Use the above to pick out code addresses on the stack.
+ */
+ if ( cLoops < 1024
+ && uXcptSP - uStack < cbToDump)
+ {
+ RTLogLoggerWeak(pLogger, NULL, "\nPotential code addresses on the stack:\n");
+ if (pFound)
+ {
+ if ( RT_VALID_PTR(pFound->FullDllName.Buffer)
+ && pFound->FullDllName.Length > 0
+ && pFound->FullDllName.Length < _8K
+ && (pFound->FullDllName.Length & 1) == 0
+ && pFound->FullDllName.Length <= pFound->FullDllName.MaximumLength)
+ RTLogLoggerWeak(pLogger, NULL, "%-*s: %p - %#010RX32 bytes into %.*ls\n",
+ sizeof(void *) * 2, "Xcpt PC", uXcptPC, (uint32_t)(uXcptPC - (uintptr_t)pFound->DllBase),
+ pFound->FullDllName.Length / sizeof(RTUTF16), pFound->FullDllName.Buffer);
+ else
+ RTLogLoggerWeak(pLogger, NULL, "%-*s: %p - %08RX32 into module at %p\n",
+ sizeof(void *) * 2, "Xcpt PC", uXcptPC, (uint32_t)(uXcptPC - (uintptr_t)pFound->DllBase),
+ pFound->DllBase);
+ }
+
+ uintptr_t const *puStack = (uintptr_t const *)uXcptSP;
+ uintptr_t cLeft = (cbToDump - (uXcptSP - uStack)) / sizeof(uintptr_t);
+ while (cLeft-- > 0)
+ {
+ uintptr_t uPtr = *puStack;
+ if (RT_VALID_PTR(uPtr))
+ {
+ /* Search the module table. */
+ pFound = NULL;
+ cLoops = 0;
+ pListEntry = pList->Flink;
+ while (pListEntry != pList && RT_VALID_PTR(pListEntry) && cLoops < 1024)
+ {
+ PLDR_DATA_TABLE_ENTRY pLdrEntry = RT_FROM_MEMBER(pListEntry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
+ uint32_t const cbLength = (uint32_t)(uintptr_t)pLdrEntry->Reserved3[1];
+ if (uPtr - (uintptr_t)pLdrEntry->DllBase < cbLength)
+ {
+ pFound = pLdrEntry;
+ break;
+ }
+
+ /* advance */
+ pListEntry = pListEntry->Flink;
+ cLoops++;
+ }
+
+ if (pFound)
+ {
+ if ( RT_VALID_PTR(pFound->FullDllName.Buffer)
+ && pFound->FullDllName.Length > 0
+ && pFound->FullDllName.Length < _8K
+ && (pFound->FullDllName.Length & 1) == 0
+ && pFound->FullDllName.Length <= pFound->FullDllName.MaximumLength)
+ RTLogLoggerWeak(pLogger, NULL, "%p: %p - %#010RX32 bytes into %.*ls\n",
+ puStack, uPtr, (uint32_t)(uPtr - (uintptr_t)pFound->DllBase),
+ pFound->FullDllName.Length / sizeof(RTUTF16), pFound->FullDllName.Buffer);
+ else
+ RTLogLoggerWeak(pLogger, NULL, "%p: %p - %08RX32 into module at %p\n",
+ puStack, uPtr, (uint32_t)(uPtr - (uintptr_t)pFound->DllBase), pFound->DllBase);
+ }
+ }
+
+ puStack++;
+ }
+ }
+ }
+
+ /*
+ * Dump the command line if we have one. We do this last in case it crashes.
+ */
+ PRTL_USER_PROCESS_PARAMETERS pProcParams = pPeb->ProcessParameters;
+ if (RT_VALID_PTR(pProcParams))
+ {
+ if (RT_VALID_PTR(pProcParams->CommandLine.Buffer)
+ && pProcParams->CommandLine.Length > 0
+ && pProcParams->CommandLine.Length <= pProcParams->CommandLine.MaximumLength
+ && !(pProcParams->CommandLine.Length & 1)
+ && !(pProcParams->CommandLine.MaximumLength & 1))
+ RTLogLoggerWeak(pLogger, NULL, "PEB/CommandLine: %.*ls\n",
+ pProcParams->CommandLine.Length / sizeof(RTUTF16), pProcParams->CommandLine.Buffer);
+ }
+ }
+ }
+
+ /*
+ * Do the default stuff, never mind us.
+ */
+ if (g_pfnUnhandledXcptFilter)
+ return g_pfnUnhandledXcptFilter(pPtrs);
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+
diff --git a/src/VBox/Runtime/r3/win/internal-r3-win.h b/src/VBox/Runtime/r3/win/internal-r3-win.h
new file mode 100644
index 00000000..562ac0ed
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/internal-r3-win.h
@@ -0,0 +1,244 @@
+/* $Id: internal-r3-win.h $ */
+/** @file
+ * IPRT - some Windows OS type constants.
+ */
+
+/*
+ * Copyright (C) 2013-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h
+#define IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include "internal/iprt.h"
+#include <iprt/types.h>
+
+
+/*******************************************************************************
+* Structures and Typedefs *
+*******************************************************************************/
+/**
+ * Windows OS type as determined by rtSystemWinOSType().
+ *
+ * @note ASSUMPTIONS are made regarding ordering. Win 9x should come first, then
+ * NT. The Win9x and NT versions should internally be ordered in ascending
+ * version/code-base order.
+ */
+typedef enum RTWINOSTYPE
+{
+ kRTWinOSType_UNKNOWN = 0,
+ kRTWinOSType_9XFIRST = 1,
+ kRTWinOSType_95 = kRTWinOSType_9XFIRST,
+ kRTWinOSType_95SP1,
+ kRTWinOSType_95OSR2,
+ kRTWinOSType_98,
+ kRTWinOSType_98SP1,
+ kRTWinOSType_98SE,
+ kRTWinOSType_ME,
+ kRTWinOSType_9XLAST = 99,
+ kRTWinOSType_NTFIRST = 100,
+ kRTWinOSType_NT310 = kRTWinOSType_NTFIRST,
+ kRTWinOSType_NT350,
+ kRTWinOSType_NT351,
+ kRTWinOSType_NT4,
+ kRTWinOSType_2K, /* 5.0 */
+ kRTWinOSType_XP, /* 5.1 */
+ kRTWinOSType_XP64, /* 5.2, workstation */
+ kRTWinOSType_2003, /* 5.2 */
+ kRTWinOSType_VISTA, /* 6.0, workstation */
+ kRTWinOSType_2008, /* 6.0, server */
+ kRTWinOSType_7, /* 6.1, workstation */
+ kRTWinOSType_2008R2, /* 6.1, server */
+ kRTWinOSType_8, /* 6.2, workstation */
+ kRTWinOSType_2012, /* 6.2, server */
+ kRTWinOSType_81, /* 6.3, workstation */
+ kRTWinOSType_2012R2, /* 6.3, server */
+ kRTWinOSType_10, /* 10.0, workstation */
+ kRTWinOSType_2016, /* 10.0 1607, server */
+ kRTWinOSType_2019, /* 10.0 1809, server */
+ kRTWinOSType_2022, /* 10.0 21H2, server */
+ kRTWinOSType_11, /* 11.0, workstation */
+ kRTWinOSType_NT_UNKNOWN = 199,
+ kRTWinOSType_NT_LAST = kRTWinOSType_UNKNOWN
+} RTWINOSTYPE;
+
+/**
+ * Windows loader protection level.
+ */
+typedef enum RTR3WINLDRPROT
+{
+ RTR3WINLDRPROT_INVALID = 0,
+ RTR3WINLDRPROT_NONE,
+ RTR3WINLDRPROT_NO_CWD,
+ RTR3WINLDRPROT_SAFE,
+ RTR3WINLDRPROT_SAFER
+} RTR3WINLDRPROT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+extern DECL_HIDDEN_DATA(RTR3WINLDRPROT) g_enmWinLdrProt;
+extern DECL_HIDDEN_DATA(RTWINOSTYPE) g_enmWinVer;
+#ifdef _WINDEF_
+extern DECL_HIDDEN_DATA(OSVERSIONINFOEXW) g_WinOsInfoEx;
+
+extern DECL_HIDDEN_DATA(HMODULE) g_hModKernel32;
+typedef UINT (WINAPI *PFNGETWINSYSDIR)(LPWSTR,UINT);
+extern DECL_HIDDEN_DATA(PFNGETWINSYSDIR) g_pfnGetSystemWindowsDirectoryW;
+typedef HANDLE (WINAPI *PFNCREATEWAITABLETIMEREX)(LPSECURITY_ATTRIBUTES, LPCWSTR, DWORD, DWORD);
+extern DECL_HIDDEN_DATA(PFNCREATEWAITABLETIMEREX) g_pfnCreateWaitableTimerExW;
+extern DECL_HIDDEN_DATA(decltype(SystemTimeToTzSpecificLocalTime) *) g_pfnSystemTimeToTzSpecificLocalTime;
+extern DECL_HIDDEN_DATA(decltype(GetHandleInformation) *) g_pfnGetHandleInformation;
+extern DECL_HIDDEN_DATA(decltype(SetHandleInformation) *) g_pfnSetHandleInformation;
+extern DECL_HIDDEN_DATA(decltype(IsDebuggerPresent) *) g_pfnIsDebuggerPresent;
+extern DECL_HIDDEN_DATA(decltype(GetSystemTimeAsFileTime) *) g_pfnGetSystemTimeAsFileTime;
+extern DECL_HIDDEN_DATA(decltype(GetProcessAffinityMask) *) g_pfnGetProcessAffinityMask;
+extern DECL_HIDDEN_DATA(decltype(SetThreadAffinityMask) *) g_pfnSetThreadAffinityMask;
+extern DECL_HIDDEN_DATA(decltype(CreateIoCompletionPort) *) g_pfnCreateIoCompletionPort;
+extern DECL_HIDDEN_DATA(decltype(GetQueuedCompletionStatus) *) g_pfnGetQueuedCompletionStatus;
+extern DECL_HIDDEN_DATA(decltype(PostQueuedCompletionStatus) *) g_pfnPostQueuedCompletionStatus;
+extern DECL_HIDDEN_DATA(decltype(SetUnhandledExceptionFilter) *) g_pfnSetUnhandledExceptionFilter;
+extern DECL_HIDDEN_DATA(decltype(UnhandledExceptionFilter) *) g_pfnUnhandledExceptionFilter;
+extern DECL_HIDDEN_DATA(decltype(IsProcessorFeaturePresent) *) g_pfnIsProcessorFeaturePresent;
+
+
+extern DECL_HIDDEN_DATA(HMODULE) g_hModNtDll;
+typedef NTSTATUS (NTAPI *PFNNTQUERYFULLATTRIBUTESFILE)(struct _OBJECT_ATTRIBUTES *, struct _FILE_NETWORK_OPEN_INFORMATION *);
+extern DECL_HIDDEN_DATA(PFNNTQUERYFULLATTRIBUTESFILE) g_pfnNtQueryFullAttributesFile;
+typedef NTSTATUS (NTAPI *PFNNTDUPLICATETOKEN)(HANDLE, ACCESS_MASK, struct _OBJECT_ATTRIBUTES *, BOOLEAN, TOKEN_TYPE, PHANDLE);
+extern DECL_HIDDEN_DATA(PFNNTDUPLICATETOKEN) g_pfnNtDuplicateToken;
+#ifdef IPRT_INCLUDED_nt_nt_h
+extern DECL_HIDDEN_DATA(decltype(NtAlertThread) *) g_pfnNtAlertThread;
+#endif
+
+extern DECL_HIDDEN_DATA(HMODULE) g_hModWinSock;
+
+/** WSAStartup */
+typedef int (WINAPI *PFNWSASTARTUP)(WORD, struct WSAData *);
+/** WSACleanup */
+typedef int (WINAPI *PFNWSACLEANUP)(void);
+/** WSAGetLastError */
+typedef int (WINAPI *PFNWSAGETLASTERROR)(void);
+/** WSASetLastError */
+typedef int (WINAPI *PFNWSASETLASTERROR)(int);
+/** WSACreateEvent */
+typedef HANDLE (WINAPI *PFNWSACREATEEVENT)(void);
+/** WSASetEvent */
+typedef BOOL (WINAPI *PFNWSASETEVENT)(HANDLE);
+/** WSACloseEvent */
+typedef BOOL (WINAPI *PFNWSACLOSEEVENT)(HANDLE);
+/** WSAEventSelect */
+typedef BOOL (WINAPI *PFNWSAEVENTSELECT)(UINT_PTR, HANDLE, LONG);
+/** WSAEnumNetworkEvents */
+typedef int (WINAPI *PFNWSAENUMNETWORKEVENTS)(UINT_PTR, HANDLE, struct _WSANETWORKEVENTS *);
+/** WSASocketW */
+typedef UINT_PTR (WINAPI *PFNWSASOCKETW)(int, int, int, struct _WSAPROTOCOL_INFOW *, unsigned, DWORD);
+/** WSASend */
+typedef int (WINAPI *PFNWSASEND)(UINT_PTR, struct _WSABUF *, DWORD, LPDWORD, DWORD dwFlags,
+ struct _OVERLAPPED *, uintptr_t /*LPWSAOVERLAPPED_COMPLETION_ROUTINE*/);
+
+/** socket */
+typedef UINT_PTR (WINAPI *PFNWINSOCKSOCKET)(int, int, int);
+/** closesocket */
+typedef int (WINAPI *PFNWINSOCKCLOSESOCKET)(UINT_PTR);
+/** recv */
+typedef int (WINAPI *PFNWINSOCKRECV)(UINT_PTR, char *, int, int);
+/** send */
+typedef int (WINAPI *PFNWINSOCKSEND)(UINT_PTR, const char *, int, int);
+/** recvfrom */
+typedef int (WINAPI *PFNWINSOCKRECVFROM)(UINT_PTR, char *, int, int, struct sockaddr *, int *);
+/** sendto */
+typedef int (WINAPI *PFNWINSOCKSENDTO)(UINT_PTR, const char *, int, int, const struct sockaddr *, int);
+/** bind */
+typedef int (WINAPI *PFNWINSOCKBIND)(UINT_PTR, const struct sockaddr *, int);
+/** listen */
+typedef int (WINAPI *PFNWINSOCKLISTEN)(UINT_PTR, int);
+/** accept */
+typedef UINT_PTR (WINAPI *PFNWINSOCKACCEPT)(UINT_PTR, struct sockaddr *, int *);
+/** connect */
+typedef int (WINAPI *PFNWINSOCKCONNECT)(UINT_PTR, const struct sockaddr *, int);
+/** shutdown */
+typedef int (WINAPI *PFNWINSOCKSHUTDOWN)(UINT_PTR, int);
+/** getsockopt */
+typedef int (WINAPI *PFNWINSOCKGETSOCKOPT)(UINT_PTR, int, int, char *, int *);
+/** setsockopt */
+typedef int (WINAPI *PFNWINSOCKSETSOCKOPT)(UINT_PTR, int, int, const char *, int);
+/** ioctlsocket */
+typedef int (WINAPI *PFNWINSOCKIOCTLSOCKET)(UINT_PTR, long, unsigned long *);
+/** getpeername */
+typedef int (WINAPI *PFNWINSOCKGETPEERNAME)(UINT_PTR, struct sockaddr *, int *);
+/** getsockname */
+typedef int (WINAPI *PFNWINSOCKGETSOCKNAME)(UINT_PTR, struct sockaddr *, int *);
+/** __WSAFDIsSet */
+typedef int (WINAPI *PFNWINSOCK__WSAFDISSET)(UINT_PTR, struct fd_set *);
+/** select */
+typedef int (WINAPI *PFNWINSOCKSELECT)(int, struct fd_set *, struct fd_set *, struct fd_set *, const struct timeval *);
+/** gethostbyname */
+typedef struct hostent *(WINAPI *PFNWINSOCKGETHOSTBYNAME)(const char *);
+
+extern DECL_HIDDEN_DATA(PFNWSASTARTUP) g_pfnWSAStartup;
+extern DECL_HIDDEN_DATA(PFNWSACLEANUP) g_pfnWSACleanup;
+extern DECL_HIDDEN_DATA(PFNWSAGETLASTERROR) g_pfnWSAGetLastError;
+extern DECL_HIDDEN_DATA(PFNWSASETLASTERROR) g_pfnWSASetLastError;
+extern DECL_HIDDEN_DATA(PFNWSACREATEEVENT) g_pfnWSACreateEvent;
+extern DECL_HIDDEN_DATA(PFNWSACLOSEEVENT) g_pfnWSACloseEvent;
+extern DECL_HIDDEN_DATA(PFNWSASETEVENT) g_pfnWSASetEvent;
+extern DECL_HIDDEN_DATA(PFNWSAEVENTSELECT) g_pfnWSAEventSelect;
+extern DECL_HIDDEN_DATA(PFNWSAENUMNETWORKEVENTS) g_pfnWSAEnumNetworkEvents;
+extern DECL_HIDDEN_DATA(PFNWSASOCKETW) g_pfnWSASocketW;
+extern DECL_HIDDEN_DATA(PFNWSASEND) g_pfnWSASend;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSOCKET) g_pfnsocket;
+extern DECL_HIDDEN_DATA(PFNWINSOCKCLOSESOCKET) g_pfnclosesocket;
+extern DECL_HIDDEN_DATA(PFNWINSOCKRECV) g_pfnrecv;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSEND) g_pfnsend;
+extern DECL_HIDDEN_DATA(PFNWINSOCKRECVFROM) g_pfnrecvfrom;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSENDTO) g_pfnsendto;
+extern DECL_HIDDEN_DATA(PFNWINSOCKBIND) g_pfnbind;
+extern DECL_HIDDEN_DATA(PFNWINSOCKLISTEN) g_pfnlisten;
+extern DECL_HIDDEN_DATA(PFNWINSOCKACCEPT) g_pfnaccept;
+extern DECL_HIDDEN_DATA(PFNWINSOCKCONNECT) g_pfnconnect;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSHUTDOWN) g_pfnshutdown;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKOPT) g_pfngetsockopt;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSETSOCKOPT) g_pfnsetsockopt;
+extern DECL_HIDDEN_DATA(PFNWINSOCKIOCTLSOCKET) g_pfnioctlsocket;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETPEERNAME) g_pfngetpeername;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETSOCKNAME) g_pfngetsockname;
+extern DECL_HIDDEN_DATA(PFNWINSOCK__WSAFDISSET) g_pfn__WSAFDIsSet;
+extern DECL_HIDDEN_DATA(PFNWINSOCKSELECT) g_pfnselect;
+extern DECL_HIDDEN_DATA(PFNWINSOCKGETHOSTBYNAME) g_pfngethostbyname;
+#endif
+
+
+#endif /* !IPRT_INCLUDED_SRC_r3_win_internal_r3_win_h */
+
diff --git a/src/VBox/Runtime/r3/win/krnlmod-win.cpp b/src/VBox/Runtime/r3/win/krnlmod-win.cpp
new file mode 100644
index 00000000..255cc2cf
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/krnlmod-win.cpp
@@ -0,0 +1,331 @@
+/* $Id: krnlmod-win.cpp $ */
+/** @file
+ * IPRT - Kernel module, Windows.
+ */
+
+/*
+ * Copyright (C) 2017-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/nt/nt.h>
+
+#include <iprt/krnlmod.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/dir.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/types.h>
+
+
+/**
+ * Internal kernel information record state.
+ */
+typedef struct RTKRNLMODINFOINT
+{
+ /** Reference counter. */
+ volatile uint32_t cRefs;
+ /** Reference count for the kernel module. */
+ uint32_t cRefKrnlMod;
+ /** Load address of the kernel module. */
+ RTR0UINTPTR uLoadAddr;
+ /** Size of the kernel module. */
+ size_t cbKrnlMod;
+ /** Pointer to the driver name. */
+ const char *pszName;
+ /** Size of the name in characters including the zero terminator. */
+ size_t cchFilePath;
+ /** Module name - variable in size. */
+ char achFilePath[1];
+} RTKRNLMODINFOINT;
+/** Pointer to the internal kernel module information record. */
+typedef RTKRNLMODINFOINT *PRTKRNLMODINFOINT;
+/** Pointer to a const internal kernel module information record. */
+typedef const RTKRNLMODINFOINT *PCRTKRNLMODINFOINT;
+
+
+/**
+ * Destroy the given kernel module information record.
+ *
+ * @param pThis The record to destroy.
+ */
+static void rtKrnlModInfoDestroy(PRTKRNLMODINFOINT pThis)
+{
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Queries the complete kernel modules structure and returns a pointer to it.
+ *
+ * @returns IPRT status code.
+ * @param ppKrnlMods Where to store the pointer to the kernel module list on success.
+ * Free with RTMemFree().
+ */
+static int rtKrnlModWinQueryKrnlMods(PRTL_PROCESS_MODULES *ppKrnlMods)
+{
+ int rc = VINF_SUCCESS;
+ RTL_PROCESS_MODULES KrnlModsSize;
+
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemModuleInformation, &KrnlModsSize, sizeof(KrnlModsSize), NULL);
+ if (NT_SUCCESS(rcNt) || rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ {
+ ULONG cbKrnlMods = RT_UOFFSETOF_DYN(RTL_PROCESS_MODULES, Modules[KrnlModsSize.NumberOfModules]);
+ PRTL_PROCESS_MODULES pKrnlMods = (PRTL_PROCESS_MODULES)RTMemAllocZ(cbKrnlMods);
+ if (RT_LIKELY(pKrnlMods))
+ {
+ rcNt = NtQuerySystemInformation(SystemModuleInformation, pKrnlMods, cbKrnlMods, NULL);
+ if (NT_SUCCESS(rcNt))
+ *ppKrnlMods = pKrnlMods;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ return rc;
+}
+
+/**
+ * Creates a new kernel module information record for the given module.
+ *
+ * @returns IPRT status code.
+ * @param pModInfo The kernel module information.
+ * @param phKrnlModInfo Where to store the handle to the kernel module information record
+ * on success.
+ */
+static int rtKrnlModWinInfoCreate(PRTL_PROCESS_MODULE_INFORMATION pModInfo, PRTKRNLMODINFO phKrnlModInfo)
+{
+ int rc = VINF_SUCCESS;
+ RT_NOREF2(pModInfo, phKrnlModInfo);
+ size_t cchFilePath = strlen((const char *)&pModInfo->FullPathName[0]) + 1;
+ PRTKRNLMODINFOINT pThis = (PRTKRNLMODINFOINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTKRNLMODINFOINT, achFilePath[cchFilePath]));
+ if (RT_LIKELY(pThis))
+ {
+ memcpy(&pThis->achFilePath[0], &pModInfo->FullPathName[0], cchFilePath);
+ pThis->cchFilePath = cchFilePath;
+ pThis->cRefs = 1;
+ pThis->cbKrnlMod = pModInfo->ImageSize;
+ pThis->uLoadAddr = (RTR0UINTPTR)pModInfo->ImageBase;
+ pThis->pszName = pModInfo->OffsetToFileName >= cchFilePath
+ ? NULL
+ : pThis->achFilePath + pModInfo->OffsetToFileName;
+
+ *phKrnlModInfo = pThis;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModQueryLoaded(const char *pszName, bool *pfLoaded)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(pfLoaded, VERR_INVALID_POINTER);
+
+ int rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfo(const char *pszName, PRTKRNLMODINFO phKrnlModInfo)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertPtrReturn(phKrnlModInfo, VERR_INVALID_POINTER);
+
+ int rc = VERR_NOT_IMPLEMENTED;
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModLoadedGetCount(void)
+{
+ uint32_t cKrnlMods = 0;
+ RTL_PROCESS_MODULES ProcMods;
+
+ NTSTATUS rcNt = NtQuerySystemInformation(SystemModuleInformation, &ProcMods, sizeof(ProcMods), NULL);
+ if (NT_SUCCESS(rcNt) || rcNt == STATUS_INFO_LENGTH_MISMATCH)
+ cKrnlMods = ProcMods.NumberOfModules;
+
+ return cKrnlMods;
+}
+
+
+RTDECL(int) RTKrnlModLoadedQueryInfoAll(PRTKRNLMODINFO pahKrnlModInfo, uint32_t cEntriesMax,
+ uint32_t *pcEntries)
+{
+ if (cEntriesMax > 0)
+ AssertPtrReturn(pahKrnlModInfo, VERR_INVALID_POINTER);
+
+ PRTL_PROCESS_MODULES pKrnlMods = NULL;
+ int rc = rtKrnlModWinQueryKrnlMods(&pKrnlMods);
+ if (RT_SUCCESS(rc))
+ {
+ if (pKrnlMods->NumberOfModules <= cEntriesMax)
+ {
+ for (unsigned i = 0; i < pKrnlMods->NumberOfModules; i++)
+ {
+ pKrnlMods->Modules[i].FullPathName[255] = '\0'; /* Paranoia */
+ rc = rtKrnlModWinInfoCreate(&pKrnlMods->Modules[i], &pahKrnlModInfo[i]);
+ if (RT_FAILURE(rc))
+ {
+ while (i-- > 0)
+ RTKrnlModInfoRelease(pahKrnlModInfo[i]);
+ break;
+ }
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (pcEntries)
+ *pcEntries = pKrnlMods->NumberOfModules;
+
+ RTMemFree(pKrnlMods);
+ }
+
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRetain(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ AssertMsg(cRefs > 1 && cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoRelease(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ if (!pThis)
+ return 0;
+ AssertPtrReturn(pThis, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ AssertMsg(cRefs < _1M, ("%#x %p\n", cRefs, pThis));
+ if (cRefs == 0)
+ rtKrnlModInfoDestroy(pThis);
+ return cRefs;
+}
+
+
+RTDECL(uint32_t) RTKrnlModInfoGetRefCnt(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cRefKrnlMod;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetName(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return pThis->pszName;
+}
+
+
+RTDECL(const char *) RTKrnlModInfoGetFilePath(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, NULL);
+
+ return &pThis->achFilePath[0];
+}
+
+
+RTDECL(size_t) RTKrnlModInfoGetSize(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->cbKrnlMod;
+}
+
+
+RTDECL(RTR0UINTPTR) RTKrnlModInfoGetLoadAddr(RTKRNLMODINFO hKrnlModInfo)
+{
+ PRTKRNLMODINFOINT pThis = hKrnlModInfo;
+ AssertPtrReturn(pThis, 0);
+
+ return pThis->uLoadAddr;
+}
+
+
+RTDECL(int) RTKrnlModInfoQueryRefModInfo(RTKRNLMODINFO hKrnlModInfo, uint32_t idx,
+ PRTKRNLMODINFO phKrnlModInfoRef)
+{
+ RT_NOREF3(hKrnlModInfo, idx, phKrnlModInfoRef);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModLoadByPath(const char *pszPath)
+{
+ AssertPtrReturn(pszPath, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTKrnlModUnloadByName(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r3/win/ldrNative-win.cpp b/src/VBox/Runtime/r3/win/ldrNative-win.cpp
new file mode 100644
index 00000000..2ff98a4c
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/ldrNative-win.cpp
@@ -0,0 +1,326 @@
+/* $Id: ldrNative-win.cpp $ */
+/** @file
+ * IPRT - Binary Image Loader, Win32 native.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/ldr.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/log.h>
+#include <iprt/once.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal/ldr.h"
+#include "internal-r3-win.h"
+
+#ifndef LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR
+# define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR UINT32_C(0x100)
+# define LOAD_LIBRARY_SEARCH_APPLICATION_DIR UINT32_C(0x200)
+# define LOAD_LIBRARY_SEARCH_SYSTEM32 UINT32_C(0x800)
+#endif
+
+
+DECLHIDDEN(int) rtldrNativeLoad(const char *pszFilename, uintptr_t *phHandle, uint32_t fFlags, PRTERRINFO pErrInfo)
+{
+ Assert(sizeof(*phHandle) >= sizeof(HMODULE));
+ AssertReturn(!(fFlags & RTLDRLOAD_FLAGS_GLOBAL), VERR_INVALID_FLAGS);
+ AssertLogRelMsgReturn(RTPathStartsWithRoot(pszFilename), /* Relative names will still be applied to the search path. */
+ ("pszFilename='%s'\n", pszFilename),
+ VERR_INTERNAL_ERROR_2);
+ AssertReleaseMsg(g_hModKernel32,
+ ("rtldrNativeLoad(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename));
+
+ /*
+ * Convert to UTF-16 and make sure it got a .DLL suffix.
+ */
+ /** @todo Implement long path support for native DLL loading on windows. @bugref{9248} */
+ int rc;
+ RTUTF16 *pwszNative = NULL;
+ if (RTPathHasSuffix(pszFilename) || (fFlags & RTLDRLOAD_FLAGS_NO_SUFFIX))
+ rc = RTStrToUtf16(pszFilename, &pwszNative);
+ else
+ {
+ size_t cwcAlloc;
+ rc = RTStrCalcUtf16LenEx(pszFilename, RTSTR_MAX, &cwcAlloc);
+ if (RT_SUCCESS(rc))
+ {
+ cwcAlloc += sizeof(".DLL");
+ pwszNative = RTUtf16Alloc(cwcAlloc * sizeof(RTUTF16));
+ if (pwszNative)
+ {
+ size_t cwcNative;
+ rc = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszNative, cwcAlloc, &cwcNative);
+ if (RT_SUCCESS(rc))
+ rc = RTUtf16CopyAscii(&pwszNative[cwcNative], cwcAlloc - cwcNative, ".DLL");
+ }
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /* Convert slashes just to be on the safe side. */
+ for (size_t off = 0;; off++)
+ {
+ RTUTF16 wc = pwszNative[off];
+ if (wc == '/')
+ pwszNative[off] = '\\';
+ else if (!wc)
+ break;
+ }
+
+ /*
+ * Attempt load.
+ */
+ HMODULE hmod;
+ static int s_iSearchDllLoadDirSupported = 0;
+ if ( !(fFlags & RTLDRLOAD_FLAGS_NT_SEARCH_DLL_LOAD_DIR)
+ || s_iSearchDllLoadDirSupported < 0)
+ hmod = LoadLibraryExW(pwszNative, NULL, 0);
+ else
+ {
+ hmod = LoadLibraryExW(pwszNative, NULL, LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_SYSTEM32
+ | LOAD_LIBRARY_SEARCH_APPLICATION_DIR);
+ if (s_iSearchDllLoadDirSupported == 0)
+ {
+ if (hmod != NULL || GetLastError() != ERROR_INVALID_PARAMETER)
+ s_iSearchDllLoadDirSupported = 1;
+ else
+ {
+ s_iSearchDllLoadDirSupported = -1;
+ hmod = LoadLibraryExW(pwszNative, NULL, 0);
+ }
+ }
+ }
+ if (hmod)
+ {
+ *phHandle = (uintptr_t)hmod;
+ RTUtf16Free(pwszNative);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Try figure why it failed to load.
+ */
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ rc = RTErrInfoSetF(pErrInfo, rc, "GetLastError=%u", dwErr);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "Error converting UTF-8 to UTF-16 string.");
+ RTUtf16Free(pwszNative);
+ return rc;
+}
+
+
+DECLCALLBACK(int) rtldrNativeGetSymbol(PRTLDRMODINTERNAL pMod, const char *pszSymbol, void **ppvValue)
+{
+ PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
+ FARPROC pfn = GetProcAddress((HMODULE)pModNative->hNative, pszSymbol);
+ if (pfn)
+ {
+ *ppvValue = (void *)pfn;
+ return VINF_SUCCESS;
+ }
+ *ppvValue = NULL;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+DECLCALLBACK(int) rtldrNativeClose(PRTLDRMODINTERNAL pMod)
+{
+ PRTLDRMODNATIVE pModNative = (PRTLDRMODNATIVE)pMod;
+ if ( (pModNative->fFlags & RTLDRLOAD_FLAGS_NO_UNLOAD)
+ || FreeLibrary((HMODULE)pModNative->hNative))
+ {
+ pModNative->hNative = (uintptr_t)INVALID_HANDLE_VALUE;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+DECLHIDDEN(int) rtldrNativeLoadSystem(const char *pszFilename, const char *pszExt, uint32_t fFlags, PRTLDRMOD phLdrMod)
+{
+ AssertReleaseMsg(g_hModKernel32,
+ ("rtldrNativeLoadSystem(%s,,) is called before IPRT has configured the windows loader!\n", pszFilename));
+
+ /*
+ * Resolve side-by-side resolver API.
+ */
+ static bool volatile s_fInitialized = false;
+ static decltype(RtlDosApplyFileIsolationRedirection_Ustr) *s_pfnApplyRedir = NULL;
+ if (!s_fInitialized)
+ {
+ s_pfnApplyRedir = (decltype(s_pfnApplyRedir))GetProcAddress(g_hModNtDll,
+ "RtlDosApplyFileIsolationRedirection_Ustr");
+ ASMCompilerBarrier();
+ s_fInitialized = true;
+ }
+
+ /*
+ * We try WinSxS via undocumented NTDLL API and flal back on the System32
+ * directory. No other locations are supported.
+ */
+ int rc = VERR_TRY_AGAIN;
+ char szPath[RTPATH_MAX];
+ char *pszPath = szPath;
+
+ /* Get the windows system32 directory so we can sanity check the WinSxS result. */
+ WCHAR wszSysDir[MAX_PATH];
+ UINT cwcSysDir = GetSystemDirectoryW(wszSysDir, MAX_PATH);
+ if (cwcSysDir >= MAX_PATH)
+ return VERR_FILENAME_TOO_LONG;
+
+ /* Try side-by-side first (see COMCTL32.DLL). */
+ if (s_pfnApplyRedir)
+ {
+ size_t cwcName = 0;
+ RTUTF16 wszName[MAX_PATH];
+ PRTUTF16 pwszName = wszName;
+ int rc2 = RTStrToUtf16Ex(pszFilename, RTSTR_MAX, &pwszName, RT_ELEMENTS(wszName), &cwcName);
+ if (RT_SUCCESS(rc2))
+ {
+ static UNICODE_STRING const s_DefaultSuffix = RTNT_CONSTANT_UNISTR(L".dll");
+ WCHAR wszPath[MAX_PATH];
+ UNICODE_STRING UniStrStatic = { 0, (USHORT)sizeof(wszPath) - sizeof(WCHAR), wszPath };
+ UNICODE_STRING UniStrDynamic = { 0, 0, NULL };
+ PUNICODE_STRING pUniStrResult = NULL;
+ UNICODE_STRING UniStrName =
+ { (USHORT)(cwcName * sizeof(RTUTF16)), (USHORT)((cwcName + 1) * sizeof(RTUTF16)), wszName };
+
+ NTSTATUS rcNt = s_pfnApplyRedir(1 /*fFlags*/,
+ &UniStrName,
+ (PUNICODE_STRING)&s_DefaultSuffix,
+ &UniStrStatic,
+ &UniStrDynamic,
+ &pUniStrResult,
+ NULL /*pNewFlags*/,
+ NULL /*pcbFilename*/,
+ NULL /*pcbNeeded*/);
+ if (NT_SUCCESS(rcNt))
+ {
+ /*
+ * Check that the resolved path has similarities to the
+ * system directory.
+ *
+ * ASSUMES the windows directory is a root directory and
+ * that both System32 and are on the same level. So, we'll
+ * have 2 matching components (or more if the resolver
+ * returns a system32 path for some reason).
+ */
+ unsigned cMatchingComponents = 0;
+ size_t off = 0;
+ while (off < pUniStrResult->Length)
+ {
+ RTUTF16 wc1 = wszSysDir[off];
+ RTUTF16 wc2 = pUniStrResult->Buffer[off];
+ if (!RTPATH_IS_SLASH(wc1))
+ {
+ if (wc1 == wc2)
+ off++;
+ else if ( wc1 < 127
+ && wc2 < 127
+ && RT_C_TO_LOWER(wc1) == RT_C_TO_LOWER(wc2) )
+ off++;
+ else
+ break;
+ }
+ else if (RTPATH_IS_SLASH(wc2))
+ {
+ cMatchingComponents += off > 0;
+ do
+ off++;
+ while ( off < pUniStrResult->Length
+ && RTPATH_IS_SLASH(wszSysDir[off])
+ && RTPATH_IS_SLASH(pUniStrResult->Buffer[off]));
+ }
+ else
+ break;
+ }
+ if (cMatchingComponents >= 2)
+ {
+ pszPath = szPath;
+ rc2 = RTUtf16ToUtf8Ex(pUniStrResult->Buffer, pUniStrResult->Length / sizeof(RTUTF16),
+ &pszPath, sizeof(szPath), NULL);
+ if (RT_SUCCESS(rc2))
+ rc = VINF_SUCCESS;
+ }
+ else
+ AssertMsgFailed(("%s -> '%*.ls'\n", pszFilename, pUniStrResult->Length, pUniStrResult->Buffer));
+ RtlFreeUnicodeString(&UniStrDynamic);
+ }
+ }
+ else
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+
+ /* If the above didn't succeed, create a system32 path. */
+ if (RT_FAILURE(rc))
+ {
+ rc = RTUtf16ToUtf8Ex(wszSysDir, RTSTR_MAX, &pszPath, sizeof(szPath), NULL);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTPathAppend(szPath, sizeof(szPath), pszFilename);
+ if (pszExt && RT_SUCCESS(rc))
+ rc = RTStrCat(szPath, sizeof(szPath), pszExt);
+ }
+ }
+
+ /* Do the actual loading, if we were successful constructing a name. */
+ if (RT_SUCCESS(rc))
+ {
+ if (RTFileExists(szPath))
+ rc = RTLdrLoadEx(szPath, phLdrMod, fFlags, NULL);
+ else
+ rc = VERR_MODULE_NOT_FOUND;
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/localipc-win.cpp b/src/VBox/Runtime/r3/win/localipc-win.cpp
new file mode 100644
index 00000000..6d56ab91
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/localipc-win.cpp
@@ -0,0 +1,1778 @@
+/* $Id: localipc-win.cpp $ */
+/** @file
+ * IPRT - Local IPC, Windows Implementation Using Named Pipes.
+ *
+ * @note This code only works on W2K because of the dependency on
+ * ConvertStringSecurityDescriptorToSecurityDescriptor.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LOCALIPC
+#include <iprt/nt/nt-and-windows.h> /* Need NtCancelIoFile and a few Rtl functions. */
+#include <sddl.h>
+#include <aclapi.h>
+
+#include "internal/iprt.h"
+#include <iprt/localipc.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/utf16.h>
+
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Pipe prefix string. */
+#define RTLOCALIPC_WIN_PREFIX L"\\\\.\\pipe\\IPRT-"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Local IPC service instance, Windows.
+ */
+typedef struct RTLOCALIPCSERVERINT
+{
+ /** The magic (RTLOCALIPCSERVER_MAGIC). */
+ uint32_t u32Magic;
+ /** The creation flags. */
+ uint32_t fFlags;
+ /** Critical section protecting the structure. */
+ RTCRITSECT CritSect;
+ /** The number of references to the instance.
+ * @remarks The reference counting isn't race proof. */
+ uint32_t volatile cRefs;
+ /** Indicates that there is a pending cancel request. */
+ bool volatile fCancelled;
+ /** The named pipe handle. */
+ HANDLE hNmPipe;
+ /** The handle to the event object we're using for overlapped I/O. */
+ HANDLE hEvent;
+ /** The overlapped I/O structure. */
+ OVERLAPPED OverlappedIO;
+ /** The full pipe name (variable length). */
+ RTUTF16 wszName[1];
+} RTLOCALIPCSERVERINT;
+/** Pointer to a local IPC server instance (Windows). */
+typedef RTLOCALIPCSERVERINT *PRTLOCALIPCSERVERINT;
+
+
+/**
+ * Local IPC session instance, Windows.
+ *
+ * This is a named pipe and we should probably merge the pipe code with this to
+ * save work and code duplication.
+ */
+typedef struct RTLOCALIPCSESSIONINT
+{
+ /** The magic (RTLOCALIPCSESSION_MAGIC). */
+ uint32_t u32Magic;
+ /** Critical section protecting the structure. */
+ RTCRITSECT CritSect;
+ /** The number of references to the instance.
+ * @remarks The reference counting isn't race proof. */
+ uint32_t volatile cRefs;
+ /** Set if the zero byte read that the poll code using is pending. */
+ bool fZeroByteRead;
+ /** Indicates that there is a pending cancel request. */
+ bool volatile fCancelled;
+ /** Set if this is the server side, clear if the client. */
+ bool fServerSide;
+ /** The named pipe handle. */
+ HANDLE hNmPipe;
+ struct
+ {
+ RTTHREAD hActiveThread;
+ /** The handle to the event object we're using for overlapped I/O. */
+ HANDLE hEvent;
+ /** The overlapped I/O structure. */
+ OVERLAPPED OverlappedIO;
+ }
+ /** Overlapped reads. */
+ Read,
+ /** Overlapped writes. */
+ Write;
+#if 0 /* Non-blocking writes are not yet supported. */
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+#endif
+ /** Buffer for the zero byte read.
+ * Used in RTLocalIpcSessionWaitForData(). */
+ uint8_t abBuf[8];
+} RTLOCALIPCSESSIONINT;
+/** Pointer to a local IPC session instance (Windows). */
+typedef RTLOCALIPCSESSIONINT *PRTLOCALIPCSESSIONINT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession);
+
+
+/**
+ * DACL for block all network access and local users other than the creator/owner.
+ *
+ * ACE format: (ace_type;ace_flags;rights;object_guid;inherit_object_guid;account_sid)
+ *
+ * Note! FILE_GENERIC_WRITE (SDDL_FILE_WRITE) is evil here because it includes
+ * the FILE_CREATE_PIPE_INSTANCE(=FILE_APPEND_DATA) flag. Thus the hardcoded
+ * value 0x0012019b in the client ACE. The server-side still needs
+ * setting FILE_CREATE_PIPE_INSTANCE although.
+ * It expands to:
+ * 0x00000001 - FILE_READ_DATA
+ * 0x00000008 - FILE_READ_EA
+ * 0x00000080 - FILE_READ_ATTRIBUTES
+ * 0x00020000 - READ_CONTROL
+ * 0x00100000 - SYNCHRONIZE
+ * 0x00000002 - FILE_WRITE_DATA
+ * 0x00000010 - FILE_WRITE_EA
+ * 0x00000100 - FILE_WRITE_ATTRIBUTES
+ * = 0x0012019b (client)
+ * + (only for server):
+ * 0x00000004 - FILE_CREATE_PIPE_INSTANCE
+ * = 0x0012019f
+ *
+ * @todo Triple check this!
+ * @todo EVERYONE -> AUTHENTICATED USERS or something more appropriate?
+ * @todo Have trouble allowing the owner FILE_CREATE_PIPE_INSTANCE access, so for now I'm hacking
+ * it just to get progress - the service runs as local system.
+ * The CREATOR OWNER and PERSONAL SELF works (the former is only involved in inheriting
+ * it seems, which is why it won't work. The latter I've no idea about. Perhaps the solution
+ * is to go the annoying route of OpenProcessToken, QueryTokenInformation,
+ * ConvertSidToStringSid and then use the result... Suggestions are very welcome
+ */
+#define RTLOCALIPC_WIN_SDDL_BASE \
+ SDDL_DACL SDDL_DELIMINATOR \
+ SDDL_ACE_BEGIN SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK SDDL_ACE_END \
+ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM SDDL_ACE_END
+#define RTLOCALIPC_WIN_SDDL_SERVER \
+ RTLOCALIPC_WIN_SDDL_BASE \
+ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE SDDL_ACE_END
+#define RTLOCALIPC_WIN_SDDL_CLIENT \
+ RTLOCALIPC_WIN_SDDL_BASE \
+ SDDL_ACE_BEGIN SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE SDDL_ACE_END
+static NTSTATUS rtLocalIpcBuildDacl(PACL pDacl, bool fServer)
+{
+ static SID_IDENTIFIER_AUTHORITY s_NtAuth = SECURITY_NT_AUTHORITY;
+ static SID_IDENTIFIER_AUTHORITY s_WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
+ union
+ {
+ SID Sid;
+ uint8_t abPadding[SECURITY_MAX_SID_SIZE];
+ } Network, LocalSystem, Everyone;
+
+
+ /* 1. SDDL_ACCESS_DENIED L";;" SDDL_GENERIC_ALL L";;;" SDDL_NETWORK */
+ NTSTATUS rcNt = RtlInitializeSid(&Network.Sid, &s_NtAuth, 1);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+ *RtlSubAuthoritySid(&Network.Sid, 0) = SECURITY_NETWORK_RID;
+
+ rcNt = RtlAddAccessDeniedAce(pDacl, ACL_REVISION, GENERIC_ALL, &Network.Sid);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+
+ /* 2. SDDL_ACCESS_ALLOWED L";;" SDDL_FILE_ALL L";;;" SDDL_LOCAL_SYSTEM */
+ rcNt = RtlInitializeSid(&LocalSystem.Sid, &s_NtAuth, 1);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+ *RtlSubAuthoritySid(&LocalSystem.Sid, 0) = SECURITY_LOCAL_SYSTEM_RID;
+
+ rcNt = RtlAddAccessAllowedAce(pDacl, ACL_REVISION, FILE_ALL_ACCESS, &Network.Sid);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+
+
+ /* 3. server: SDDL_ACCESS_ALLOWED L";;" L"0x0012019f" L";;;" SDDL_EVERYONE
+ client: SDDL_ACCESS_ALLOWED L";;" L"0x0012019b" L";;;" SDDL_EVERYONE */
+ rcNt = RtlInitializeSid(&Everyone.Sid, &s_NtAuth, 1);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+ *RtlSubAuthoritySid(&Everyone.Sid, 0) = SECURITY_WORLD_RID;
+
+ DWORD const fAccess = FILE_READ_DATA /* 0x00000001 */
+ | FILE_WRITE_DATA /* 0x00000002 */
+ | FILE_CREATE_PIPE_INSTANCE * fServer /* 0x00000004 */
+ | FILE_READ_EA /* 0x00000008 */
+ | FILE_WRITE_EA /* 0x00000010 */
+ | FILE_READ_ATTRIBUTES /* 0x00000080 */
+ | FILE_WRITE_ATTRIBUTES /* 0x00000100 */
+ | READ_CONTROL /* 0x00020000 */
+ | SYNCHRONIZE; /* 0x00100000*/
+ Assert(fAccess == (fServer ? 0x0012019fU : 0x0012019bU));
+
+ rcNt = RtlAddAccessAllowedAce(pDacl, ACL_REVISION, fAccess, &Network.Sid);
+ AssertReturn(NT_SUCCESS(rcNt), rcNt);
+
+ return true;
+}
+
+
+/**
+ * Builds and allocates the security descriptor required for securing the local pipe.
+ *
+ * @return IPRT status code.
+ * @param ppDesc Where to store the allocated security descriptor on success.
+ * Must be free'd using LocalFree().
+ * @param fServer Whether it's for a server or client instance.
+ */
+static int rtLocalIpcServerWinAllocSecurityDescriptor(PSECURITY_DESCRIPTOR *ppDesc, bool fServer)
+{
+ int rc;
+ PSECURITY_DESCRIPTOR pSecDesc = NULL;
+
+#if 0
+ /*
+ * Resolve the API the first time around.
+ */
+ static bool volatile s_fResolvedApis = false;
+ /** advapi32.dll API ConvertStringSecurityDescriptorToSecurityDescriptorW. */
+ static decltype(ConvertStringSecurityDescriptorToSecurityDescriptorW) *s_pfnSSDLToSecDescW = NULL;
+
+ if (!s_fResolvedApis)
+ {
+ s_pfnSSDLToSecDescW
+ = (decltype(s_pfnSSDLToSecDescW))RTLdrGetSystemSymbol("advapi32.dll",
+ "ConvertStringSecurityDescriptorToSecurityDescriptorW");
+ ASMCompilerBarrier();
+ s_fResolvedApis = true;
+ }
+ if (s_pfnSSDLToSecDescW)
+ {
+ /*
+ * We'll create a security descriptor from a SDDL that denies
+ * access to network clients (this is local IPC after all), it
+ * makes some further restrictions to prevent non-authenticated
+ * users from screwing around.
+ */
+ PCRTUTF16 pwszSDDL = fServer ? RTLOCALIPC_WIN_SDDL_SERVER : RTLOCALIPC_WIN_SDDL_CLIENT;
+ ULONG cbSecDesc = 0;
+ SetLastError(0);
+ if (s_pfnSSDLToSecDescW(pwszSDDL, SDDL_REVISION_1, &pSecDesc, &cbSecDesc))
+ {
+ DWORD dwErr = GetLastError(); RT_NOREF(dwErr);
+ AssertPtr(pSecDesc);
+ *ppDesc = pSecDesc;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+#endif
+ {
+ /*
+ * Manually construct the descriptor.
+ *
+ * This is a bit crude. The 8KB is probably 50+ times more than what we need.
+ */
+ uint32_t const cbAlloc = SECURITY_DESCRIPTOR_MIN_LENGTH * 2 + _8K;
+ pSecDesc = LocalAlloc(LMEM_FIXED, cbAlloc);
+ if (!pSecDesc)
+ return VERR_NO_MEMORY;
+ RT_BZERO(pSecDesc, cbAlloc);
+
+ uint32_t const cbDacl = cbAlloc - SECURITY_DESCRIPTOR_MIN_LENGTH * 2;
+ PACL const pDacl = (PACL)((uint8_t *)pSecDesc + SECURITY_DESCRIPTOR_MIN_LENGTH * 2);
+
+ if ( InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)
+ && InitializeAcl(pDacl, cbDacl, ACL_REVISION))
+ {
+ if (rtLocalIpcBuildDacl(pDacl, fServer))
+ {
+ *ppDesc = pSecDesc;
+ return VINF_SUCCESS;
+ }
+ rc = VERR_GENERAL_FAILURE;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ LocalFree(pSecDesc);
+ }
+ return rc;
+}
+
+
+/**
+ * Creates a named pipe instance.
+ *
+ * This is used by both RTLocalIpcServerCreate and RTLocalIpcServerListen.
+ *
+ * @return IPRT status code.
+ * @param phNmPipe Where to store the named pipe handle on success.
+ * This will be set to INVALID_HANDLE_VALUE on failure.
+ * @param pwszPipeName The named pipe name, full, UTF-16 encoded.
+ * @param fFirst Set on the first call (from RTLocalIpcServerCreate),
+ * otherwise clear. Governs the
+ * FILE_FLAG_FIRST_PIPE_INSTANCE flag.
+ */
+static int rtLocalIpcServerWinCreatePipeInstance(PHANDLE phNmPipe, PCRTUTF16 pwszPipeName, bool fFirst)
+{
+ *phNmPipe = INVALID_HANDLE_VALUE;
+
+ /*
+ * Create a security descriptor blocking access to the pipe via network.
+ */
+ PSECURITY_DESCRIPTOR pSecDesc;
+ int rc = rtLocalIpcServerWinAllocSecurityDescriptor(&pSecDesc, fFirst /* Server? */);
+ if (RT_SUCCESS(rc))
+ {
+#if 0
+ { /* Just for checking the security descriptor out in the debugger (!sd <addr> doesn't work): */
+ DWORD dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, NULL, NULL, NULL, NULL, pSecDesc);
+ __debugbreak(); RT_NOREF(dwRet);
+
+ PTRUSTEE_W pOwner = NULL;
+ PTRUSTEE_W pGroup = NULL;
+ ULONG cAces = 0;
+ PEXPLICIT_ACCESS_W paAces = NULL;
+ ULONG cAuditEntries = 0;
+ PEXPLICIT_ACCESS_W paAuditEntries = NULL;
+ dwRet = LookupSecurityDescriptorPartsW(&pOwner, NULL, NULL, NULL, NULL, NULL, pSecDesc);
+ dwRet = LookupSecurityDescriptorPartsW(NULL, &pGroup, NULL, NULL, NULL, NULL, pSecDesc);
+ dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, &cAces, &paAces, NULL, NULL, pSecDesc);
+ dwRet = LookupSecurityDescriptorPartsW(NULL, NULL, NULL, NULL, &cAuditEntries, &paAuditEntries, pSecDesc);
+ __debugbreak(); RT_NOREF(dwRet);
+ }
+#endif
+
+ /*
+ * Now, create the pipe.
+ */
+ SECURITY_ATTRIBUTES SecAttrs;
+ SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ SecAttrs.lpSecurityDescriptor = pSecDesc;
+ SecAttrs.bInheritHandle = FALSE;
+
+ DWORD fOpenMode = PIPE_ACCESS_DUPLEX
+ | PIPE_WAIT
+ | FILE_FLAG_OVERLAPPED;
+ if ( fFirst
+ && ( g_enmWinVer >= kRTWinOSType_XP
+ || ( g_enmWinVer == kRTWinOSType_2K
+ && g_WinOsInfoEx.wServicePackMajor >= 2) ) )
+ fOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE; /* Introduced with W2K SP2 */
+
+ HANDLE hNmPipe = CreateNamedPipeW(pwszPipeName, /* lpName */
+ fOpenMode, /* dwOpenMode */
+ PIPE_TYPE_BYTE, /* dwPipeMode */
+ PIPE_UNLIMITED_INSTANCES, /* nMaxInstances */
+ PAGE_SIZE, /* nOutBufferSize (advisory) */
+ PAGE_SIZE, /* nInBufferSize (ditto) */
+ 30*1000, /* nDefaultTimeOut = 30 sec */
+ &SecAttrs); /* lpSecurityAttributes */
+ if (hNmPipe != INVALID_HANDLE_VALUE)
+ {
+#if 0 /* For checking access control stuff in windbg (doesn't work): */
+ PSECURITY_DESCRIPTOR pSecDesc2 = NULL;
+ PACL pDacl = NULL;
+ DWORD dwRet;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, NULL, &pSecDesc2);
+ PACL pSacl = NULL;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, SACL_SECURITY_INFORMATION, NULL, NULL, NULL, &pSacl, &pSecDesc2);
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION, NULL, NULL, &pDacl, &pSacl, &pSecDesc2);
+ PSID pSidOwner = NULL;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, &pSidOwner, NULL, NULL, NULL, &pSecDesc2);
+ PSID pSidGroup = NULL;
+ dwRet = GetSecurityInfo(hNmPipe, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION, NULL, &pSidGroup, NULL, NULL, &pSecDesc2);
+ __debugbreak();
+ RT_NOREF(dwRet);
+#endif
+ *phNmPipe = hNmPipe;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ LocalFree(pSecDesc);
+ }
+ return rc;
+}
+
+
+/**
+ * Validates the user specified name.
+ *
+ * @returns IPRT status code.
+ * @param pszName The name to validate.
+ * @param pcwcFullName Where to return the UTF-16 length of the full name.
+ * @param fNative Whether it's a native name or a portable name.
+ */
+static int rtLocalIpcWinValidateName(const char *pszName, size_t *pcwcFullName, bool fNative)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName, VERR_INVALID_NAME);
+
+ if (!fNative)
+ {
+ size_t cwcName = RT_ELEMENTS(RTLOCALIPC_WIN_PREFIX) - 1;
+ for (;;)
+ {
+ char ch = *pszName++;
+ if (!ch)
+ break;
+ AssertReturn(!RT_C_IS_CNTRL(ch), VERR_INVALID_NAME);
+ AssertReturn((unsigned)ch < 0x80, VERR_INVALID_NAME);
+ AssertReturn(ch != '\\', VERR_INVALID_NAME);
+ AssertReturn(ch != '/', VERR_INVALID_NAME);
+ cwcName++;
+ }
+ *pcwcFullName = cwcName;
+ }
+ else
+ {
+ int rc = RTStrCalcUtf16LenEx(pszName, RTSTR_MAX, pcwcFullName);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Constructs the full pipe name as UTF-16.
+ *
+ * @returns IPRT status code.
+ * @param pszName The user supplied name. ASSUMES reasonable length
+ * for now, so no long path prefixing needed.
+ * @param pwszFullName The output buffer.
+ * @param cwcFullName The output buffer size excluding the terminator.
+ * @param fNative Whether the user supplied name is a native or
+ * portable one.
+ */
+static int rtLocalIpcWinConstructName(const char *pszName, PRTUTF16 pwszFullName, size_t cwcFullName, bool fNative)
+{
+ if (!fNative)
+ {
+ static RTUTF16 const s_wszPrefix[] = RTLOCALIPC_WIN_PREFIX;
+ Assert(cwcFullName * sizeof(RTUTF16) > sizeof(s_wszPrefix));
+ memcpy(pwszFullName, s_wszPrefix, sizeof(s_wszPrefix));
+ cwcFullName -= RT_ELEMENTS(s_wszPrefix) - 1;
+ pwszFullName += RT_ELEMENTS(s_wszPrefix) - 1;
+ }
+ return RTStrToUtf16Ex(pszName, RTSTR_MAX, &pwszFullName, cwcFullName + 1, NULL);
+}
+
+
+RTDECL(int) RTLocalIpcServerCreate(PRTLOCALIPCSERVER phServer, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Validate parameters.
+ */
+ AssertPtrReturn(phServer, VERR_INVALID_POINTER);
+ *phServer = NIL_RTLOCALIPCSERVER;
+ AssertReturn(!(fFlags & ~RTLOCALIPC_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+ size_t cwcFullName;
+ int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Allocate and initialize the instance data.
+ */
+ size_t cbThis = RT_UOFFSETOF_DYN(RTLOCALIPCSERVERINT, wszName[cwcFullName + 1]);
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)RTMemAllocVar(cbThis);
+ AssertReturn(pThis, VERR_NO_MEMORY);
+
+ pThis->u32Magic = RTLOCALIPCSERVER_MAGIC;
+ pThis->cRefs = 1; /* the one we return */
+ pThis->fCancelled = false;
+
+ rc = rtLocalIpcWinConstructName(pszName, pThis->wszName, cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
+ FALSE /*bInitialState*/, NULL /*lpName*/);
+ if (pThis->hEvent != NULL)
+ {
+ RT_ZERO(pThis->OverlappedIO);
+ pThis->OverlappedIO.Internal = STATUS_PENDING;
+ pThis->OverlappedIO.hEvent = pThis->hEvent;
+
+ rc = rtLocalIpcServerWinCreatePipeInstance(&pThis->hNmPipe, pThis->wszName, true /* fFirst */);
+ if (RT_SUCCESS(rc))
+ {
+ *phServer = pThis;
+ return VINF_SUCCESS;
+ }
+
+ BOOL fRc = CloseHandle(pThis->hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ int rc2 = RTCritSectDelete(&pThis->CritSect);
+ AssertRC(rc2);
+ }
+ }
+ RTMemFree(pThis);
+ }
+ return rc;
+}
+
+
+/**
+ * Retains a reference to the server instance.
+ *
+ * @param pThis The server instance.
+ */
+DECLINLINE(void) rtLocalIpcServerRetain(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
+}
+
+
+/**
+ * Call when the reference count reaches 0.
+ *
+ * Caller owns the critsect.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The instance to destroy.
+ */
+DECL_NO_INLINE(static, int) rtLocalIpcServerWinDestroy(PRTLOCALIPCSERVERINT pThis)
+{
+ Assert(pThis->u32Magic == ~RTLOCALIPCSERVER_MAGIC);
+ pThis->u32Magic = ~RTLOCALIPCSERVER_MAGIC;
+
+ BOOL fRc = CloseHandle(pThis->hNmPipe);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ pThis->hNmPipe = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(pThis->hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ pThis->hEvent = NULL;
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+ return VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * Server instance destructor.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The server instance.
+ */
+DECL_NO_INLINE(static, int) rtLocalIpcServerDtor(PRTLOCALIPCSERVERINT pThis)
+{
+ RTCritSectEnter(&pThis->CritSect);
+ return rtLocalIpcServerWinDestroy(pThis);
+}
+
+
+/**
+ * Releases a reference to the server instance.
+ *
+ * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
+ * @param pThis The server instance.
+ */
+DECLINLINE(int) rtLocalIpcServerRelease(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcServerDtor(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Releases a reference to the server instance and leaves the critsect.
+ *
+ * @returns VINF_SUCCESS if only release, VINF_OBJECT_DESTROYED if destroyed.
+ * @param pThis The server instance.
+ */
+DECLINLINE(int) rtLocalIpcServerReleaseAndUnlock(PRTLOCALIPCSERVERINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcServerWinDestroy(pThis);
+ return RTCritSectLeave(&pThis->CritSect);
+}
+
+
+
+RTDECL(int) RTLocalIpcServerDestroy(RTLOCALIPCSERVER hServer)
+{
+ /*
+ * Validate input.
+ */
+ if (hServer == NIL_RTLOCALIPCSERVER)
+ return VINF_SUCCESS;
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Cancel any thread currently busy using the server,
+ * leaving the cleanup to it.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTLOCALIPCSERVER_MAGIC, RTLOCALIPCSERVER_MAGIC), VERR_WRONG_ORDER);
+
+ RTCritSectEnter(&pThis->CritSect);
+
+ /* Cancel everything. */
+ ASMAtomicUoWriteBool(&pThis->fCancelled, true);
+ if (pThis->cRefs > 1)
+ {
+ BOOL fRc = SetEvent(pThis->hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ }
+
+ return rtLocalIpcServerReleaseAndUnlock(pThis);
+}
+
+
+RTDECL(int) RTLocalIpcServerGrantGroupAccess(RTLOCALIPCSERVER hServer, RTGID gid)
+{
+ RT_NOREF_PV(hServer); RT_NOREF(gid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTLocalIpcServerListen(RTLOCALIPCSERVER hServer, PRTLOCALIPCSESSION phClientSession)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(phClientSession, VERR_INVALID_POINTER);
+
+ /*
+ * Enter the critsect before inspecting the object further.
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ rtLocalIpcServerRetain(pThis);
+ if (!pThis->fCancelled)
+ {
+ ResetEvent(pThis->hEvent);
+
+ RTCritSectLeave(&pThis->CritSect);
+
+ /*
+ * Try connect a client. We need to use overlapped I/O here because
+ * of the cancellation done by RTLocalIpcServerCancel and RTLocalIpcServerDestroy.
+ */
+ SetLastError(NO_ERROR);
+ BOOL fRc = ConnectNamedPipe(pThis->hNmPipe, &pThis->OverlappedIO);
+ DWORD dwErr = fRc ? NO_ERROR : GetLastError();
+ if ( !fRc
+ && dwErr == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->hEvent, INFINITE);
+ DWORD dwIgnored;
+ fRc = GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &dwIgnored, FALSE /* bWait*/);
+ dwErr = fRc ? NO_ERROR : GetLastError();
+ }
+
+ RTCritSectEnter(&pThis->CritSect);
+ if ( !pThis->fCancelled /* Event signalled but not cancelled? */
+ && pThis->u32Magic == RTLOCALIPCSERVER_MAGIC)
+ {
+ /*
+ * Still alive, some error or an actual client.
+ *
+ * If it's the latter we'll have to create a new pipe instance that
+ * replaces the current one for the server. The current pipe instance
+ * will be assigned to the client session.
+ */
+ if ( fRc
+ || dwErr == ERROR_PIPE_CONNECTED)
+ {
+ HANDLE hNmPipe;
+ rc = rtLocalIpcServerWinCreatePipeInstance(&hNmPipe, pThis->wszName, false /* fFirst */);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hNmPipeSession = pThis->hNmPipe; /* consumed */
+ pThis->hNmPipe = hNmPipe;
+ rc = rtLocalIpcWinCreateSession(phClientSession, hNmPipeSession);
+ }
+ else
+ {
+ /*
+ * We failed to create a new instance for the server, disconnect
+ * the client and fail. Don't try service the client here.
+ */
+ fRc = DisconnectNamedPipe(pThis->hNmPipe);
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ else
+ {
+ /*
+ * Cancelled.
+ *
+ * Cancel the overlapped io if it didn't complete (must be done
+ * in the this thread) or disconnect the client.
+ */
+ Assert(pThis->fCancelled);
+ if ( fRc
+ || dwErr == ERROR_PIPE_CONNECTED)
+ fRc = DisconnectNamedPipe(pThis->hNmPipe);
+ else if (dwErr == ERROR_IO_PENDING)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtCancelIoFile(pThis->hNmPipe, &Ios);
+ fRc = NT_SUCCESS(rcNt);
+ }
+ else
+ fRc = TRUE;
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ rc = VERR_CANCELLED;
+ }
+ }
+ else
+ {
+ /*pThis->fCancelled = false; - Terrible interface idea. Add API to clear fCancelled if ever required. */
+ rc = VERR_CANCELLED;
+ }
+ rtLocalIpcServerReleaseAndUnlock(pThis);
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcServerCancel(RTLOCALIPCSERVER hServer)
+{
+ /*
+ * Validate input.
+ */
+ PRTLOCALIPCSERVERINT pThis = (PRTLOCALIPCSERVERINT)hServer;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSERVER_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Enter the critical section, then set the cancellation flag
+ * and signal the event (to wake up anyone in/at WaitForSingleObject).
+ */
+ rtLocalIpcServerRetain(pThis);
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ ASMAtomicUoWriteBool(&pThis->fCancelled, true);
+
+ BOOL fRc = SetEvent(pThis->hEvent);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("dwErr=%u\n", dwErr));
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ rtLocalIpcServerReleaseAndUnlock(pThis);
+ }
+ else
+ rtLocalIpcServerRelease(pThis);
+ return rc;
+}
+
+
+/**
+ * Create a session instance for a new server client or a client connect.
+ *
+ * @returns IPRT status code.
+ *
+ * @param ppSession Where to store the session handle on success.
+ * @param hNmPipeSession The named pipe handle if server calling,
+ * INVALID_HANDLE_VALUE if client connect. This will
+ * be consumed by this session, meaning on failure to
+ * create the session it will be closed.
+ */
+static int rtLocalIpcWinCreateSession(PRTLOCALIPCSESSIONINT *ppSession, HANDLE hNmPipeSession)
+{
+ AssertPtr(ppSession);
+
+ /*
+ * Allocate and initialize the session instance data.
+ */
+ int rc;
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTLOCALIPCSESSION_MAGIC;
+ pThis->cRefs = 1; /* our ref */
+ pThis->fCancelled = false;
+ pThis->fZeroByteRead = false;
+ pThis->fServerSide = hNmPipeSession != INVALID_HANDLE_VALUE;
+ pThis->hNmPipe = hNmPipeSession;
+#if 0 /* Non-blocking writes are not yet supported. */
+ pThis->pbBounceBuf = NULL;
+ pThis->cbBounceBufAlloc = 0;
+ pThis->cbBounceBufUsed = 0;
+#endif
+ rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Read.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
+ FALSE /*bInitialState*/, NULL /*lpName*/);
+ if (pThis->Read.hEvent != NULL)
+ {
+ pThis->Read.OverlappedIO.Internal = STATUS_PENDING;
+ pThis->Read.OverlappedIO.hEvent = pThis->Read.hEvent;
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+
+ pThis->Write.hEvent = CreateEvent(NULL /*lpEventAttributes*/, TRUE /*bManualReset*/,
+ FALSE /*bInitialState*/, NULL /*lpName*/);
+ if (pThis->Write.hEvent != NULL)
+ {
+ pThis->Write.OverlappedIO.Internal = STATUS_PENDING;
+ pThis->Write.OverlappedIO.hEvent = pThis->Write.hEvent;
+ pThis->Write.hActiveThread = NIL_RTTHREAD;
+
+ *ppSession = pThis;
+ return VINF_SUCCESS;
+ }
+
+ CloseHandle(pThis->Read.hEvent);
+ }
+
+ /* bail out */
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ if (hNmPipeSession != INVALID_HANDLE_VALUE)
+ {
+ BOOL fRc = CloseHandle(hNmPipeSession);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionConnect(PRTLOCALIPCSESSION phSession, const char *pszName, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(phSession, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTLOCALIPC_C_FLAGS_VALID_MASK), VERR_INVALID_FLAGS);
+
+ size_t cwcFullName;
+ int rc = rtLocalIpcWinValidateName(pszName, &cwcFullName, RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create a session (shared with server client session creation).
+ */
+ PRTLOCALIPCSESSIONINT pThis;
+ rc = rtLocalIpcWinCreateSession(&pThis, INVALID_HANDLE_VALUE);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Try open the pipe.
+ */
+ PSECURITY_DESCRIPTOR pSecDesc;
+ rc = rtLocalIpcServerWinAllocSecurityDescriptor(&pSecDesc, false /*fServer*/);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszFullName = RTUtf16Alloc((cwcFullName + 1) * sizeof(RTUTF16));
+ if (pwszFullName)
+ rc = rtLocalIpcWinConstructName(pszName, pwszFullName, cwcFullName,
+ RT_BOOL(fFlags & RTLOCALIPC_C_FLAGS_NATIVE_NAME));
+ else
+ rc = VERR_NO_UTF16_MEMORY;
+ if (RT_SUCCESS(rc))
+ {
+ SECURITY_ATTRIBUTES SecAttrs;
+ SecAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
+ SecAttrs.lpSecurityDescriptor = pSecDesc;
+ SecAttrs.bInheritHandle = FALSE;
+
+ /* The SECURITY_XXX flags are needed in order to prevent the server from impersonating with
+ this thread's security context (supported at least back to NT 3.51). See @bugref{9773}. */
+ HANDLE hPipe = CreateFileW(pwszFullName,
+ GENERIC_READ | GENERIC_WRITE,
+ 0 /*no sharing*/,
+ &SecAttrs,
+ OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_ANONYMOUS,
+ NULL /*no template handle*/);
+ if (hPipe != INVALID_HANDLE_VALUE)
+ {
+ pThis->hNmPipe = hPipe;
+
+ LocalFree(pSecDesc);
+ RTUtf16Free(pwszFullName);
+
+ /*
+ * We're done!
+ */
+ *phSession = pThis;
+ return VINF_SUCCESS;
+ }
+
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ RTUtf16Free(pwszFullName);
+ LocalFree(pSecDesc);
+ }
+
+ /* destroy the session handle. */
+ CloseHandle(pThis->Read.hEvent);
+ CloseHandle(pThis->Write.hEvent);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Cancells all pending I/O operations, forcing the methods to return with
+ * VERR_CANCELLED (unless they've got actual data to return).
+ *
+ * Used by RTLocalIpcSessionCancel and RTLocalIpcSessionClose.
+ *
+ * @returns IPRT status code.
+ * @param pThis The client session instance.
+ */
+static int rtLocalIpcWinCancel(PRTLOCALIPCSESSIONINT pThis)
+{
+ ASMAtomicUoWriteBool(&pThis->fCancelled, true);
+
+ /*
+ * Call NtCancelIoFile since this call cancels both read and write
+ * oriented operations.
+ */
+ if ( pThis->fZeroByteRead
+ || pThis->Read.hActiveThread != NIL_RTTHREAD
+ || pThis->Write.hActiveThread != NIL_RTTHREAD)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hNmPipe, &Ios);
+ }
+
+ /*
+ * Set both event semaphores.
+ */
+ BOOL fRc = SetEvent(pThis->Read.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ fRc = SetEvent(pThis->Write.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Retains a reference to the session instance.
+ *
+ * @param pThis The client session instance.
+ */
+DECLINLINE(void) rtLocalIpcSessionRetain(PRTLOCALIPCSESSIONINT pThis)
+{
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs); NOREF(cRefs);
+}
+
+
+RTDECL(uint32_t) RTLocalIpcSessionRetain(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2 && cRefs);
+ return cRefs;
+}
+
+
+/**
+ * Call when the reference count reaches 0.
+ *
+ * Caller owns the critsect.
+ *
+ * @returns VINF_OBJECT_DESTROYED
+ * @param pThis The instance to destroy.
+ */
+DECL_NO_INLINE(static, int) rtLocalIpcSessionWinDestroy(PRTLOCALIPCSESSIONINT pThis)
+{
+ BOOL fRc = CloseHandle(pThis->hNmPipe);
+ AssertMsg(fRc, ("%d\n", GetLastError())); NOREF(fRc);
+ pThis->hNmPipe = INVALID_HANDLE_VALUE;
+
+ fRc = CloseHandle(pThis->Write.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ pThis->Write.hEvent = NULL;
+
+ fRc = CloseHandle(pThis->Read.hEvent);
+ AssertMsg(fRc, ("%d\n", GetLastError()));
+ pThis->Read.hEvent = NULL;
+
+ int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+ return VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * Releases a reference to the session instance and unlock it.
+ *
+ * @returns VINF_SUCCESS or VINF_OBJECT_DESTROYED as appropriate.
+ * @param pThis The session instance.
+ */
+DECLINLINE(int) rtLocalIpcSessionReleaseAndUnlock(PRTLOCALIPCSESSIONINT pThis)
+{
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (!cRefs)
+ return rtLocalIpcSessionWinDestroy(pThis);
+
+ int rc2 = RTCritSectLeave(&pThis->CritSect); AssertRC(rc2);
+ Log(("rtLocalIpcSessionReleaseAndUnlock: %u refs left\n", cRefs));
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(uint32_t) RTLocalIpcSessionRelease(RTLOCALIPCSESSION hSession)
+{
+ if (hSession == NIL_RTLOCALIPCSESSION)
+ return 0;
+
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, UINT32_MAX);
+
+ uint32_t cRefs = ASMAtomicDecU32(&pThis->cRefs);
+ Assert(cRefs < UINT32_MAX / 2);
+ if (cRefs)
+ Log(("RTLocalIpcSessionRelease: %u refs left\n", cRefs));
+ else
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ rtLocalIpcSessionWinDestroy(pThis);
+ }
+ return cRefs;
+}
+
+
+RTDECL(int) RTLocalIpcSessionClose(RTLOCALIPCSESSION hSession)
+{
+ /*
+ * Validate input.
+ */
+ if (hSession == NIL_RTLOCALIPCSESSION)
+ return VINF_SUCCESS;
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the instance, cancel all outstanding I/O and drop our reference.
+ */
+ RTCritSectEnter(&pThis->CritSect);
+ rtLocalIpcWinCancel(pThis);
+ return rtLocalIpcSessionReleaseAndUnlock(pThis);
+}
+
+
+/**
+ * Handles WaitForSingleObject return value when waiting for a zero byte read.
+ *
+ * The zero byte read is started by the RTLocalIpcSessionWaitForData method and
+ * left pending when the function times out. This saves us the problem of
+ * NtCancelIoFile messing with all active I/O operations and the trouble of
+ * restarting the zero byte read the next time the method is called. However
+ * should RTLocalIpcSessionRead be called after a failed
+ * RTLocalIpcSessionWaitForData call, the zero byte read will still be pending
+ * and it must wait for it to complete before the OVERLAPPEDIO structure can be
+ * reused.
+ *
+ * Thus, both functions will do WaitForSingleObject and share this routine to
+ * handle the outcome.
+ *
+ * @returns IPRT status code.
+ * @param pThis The session instance.
+ * @param rcWait The WaitForSingleObject return code.
+ */
+static int rtLocalIpcWinGetZeroReadResult(PRTLOCALIPCSESSIONINT pThis, DWORD rcWait)
+{
+ int rc;
+ DWORD cbRead = 42;
+ if (rcWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, !pThis->fCancelled /*fWait*/))
+ {
+ Assert(cbRead == 0);
+ rc = VINF_SUCCESS;
+ pThis->fZeroByteRead = false;
+ }
+ else if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ /* We try get the result here too, just in case we're lucky, but no waiting. */
+ DWORD dwErr = GetLastError();
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, FALSE /*fWait*/))
+ {
+ Assert(cbRead == 0);
+ rc = VINF_SUCCESS;
+ pThis->fZeroByteRead = false;
+ }
+ else if (rcWait == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (rcWait == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionRead(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ /* pcbRead is optional. */
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Read.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Read.hActiveThread = RTThreadSelf();
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ DWORD cbRead = 0;
+ if (!pThis->fCancelled)
+ {
+ /*
+ * Wait for pending zero byte read, if necessary.
+ * Note! It cannot easily be cancelled due to concurrent current writes.
+ */
+ if (!pThis->fZeroByteRead)
+ { /* likely */ }
+ else
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, RT_MS_1MIN);
+ RTCritSectEnter(&pThis->CritSect);
+
+ rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
+ if (RT_SUCCESS(rc) || rc == VERR_TIMEOUT)
+ continue;
+ break;
+ }
+
+ /*
+ * Kick of a an overlapped read. It should return immediately if
+ * there is bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
+ RTCritSectLeave(&pThis->CritSect);
+
+ if (ReadFile(pThis->hNmPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Read.OverlappedIO))
+ {
+ RTCritSectEnter(&pThis->CritSect);
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ {
+ if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc\n", rc));
+ }
+ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ /* Advance. */
+ cbToRead -= cbRead;
+ cbTotalRead += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionReadNB(RTLOCALIPCSESSION hSession, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+ *pcbRead = 0;
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Read.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Read.hActiveThread = RTThreadSelf();
+
+ for (;;)
+ {
+ DWORD cbRead = 0;
+ if (!pThis->fCancelled)
+ {
+ /*
+ * Wait for pending zero byte read, if necessary.
+ * Note! It cannot easily be cancelled due to concurrent current writes.
+ */
+ if (!pThis->fZeroByteRead)
+ { /* likely */ }
+ else
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
+ RTCritSectEnter(&pThis->CritSect);
+
+ rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
+ if (RT_SUCCESS(rc))
+ continue;
+
+ if (rc == VERR_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ break;
+ }
+
+ /*
+ * Figure out how much we can read (cannot try and cancel here
+ * like in the anonymous pipe code).
+ */
+ DWORD cbAvailable;
+ if (PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+ if (cbAvailable == 0 || cbToRead == 0)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ break;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+ if (cbAvailable > cbToRead)
+ cbAvailable = (DWORD)cbToRead;
+
+ /*
+ * Kick of a an overlapped read. It should return immediately, so we
+ * don't really need to leave the critsect here.
+ */
+ rc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(rc == TRUE);
+ if (ReadFile(pThis->hNmPipe, pvBuf, cbAvailable, &cbRead, &pThis->Read.OverlappedIO))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ DWORD rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, 0);
+ if (rcWait == WAIT_TIMEOUT)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+ rcWait = WaitForSingleObject(pThis->Read.OverlappedIO.hEvent, INFINITE);
+ RTCritSectEnter(&pThis->CritSect);
+ }
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Read.OverlappedIO, &cbRead, TRUE /*fWait*/))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ if (pThis->fCancelled)
+ rc = VERR_CANCELLED;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc\n", rc));
+ }
+ }
+ else
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+#if 0 /* Non-blocking writes are not yet supported. */
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTLocalIpcSessionClose and RTLocalIpcSessionWrite.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtLocalIpcSessionWriteCheckCompletion(PRTLOCALIPCSESSIONINT pThis)
+{
+ int rc;
+ DWORD rcWait = WaitForSingleObject(pThis->OverlappedIO.hEvent, 0);
+ if (rcWait == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->OverlappedIO, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fIOPending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
+ rc = ResetEvent(pThis->OverlappedIO.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hNmPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->OverlappedIO))
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fIOPending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (rcWait == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (rcWait == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+#endif
+
+
+RTDECL(int) RTLocalIpcSessionWrite(RTLOCALIPCSESSION hSession, const void *pvBuf, size_t cbToWrite)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite, VERR_INVALID_PARAMETER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Write.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Write.hActiveThread = RTThreadSelf();
+
+ /*
+ * Try write everything. No bounce buffering necessary.
+ */
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ DWORD cbWritten = 0;
+ if (!pThis->fCancelled)
+ {
+ BOOL fRc = ResetEvent(pThis->Write.OverlappedIO.hEvent); Assert(fRc == TRUE);
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
+ fRc = WriteFile(pThis->hNmPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Write.OverlappedIO);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ {
+ DWORD rcWait = WaitForSingleObject(pThis->Write.OverlappedIO.hEvent, INFINITE);
+ if (rcWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hNmPipe, &pThis->Write.OverlappedIO, &cbWritten, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (rcWait == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (rcWait == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+
+ if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
+ cbWritten = cbToWriteInThisIteration;
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ /* Advance. */
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ pThis->Write.hActiveThread = NIL_RTTHREAD;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionFlush(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ if (pThis->Write.hActiveThread == NIL_RTTHREAD)
+ {
+ /* No flushing on Windows needed since RTLocalIpcSessionWrite will block until
+ * all data was written (or an error occurred). */
+ /** @todo r=bird: above comment is misinformed.
+ * Implement this as soon as we want an explicit asynchronous version of
+ * RTLocalIpcSessionWrite on Windows. */
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionWaitForData(RTLOCALIPCSESSION hSession, uint32_t cMillies)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const msStart = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ if (pThis->Read.hActiveThread == NIL_RTTHREAD)
+ {
+ pThis->Read.hActiveThread = RTThreadSelf();
+
+ /*
+ * Wait loop.
+ */
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ /*
+ * Check for cancellation before we continue.
+ */
+ if (!pThis->fCancelled)
+ { /* likely */ }
+ else
+ {
+ rc = VERR_CANCELLED;
+ break;
+ }
+
+ /*
+ * Prep something we can wait on.
+ */
+ HANDLE hWait = INVALID_HANDLE_VALUE;
+ if (pThis->fZeroByteRead)
+ hWait = pThis->Read.OverlappedIO.hEvent;
+ else
+ {
+ /* Peek at the pipe buffer and see how many bytes it contains. */
+ DWORD cbAvailable;
+ if ( PeekNamedPipe(pThis->hNmPipe, NULL, 0, NULL, &cbAvailable, NULL)
+ && cbAvailable)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Start a zero byte read operation that we can wait on. */
+ if (cMillies == 0)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ BOOL fRc = ResetEvent(pThis->Read.OverlappedIO.hEvent); Assert(fRc == TRUE); NOREF(fRc);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hNmPipe, pThis->abBuf, 0 /*cbToRead*/, &cbRead, &pThis->Read.OverlappedIO))
+ {
+ rc = VINF_SUCCESS;
+ if (iLoop > 10)
+ RTThreadYield();
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fZeroByteRead = true;
+ hWait = pThis->Read.OverlappedIO.hEvent;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_FAILURE(rc))
+ break;
+ }
+
+ /*
+ * Check for timeout.
+ */
+ DWORD cMsMaxWait = INFINITE; /* (MSC maybe used uninitialized) */
+ if (cMillies == RT_INDEFINITE_WAIT)
+ cMsMaxWait = INFINITE;
+ else if ( hWait != INVALID_HANDLE_VALUE
+ || iLoop > 10)
+ {
+ uint64_t cMsElapsed = RTTimeMilliTS() - msStart;
+ if (cMsElapsed <= cMillies)
+ cMsMaxWait = cMillies - (uint32_t)cMsElapsed;
+ else if (iLoop == 0)
+ cMsMaxWait = cMillies ? 1 : 0;
+ else
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ }
+
+ /*
+ * Wait and collect the result.
+ */
+ if (hWait != INVALID_HANDLE_VALUE)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD rcWait = WaitForSingleObject(hWait, cMsMaxWait);
+
+ int rc2 = RTCritSectEnter(&pThis->CritSect);
+ AssertRC(rc2);
+
+ rc = rtLocalIpcWinGetZeroReadResult(pThis, rcWait);
+ break;
+ }
+ }
+
+ pThis->Read.hActiveThread = NIL_RTTHREAD;
+ }
+
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionCancel(RTLOCALIPCSESSION hSession)
+{
+ PRTLOCALIPCSESSIONINT pThis = (PRTLOCALIPCSESSIONINT)hSession;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTLOCALIPCSESSION_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Enter the critical section, then set the cancellation flag
+ * and signal the event (to wake up anyone in/at WaitForSingleObject).
+ */
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rtLocalIpcSessionRetain(pThis);
+ rc = rtLocalIpcWinCancel(pThis);
+ rtLocalIpcSessionReleaseAndUnlock(pThis);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryProcess(RTLOCALIPCSESSION hSession, PRTPROCESS pProcess)
+{
+ RT_NOREF_PV(hSession); RT_NOREF_PV(pProcess);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryUserId(RTLOCALIPCSESSION hSession, PRTUID pUid)
+{
+ RT_NOREF_PV(hSession); RT_NOREF_PV(pUid);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTLocalIpcSessionQueryGroupId(RTLOCALIPCSESSION hSession, PRTGID pGid)
+{
+ RT_NOREF_PV(hSession); RT_NOREF_PV(pGid);
+ return VERR_NOT_SUPPORTED;
+}
+
diff --git a/src/VBox/Runtime/r3/win/mp-win.cpp b/src/VBox/Runtime/r3/win/mp-win.cpp
new file mode 100644
index 00000000..b76ab254
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/mp-win.cpp
@@ -0,0 +1,822 @@
+/* $Id: mp-win.cpp $ */
+/** @file
+ * IPRT - Multiprocessor, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYSTEM
+#include <iprt/win/windows.h>
+
+#include <iprt/mp.h>
+#include "internal/iprt.h"
+
+#include <iprt/alloca.h>
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/param.h>
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+# include <iprt/asm-amd64-x86.h>
+#endif
+#if defined(VBOX) && !defined(IN_GUEST) && !defined(IN_RT_STATIC)
+# include <VBox/sup.h>
+# define IPRT_WITH_GIP_MP_INFO
+#else
+# undef IPRT_WITH_GIP_MP_INFO
+#endif
+
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def RTMPWIN_UPDATE_GIP_GLOBALS
+ * Does lazy (re-)initialization using information provieded by GIP. */
+#ifdef IPRT_WITH_GIP_MP_INFO
+# define RTMPWIN_UPDATE_GIP_GLOBALS() \
+ do { RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP(); } while (0)
+#else
+# define RTMPWIN_UPDATE_GIP_GLOBALS() do { } while (0)
+#endif
+
+/** @def RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP
+ * Does lazy (re-)initialization using information provieded by GIP and
+ * declare and initalize a pGip local variable. */
+#ifdef IPRT_WITH_GIP_MP_INFO
+#define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() \
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage; \
+ if (pGip) \
+ { \
+ if ( pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC \
+ && RTOnce(&g_MpInitOnceGip, rtMpWinInitOnceGip, NULL) == VINF_SUCCESS) \
+ { \
+ if (g_cRtMpWinActiveCpus >= pGip->cOnlineCpus) \
+ { /* likely */ } \
+ else \
+ rtMpWinRefreshGip(); \
+ } \
+ else \
+ pGip = NULL; \
+ } else do { } while (0)
+#else
+# define RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP() do { } while (0)
+#endif
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Initialize once. */
+static RTONCE g_MpInitOnce = RTONCE_INITIALIZER;
+#ifdef IPRT_WITH_GIP_MP_INFO
+/** Initialize once using GIP. */
+static RTONCE g_MpInitOnceGip = RTONCE_INITIALIZER;
+#endif
+
+static decltype(GetMaximumProcessorCount) *g_pfnGetMaximumProcessorCount;
+//static decltype(GetActiveProcessorCount) *g_pfnGetActiveProcessorCount;
+static decltype(GetCurrentProcessorNumber) *g_pfnGetCurrentProcessorNumber;
+static decltype(GetCurrentProcessorNumberEx) *g_pfnGetCurrentProcessorNumberEx;
+static decltype(GetLogicalProcessorInformation) *g_pfnGetLogicalProcessorInformation;
+static decltype(GetLogicalProcessorInformationEx) *g_pfnGetLogicalProcessorInformationEx;
+
+
+/** The required buffer size for getting group relations. */
+static uint32_t g_cbRtMpWinGrpRelBuf;
+/** The max number of CPUs (RTMpGetCount). */
+static uint32_t g_cRtMpWinMaxCpus;
+/** The max number of CPU cores (RTMpGetCoreCount). */
+static uint32_t g_cRtMpWinMaxCpuCores;
+/** The max number of groups. */
+static uint32_t g_cRtMpWinMaxCpuGroups;
+/** The number of active CPUs the last time we checked. */
+static uint32_t volatile g_cRtMpWinActiveCpus;
+/** Static per group info.
+ * @remarks With 256 entries this takes up 33KB.
+ * @sa g_aRtMpNtCpuGroups */
+static struct
+{
+ /** The max CPUs in the group. */
+ uint16_t cMaxCpus;
+ /** The number of active CPUs at the time of initialization. */
+ uint16_t cActiveCpus;
+ /** CPU set indexes for each CPU in the group. */
+ int16_t aidxCpuSetMembers[64];
+} g_aRtMpWinCpuGroups[256];
+/** Maps CPU set indexes to RTCPUID.
+ * @sa g_aidRtMpNtByCpuSetIdx */
+RTCPUID g_aidRtMpWinByCpuSetIdx[RTCPUSET_MAX_CPUS];
+
+
+/**
+ * @callback_method_impl{FNRTONCE,
+ * Resolves dynamic imports and initializes globals.}
+ */
+static DECLCALLBACK(int32_t) rtMpWinInitOnce(void *pvUser)
+{
+ RT_NOREF(pvUser);
+
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize != 0);
+ Assert(g_hModKernel32 != NULL);
+
+ /*
+ * Resolve dynamic APIs.
+ */
+#define RESOLVE_API(a_szMod, a_FnName) \
+ do { \
+ RT_CONCAT(g_pfn,a_FnName) = (decltype(a_FnName) *)GetProcAddress(g_hModKernel32, #a_FnName); \
+ } while (0)
+ RESOLVE_API("kernel32.dll", GetMaximumProcessorCount);
+ //RESOLVE_API("kernel32.dll", GetActiveProcessorCount); - slow :/
+ RESOLVE_API("kernel32.dll", GetCurrentProcessorNumber);
+ RESOLVE_API("kernel32.dll", GetCurrentProcessorNumberEx);
+ RESOLVE_API("kernel32.dll", GetLogicalProcessorInformation);
+ RESOLVE_API("kernel32.dll", GetLogicalProcessorInformationEx);
+
+ /*
+ * Reset globals.
+ */
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++)
+ g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID;
+ for (unsigned idxGroup = 0; idxGroup < RT_ELEMENTS(g_aRtMpWinCpuGroups); idxGroup++)
+ {
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = 0;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0;
+ for (unsigned idxMember = 0; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers); idxMember++)
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
+ }
+
+ /*
+ * Query group information, partitioning CPU IDs and CPU set indexes.
+ *
+ * We ASSUME the GroupInfo index is the same as the group number.
+ *
+ * We CANNOT ASSUME that the kernel CPU indexes are assigned in any given
+ * way, though they usually are in group order by active processor. So,
+ * we do that to avoid trouble. We must use information provided thru GIP
+ * if we want the kernel CPU set indexes. Even there, the inactive CPUs
+ * wont have sensible indexes. Sigh.
+ *
+ * We try to assign IDs to inactive CPUs in the same manner as mp-r0drv-nt.cpp
+ *
+ * Note! We will die (AssertFatal) if there are too many processors!
+ */
+ union
+ {
+ SYSTEM_INFO SysInfo;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX Info;
+ uint8_t abPaddingG[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + sizeof(PROCESSOR_GROUP_INFO) * 256];
+ uint8_t abPaddingC[ sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + (sizeof(PROCESSOR_RELATIONSHIP) + sizeof(GROUP_AFFINITY))
+ * RTCPUSET_MAX_CPUS];
+ } uBuf;
+ if (g_pfnGetLogicalProcessorInformationEx)
+ {
+ /* Query the information. */
+ DWORD cbData = sizeof(uBuf);
+ AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, &uBuf.Info, &cbData) != FALSE,
+ ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf)));
+ AssertFatalMsg(uBuf.Info.Relationship == RelationGroup,
+ ("Relationship = %u, expected %u!\n", uBuf.Info.Relationship, RelationGroup));
+ AssertFatalMsg(uBuf.Info.Group.MaximumGroupCount <= RT_ELEMENTS(g_aRtMpWinCpuGroups),
+ ("MaximumGroupCount is %u, we only support up to %u!\n",
+ uBuf.Info.Group.MaximumGroupCount, RT_ELEMENTS(g_aRtMpWinCpuGroups)));
+
+ AssertMsg(uBuf.Info.Group.MaximumGroupCount == uBuf.Info.Group.ActiveGroupCount, /* 2nd assumption mentioned above. */
+ ("%u vs %u\n", uBuf.Info.Group.MaximumGroupCount, uBuf.Info.Group.ActiveGroupCount));
+ AssertFatal(uBuf.Info.Group.MaximumGroupCount >= uBuf.Info.Group.ActiveGroupCount);
+
+ g_cRtMpWinMaxCpuGroups = uBuf.Info.Group.MaximumGroupCount;
+
+ /* Count max cpus (see mp-r0drv0-nt.cpp) why we don't use GetMaximumProcessorCount(ALL). */
+ uint32_t idxGroup;
+ g_cRtMpWinMaxCpus = 0;
+ for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++)
+ g_cRtMpWinMaxCpus += uBuf.Info.Group.GroupInfo[idxGroup].MaximumProcessorCount;
+
+ /* Process the active groups. */
+ uint32_t cActive = 0;
+ uint32_t cInactive = 0;
+ uint32_t idxCpu = 0;
+ uint32_t idxCpuSetNextInactive = g_cRtMpWinMaxCpus - 1;
+ for (idxGroup = 0; idxGroup < uBuf.Info.Group.ActiveGroupCount; idxGroup++)
+ {
+ PROCESSOR_GROUP_INFO const *pGroupInfo = &uBuf.Info.Group.GroupInfo[idxGroup];
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = pGroupInfo->MaximumProcessorCount;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = pGroupInfo->ActiveProcessorCount;
+ for (uint32_t idxMember = 0; idxMember < pGroupInfo->MaximumProcessorCount; idxMember++)
+ {
+ if (pGroupInfo->ActiveProcessorMask & RT_BIT_64(idxMember))
+ {
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpu;
+ g_aidRtMpWinByCpuSetIdx[idxCpu] = idxCpu;
+ idxCpu++;
+ cActive++;
+ }
+ else
+ {
+ if (idxCpuSetNextInactive >= idxCpu)
+ {
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
+ g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive;
+ idxCpuSetNextInactive--;
+ }
+ cInactive++;
+ }
+ }
+ }
+ g_cRtMpWinActiveCpus = cActive;
+ Assert(cActive + cInactive <= g_cRtMpWinMaxCpus);
+ Assert(idxCpu <= idxCpuSetNextInactive + 1);
+ Assert(idxCpu <= g_cRtMpWinMaxCpus);
+
+ /* Just in case the 2nd assumption doesn't hold true and there are inactive groups. */
+ for (; idxGroup < uBuf.Info.Group.MaximumGroupCount; idxGroup++)
+ {
+ DWORD cMaxMembers = g_pfnGetMaximumProcessorCount(idxGroup);
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = 0;
+ for (uint32_t idxMember = 0; idxMember < cMaxMembers; idxMember++)
+ {
+ if (idxCpuSetNextInactive >= idxCpu)
+ {
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxCpuSetNextInactive;
+ g_aidRtMpWinByCpuSetIdx[idxCpuSetNextInactive] = idxCpuSetNextInactive;
+ idxCpuSetNextInactive--;
+ }
+ cInactive++;
+ }
+ }
+ Assert(cActive + cInactive <= g_cRtMpWinMaxCpus);
+ Assert(idxCpu <= idxCpuSetNextInactive + 1);
+ }
+ else
+ {
+ /* Legacy: */
+ GetSystemInfo(&uBuf.SysInfo);
+ g_cRtMpWinMaxCpuGroups = 1;
+ g_cRtMpWinMaxCpus = uBuf.SysInfo.dwNumberOfProcessors;
+ g_aRtMpWinCpuGroups[0].cMaxCpus = uBuf.SysInfo.dwNumberOfProcessors;
+ g_aRtMpWinCpuGroups[0].cActiveCpus = uBuf.SysInfo.dwNumberOfProcessors;
+
+ for (uint32_t idxMember = 0; idxMember < uBuf.SysInfo.dwNumberOfProcessors; idxMember++)
+ {
+ g_aRtMpWinCpuGroups[0].aidxCpuSetMembers[idxMember] = idxMember;
+ g_aidRtMpWinByCpuSetIdx[idxMember] = idxMember;
+ }
+ }
+
+ AssertFatalMsg(g_cRtMpWinMaxCpus <= RTCPUSET_MAX_CPUS,
+ ("g_cRtMpWinMaxCpus=%u (%#x); RTCPUSET_MAX_CPUS=%u\n", g_cRtMpWinMaxCpus, g_cRtMpWinMaxCpus, RTCPUSET_MAX_CPUS));
+
+ g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO);
+
+ /*
+ * Get information about cores.
+ *
+ * Note! This will only give us info about active processors according to
+ * MSDN, we'll just have to hope that CPUs aren't hotplugged after we
+ * initialize here (or that the API consumers doesn't care too much).
+ */
+ /** @todo A hot CPU plug event would be nice. */
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus;
+ if (g_pfnGetLogicalProcessorInformationEx)
+ {
+ /* Query the information. */
+ DWORD cbData = sizeof(uBuf);
+ AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationProcessorCore, &uBuf.Info, &cbData) != FALSE,
+ ("last error = %u, cbData = %u (in %u)\n", GetLastError(), cbData, sizeof(uBuf)));
+ g_cRtMpWinMaxCpuCores = 0;
+ for (uint32_t off = 0; off < cbData; )
+ {
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pCur = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)&uBuf.abPaddingG[off];
+ AssertFatalMsg(pCur->Relationship == RelationProcessorCore,
+ ("off = %#x, Relationship = %u, expected %u!\n", off, pCur->Relationship, RelationProcessorCore));
+ g_cRtMpWinMaxCpuCores++;
+ off += pCur->Size;
+ }
+
+#if ARCH_BITS == 32
+ if (g_cRtMpWinMaxCpuCores > g_cRtMpWinMaxCpus)
+ {
+ /** @todo WOW64 trouble where the emulation environment has folded the high
+ * processor masks (63..32) into the low (31..0), hiding some
+ * processors from us. Currently we don't deal with that. */
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus;
+ }
+ else
+ AssertStmt(g_cRtMpWinMaxCpuCores > 0, g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
+#else
+ AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus,
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
+#endif
+ }
+ else
+ {
+ /*
+ * Sadly, on XP and Server 2003, even if the API is present, it does not tell us
+ * how many physical cores there are (any package will look like a single core).
+ * That is worse than not using the API at all, so just skip it unless it's Vista+.
+ */
+ if ( g_pfnGetLogicalProcessorInformation
+ && g_WinOsInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
+ && g_WinOsInfoEx.dwMajorVersion >= 6)
+ {
+ /* Query the info. */
+ DWORD cbSysProcInfo = _4K;
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION paSysInfo = NULL;
+ BOOL fRc = FALSE;
+ do
+ {
+ cbSysProcInfo = RT_ALIGN_32(cbSysProcInfo, 256);
+ void *pv = RTMemRealloc(paSysInfo, cbSysProcInfo);
+ if (!pv)
+ break;
+ paSysInfo = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)pv;
+ fRc = g_pfnGetLogicalProcessorInformation(paSysInfo, &cbSysProcInfo);
+ } while (!fRc && GetLastError() == ERROR_INSUFFICIENT_BUFFER);
+ if (fRc)
+ {
+ /* Count the cores in the result. */
+ g_cRtMpWinMaxCpuCores = 0;
+ uint32_t i = cbSysProcInfo / sizeof(paSysInfo[0]);
+ while (i-- > 0)
+ if (paSysInfo[i].Relationship == RelationProcessorCore)
+ g_cRtMpWinMaxCpuCores++;
+
+ AssertStmt(g_cRtMpWinMaxCpuCores > 0 && g_cRtMpWinMaxCpuCores <= g_cRtMpWinMaxCpus,
+ g_cRtMpWinMaxCpuCores = g_cRtMpWinMaxCpus);
+ }
+ RTMemFree(paSysInfo);
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IPRT_WITH_GIP_MP_INFO
+/**
+ * @callback_method_impl{FNRTONCE, Updates globals with information from GIP.}
+ */
+static DECLCALLBACK(int32_t) rtMpWinInitOnceGip(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if ( pGip
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
+ {
+ /*
+ * Update globals.
+ */
+ if (g_cRtMpWinMaxCpus != pGip->cPossibleCpus)
+ g_cRtMpWinMaxCpus = pGip->cPossibleCpus;
+ if (g_cRtMpWinActiveCpus != pGip->cOnlineCpus)
+ g_cRtMpWinActiveCpus = pGip->cOnlineCpus;
+ Assert(g_cRtMpWinMaxCpuGroups == pGip->cPossibleCpuGroups);
+ if (g_cRtMpWinMaxCpuGroups != pGip->cPossibleCpuGroups)
+ {
+ g_cRtMpWinMaxCpuGroups = pGip->cPossibleCpuGroups;
+ g_cbRtMpWinGrpRelBuf = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)
+ + (g_cRtMpWinMaxCpuGroups + 2) * sizeof(PROCESSOR_GROUP_INFO);
+ }
+
+ /*
+ * Update CPU set IDs.
+ */
+ for (unsigned i = g_cRtMpWinMaxCpus; i < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx); i++)
+ g_aidRtMpWinByCpuSetIdx[i] = NIL_RTCPUID;
+
+ unsigned const cbGip = pGip->cPages * PAGE_SIZE;
+ for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++)
+ {
+ uint32_t idxMember;
+ uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup];
+ if (offCpuGroup < cbGip)
+ {
+ PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup);
+ uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers;
+ AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers),
+ cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers));
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = cMaxMembers;
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
+
+ for (idxMember = 0; idxMember < cMaxMembers; idxMember++)
+ {
+ int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember];
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet;
+ if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
+# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
+# else
+ g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet;
+# endif
+ }
+ }
+ else
+ idxMember = 0;
+ for (; idxMember < RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers); idxMember++)
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = -1;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Refreshes globals from GIP after one or more CPUs were added.
+ *
+ * There are potential races here. We might race other threads and we may race
+ * more CPUs being added.
+ */
+static void rtMpWinRefreshGip(void)
+{
+ PSUPGLOBALINFOPAGE pGip = g_pSUPGlobalInfoPage;
+ if ( pGip
+ && pGip->u32Magic == SUPGLOBALINFOPAGE_MAGIC)
+ {
+ /*
+ * Since CPUs cannot be removed, we only have to update the IDs and
+ * indexes of CPUs that we think are inactive and the group member counts.
+ */
+ for (;;)
+ {
+ unsigned const cbGip = pGip->cPages * PAGE_SIZE;
+ uint32_t const cGipActiveCpus = pGip->cOnlineCpus;
+ uint32_t const cMyActiveCpus = ASMAtomicReadU32(&g_cRtMpWinActiveCpus);
+ ASMCompilerBarrier();
+
+ for (uint32_t idxGroup = 0; idxGroup < g_cRtMpWinMaxCpuGroups; idxGroup++)
+ {
+ uint32_t offCpuGroup = pGip->aoffCpuGroup[idxGroup];
+ if (offCpuGroup < cbGip)
+ {
+ PSUPGIPCPUGROUP pGipCpuGrp = (PSUPGIPCPUGROUP)((uintptr_t)pGip + offCpuGroup);
+ uint32_t cMaxMembers = pGipCpuGrp->cMaxMembers;
+ AssertStmt(cMaxMembers <= RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers),
+ cMaxMembers = RT_ELEMENTS(g_aRtMpWinCpuGroups[0].aidxCpuSetMembers));
+ for (uint32_t idxMember = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus; idxMember < cMaxMembers; idxMember++)
+ {
+ int16_t idxSet = pGipCpuGrp->aiCpuSetIdxs[idxMember];
+ g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember] = idxSet;
+ if ((unsigned)idxSet < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
+# ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ g_aidRtMpWinByCpuSetIdx[idxSet] = RTMPCPUID_FROM_GROUP_AND_NUMBER(idxGroup, idxMember);
+# else
+ g_aidRtMpWinByCpuSetIdx[idxSet] = idxSet;
+# endif
+ }
+ g_aRtMpWinCpuGroups[idxGroup].cMaxCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
+ g_aRtMpWinCpuGroups[idxGroup].cActiveCpus = RT_MIN(pGipCpuGrp->cMembers, cMaxMembers);
+ }
+ else
+ Assert(g_aRtMpWinCpuGroups[idxGroup].cActiveCpus == 0);
+ }
+
+ ASMCompilerBarrier();
+ if (cGipActiveCpus == pGip->cOnlineCpus)
+ if (ASMAtomicCmpXchgU32(&g_cRtMpWinActiveCpus, cGipActiveCpus, cMyActiveCpus))
+ break;
+ }
+ }
+}
+
+#endif /* IPRT_WITH_GIP_MP_INFO */
+
+
+/*
+ * Conversion between CPU ID and set index.
+ */
+
+RTDECL(int) RTMpCpuIdToSetIndex(RTCPUID idCpu)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ if (idCpu != NIL_RTCPUID)
+ return RTMpSetIndexFromCpuGroupMember(rtMpCpuIdGetGroup(idCpu), rtMpCpuIdGetGroupMember(idCpu));
+ return -1;
+
+#else
+ /* 1:1 mapping, just do range checking. */
+ return idCpu < g_cRtMpWinMaxCpus ? idCpu : -1;
+#endif
+}
+
+
+RTDECL(RTCPUID) RTMpCpuIdFromSetIndex(int iCpu)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ if ((unsigned)iCpu < RT_ELEMENTS(g_aidRtMpWinByCpuSetIdx))
+ {
+ RTCPUID idCpu = g_aidRtMpWinByCpuSetIdx[iCpu];
+
+#if defined(IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER) && defined(RT_STRICT)
+ /* Check the correctness of the mapping table. */
+ RTCPUID idCpuGip = NIL_RTCPUID;
+ if ( pGip
+ && (unsigned)iCpu < RT_ELEMENTS(pGip->aiCpuFromCpuSetIdx))
+ {
+ unsigned idxSupCpu = pGip->aiCpuFromCpuSetIdx[idxGuess];
+ if (idxSupCpu < pGip->cCpus)
+ if (pGip->aCPUs[idxSupCpu].enmState != SUPGIPCPUSTATE_INVALID)
+ idCpuGip = pGip->aCPUs[idxSupCpu].idCpu;
+ }
+ AssertMsg(idCpu == idCpuGip, ("table:%#x gip:%#x\n", idCpu, idCpuGip));
+#endif
+
+ return idCpu;
+ }
+ return NIL_RTCPUID;
+}
+
+
+RTDECL(int) RTMpSetIndexFromCpuGroupMember(uint32_t idxGroup, uint32_t idxMember)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ if (idxGroup < g_cRtMpWinMaxCpuGroups)
+ if (idxMember < g_aRtMpWinCpuGroups[idxGroup].cMaxCpus)
+ return g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember];
+ return -1;
+}
+
+
+RTDECL(uint32_t) RTMpGetCpuGroupCounts(uint32_t idxGroup, uint32_t *pcActive)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ if (idxGroup < g_cRtMpWinMaxCpuGroups)
+ {
+ if (pcActive)
+ *pcActive = g_aRtMpWinCpuGroups[idxGroup].cActiveCpus;
+ return g_aRtMpWinCpuGroups[idxGroup].cMaxCpus;
+ }
+ if (pcActive)
+ *pcActive = 0;
+ return 0;
+}
+
+
+RTDECL(uint32_t) RTMpGetMaxCpuGroupCount(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ return g_cRtMpWinMaxCpuGroups;
+}
+
+
+
+/*
+ * Get current CPU.
+ */
+
+RTDECL(RTCPUID) RTMpCpuId(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ PROCESSOR_NUMBER ProcNum;
+ ProcNum.Group = 0;
+ ProcNum.Number = 0xff;
+ if (g_pfnGetCurrentProcessorNumberEx)
+ g_pfnGetCurrentProcessorNumberEx(&ProcNum);
+ else if (g_pfnGetCurrentProcessorNumber)
+ {
+ DWORD iCpu = g_pfnGetCurrentProcessorNumber();
+ Assert(iCpu < g_cRtMpWinMaxCpus);
+ ProcNum.Number = iCpu;
+ }
+ else
+ {
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ ProcNum.Number = ASMGetApicId();
+#else
+# error "Not ported to this architecture."
+ return NIL_RTCPUID;
+#endif
+ }
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(ProcNum.Group, ProcNum.Number);
+#else
+ return RTMpSetIndexFromCpuGroupMember(ProcNum.Group, ProcNum.Number);
+#endif
+}
+
+
+/*
+ * Possible CPUs and cores.
+ */
+
+RTDECL(RTCPUID) RTMpGetMaxCpuId(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+#ifdef IPRT_WITH_RTCPUID_AS_GROUP_AND_NUMBER
+ return RTMPCPUID_FROM_GROUP_AND_NUMBER(g_cRtMpWinMaxCpuGroups - 1,
+ g_aRtMpWinCpuGroups[g_cRtMpWinMaxCpuGroups - 1].cMaxCpus - 1);
+#else
+ return g_cRtMpWinMaxCpus - 1;
+#endif
+}
+
+
+RTDECL(bool) RTMpIsCpuPossible(RTCPUID idCpu)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ /* Any CPU between 0 and g_cRtMpWinMaxCpus are possible. */
+ return idCpu < g_cRtMpWinMaxCpus;
+}
+
+
+RTDECL(PRTCPUSET) RTMpGetSet(PRTCPUSET pSet)
+{
+ RTCPUID iCpu = RTMpGetCount();
+ RTCpuSetEmpty(pSet);
+ while (iCpu-- > 0)
+ RTCpuSetAddByIndex(pSet, iCpu);
+ return pSet;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCount(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ return g_cRtMpWinMaxCpus;
+}
+
+
+RTDECL(RTCPUID) RTMpGetCoreCount(void)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+ RTMPWIN_UPDATE_GIP_GLOBALS();
+
+ return g_cRtMpWinMaxCpuCores;
+}
+
+
+/*
+ * Online CPUs and cores.
+ */
+
+RTDECL(PRTCPUSET) RTMpGetOnlineSet(PRTCPUSET pSet)
+{
+ RTOnce(&g_MpInitOnce, rtMpWinInitOnce, NULL);
+
+#ifdef IPRT_WITH_GIP_MP_INFO
+ RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP();
+ if (pGip)
+ {
+ *pSet = pGip->OnlineCpuSet;
+ return pSet;
+ }
+#endif
+
+ if (g_pfnGetLogicalProcessorInformationEx)
+ {
+ /*
+ * Get the group relation info.
+ *
+ * In addition to the ASSUMPTIONS that are documented in rtMpWinInitOnce,
+ * we ASSUME that PROCESSOR_GROUP_INFO::MaximumProcessorCount gives the
+ * active processor mask width.
+ */
+ /** @todo this is not correct for WOW64 */
+ DWORD cbInfo = g_cbRtMpWinGrpRelBuf;
+ SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *pInfo = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX *)alloca(cbInfo);
+ AssertFatalMsg(g_pfnGetLogicalProcessorInformationEx(RelationGroup, pInfo, &cbInfo) != FALSE,
+ ("last error = %u, cbInfo = %u (in %u)\n", GetLastError(), cbInfo, g_cbRtMpWinGrpRelBuf));
+ AssertFatalMsg(pInfo->Relationship == RelationGroup,
+ ("Relationship = %u, expected %u!\n", pInfo->Relationship, RelationGroup));
+ AssertFatalMsg(pInfo->Group.MaximumGroupCount == g_cRtMpWinMaxCpuGroups,
+ ("MaximumGroupCount is %u, expected %u!\n", pInfo->Group.MaximumGroupCount, g_cRtMpWinMaxCpuGroups));
+
+ RTCpuSetEmpty(pSet);
+ for (uint32_t idxGroup = 0; idxGroup < pInfo->Group.MaximumGroupCount; idxGroup++)
+ {
+ Assert(pInfo->Group.GroupInfo[idxGroup].MaximumProcessorCount == g_aRtMpWinCpuGroups[idxGroup].cMaxCpus);
+ Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount <= g_aRtMpWinCpuGroups[idxGroup].cMaxCpus);
+
+ KAFFINITY fActive = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorMask;
+ if (fActive != 0)
+ {
+#ifdef RT_STRICT
+ uint32_t cMembersLeft = pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount;
+#endif
+ int const cMembers = g_aRtMpWinCpuGroups[idxGroup].cMaxCpus;
+ for (int idxMember = 0; idxMember < cMembers; idxMember++)
+ {
+ if (fActive & 1)
+ {
+#ifdef RT_STRICT
+ cMembersLeft--;
+#endif
+ RTCpuSetAddByIndex(pSet, g_aRtMpWinCpuGroups[idxGroup].aidxCpuSetMembers[idxMember]);
+ fActive >>= 1;
+ if (!fActive)
+ break;
+ }
+ else
+ {
+ fActive >>= 1;
+ }
+ }
+ Assert(cMembersLeft == 0);
+ }
+ else
+ Assert(pInfo->Group.GroupInfo[idxGroup].ActiveProcessorCount == 0);
+ }
+
+ return pSet;
+ }
+
+ /*
+ * Legacy fallback code.
+ */
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ return RTCpuSetFromU64(pSet, SysInfo.dwActiveProcessorMask);
+}
+
+
+RTDECL(bool) RTMpIsCpuOnline(RTCPUID idCpu)
+{
+ RTCPUSET Set;
+ return RTCpuSetIsMember(RTMpGetOnlineSet(&Set), idCpu);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCount(void)
+{
+#ifdef IPRT_WITH_GIP_MP_INFO
+ RTMPWIN_UPDATE_GIP_GLOBALS_AND_GET_PGIP();
+ if (pGip)
+ return pGip->cOnlineCpus;
+#endif
+
+ RTCPUSET Set;
+ RTMpGetOnlineSet(&Set);
+ return RTCpuSetCount(&Set);
+}
+
+
+RTDECL(RTCPUID) RTMpGetOnlineCoreCount(void)
+{
+ /** @todo this isn't entirely correct, but whatever. */
+ return RTMpGetCoreCount();
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp
new file mode 100644
index 00000000..5f74b4f5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdErr-win.cpp
@@ -0,0 +1,57 @@
+/* $Id: nocrt-RTLogWriteStdErr-win.cpp $ */
+/** @file
+ * IPRT - Log To StdErr, Windows no-CRT.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/log.h>
+#include "internal/iprt.h"
+
+#include <iprt/win/windows.h>
+
+
+RTDECL(void) RTLogWriteStdErr(const char *pch, size_t cb)
+{
+ HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
+ if (hStdErr != NULL && hStdErr != INVALID_HANDLE_VALUE)
+ {
+ DWORD cbIgn; /* NT3.1 requires the return size parameter. */
+ WriteFile(hStdErr, pch, (DWORD)cb, &cbIgn, NULL); /** @todo do we need to translate \\n to \\r\\n? */
+ }
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdErr);
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp
new file mode 100644
index 00000000..56e29134
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-RTLogWriteStdOut-win.cpp
@@ -0,0 +1,58 @@
+/* $Id: nocrt-RTLogWriteStdOut-win.cpp $ */
+/** @file
+ * IPRT - Log To StdOut, Windows no-CRT.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/log.h>
+#include "internal/iprt.h"
+
+#include <iprt/win/windows.h>
+
+
+RTDECL(void) RTLogWriteStdOut(const char *pch, size_t cb)
+{
+ /** @todo should flush the stdout stream first... */
+ HANDLE hStdErr = GetStdHandle(STD_OUTPUT_HANDLE);
+ if (hStdErr != NULL && hStdErr != INVALID_HANDLE_VALUE)
+ {
+ DWORD cbIgn; /* NT3.1 requires the return size parameter. */
+ WriteFile(hStdErr, pch, (DWORD)cb, &cbIgn, NULL); /** @todo do we need to translate \\n to \\r\\n? */
+ }
+}
+RT_EXPORT_SYMBOL(RTLogWriteStdOut);
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm b/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm
new file mode 100644
index 00000000..1943ad69
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-WinMainCRTStartup-win.asm
@@ -0,0 +1,44 @@
+; $Id: nocrt-WinMainCRTStartup-win.asm $
+;; @file
+; IPRT - Alias WinMainCRTStartup to CustomMainEntrypoint in nocrt-startup-exe-win.cpp.
+;
+
+;
+; Copyright (C) 2022-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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+extern NAME(CustomMainEntrypoint)
+BEGINPROC WinMainCRTStartup
+ jmp NAME(CustomMainEntrypoint)
+ENDPROC WinMainCRTStartup
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp b/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp
new file mode 100644
index 00000000..128b58df
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-alloc-win.cpp
@@ -0,0 +1,115 @@
+/* $Id: nocrt-alloc-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Basic allocators, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/mem.h>
+
+#include <iprt/nt/nt-and-windows.h>
+
+
+#undef RTMemTmpFree
+RTDECL(void) RTMemTmpFree(void *pv)
+{
+ HeapFree(GetProcessHeap(), 0, pv);
+}
+
+
+#undef RTMemFree
+RTDECL(void) RTMemFree(void *pv)
+{
+ HeapFree(GetProcessHeap(), 0, pv);
+}
+
+
+#undef RTMemTmpAllocTag
+RTDECL(void *) RTMemTmpAllocTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+
+#undef RTMemTmpAllocZTag
+RTDECL(void *) RTMemTmpAllocZTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
+}
+
+
+#undef RTMemAllocTag
+RTDECL(void *) RTMemAllocTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), 0, cb);
+}
+
+
+#undef RTMemAllocZTag
+RTDECL(void *) RTMemAllocZTag(size_t cb, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cb);
+}
+
+
+#undef RTMemAllocVarTag
+RTDECL(void *) RTMemAllocVarTag(size_t cbUnaligned, const char *pszTag)
+{
+ return RTMemAllocTag(cbUnaligned, pszTag);
+}
+
+
+#undef RTMemAllocZVarTag
+RTDECL(void *) RTMemAllocZVarTag(size_t cbUnaligned, const char *pszTag)
+{
+ return RTMemAllocZTag(cbUnaligned, pszTag);
+}
+
+
+#undef RTMemReallocTag
+RTDECL(void *) RTMemReallocTag(void *pvOld, size_t cbNew, const char *pszTag)
+{
+ RT_NOREF(pszTag);
+ if (pvOld)
+ return HeapReAlloc(GetProcessHeap(), 0, pvOld, cbNew);
+ return HeapAlloc(GetProcessHeap(), 0, cbNew);
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm b/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm
new file mode 100644
index 00000000..881463ec
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-atexit-win.asm
@@ -0,0 +1,44 @@
+; $Id: nocrt-atexit-win.asm $
+;; @file
+; IPRT - Alias atexit to rtnocrt_atexit in nocrt-startup-exe-win.cpp.
+;
+
+;
+; Copyright (C) 2022-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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+extern NAME(nocrt_atexit)
+BEGINPROC atexit
+ jmp NAME(nocrt_atexit)
+ENDPROC atexit
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp b/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp
new file mode 100644
index 00000000..c9bfa1b4
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-fatal-write-win.cpp
@@ -0,0 +1,150 @@
+/* $Id: nocrt-fatal-write-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Fatal Message Writing Primitives, Windows.
+ */
+
+/*
+ * Copyright (C) 2022-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+
+#include <iprt/win/windows.h>
+#include <iprt/string.h>
+
+
+/** @todo invent some kind of weak linking with the debug and release
+ * loggers, e.g. theset some innocent function we can call to do
+ * logging and we'll call it if it isn't NULL. */
+
+void rtNoCrtFatalWrite(const char *pchMsg, size_t cchMsg)
+{
+ DWORD cbIgn;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE), pchMsg, (DWORD)cchMsg, &cbIgn, NULL);
+}
+
+
+void rtNoCrtFatalWriteBegin(const char *pchMsg, size_t cchMsg)
+{
+ rtNoCrtFatalWrite(pchMsg, cchMsg);
+}
+
+
+void rtNoCrtFatalWriteEnd(const char *pchMsg, size_t cchMsg)
+{
+ rtNoCrtFatalWrite(pchMsg, cchMsg);
+}
+
+
+void rtNoCrtFatalWritePtr(void const *pv)
+{
+ char szValue[128];
+#if ARCH_BITS == 64
+ ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), (uintptr_t)pv, 16, 16, 16, RTSTR_F_WIDTH | RTSTR_F_64BIT);
+#elif ARCH_BITS == 32
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), (uintptr_t)pv, 16, 8, 8, RTSTR_F_WIDTH | RTSTR_F_32BIT);
+#else
+# error ARCH_BITS
+#endif
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteX64(uint64_t uValue)
+{
+ char szValue[128];
+ ssize_t cchValue = RTStrFormatU64(szValue, sizeof(szValue), uValue, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_64BIT);
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteX32(uint32_t uValue)
+{
+ char szValue[128];
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), uValue, 16, 0, 0, RTSTR_F_SPECIAL | RTSTR_F_32BIT);
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteRc(int rc)
+{
+ char szValue[128];
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), rc, 10, 0, 0, RTSTR_F_32BIT | RTSTR_F_VALSIGNED);
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+void rtNoCrtFatalWriteWinRc(uint32_t rc)
+{
+ char szValue[168];
+ ssize_t cchValue = RTStrFormatU32(szValue, sizeof(szValue), rc, 10, 0, 0, RTSTR_F_32BIT);
+ szValue[cchValue++] = ' ';
+ szValue[cchValue++] = '(';
+ cchValue += RTStrFormatU32(&szValue[cchValue], sizeof(szValue) - cchValue, rc, 16, 0, 0, RTSTR_F_32BIT | RTSTR_F_SPECIAL);
+ szValue[cchValue++] = ')';
+ rtNoCrtFatalWrite(szValue, (size_t)cchValue);
+}
+
+
+void rtNoCrtFatalWriteStr(const char *pszString)
+{
+ if (RT_VALID_PTR(pszString))
+ {
+ size_t cch = 0;
+ while (pszString[cch] != '\0')
+ cch++;
+ rtNoCrtFatalWrite(pszString, cch);
+ }
+ else
+ {
+ rtNoCrtFatalWrite(RT_STR_TUPLE("<pszString="));
+ rtNoCrtFatalWritePtr(pszString);
+ rtNoCrtFatalWrite(RT_STR_TUPLE(">"));
+ }
+}
+
+
+void rtNoCrtFatalMsg(const char *pchMsg, size_t cchMsg)
+{
+ rtNoCrtFatalWriteBegin(pchMsg, cchMsg);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE(""));
+}
+
+
+void rtNoCrtFatalMsgWithRc(const char *pchMsg, size_t cchMsg, int rc)
+{
+ rtNoCrtFatalWriteBegin(pchMsg, cchMsg);
+ rtNoCrtFatalWriteRc(rc);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm b/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm
new file mode 100644
index 00000000..54651dd1
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-mainCRTStartup-win.asm
@@ -0,0 +1,44 @@
+; $Id: nocrt-mainCRTStartup-win.asm $
+;; @file
+; IPRT - Alias mainCRTStartup to CustomMainEntrypoint in nocrt-startup-exe-win.cpp.
+;
+
+;
+; Copyright (C) 2022-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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+extern NAME(CustomMainEntrypoint)
+BEGINPROC mainCRTStartup
+ jmp NAME(CustomMainEntrypoint)
+ENDPROC mainCRTStartup
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp
new file mode 100644
index 00000000..598339db
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-startup-common-win.cpp
@@ -0,0 +1,211 @@
+/* $Id: nocrt-startup-common-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Common Windows startup code.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include "internal/process.h"
+
+#include <iprt/nt/nt-and-windows.h>
+#ifndef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+# include <iprt/assert.h>
+#endif
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal/compiler-vcc.h"
+#include "internal/process.h"
+
+
+#ifdef RT_ARCH_X86
+/**
+ * NT 3.1 does not know about the IMAGE_SECTION_HEADER::Misc.VirtualSize field
+ * and will therefore not handle merging initialized and uninitialized data into
+ * the same section.
+ *
+ * We work around this by manually zeroing the uninitialized data before any
+ * other code has been executed.
+ */
+void rtVccWinInitBssOnNt3(void *pvImageBase)
+{
+ /* We are called really early on, so we must figure out the NT version
+ on our own. It doesn't have to be all that accurate, though, as only
+ NT 3.10 is affected (3.50 isn't). */
+ DWORD const dwRawVer = GetVersion();
+ DWORD const uMajorVer = RT_BYTE1(dwRawVer);
+ DWORD const uMinorVer = RT_BYTE2(dwRawVer);
+ if (uMajorVer != 3 || uMinorVer >= 50)
+ return;
+
+ /*
+ * Locate the NT headers.
+ */
+ PIMAGE_NT_HEADERS pNtHdrs;
+ PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)pvImageBase;
+ if (pDosHdr->e_magic == IMAGE_DOS_SIGNATURE)
+ pNtHdrs = (PIMAGE_NT_HEADERS)((uintptr_t)pvImageBase + pDosHdr->e_lfanew);
+ else
+ pNtHdrs = (PIMAGE_NT_HEADERS)pvImageBase;
+ if (pNtHdrs->Signature == IMAGE_NT_SIGNATURE)
+ {
+ /*
+ * Locate the section table and walk thru it, memsetting anything that
+ * wasn't loaded from the file.
+ */
+ PIMAGE_SECTION_HEADER paSHdrs = (PIMAGE_SECTION_HEADER)( (uintptr_t)&pNtHdrs->OptionalHeader
+ + pNtHdrs->FileHeader.SizeOfOptionalHeader);
+ uint32_t const cSections = pNtHdrs->FileHeader.NumberOfSections;
+ for (uint32_t i = 0; i < cSections; i++)
+ {
+ if (paSHdrs[i].Misc.VirtualSize > paSHdrs[i].SizeOfRawData)
+ {
+ /* ASSUMES VirtualAddress is still an RVAs */
+ uint8_t *pbToZero = (uint8_t *)pvImageBase + paSHdrs[i].VirtualAddress + paSHdrs[i].SizeOfRawData;
+ uint32_t const cbToZero = paSHdrs[i].Misc.VirtualSize - paSHdrs[i].SizeOfRawData;
+
+# if 0 /* very very crude debugging */
+ const char *pszHex = "0123456789abcdef";
+ char szMsg[128];
+ char *psz = szMsg;
+ *psz++ = 'd'; *psz++ = 'b'; *psz++ = 'g'; *psz++ = ':'; *psz++ = ' ';
+ for (uint32_t q = 0, u = i; q < 8; q++, u >>= 4)
+ psz[7 - q] = pszHex[u & 0xf];
+ psz += 8;
+ *psz++ = ':';
+ *psz++ = ' ';
+ for (uint32_t q = 0, u = (uint32_t)pbToZero; q < 8; q++, u >>= 4)
+ psz[7 - q] = pszHex[u & 0xf];
+ psz += 8;
+ *psz++ = ' ';
+ *psz++ = 'L';
+ *psz++ = 'B';
+ *psz++ = ' ';
+ for (uint32_t q = 0, u = cbToZero; q < 8; q++, u >>= 4)
+ psz[7 - q] = pszHex[u & 0xf];
+ psz += 8;
+ *psz++ = ' ';
+ for (uint32_t q = 0; q < 8; q++)
+ *psz++ = paSHdrs[i].Name[q] ? paSHdrs[i].Name[q] : ' ';
+ *psz++ = ' '; *psz++ = '/'; *psz++ = ' '; *psz++ = 'v'; *psz++ = 'e'; *psz++ = 'r'; *psz++ = ' ';
+ *psz++ = pszHex[(uMajorVer >> 4) & 0xf];
+ *psz++ = pszHex[uMajorVer & 0xf];
+ *psz++ = '.';
+ *psz++ = pszHex[(uMinorVer >> 4) & 0xf];
+ *psz++ = pszHex[uMinorVer & 0xf];
+ *psz++ = '\r'; *psz++ = '\n';
+ *psz = '\0';
+ DWORD cbIgn;
+ HANDLE hOut = RTNtCurrentPeb()->ProcessParameters->StandardOutput;
+ if (hOut == NULL || hOut == INVALID_HANDLE_VALUE)
+ hOut = RTNtCurrentPeb()->ProcessParameters->StandardError;
+ if (hOut == NULL || hOut == INVALID_HANDLE_VALUE)
+ hOut = RTNtCurrentPeb()->ProcessParameters->ConsoleHandle;
+ if (hOut == NULL || hOut == INVALID_HANDLE_VALUE)
+ hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+ WriteFile(hOut, szMsg, psz - szMsg, &cbIgn, NULL);
+# endif
+
+ if (paSHdrs[i].Characteristics & IMAGE_SCN_MEM_WRITE)
+ memset(pbToZero, 0, cbToZero);
+ else
+ {
+ /* The section is not writable, so temporarily make it writable. */
+ PVOID pvAligned = pbToZero - ((uintptr_t)pbToZero & PAGE_OFFSET_MASK);
+ ULONG cbAligned = RT_ALIGN_32(cbToZero + ((uintptr_t)pbToZero & PAGE_OFFSET_MASK), PAGE_SIZE);
+ ULONG fNewProt = paSHdrs[i].Characteristics & IMAGE_SCN_MEM_EXECUTE
+ ? PAGE_EXECUTE_READWRITE : PAGE_READWRITE;
+ ULONG fOldProt = fNewProt;
+ NTSTATUS rcNt = NtProtectVirtualMemory(NtCurrentProcess(), &pvAligned, &cbAligned, fNewProt, &fOldProt);
+ if (NT_SUCCESS(rcNt))
+ {
+ memset(pbToZero, 0, cbToZero);
+
+ rcNt = NtProtectVirtualMemory(NtCurrentProcess(), &pvAligned, &cbAligned, fOldProt, &fNewProt);
+ }
+ else
+ RT_BREAKPOINT();
+ }
+ }
+ }
+ }
+ else
+ RT_BREAKPOINT();
+}
+#endif
+
+
+void rtVccWinInitProcExecPath(void)
+{
+ WCHAR wszPath[RTPATH_MAX];
+ UINT cwcPath = GetModuleFileNameW(NULL, wszPath, RT_ELEMENTS(wszPath));
+ if (cwcPath)
+ {
+ char *pszDst = g_szrtProcExePath;
+ int rc = RTUtf16ToUtf8Ex(wszPath, cwcPath, &pszDst, sizeof(g_szrtProcExePath), &g_cchrtProcExePath);
+ if (RT_SUCCESS(rc))
+ {
+ g_cchrtProcExeDir = g_offrtProcName = RTPathFilename(pszDst) - g_szrtProcExePath;
+ while ( g_cchrtProcExeDir >= 2
+ && RTPATH_IS_SLASH(g_szrtProcExePath[g_cchrtProcExeDir - 1])
+ && g_szrtProcExePath[g_cchrtProcExeDir - 2] != ':')
+ g_cchrtProcExeDir--;
+ }
+ else
+ {
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("initProcExecPath: RTUtf16ToUtf8Ex failed: %Rrc\n", rc);
+#else
+ rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("initProcExecPath: RTUtf16ToUtf8Ex failed: "), rc);
+#endif
+ }
+ }
+ else
+ {
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("initProcExecPath: GetModuleFileNameW failed: %Rhrc\n", GetLastError());
+#else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("initProcExecPath: GetModuleFileNameW failed: "));
+ rtNoCrtFatalWriteWinRc(GetLastError());
+ rtNoCrtFatalWrite(RT_STR_TUPLE("\r\n"));
+#endif
+ }
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp
new file mode 100644
index 00000000..77bf0951
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-startup-dll-win.cpp
@@ -0,0 +1,162 @@
+/* $Id: nocrt-startup-dll-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Windows EXE startup code.
+ *
+ * @note Does not run static constructors and destructors!
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include "internal/process.h"
+
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#ifdef IPRT_NO_CRT
+# include <iprt/asm.h>
+# include <iprt/nocrt/stdlib.h>
+#endif
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile int32_t g_cAttached = 0;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved);
+
+
+DECL_NO_INLINE(static, BOOL) rtVccDllMainForward(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved)
+{
+ return DllMain(hInstance, dwReason, pvReserved);
+}
+
+
+
+DECL_NO_INLINE(static, BOOL) rtVccDllMainProcessAttach(HINSTANCE hInstance, LPVOID pvReserved)
+{
+ /*
+ * Initialize the CRT the first time thru.
+ */
+ if (g_cAttached == 0)
+ {
+ rtVccWinInitProcExecPath();
+
+ int rc = rtVccInitializersRunInit();
+ if (RT_FAILURE(rc))
+ return FALSE;
+ }
+ g_cAttached++;
+
+ /*
+ * Call the DllMain function.
+ */
+ BOOL fRet = rtVccDllMainForward(hInstance, DLL_PROCESS_ATTACH, pvReserved);
+
+ /*
+ * On failure, we call the DllMain function again, decrement the init counter
+ * and probably run termination callbacks.
+ */
+ if (!fRet)
+ {
+ rtVccDllMainForward(hInstance, DLL_PROCESS_DETACH, pvReserved);
+ if (--g_cAttached == 0)
+ {
+ rtVccTermRunAtExit();
+ rtVccInitializersRunTerm();
+ }
+ }
+ return fRet;
+}
+
+
+DECL_NO_INLINE(static, BOOL) rtVccDllMainProcessDetach(HINSTANCE hInstance, LPVOID pvReserved)
+{
+ /*
+ * Make sure there isn't an imbalance before calling DllMain and shutting
+ * down our own internals.
+ */
+ if (g_cAttached <= 0)
+ return FALSE;
+
+ /*
+ * Call DllMain.
+ */
+ BOOL fRet = rtVccDllMainForward(hInstance, DLL_PROCESS_DETACH, pvReserved);
+
+ /*
+ * Work g_cAttached and probably do uninitialization. We'll do this regardless
+ * of what DllMain returned.
+ */
+ if (--g_cAttached == 0)
+ {
+ rtVccTermRunAtExit();
+ rtVccInitializersRunTerm();
+ }
+ return fRet;
+}
+
+
+extern "C" BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInstance, DWORD dwReason, LPVOID pvReserved)
+{
+ switch (dwReason)
+ {
+ case DLL_PROCESS_ATTACH:
+#ifdef RT_ARCH_X86
+ rtVccWinInitBssOnNt3((PVOID)hInstance);
+#endif
+ rtVccInitSecurityCookie(); /* This function must be minimal because of this! */
+ return rtVccDllMainProcessAttach(hInstance, pvReserved);
+
+ case DLL_PROCESS_DETACH:
+ return rtVccDllMainProcessDetach(hInstance, pvReserved);
+
+ default:
+ return rtVccDllMainForward(hInstance, dwReason, pvReserved);
+ }
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp b/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp
new file mode 100644
index 00000000..543ea05c
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-startup-exe-win.cpp
@@ -0,0 +1,174 @@
+/* $Id: nocrt-startup-exe-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - Windows EXE startup code.
+ *
+ * @note Does not run static constructors and destructors!
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/nocrt.h"
+#include "internal/process.h"
+
+#include <iprt/nt/nt-and-windows.h>
+#include <iprt/getopt.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include "internal/compiler-vcc.h"
+
+
+/*********************************************************************************************************************************
+* External Symbols *
+*********************************************************************************************************************************/
+extern int main(int argc, char **argv, char **envp); /* in program */
+#ifndef IPRT_NO_CRT
+extern DECLHIDDEN(void) InitStdHandles(PRTL_USER_PROCESS_PARAMETERS pParams); /* nocrt-streams-win.cpp */ /** @todo put in header */
+#endif
+
+
+static int rtTerminateProcess(int32_t rcExit, bool fDoAtExit)
+{
+#ifdef IPRT_NO_CRT
+ /*
+ * Run atexit callback in reverse order.
+ */
+ if (fDoAtExit)
+ {
+ rtVccTermRunAtExit();
+ rtVccInitializersRunTerm();
+ }
+#else
+ RT_NOREF(fDoAtExit);
+#endif
+
+ /*
+ * Terminate.
+ */
+ for (;;)
+ NtTerminateProcess(NtCurrentProcess(), rcExit);
+}
+
+
+DECLASM(void) CustomMainEntrypoint(void)
+{
+ /* Looks like might have gotten the PPEB as parameter here before NT4,
+ however, there the EXE entry function clearly takes no parameters.
+ So, we have to retrieve the PEB our selves here. */
+ PPEB_COMMON const pPeb = RTNtCurrentPeb();
+
+ /*
+ * Initialize stuff.
+ */
+#ifdef IPRT_NO_CRT
+# ifdef RT_ARCH_X86
+ rtVccWinInitBssOnNt3(pPeb->ImageBaseAddress);
+# endif
+ rtVccInitSecurityCookie();
+#else
+ InitStdHandles(pPeb->ProcessParameters);
+#endif
+ rtVccWinInitProcExecPath();
+
+ RTEXITCODE rcExit;
+#ifdef IPRT_NO_CRT
+ AssertCompile(sizeof(rcExit) == sizeof(int));
+ rcExit = (RTEXITCODE)rtVccInitializersRunInit();
+ if (rcExit == RTEXITCODE_SUCCESS)
+#endif
+ {
+ /*
+ * Get and convert the command line to argc/argv format.
+ */
+ rcExit = RTEXITCODE_INIT;
+ UNICODE_STRING const *pCmdLine = pPeb->ProcessParameters ? &pPeb->ProcessParameters->CommandLine : NULL;
+ if (pCmdLine)
+ {
+ char *pszCmdLine = NULL;
+ int rc = RTUtf16ToUtf8Ex(pCmdLine->Buffer, pCmdLine->Length / sizeof(WCHAR), &pszCmdLine, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ char **papszArgv;
+ int cArgs = 0;
+ rc = RTGetOptArgvFromString(&papszArgv, &cArgs, pszCmdLine,
+ RTGETOPTARGV_CNV_MODIFY_INPUT | RTGETOPTARGV_CNV_QUOTE_MS_CRT, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Call the main function.
+ */
+ AssertCompile(sizeof(rcExit) == sizeof(int));
+ rcExit = (RTEXITCODE)main(cArgs, papszArgv, NULL /*envp*/);
+ }
+ else
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("Error parsing command line: %Rrc\n", rc);
+#else
+ rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("Error parsing command line: "), rc);
+#endif
+ }
+ else
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("Failed to convert command line to UTF-8: %Rrc\n", rc);
+#else
+ rtNoCrtFatalMsgWithRc(RT_STR_TUPLE("Failed to convert command line to UTF-8: "), rc);
+#endif
+ }
+ else
+#ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("No command line\n");
+#else
+ rtNoCrtFatalMsg(RT_STR_TUPLE("No command line\r\n"));
+#endif
+ rtTerminateProcess(rcExit, true /*fDoAtExit*/);
+ }
+#ifdef IPRT_NO_CRT
+ else
+ {
+# ifdef IPRT_NOCRT_WITHOUT_FATAL_WRITE
+ RTMsgError("A C static initializor failed (%d)\n", rcExit);
+# else
+ rtNoCrtFatalWriteBegin(RT_STR_TUPLE("A C static initializor failed ("));
+ rtNoCrtFatalWriteWinRc(rcExit);
+ rtNoCrtFatalWriteEnd(RT_STR_TUPLE("\r\n"));
+# endif
+ rtTerminateProcess(rcExit, false /*fDoAtExit*/);
+ }
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp b/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp
new file mode 100644
index 00000000..269c9bd2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/nocrt-streams-win.cpp
@@ -0,0 +1,223 @@
+/* $Id: nocrt-streams-win.cpp $ */
+/** @file
+ * IPRT - No-CRT - minimal stream implementation
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/stream.h>
+
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/string.h>
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct PRINTFBUF
+{
+ HANDLE hNative;
+ size_t offBuf;
+ char szBuf[128];
+} PRINTFBUF;
+
+struct RTSTREAM
+{
+ int iStream;
+ HANDLE hNative;
+ RTFILE hFile;
+};
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#define MAKE_SURE_WE_HAVE_HFILE_RETURN(a_pStream) do { \
+ if ((a_pStream)->hFile != NIL_RTFILE) \
+ break; \
+ int rc = RTFileFromNative(&(a_pStream)->hFile, (uintptr_t)(a_pStream)->hNative); \
+ AssertRCReturn(rc, rc); \
+ } while (0)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+RTSTREAM g_aStdStreams[3] =
+{
+ { 0, NULL, NIL_RTFILE },
+ { 1, NULL, NIL_RTFILE },
+ { 2, NULL, NIL_RTFILE },
+};
+
+RTSTREAM *g_pStdIn = &g_aStdStreams[0];
+RTSTREAM *g_pStdOut = &g_aStdStreams[1];
+RTSTREAM *g_pStdErr = &g_aStdStreams[2];
+
+
+
+DECLHIDDEN(void) InitStdHandles(PRTL_USER_PROCESS_PARAMETERS pParams)
+{
+ if (pParams)
+ {
+ g_pStdIn->hNative = pParams->StandardInput;
+ g_pStdOut->hNative = pParams->StandardOutput;
+ g_pStdErr->hNative = pParams->StandardError;
+ }
+}
+
+
+static void FlushPrintfBuffer(PRINTFBUF *pBuf)
+{
+ if (pBuf->offBuf)
+ {
+ DWORD cbWritten = 0;
+ WriteFile(pBuf->hNative, pBuf->szBuf, (DWORD)pBuf->offBuf, &cbWritten, NULL);
+ pBuf->offBuf = 0;
+ pBuf->szBuf[0] = '\0';
+ }
+}
+
+
+/** @callback_method_impl{FNRTSTROUTPUT} */
+static DECLCALLBACK(size_t) MyPrintfOutputter(void *pvArg, const char *pachChars, size_t cbChars)
+{
+ PRINTFBUF *pBuf = (PRINTFBUF *)pvArg;
+ if (cbChars != 0)
+ {
+ size_t offSrc = 0;
+ while (offSrc < cbChars)
+ {
+ size_t cbLeft = sizeof(pBuf->szBuf) - pBuf->offBuf - 1;
+ if (cbLeft > 0)
+ {
+ size_t cbToCopy = RT_MIN(cbChars - offSrc, cbLeft);
+ memcpy(&pBuf->szBuf[pBuf->offBuf], &pachChars[offSrc], cbToCopy);
+ pBuf->offBuf += cbToCopy;
+ pBuf->szBuf[pBuf->offBuf] = '\0';
+ if (cbLeft > cbToCopy)
+ break;
+ offSrc += cbToCopy;
+ }
+ FlushPrintfBuffer(pBuf);
+ }
+ }
+ else /* Special zero byte write at the end of the formatting. */
+ FlushPrintfBuffer(pBuf);
+ return cbChars;
+}
+
+
+RTR3DECL(int) RTStrmPrintfV(PRTSTREAM pStream, const char *pszFormat, va_list args)
+{
+ PRINTFBUF Buf;
+ Buf.hNative = pStream->hNative;
+ Buf.offBuf = 0;
+ Buf.szBuf[0] = '\0';
+
+ return (int)RTStrFormatV(MyPrintfOutputter, &Buf, NULL, NULL, pszFormat, args);
+}
+
+
+RTR3DECL(int) RTStrmPrintf(PRTSTREAM pStream, const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ int rc = RTStrmPrintfV(pStream, pszFormat, args);
+ va_end(args);
+ return rc;
+}
+
+
+RTR3DECL(int) RTPrintfV(const char *pszFormat, va_list va)
+{
+ PRINTFBUF Buf;
+ Buf.hNative = g_pStdOut->hNative;
+ Buf.offBuf = 0;
+ Buf.szBuf[0] = '\0';
+
+ return (int)RTStrFormatV(MyPrintfOutputter, &Buf, NULL, NULL, pszFormat, va);
+}
+
+
+RTR3DECL(int) RTPrintf(const char *pszFormat, ...)
+{
+ va_list va;
+ va_start(va, pszFormat);
+ int rc = RTPrintfV(pszFormat, va);
+ va_end(va);
+ return rc;
+}
+
+#ifndef IPRT_MINIMAL_STREAM
+
+# if 0
+RTR3DECL(int) RTStrmReadEx(PRTSTREAM pStream, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream);
+ return RTFileRead(pStream->hFile, pvBuf, cbToRead, pcbRead);
+}
+# endif
+
+
+RTR3DECL(int) RTStrmWriteEx(PRTSTREAM pStream, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream);
+ return RTFileWrite(pStream->hFile, pvBuf, cbToWrite, pcbWritten);
+}
+
+
+RTR3DECL(int) RTStrmFlush(PRTSTREAM pStream)
+{
+ MAKE_SURE_WE_HAVE_HFILE_RETURN(pStream);
+ return RTFileFlush(pStream->hFile);
+}
+
+
+RTR3DECL(int) RTStrmSetMode(PRTSTREAM pStream, int fBinary, int fCurrentCodeSet)
+{
+ AssertReturn(fBinary != (int)false, VERR_NOT_IMPLEMENTED);
+ AssertReturn(fCurrentCodeSet <= (int)false, VERR_NOT_IMPLEMENTED);
+ RT_NOREF(pStream);
+ return VINF_SUCCESS;
+}
+
+#endif
diff --git a/src/VBox/Runtime/r3/win/ntdll-mini-implib.def b/src/VBox/Runtime/r3/win/ntdll-mini-implib.def
new file mode 100644
index 00000000..c382847e
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/ntdll-mini-implib.def
@@ -0,0 +1,163 @@
+; $Id: ntdll-mini-implib.def $
+;; @file
+; IPRT - Minimal NTDLL import library defintion file.
+;
+
+;
+; Copyright (C) 2010-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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+LIBRARY ntdll.dll
+EXPORTS
+ ; Exported name - The name x86 name sought by the linker.
+ ; - This needs to be defined as a symbol, we generate assembly.
+
+ CsrClientCallServer ;;= _CsrClientCallServer@16
+
+ NtAlertThread ;;= _NtAlertThread@4
+ NtAllocateVirtualMemory ;;= _NtAllocateVirtualMemory@24
+ NtCancelIoFile ;;= _NtCancelIoFile@8
+ NtClearEvent ;;= _NtClearEvent@4
+ NtClose ;;= _NtClose@4
+ NtCreateEvent ;;= _NtCreateEvent@20
+ NtCreateFile ;;= _NtCreateFile@44
+ NtCreateSection ;;= _NtCreateSection@28
+ NtCreateSymbolicLinkObject ;;= _NtCreateSymbolicLinkObject@16
+ NtDelayExecution ;;= _NtDelayExecution@8
+ NtDeviceIoControlFile ;;= _NtDeviceIoControlFile@40
+ NtDuplicateObject ;;= _NtDuplicateObject@28
+ NtEnumerateKey ;;= _NtEnumerateKey@24
+ NtFlushBuffersFile ;;= _NtFlushBuffersFile@8
+ NtFlushVirtualMemory ;;= _NtFlushVirtualMemory@16
+ NtFreeVirtualMemory ;;= _NtFreeVirtualMemory@16
+ NtGetContextThread ;;= _NtGetContextThread@8
+ NtMapViewOfSection ;;= _NtMapViewOfSection@40
+ NtOpenDirectoryObject ;;= _NtOpenDirectoryObject@12
+ NtOpenEvent ;;= _NtOpenEvent@12
+ NtOpenKey ;;= _NtOpenKey@12
+ NtOpenProcess ;;= _NtOpenProcess@16
+ NtOpenProcessToken ;;= _NtOpenProcessToken@12
+ NtOpenSymbolicLinkObject ;;= _NtOpenSymbolicLinkObject@12
+ NtOpenThread ;;= _NtOpenThread@16
+ NtOpenThreadToken ;;= _NtOpenThreadToken@16
+ NtProtectVirtualMemory ;;= _NtProtectVirtualMemory@20
+ NtQueryAttributesFile ;;= _NtQueryAttributesFile@8
+ NtQueryDirectoryFile ;;= _NtQueryDirectoryFile@44
+ NtQueryDirectoryObject ;;= _NtQueryDirectoryObject@28
+ NtQueryFullAttributesFile ;;= _NtQueryFullAttributesFile@8
+ NtQueryEvent ;;= _NtQueryEvent@20
+ NtQueryInformationFile ;;= _NtQueryInformationFile@20
+ NtQueryInformationProcess ;;= _NtQueryInformationProcess@20
+ NtQueryInformationThread ;;= _NtQueryInformationThread@20
+ NtQueryInformationToken ;;= _NtQueryInformationToken@20
+ NtQueryKey ;;= _NtQueryKey@20
+ NtQueryObject ;;= _NtQueryObject@20
+ NtQuerySection ;;= _NtQuerySection@20
+ NtQuerySecurityObject ;;= _NtQuerySecurityObject@20
+ NtQuerySymbolicLinkObject ;;= _NtQuerySymbolicLinkObject@12
+ NtQuerySystemInformation ;;= _NtQuerySystemInformation@16
+ NtQueryTimerResolution ;;= _NtQueryTimerResolution@12
+ NtQueryValueKey ;;= _NtQueryValueKey@24
+ NtQueryVirtualMemory ;;= _NtQueryVirtualMemory@24
+ NtQueryVolumeInformationFile ;;= _NtQueryVolumeInformationFile@20
+ NtReadFile ;;= _NtReadFile@36
+ NtReadVirtualMemory ;;= _NtReadVirtualMemory@20
+ NtResetEvent ;;= _NtResetEvent@8
+ NtResumeProcess ;;= _NtResumeProcess@4
+ NtResumeThread ;;= _NtResumeThread@8
+ NtSetContextThread ;;= _NtSetContextThread@8
+ NtSetEvent ;;= _NtSetEvent@8
+ NtSetInformationFile ;;= _NtSetInformationFile@20
+ NtSetInformationObject ;;= _NtSetInformationObject@16
+ NtSetInformationProcess ;;= _NtSetInformationProcess@16
+ NtSetInformationThread ;;= _NtSetInformationThread@16
+ NtSetTimerResolution ;;= _NtSetTimerResolution@12
+ NtSuspendProcess ;;= _NtSuspendProcess@4
+ NtSuspendThread ;;= _NtSuspendThread@8
+ NtTerminateProcess ;;= _NtTerminateProcess@8
+ NtTerminateThread ;;= _NtTerminateThread@8
+ NtUnmapViewOfSection ;;= _NtUnmapViewOfSection@8
+ NtWaitForMultipleObjects ;;= _NtWaitForMultipleObjects@20
+ NtWaitForSingleObject ;;= _NtWaitForSingleObject@12
+ NtWriteFile ;;= _NtWriteFile@36
+ NtWriteVirtualMemory ;;= _NtWriteVirtualMemory@20
+ NtYieldExecution ;;= _NtYieldExecution@0
+
+ LdrInitializeThunk ;;= _LdrInitializeThunk@12
+ LdrRegisterDllNotification ;;= _LdrRegisterDllNotification@16
+ LdrLoadDll ;;= _LdrLoadDll@16
+ LdrUnloadDll ;;= _LdrUnloadDll@4
+ LdrGetDllHandle ;;= _LdrGetDllHandle@16
+ LdrGetDllHandleEx ;;= _LdrGetDllHandleEx@20
+ LdrGetDllHandleByMapping ;;= _LdrGetDllHandleByMapping@8
+ LdrGetDllHandleByName ;;= _LdrGetDllHandleByName@12
+ LdrAddRefDll ;;= _LdrAddRefDll@8
+ LdrGetProcedureAddress ;;= _LdrGetProcedureAddress@12
+ LdrGetProcedureAddressEx ;;= _LdrGetProcedureAddressEx@16
+ LdrLockLoaderLock ;;= _LdrLockLoaderLock@12
+ LdrUnlockLoaderLock ;;= _LdrUnlockLoaderLock@8
+
+ RtlAcquirePebLock ;;= _RtlAcquirePebLock@0
+ RtlAddAccessAllowedAce ;;= _RtlAddAccessAllowedAce@16
+ RtlAddAccessDeniedAce ;;= _RtlAddAccessDeniedAce@16
+ RtlAllocateHeap ;;= _RtlAllocateHeap@12
+ RtlCompactHeap ;;= _RtlCompactHeap@8
+ RtlCopySid ;;= _RtlCopySid@12
+ RtlCreateAcl ;;= _RtlCreateAcl@12
+ RtlCreateHeap ;;= _RtlCreateHeap@24
+ RtlCreateProcessParameters ;;= _RtlCreateProcessParameters@40
+ RtlCreateSecurityDescriptor ;;= _RtlCreateSecurityDescriptor@8
+ RtlCreateUserProcess ;;= _RtlCreateUserProcess@40
+ RtlCreateUserThread ;;= _RtlCreateUserThread@40
+ RtlDestroyProcessParameters ;;= _RtlDestroyProcessParameters@4
+ RtlDosApplyFileIsolationRedirection_Ustr ;;= _RtlDosApplyFileIsolationRedirection_Ustr@36
+ RtlEqualSid ;;= _RtlEqualSid@8
+ RtlExitUserProcess ;;= _RtlExitUserProcess@4
+ RtlExitUserThread ;;= _RtlExitUserThread@4
+ RtlExpandEnvironmentStrings_U ;;= _RtlExpandEnvironmentStrings_U@16
+ RtlFreeHeap ;;= _RtlFreeHeap@12
+ RtlFreeUnicodeString ;;= _RtlFreeUnicodeString@4
+ RtlGetLastNtStatus ;;= _RtlGetLastNtStatus@0
+ RtlGetLastWin32Error ;;= _RtlGetLastWin32Error@0
+ RtlGetVersion ;;= _RtlGetVersion@4
+ RtlGetNtProductType ;;= _RtlGetNtProductType@4
+ RtlInitializeSid ;;= _RtlInitializeSid@12
+ RtlNtStatusToDosError ;;= _RtlNtStatusToDosError@4
+ RtlReAllocateHeap ;;= _RtlReAllocateHeap@16
+ RtlReleasePebLock ;;= _RtlReleasePebLock@0
+ RtlRestoreLastWin32Error ;;= _RtlRestoreLastWin32Error@4
+ RtlSetDaclSecurityDescriptor ;;= _RtlSetDaclSecurityDescriptor@16
+ RtlSetLastWin32Error ;;= _RtlSetLastWin32Error@4
+ RtlSetLastWin32ErrorAndNtStatusFromNtStatus ;;= _RtlSetLastWin32ErrorAndNtStatusFromNtStatus@4
+ RtlSizeHeap ;;= _RtlSizeHeap@12
+ RtlSubAuthoritySid ;;= _RtlSubAuthoritySid@8
+ RtlQueryPerformanceCounter ;;= _RtlQueryPerformanceCounter@4
+ RtlGetSystemTimePrecise ;;= _RtlGetSystemTimePrecise@0
+
diff --git a/src/VBox/Runtime/r3/win/path-win.cpp b/src/VBox/Runtime/r3/win/path-win.cpp
new file mode 100644
index 00000000..587de0f6
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/path-win.cpp
@@ -0,0 +1,743 @@
+/* $Id: path-win.cpp $ */
+/** @file
+ * IPRT - Path manipulation.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PATH
+#include <iprt/win/windows.h>
+#include <iprt/win/shlobj.h>
+
+#include <iprt/path.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/utf16.h>
+#include "internal/path.h"
+#include "internal/fs.h"
+
+/* Needed for lazy loading SHGetFolderPathW in RTPathUserDocuments(). */
+typedef HRESULT WINAPI FNSHGETFOLDERPATHW(HWND, int, HANDLE, DWORD, LPWSTR);
+typedef FNSHGETFOLDERPATHW *PFNSHGETFOLDERPATHW;
+
+
+RTDECL(int) RTPathReal(const char *pszPath, char *pszRealPath, size_t cchRealPath)
+{
+ /*
+ * Convert to UTF-16, call Win32 APIs, convert back.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ LPWSTR lpFile;
+ WCHAR wsz[RTPATH_MAX];
+ rc = GetFullPathNameW((LPCWSTR)pwszPath, RT_ELEMENTS(wsz), &wsz[0], &lpFile);
+ if (rc > 0 && rc < RT_ELEMENTS(wsz))
+ {
+ /* Check that it exists. (Use RTPathAbs() to just resolve the name.) */
+ DWORD dwAttr = GetFileAttributesW(wsz);
+ if (dwAttr != INVALID_FILE_ATTRIBUTES)
+ rc = RTUtf16ToUtf8Ex((PRTUTF16)&wsz[0], RTSTR_MAX, &pszRealPath, cchRealPath, NULL);
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (rc <= 0)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+
+ RTPathWinFree(pwszPath);
+ }
+ return rc;
+}
+
+#if 0
+RTDECL(int) RTPathAbs(const char *pszPath, char *pszAbsPath, size_t cchAbsPath)
+{
+ /*
+ * Validation.
+ */
+ AssertPtr(pszAbsPath);
+ AssertPtr(pszPath);
+ if (RT_UNLIKELY(!*pszPath))
+ return VERR_INVALID_PARAMETER;
+
+ /*
+ * Convert to UTF-16, call Win32 API, convert back.
+ */
+ LPWSTR pwszPath;
+ int rc = RTStrToUtf16(pszPath, &pwszPath);
+ if (!RT_SUCCESS(rc))
+ return (rc);
+
+ LPWSTR pwszFile; /* Ignored */
+ RTUTF16 wsz[RTPATH_MAX];
+ rc = GetFullPathNameW(pwszPath, RT_ELEMENTS(wsz), &wsz[0], &pwszFile);
+ if (rc > 0 && rc < RT_ELEMENTS(wsz))
+ {
+ size_t cch;
+ rc = RTUtf16ToUtf8Ex(&wsz[0], RTSTR_MAX, &pszAbsPath, cchAbsPath, &cch);
+ if (RT_SUCCESS(rc))
+ {
+# if 1 /** @todo This code is completely bonkers. */
+ /*
+ * Remove trailing slash if the path may be pointing to a directory.
+ * (See posix variant.)
+ */
+ if ( cch > 1
+ && RTPATH_IS_SLASH(pszAbsPath[cch - 1])
+ && !RTPATH_IS_VOLSEP(pszAbsPath[cch - 2])
+ && !RTPATH_IS_SLASH(pszAbsPath[cch - 2]))
+ pszAbsPath[cch - 1] = '\0';
+# endif
+ }
+ }
+ else if (rc <= 0)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+
+ RTUtf16Free(pwszPath);
+ return rc;
+}
+#endif
+
+
+RTDECL(int) RTPathUserHome(char *pszPath, size_t cchPath)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cchPath, VERR_INVALID_PARAMETER);
+
+ RTUTF16 wszPath[RTPATH_MAX];
+ bool fValidFolderPath = false;
+
+ /*
+ * Try with Windows XP+ functionality first.
+ */
+ RTLDRMOD hShell32;
+ int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32);
+ if (RT_SUCCESS(rc))
+ {
+ PFNSHGETFOLDERPATHW pfnSHGetFolderPathW;
+ rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW);
+ if (RT_SUCCESS(rc))
+ {
+ HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PROFILE, NULL, SHGFP_TYPE_CURRENT, wszPath);
+ fValidFolderPath = (hrc == S_OK);
+ }
+ RTLdrClose(hShell32);
+ }
+
+ DWORD dwAttr;
+ if ( !fValidFolderPath
+ || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
+ || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /*
+ * Fall back to Windows specific environment variables. HOME is not used.
+ */
+ if ( !GetEnvironmentVariableW(L"USERPROFILE", &wszPath[0], RTPATH_MAX)
+ || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
+ || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ {
+ /* %HOMEDRIVE%%HOMEPATH% */
+ if (!GetEnvironmentVariableW(L"HOMEDRIVE", &wszPath[0], RTPATH_MAX))
+ return VERR_PATH_NOT_FOUND;
+ size_t const cwc = RTUtf16Len(&wszPath[0]);
+ if ( !GetEnvironmentVariableW(L"HOMEPATH", &wszPath[cwc], RTPATH_MAX - (DWORD)cwc)
+ || (dwAttr = GetFileAttributesW(&wszPath[0])) == INVALID_FILE_ATTRIBUTES
+ || !(dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ return VERR_PATH_NOT_FOUND;
+ }
+ }
+
+ /*
+ * Convert and return.
+ */
+ return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
+}
+
+
+RTDECL(int) RTPathUserDocuments(char *pszPath, size_t cchPath)
+{
+ /*
+ * Validate input
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(cchPath, VERR_INVALID_PARAMETER);
+
+ RTLDRMOD hShell32;
+ int rc = RTLdrLoadSystem("Shell32.dll", true /*fNoUnload*/, &hShell32);
+ if (RT_SUCCESS(rc))
+ {
+ PFNSHGETFOLDERPATHW pfnSHGetFolderPathW;
+ rc = RTLdrGetSymbol(hShell32, "SHGetFolderPathW", (void**)&pfnSHGetFolderPathW);
+ if (RT_SUCCESS(rc))
+ {
+ RTUTF16 wszPath[RTPATH_MAX];
+ HRESULT hrc = pfnSHGetFolderPathW(0, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, wszPath);
+ if ( hrc == S_OK /* Found */
+ || hrc == S_FALSE) /* Found, but doesn't exist */
+ {
+ /*
+ * Convert and return.
+ */
+ RTLdrClose(hShell32);
+ return RTUtf16ToUtf8Ex(&wszPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
+ }
+ }
+ RTLdrClose(hShell32);
+ }
+ return VERR_PATH_NOT_FOUND;
+}
+
+
+#if 0 /* use nt version of this */
+
+RTR3DECL(int) RTPathQueryInfo(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs)
+{
+ return RTPathQueryInfoEx(pszPath, pObjInfo, enmAdditionalAttribs, RTPATH_F_ON_LINK);
+}
+#endif
+#if 0
+
+
+RTR3DECL(int) RTPathQueryInfoEx(const char *pszPath, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAdditionalAttribs, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pObjInfo, VERR_INVALID_POINTER);
+ AssertMsgReturn( enmAdditionalAttribs >= RTFSOBJATTRADD_NOTHING
+ && enmAdditionalAttribs <= RTFSOBJATTRADD_LAST,
+ ("Invalid enmAdditionalAttribs=%p\n", enmAdditionalAttribs),
+ VERR_INVALID_PARAMETER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Query file info.
+ */
+ uint32_t uReparseTag = RTFSMODE_SYMLINK_REPARSE_TAG;
+ WIN32_FILE_ATTRIBUTE_DATA Data;
+ PRTUTF16 pwszPath;
+ int rc = RTStrToUtf16(pszPath, &pwszPath);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (!GetFileAttributesExW(pwszPath, GetFileExInfoStandard, &Data))
+ {
+ /* Fallback to FindFileFirst in case of sharing violation. */
+ if (GetLastError() == ERROR_SHARING_VIOLATION)
+ {
+ WIN32_FIND_DATAW FindData;
+ HANDLE hDir = FindFirstFileW(pwszPath, &FindData);
+ if (hDir == INVALID_HANDLE_VALUE)
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTUtf16Free(pwszPath);
+ return rc;
+ }
+ FindClose(hDir);
+
+ Data.dwFileAttributes = FindData.dwFileAttributes;
+ Data.ftCreationTime = FindData.ftCreationTime;
+ Data.ftLastAccessTime = FindData.ftLastAccessTime;
+ Data.ftLastWriteTime = FindData.ftLastWriteTime;
+ Data.nFileSizeHigh = FindData.nFileSizeHigh;
+ Data.nFileSizeLow = FindData.nFileSizeLow;
+ uReparseTag = FindData.dwReserved0;
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTUtf16Free(pwszPath);
+ return rc;
+ }
+ }
+
+ /*
+ * Getting the information for the link target is a bit annoying and
+ * subject to the same access violation mess as above.. :/
+ */
+ /** @todo we're too lazy wrt to error paths here... */
+ if ( (Data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)
+ && ((fFlags & RTPATH_F_FOLLOW_LINK) || uReparseTag != RTFSMODE_SYMLINK_REPARSE_TAG))
+ {
+ HANDLE hFinal = CreateFileW(pwszPath,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hFinal != INVALID_HANDLE_VALUE)
+ {
+ BY_HANDLE_FILE_INFORMATION FileData;
+ if (GetFileInformationByHandle(hFinal, &FileData))
+ {
+ Data.dwFileAttributes = FileData.dwFileAttributes;
+ Data.ftCreationTime = FileData.ftCreationTime;
+ Data.ftLastAccessTime = FileData.ftLastAccessTime;
+ Data.ftLastWriteTime = FileData.ftLastWriteTime;
+ Data.nFileSizeHigh = FileData.nFileSizeHigh;
+ Data.nFileSizeLow = FileData.nFileSizeLow;
+ uReparseTag = 0;
+ }
+ CloseHandle(hFinal);
+ }
+ else if (GetLastError() != ERROR_SHARING_VIOLATION)
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTUtf16Free(pwszPath);
+ return rc;
+ }
+ }
+
+ RTUtf16Free(pwszPath);
+
+ /*
+ * Setup the returned data.
+ */
+ pObjInfo->cbObject = ((uint64_t)Data.nFileSizeHigh << 32)
+ | (uint64_t)Data.nFileSizeLow;
+ pObjInfo->cbAllocated = pObjInfo->cbObject;
+
+ Assert(sizeof(uint64_t) == sizeof(Data.ftCreationTime));
+ RTTimeSpecSetNtTime(&pObjInfo->BirthTime, *(uint64_t *)&Data.ftCreationTime);
+ RTTimeSpecSetNtTime(&pObjInfo->AccessTime, *(uint64_t *)&Data.ftLastAccessTime);
+ RTTimeSpecSetNtTime(&pObjInfo->ModificationTime, *(uint64_t *)&Data.ftLastWriteTime);
+ pObjInfo->ChangeTime = pObjInfo->ModificationTime;
+
+ pObjInfo->Attr.fMode = rtFsModeFromDos((Data.dwFileAttributes << RTFS_DOS_SHIFT) & RTFS_DOS_MASK_NT,
+ pszPath, strlen(pszPath), uReparseTag);
+
+ /*
+ * Requested attributes (we cannot provide anything actually).
+ */
+ switch (enmAdditionalAttribs)
+ {
+ case RTFSOBJATTRADD_NOTHING:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_NOTHING;
+ break;
+
+ case RTFSOBJATTRADD_UNIX:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX;
+ pObjInfo->Attr.u.Unix.uid = ~0U;
+ pObjInfo->Attr.u.Unix.gid = ~0U;
+ pObjInfo->Attr.u.Unix.cHardlinks = 1;
+ pObjInfo->Attr.u.Unix.INodeIdDevice = 0; /** @todo use volume serial number */
+ pObjInfo->Attr.u.Unix.INodeId = 0; /** @todo use fileid (see GetFileInformationByHandle). */
+ pObjInfo->Attr.u.Unix.fFlags = 0;
+ pObjInfo->Attr.u.Unix.GenerationId = 0;
+ pObjInfo->Attr.u.Unix.Device = 0;
+ break;
+
+ case RTFSOBJATTRADD_UNIX_OWNER:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_OWNER;
+ pObjInfo->Attr.u.UnixOwner.uid = ~0U;
+ pObjInfo->Attr.u.UnixOwner.szName[0] = '\0'; /** @todo return something sensible here. */
+ break;
+
+ case RTFSOBJATTRADD_UNIX_GROUP:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_UNIX_GROUP;
+ pObjInfo->Attr.u.UnixGroup.gid = ~0U;
+ pObjInfo->Attr.u.UnixGroup.szName[0] = '\0';
+ break;
+
+ case RTFSOBJATTRADD_EASIZE:
+ pObjInfo->Attr.enmAdditional = RTFSOBJATTRADD_EASIZE;
+ pObjInfo->Attr.u.EASize.cb = 0;
+ break;
+
+ default:
+ AssertMsgFailed(("Impossible!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* using NT version*/
+
+
+RTR3DECL(int) RTPathSetTimes(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime)
+{
+ return RTPathSetTimesEx(pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, RTPATH_F_ON_LINK);
+}
+
+
+RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime,
+ PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER);
+ AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER);
+ AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the path.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hFile;
+ if (fFlags & RTPATH_F_FOLLOW_LINK)
+ hFile = CreateFileW(pwszPath,
+ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
+ NULL, /* security attribs */
+ OPEN_EXISTING, /* dwCreationDisposition */
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ else
+ {
+/** @todo Symlink: Test RTPathSetTimesEx on Windows. (The code is disabled
+ * because it's not tested yet.) */
+#if 0 //def FILE_FLAG_OPEN_REPARSE_POINT
+ hFile = CreateFileW(pwszPath,
+ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
+ NULL, /* security attribs */
+ OPEN_EXISTING, /* dwCreationDisposition */
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OPEN_REPARSE_POINT,
+ NULL);
+
+ if (hFile == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+#endif
+ hFile = CreateFileW(pwszPath,
+ FILE_WRITE_ATTRIBUTES, /* dwDesiredAccess */
+ FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE, /* dwShareMode */
+ NULL, /* security attribs */
+ OPEN_EXISTING, /* dwCreationDisposition */
+ FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ if (hFile != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Check if it's a no-op.
+ */
+ if (!pAccessTime && !pModificationTime && !pBirthTime)
+ rc = VINF_SUCCESS; /* NOP */
+ else
+ {
+ /*
+ * Convert the input and call the API.
+ */
+ FILETIME CreationTimeFT;
+ PFILETIME pCreationTimeFT = NULL;
+ if (pBirthTime)
+ pCreationTimeFT = RTTimeSpecGetNtFileTime(pBirthTime, &CreationTimeFT);
+
+ FILETIME LastAccessTimeFT;
+ PFILETIME pLastAccessTimeFT = NULL;
+ if (pAccessTime)
+ pLastAccessTimeFT = RTTimeSpecGetNtFileTime(pAccessTime, &LastAccessTimeFT);
+
+ FILETIME LastWriteTimeFT;
+ PFILETIME pLastWriteTimeFT = NULL;
+ if (pModificationTime)
+ pLastWriteTimeFT = RTTimeSpecGetNtFileTime(pModificationTime, &LastWriteTimeFT);
+
+ if (SetFileTime(hFile, pCreationTimeFT, pLastAccessTimeFT, pLastWriteTimeFT))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTPathSetTimes('%s', %p, %p, %p, %p): SetFileTime failed with lasterr %d (%Rrc)\n",
+ pszPath, pAccessTime, pModificationTime, pChangeTime, pBirthTime, Err, rc));
+ }
+ }
+ BOOL fRc = CloseHandle(hFile); Assert(fRc); NOREF(fRc);
+ }
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("RTPathSetTimes('%s',,,,): failed with %Rrc and lasterr=%u\n", pszPath, rc, Err));
+ }
+
+ RTPathWinFree(pwszPath);
+ }
+
+ LogFlow(("RTPathSetTimes(%p:{%s}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}, %p:{%RDtimespec}): return %Rrc\n",
+ pszPath, pszPath, pAccessTime, pAccessTime, pModificationTime, pModificationTime,
+ pChangeTime, pChangeTime, pBirthTime, pBirthTime));
+ return rc;
+}
+
+
+
+
+/**
+ * Internal worker for RTFileRename and RTFileMove.
+ *
+ * @returns iprt status code.
+ * @param pszSrc The source filename.
+ * @param pszDst The destination filename.
+ * @param fFlags The windows MoveFileEx flags.
+ * @param fFileType The filetype. We use the RTFMODE filetypes here. If it's 0,
+ * anything goes. If it's RTFS_TYPE_DIRECTORY we'll check that the
+ * source is a directory. If Its RTFS_TYPE_FILE we'll check that it's
+ * not a directory (we are NOT checking whether it's a file).
+ */
+DECLHIDDEN(int) rtPathWin32MoveRename(const char *pszSrc, const char *pszDst, uint32_t fFlags, RTFMODE fFileType)
+{
+ /*
+ * Convert the strings.
+ */
+ PRTUTF16 pwszSrc;
+ int rc = RTPathWinFromUtf8(&pwszSrc, pszSrc, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszDst;
+ rc = RTPathWinFromUtf8(&pwszDst, pszDst, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check object type if requested.
+ * This is open to race conditions.
+ */
+ if (fFileType)
+ {
+ DWORD dwAttr = GetFileAttributesW(pwszSrc);
+ if (dwAttr == INVALID_FILE_ATTRIBUTES)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else if (RTFS_IS_DIRECTORY(fFileType))
+ rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VINF_SUCCESS : VERR_NOT_A_DIRECTORY;
+ else
+ rc = dwAttr & FILE_ATTRIBUTE_DIRECTORY ? VERR_IS_A_DIRECTORY : VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ if (MoveFileExW(pwszSrc, pwszDst, fFlags))
+ rc = VINF_SUCCESS;
+ else
+ {
+ DWORD Err = GetLastError();
+ rc = RTErrConvertFromWin32(Err);
+ Log(("MoveFileExW('%s', '%s', %#x, %RTfmode): fails with rc=%Rrc & lasterr=%d\n",
+ pszSrc, pszDst, fFlags, fFileType, rc, Err));
+ }
+ }
+ RTPathWinFree(pwszDst);
+ }
+ RTPathWinFree(pwszSrc);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathRename(const char *pszSrc, const char *pszDst, unsigned fRename)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszSrc, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszDst, VERR_INVALID_POINTER);
+ AssertMsgReturn(*pszSrc, ("%p\n", pszSrc), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(*pszDst, ("%p\n", pszDst), VERR_INVALID_PARAMETER);
+ AssertMsgReturn(!(fRename & ~RTPATHRENAME_FLAGS_REPLACE), ("%#x\n", fRename), VERR_INVALID_PARAMETER);
+
+ /*
+ * Call the worker.
+ */
+ int rc = rtPathWin32MoveRename(pszSrc, pszDst, fRename & RTPATHRENAME_FLAGS_REPLACE ? MOVEFILE_REPLACE_EXISTING : 0, 0);
+
+ LogFlow(("RTPathRename(%p:{%s}, %p:{%s}, %#x): returns %Rrc\n", pszSrc, pszSrc, pszDst, pszDst, fRename, rc));
+ return rc;
+}
+
+
+RTR3DECL(int) RTPathUnlink(const char *pszPath, uint32_t fUnlink)
+{
+ RT_NOREF_PV(pszPath); RT_NOREF_PV(fUnlink);
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+RTDECL(bool) RTPathExists(const char *pszPath)
+{
+ return RTPathExistsEx(pszPath, RTPATH_F_FOLLOW_LINK);
+}
+
+
+RTDECL(bool) RTPathExistsEx(const char *pszPath, uint32_t fFlags)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, false);
+ AssertReturn(*pszPath, false);
+ Assert(RTPATH_F_IS_VALID(fFlags, 0));
+
+ /*
+ * Try query file info.
+ */
+ DWORD dwAttr;
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ dwAttr = GetFileAttributesW(pwszPath);
+ RTPathWinFree(pwszPath);
+ }
+ else
+ dwAttr = INVALID_FILE_ATTRIBUTES;
+ if (dwAttr == INVALID_FILE_ATTRIBUTES)
+ return false;
+
+#ifdef FILE_ATTRIBUTE_REPARSE_POINT
+ if ( (fFlags & RTPATH_F_FOLLOW_LINK)
+ && (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT))
+ {
+ AssertFailed();
+ /** @todo Symlinks: RTPathExists+RTPathExistsEx is misbehaving on symbolic
+ * links on Windows. */
+ }
+#endif
+
+ return true;
+}
+
+
+RTDECL(int) RTPathGetCurrent(char *pszPath, size_t cchPath)
+{
+ int rc;
+
+ if (cchPath > 0)
+ {
+ /*
+ * GetCurrentDirectory may in some cases omit the drive letter, according
+ * to MSDN, thus the GetFullPathName call.
+ */
+ RTUTF16 wszCurPath[RTPATH_MAX];
+ if (GetCurrentDirectoryW(RTPATH_MAX, wszCurPath))
+ {
+ RTUTF16 wszFullPath[RTPATH_MAX];
+ if (GetFullPathNameW(wszCurPath, RTPATH_MAX, wszFullPath, NULL))
+ {
+ if ( wszFullPath[1] == ':'
+ && RT_C_IS_LOWER(wszFullPath[0]))
+ wszFullPath[0] = RT_C_TO_UPPER(wszFullPath[0]);
+
+ rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cchPath, NULL);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ return rc;
+}
+
+
+RTDECL(int) RTPathSetCurrent(const char *pszPath)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pszPath, VERR_INVALID_POINTER);
+ AssertReturn(*pszPath, VERR_INVALID_PARAMETER);
+
+ /*
+ * This interface is almost identical to the Windows API.
+ */
+ PRTUTF16 pwszPath;
+ int rc = RTPathWinFromUtf8(&pwszPath, pszPath, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo improve the slash stripping a bit? */
+ size_t cwc = RTUtf16Len(pwszPath);
+ if ( cwc >= 2
+ && ( pwszPath[cwc - 1] == L'/'
+ || pwszPath[cwc - 1] == L'\\')
+ && pwszPath[cwc - 2] != ':')
+ pwszPath[cwc - 1] = L'\0';
+
+ if (!SetCurrentDirectoryW(pwszPath))
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTPathWinFree(pwszPath);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPathGetCurrentOnDrive(char chDrive, char *pszPath, size_t cbPath)
+{
+ int rc;
+ if (cbPath > 0)
+ {
+ WCHAR wszInput[4];
+ wszInput[0] = chDrive;
+ wszInput[1] = ':';
+ wszInput[2] = '\0';
+ RTUTF16 wszFullPath[RTPATH_MAX];
+ if (GetFullPathNameW(wszInput, RTPATH_MAX, wszFullPath, NULL))
+ rc = RTUtf16ToUtf8Ex(&wszFullPath[0], RTSTR_MAX, &pszPath, cbPath, NULL);
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/pathint-win.cpp b/src/VBox/Runtime/r3/win/pathint-win.cpp
new file mode 100644
index 00000000..81799cc5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/pathint-win.cpp
@@ -0,0 +1,204 @@
+/* $Id: pathint-win.cpp $ */
+/** @file
+ * IPRT - Windows, Internal Path stuff.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_FS
+#include <iprt/path.h>
+
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+
+#include <iprt/nt/nt-and-windows.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The max number of non-null characters we pass to an Win32 API.
+ * You would think that MAX_PATH gives this length, however CreateDirectoryW was
+ * found to fail on Windows 10 (1803++) if given a perfectly formed path argument
+ * of 248 or more characters. Same when going thru UNC.
+ *
+ * So, to be conservative, we put the max number of characters in a non-\\?\
+ * path to 243, not counting the terminator.
+ */
+#define ACTUAL_MAX_PATH 243
+
+
+DECL_NO_INLINE(static, bool) rtPathWinTryConvertToAbs(PRTUTF16 *ppwszPath)
+{
+ RTUTF16 wszFullPath[MAX_PATH + 1];
+ DWORD cwcFull = GetFullPathNameW(*ppwszPath, MAX_PATH + 1, wszFullPath, NULL);
+ if (cwcFull <= ACTUAL_MAX_PATH)
+ {
+ RTUtf16Free(*ppwszPath);
+ PRTUTF16 const pwszCopy = RTUtf16Dup(wszFullPath);
+ *ppwszPath = pwszCopy;
+ if (pwszCopy)
+ return true;
+ }
+ return false;
+}
+
+
+RTDECL(int) RTPathWinFromUtf8(PRTUTF16 *ppwszPath, const char *pszPath, uint32_t fFlags)
+{
+ Assert(fFlags == 0);
+ RT_NOREF(fFlags);
+
+ /*
+ * Do a straight conversion first.
+ */
+ *ppwszPath = NULL;
+ size_t cwcResult = 0;
+ int rc = RTStrToUtf16Ex(pszPath, RTSTR_MAX, ppwszPath, 0, &cwcResult);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Check the resulting length. This is straight forward for absolute
+ * paths, but gets complicated for relative ones.
+ */
+ if (cwcResult <= ACTUAL_MAX_PATH)
+ {
+ if (RT_C_IS_ALPHA(pszPath[0]) && pszPath[1] == ':')
+ {
+ if (RTPATH_IS_SLASH(pszPath[2]))
+ return VINF_SUCCESS;
+
+ /* Drive relative path. Found no simple way of getting the current
+ path of a drive, so we try convert it to an absolute path and see
+ how that works out. It is what the API we're calling will have to
+ do anyway, so this should perform just as well. */
+ if (rtPathWinTryConvertToAbs(ppwszPath))
+ return VINF_SUCCESS;
+ }
+ else if (RTPATH_IS_SLASH(pszPath[0]))
+ {
+ if ( RTPATH_IS_SLASH(pszPath[1])
+ && !RTPATH_IS_SLASH(pszPath[2])
+ && pszPath[2] != '\0')
+ {
+ /* Passthru prefix '\\?\' is fine. */
+ if ( pszPath[2] == '?'
+ && !RTPATH_IS_SLASH(pszPath[3]))
+ return VINF_SUCCESS;
+
+ /* UNC requires a longer prefix ('\??\UNC\' instead of '\??\'), so
+ subtract 3 chars from the max limit to be on the safe side. */
+ if (cwcResult <= ACTUAL_MAX_PATH - 3)
+ return VINF_SUCCESS;
+ }
+ else
+ {
+ /* Drive relative. Win32 will prepend a two letter drive specification. */
+ if (cwcResult <= ACTUAL_MAX_PATH - 2)
+ return VINF_SUCCESS;
+ }
+ }
+ else
+ {
+ /* Relative to CWD. We can use the API to get it's current length.
+ Any race conditions here is entirely the caller's problem. */
+ size_t cwcCwd = GetCurrentDirectoryW(0, NULL);
+ if (cwcCwd + cwcResult <= ACTUAL_MAX_PATH - 1)
+ return VINF_SUCCESS;
+ }
+ }
+ /*
+ * We're good if the caller already supplied the passthru/length prefix: '\\?\'
+ */
+ else if ( pszPath[1] == '?'
+ && RTPATH_IS_SLASH(pszPath[3])
+ && RTPATH_IS_SLASH(pszPath[1])
+ && RTPATH_IS_SLASH(pszPath[0]))
+ return VINF_SUCCESS;
+
+ /*
+ * Long path requiring \\?\ prefixing.
+ *
+ * We piggy back on the NT conversion here and ASSUME RTUtf16Free is the right
+ * way to free the result.
+ */
+ RTUtf16Free(*ppwszPath);
+ *ppwszPath = NULL;
+
+ struct _UNICODE_STRING NtName = { 0, 0, NULL };
+ HANDLE hRootDir = NULL;
+ rc = RTNtPathFromWinUtf8(&NtName, &hRootDir, pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ /* No root dir handle. */
+ if (hRootDir == NULL)
+ {
+ /* Convert the NT '\??\' prefix to a win32 passthru prefix '\\?\' */
+ if ( NtName.Buffer[0] == '\\'
+ && NtName.Buffer[1] == '?'
+ && NtName.Buffer[2] == '?'
+ && NtName.Buffer[3] == '\\')
+ {
+ NtName.Buffer[1] = '\\';
+
+ /* Zero termination paranoia. */
+ if (NtName.Buffer[NtName.Length / sizeof(RTUTF16)] == '\0')
+ {
+ *ppwszPath = NtName.Buffer;
+ return VINF_SUCCESS;
+ }
+ AssertMsgFailed(("Length=%u %.*ls\n", NtName.Length, NtName.Length / sizeof(RTUTF16), NtName.Buffer));
+ }
+ else
+ AssertMsgFailed(("%ls\n", NtName.Buffer));
+ }
+ else
+ AssertMsgFailed(("%s\n", pszPath));
+ RTNtPathFree(&NtName, &hRootDir);
+ }
+ }
+ return rc;
+}
+
+
+RTDECL(void) RTPathWinFree(PRTUTF16 pwszPath)
+{
+ RTUtf16Free(pwszPath);
+}
+
diff --git a/src/VBox/Runtime/r3/win/pipe-win.cpp b/src/VBox/Runtime/r3/win/pipe-win.cpp
new file mode 100644
index 00000000..93bf9afc
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/pipe-win.cpp
@@ -0,0 +1,1537 @@
+/* $Id: pipe-win.cpp $ */
+/** @file
+ * IPRT - Anonymous Pipes, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2010-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/pipe.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/poll.h>
+#include <iprt/process.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/pipe.h"
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTPIPE_NT_SIZE _64K
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTPIPEINTERNAL
+{
+ /** Magic value (RTPIPE_MAGIC). */
+ uint32_t u32Magic;
+ /** The pipe handle. */
+ HANDLE hPipe;
+ /** Set if this is the read end, clear if it's the write end. */
+ bool fRead;
+ /** RTPipeFromNative: Leave native handle open on RTPipeClose. */
+ bool fLeaveOpen;
+ /** Set if there is already pending I/O. */
+ bool fIOPending;
+ /** Set if the zero byte read that the poll code using is pending. */
+ bool fZeroByteRead;
+ /** Set if the pipe is broken. */
+ bool fBrokenPipe;
+ /** Set if we've promised that the handle is writable. */
+ bool fPromisedWritable;
+ /** Set if created inheritable. */
+ bool fCreatedInheritable;
+ /** Usage counter. */
+ uint32_t cUsers;
+ /** The overlapped I/O structure we use. */
+ OVERLAPPED Overlapped;
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+ /** The handle of the poll set currently polling on this pipe.
+ * We can only have one poller at the time (lazy bird). */
+ RTPOLLSET hPollSet;
+ /** Critical section protecting the above members.
+ * (Taking the lazy/simple approach.) */
+ RTCRITSECT CritSect;
+ /** Buffer for the zero byte read. */
+ uint8_t abBuf[8];
+} RTPIPEINTERNAL;
+
+
+
+/**
+ * Wrapper for getting FILE_PIPE_LOCAL_INFORMATION via the NT API.
+ *
+ * @returns Success indicator (true/false).
+ * @param pThis The pipe.
+ * @param pInfo The info structure.
+ */
+static bool rtPipeQueryNtInfo(RTPIPEINTERNAL *pThis, FILE_PIPE_LOCAL_INFORMATION *pInfo)
+{
+ IO_STATUS_BLOCK Ios;
+ RT_ZERO(Ios);
+ RT_ZERO(*pInfo);
+ NTSTATUS rcNt = NtQueryInformationFile(pThis->hPipe, &Ios, pInfo, sizeof(*pInfo), FilePipeLocalInformation);
+ return rcNt >= 0;
+}
+
+
+RTDECL(int) RTPipeCreate(PRTPIPE phPipeRead, PRTPIPE phPipeWrite, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipeRead, VERR_INVALID_POINTER);
+ AssertPtrReturn(phPipeWrite, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_C_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the read end of the pipe.
+ */
+ DWORD dwErr;
+ HANDLE hPipeR;
+ HANDLE hPipeW;
+ int rc;
+ for (;;)
+ {
+ static volatile uint32_t g_iNextPipe = 0;
+ char szName[128];
+ RTStrPrintf(szName, sizeof(szName), "\\\\.\\pipe\\iprt-pipe-%u-%u", RTProcSelf(), ASMAtomicIncU32(&g_iNextPipe));
+
+ SECURITY_ATTRIBUTES SecurityAttributes;
+ PSECURITY_ATTRIBUTES pSecurityAttributes = NULL;
+ if (fFlags & RTPIPE_C_INHERIT_READ)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ DWORD dwOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED;
+#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
+ dwOpenMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+#endif
+
+ DWORD dwPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT;
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ dwPipeMode |= PIPE_REJECT_REMOTE_CLIENTS;
+#endif
+
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+#ifdef PIPE_REJECT_REMOTE_CLIENTS
+ if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ dwPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS;
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+ }
+#endif
+#ifdef FILE_FLAG_FIRST_PIPE_INSTANCE
+ if (hPipeR == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER)
+ {
+ dwOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE;
+ hPipeR = CreateNamedPipeA(szName, dwOpenMode, dwPipeMode, 1 /*nMaxInstances*/, RTPIPE_NT_SIZE, RTPIPE_NT_SIZE,
+ NMPWAIT_USE_DEFAULT_WAIT, pSecurityAttributes);
+ }
+#endif
+ if (hPipeR != INVALID_HANDLE_VALUE)
+ {
+ /*
+ * Connect to the pipe (the write end).
+ * We add FILE_READ_ATTRIBUTES here to make sure we can query the
+ * pipe state later on.
+ */
+ pSecurityAttributes = NULL;
+ if (fFlags & RTPIPE_C_INHERIT_WRITE)
+ {
+ SecurityAttributes.nLength = sizeof(SecurityAttributes);
+ SecurityAttributes.lpSecurityDescriptor = NULL;
+ SecurityAttributes.bInheritHandle = TRUE;
+ pSecurityAttributes = &SecurityAttributes;
+ }
+
+ hPipeW = CreateFileA(szName,
+ GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
+ 0 /*dwShareMode*/,
+ pSecurityAttributes,
+ OPEN_EXISTING /* dwCreationDisposition */,
+ FILE_FLAG_OVERLAPPED /*dwFlagsAndAttributes*/,
+ NULL /*hTemplateFile*/);
+ if (hPipeW != INVALID_HANDLE_VALUE)
+ break;
+ dwErr = GetLastError();
+ CloseHandle(hPipeR);
+ }
+ else
+ dwErr = GetLastError();
+ if ( dwErr != ERROR_PIPE_BUSY /* already exist - compatible */
+ && dwErr != ERROR_ACCESS_DENIED /* already exist - incompatible */)
+ return RTErrConvertFromWin32(dwErr);
+ /* else: try again with a new name */
+ }
+
+ /*
+ * Create the two handles.
+ */
+ RTPIPEINTERNAL *pThisR = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisR)
+ {
+ RTPIPEINTERNAL *pThisW = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (pThisW)
+ {
+ rc = RTCritSectInit(&pThisR->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCritSectInit(&pThisW->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThisR->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThisR->Overlapped.hEvent != NULL)
+ {
+ pThisW->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThisW->Overlapped.hEvent != NULL)
+ {
+ pThisR->u32Magic = RTPIPE_MAGIC;
+ pThisW->u32Magic = RTPIPE_MAGIC;
+ pThisR->hPipe = hPipeR;
+ pThisW->hPipe = hPipeW;
+ pThisR->fRead = true;
+ pThisW->fRead = false;
+ pThisR->fLeaveOpen = false;
+ pThisW->fLeaveOpen = false;
+ //pThisR->fIOPending = false;
+ //pThisW->fIOPending = false;
+ //pThisR->fZeroByteRead = false;
+ //pThisW->fZeroByteRead = false;
+ //pThisR->fBrokenPipe = false;
+ //pThisW->fBrokenPipe = false;
+ //pThisW->fPromisedWritable = false;
+ //pThisR->fPromisedWritable = false;
+ pThisW->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_WRITE);
+ pThisR->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_C_INHERIT_READ);
+ //pThisR->cUsers = 0;
+ //pThisW->cUsers = 0;
+ //pThisR->pbBounceBuf = NULL;
+ //pThisW->pbBounceBuf = NULL;
+ //pThisR->cbBounceBufUsed = 0;
+ //pThisW->cbBounceBufUsed = 0;
+ //pThisR->cbBounceBufAlloc = 0;
+ //pThisW->cbBounceBufAlloc = 0;
+ pThisR->hPollSet = NIL_RTPOLLSET;
+ pThisW->hPollSet = NIL_RTPOLLSET;
+
+ *phPipeRead = pThisR;
+ *phPipeWrite = pThisW;
+ return VINF_SUCCESS;
+ }
+ CloseHandle(pThisR->Overlapped.hEvent);
+ }
+ RTCritSectDelete(&pThisW->CritSect);
+ }
+ RTCritSectDelete(&pThisR->CritSect);
+ }
+ RTMemFree(pThisW);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ RTMemFree(pThisR);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ CloseHandle(hPipeR);
+ CloseHandle(hPipeW);
+ return rc;
+}
+
+
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTPipeClose, RTPipeWrite and RTPipeWriteBlocking.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtPipeWriteCheckCompletion(RTPIPEINTERNAL *pThis)
+{
+ int rc;
+ DWORD dwRc = WaitForSingleObject(pThis->Overlapped.hEvent, 0);
+ if (dwRc == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fIOPending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ pThis->cbBounceBufUsed -= cbWritten;
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed);
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed, &cbWritten, &pThis->Overlapped))
+ {
+ DWORD const dwErr = GetLastError();
+ if (dwErr == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwErr == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fIOPending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fIOPending = false;
+ if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+
+
+
+RTDECL(int) RTPipeCloseEx(RTPIPE hPipe, bool fLeaveOpen)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ if (pThis == NIL_RTPIPE)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTPIPE_MAGIC, RTPIPE_MAGIC), VERR_INVALID_HANDLE);
+ RTCritSectEnter(&pThis->CritSect);
+ Assert(pThis->cUsers == 0);
+
+ if (!pThis->fRead && pThis->fIOPending)
+ rtPipeWriteCheckCompletion(pThis);
+
+ if (!fLeaveOpen && !pThis->fLeaveOpen)
+ CloseHandle(pThis->hPipe);
+ pThis->hPipe = INVALID_HANDLE_VALUE;
+
+ CloseHandle(pThis->Overlapped.hEvent);
+ pThis->Overlapped.hEvent = NULL;
+
+ RTMemFree(pThis->pbBounceBuf);
+ pThis->pbBounceBuf = NULL;
+
+ RTCritSectLeave(&pThis->CritSect);
+ RTCritSectDelete(&pThis->CritSect);
+
+ RTMemFree(pThis);
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeClose(RTPIPE hPipe)
+{
+ return RTPipeCloseEx(hPipe, false /*fLeaveOpen*/);
+}
+
+
+RTDECL(int) RTPipeFromNative(PRTPIPE phPipe, RTHCINTPTR hNativePipe, uint32_t fFlags)
+{
+ AssertPtrReturn(phPipe, VERR_INVALID_POINTER);
+ AssertReturn(!(fFlags & ~RTPIPE_N_VALID_MASK_FN), VERR_INVALID_PARAMETER);
+ AssertReturn(!!(fFlags & RTPIPE_N_READ) != !!(fFlags & RTPIPE_N_WRITE), VERR_INVALID_PARAMETER);
+
+ /*
+ * Get and validate the pipe handle info.
+ */
+ HANDLE hNative = (HANDLE)hNativePipe;
+ AssertReturn(GetFileType(hNative) == FILE_TYPE_PIPE, VERR_INVALID_HANDLE);
+
+ DWORD cMaxInstances;
+ DWORD fInfo;
+ if (!GetNamedPipeInfo(hNative, &fInfo, NULL, NULL, &cMaxInstances))
+ return RTErrConvertFromWin32(GetLastError());
+ /* Doesn't seem to matter to much if the pipe is message or byte type. Cygwin
+ seems to hand us such pipes when capturing output (@bugref{9397}), so just
+ ignore skip this check:
+ AssertReturn(!(fInfo & PIPE_TYPE_MESSAGE), VERR_INVALID_HANDLE); */
+ AssertReturn(cMaxInstances == 1, VERR_INVALID_HANDLE);
+
+ DWORD cInstances;
+ DWORD fState;
+ if (!GetNamedPipeHandleState(hNative, &fState, &cInstances, NULL, NULL, NULL, 0))
+ return RTErrConvertFromWin32(GetLastError());
+ AssertReturn(!(fState & PIPE_NOWAIT), VERR_INVALID_HANDLE);
+ AssertReturn(!(fState & PIPE_READMODE_MESSAGE), VERR_INVALID_HANDLE);
+ AssertReturn(cInstances <= 1, VERR_INVALID_HANDLE);
+
+ /*
+ * Looks kind of OK, create a handle so we can try rtPipeQueryNtInfo on it
+ * and see if we need to duplicate it to make that call work.
+ */
+ RTPIPEINTERNAL *pThis = (RTPIPEINTERNAL *)RTMemAllocZ(sizeof(RTPIPEINTERNAL));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+ int rc = RTCritSectInit(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ pThis->Overlapped.hEvent = CreateEvent(NULL, TRUE /*fManualReset*/,
+ TRUE /*fInitialState*/, NULL /*pName*/);
+ if (pThis->Overlapped.hEvent != NULL)
+ {
+ pThis->u32Magic = RTPIPE_MAGIC;
+ pThis->hPipe = hNative;
+ pThis->fRead = RT_BOOL(fFlags & RTPIPE_N_READ);
+ pThis->fLeaveOpen = RT_BOOL(fFlags & RTPIPE_N_LEAVE_OPEN);
+ //pThis->fIOPending = false;
+ //pThis->fZeroByteRead = false;
+ //pThis->fBrokenPipe = false;
+ //pThis->fPromisedWritable = false;
+ pThis->fCreatedInheritable = RT_BOOL(fFlags & RTPIPE_N_INHERIT);
+ //pThis->cUsers = 0;
+ //pThis->pbBounceBuf = NULL;
+ //pThis->cbBounceBufUsed = 0;
+ //pThis->cbBounceBufAlloc = 0;
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ HANDLE hNative2 = INVALID_HANDLE_VALUE;
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ RT_ZERO(Info);
+ if ( g_pfnSetHandleInformation
+ && rtPipeQueryNtInfo(pThis, &Info))
+ rc = VINF_SUCCESS;
+ else
+ {
+ if (DuplicateHandle(GetCurrentProcess() /*hSrcProcess*/, hNative /*hSrcHandle*/,
+ GetCurrentProcess() /*hDstProcess*/, &hNative2 /*phDstHandle*/,
+ pThis->fRead ? GENERIC_READ : GENERIC_WRITE | FILE_READ_ATTRIBUTES /*dwDesiredAccess*/,
+ !!(fFlags & RTPIPE_N_INHERIT) /*fInheritHandle*/,
+ 0 /*dwOptions*/))
+ {
+ pThis->hPipe = hNative2;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ pThis->fLeaveOpen = false;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ rc = VERR_ACCESS_DENIED;
+ CloseHandle(hNative2);
+ }
+ }
+ else
+ hNative2 = INVALID_HANDLE_VALUE;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Verify the pipe state and correct the inheritability.
+ */
+ AssertStmt( Info.NamedPipeState == FILE_PIPE_CONNECTED_STATE
+ || Info.NamedPipeState == FILE_PIPE_CLOSING_STATE
+ || Info.NamedPipeState == FILE_PIPE_DISCONNECTED_STATE,
+ VERR_INVALID_HANDLE);
+ AssertStmt( Info.NamedPipeConfiguration
+ == ( Info.NamedPipeEnd == FILE_PIPE_SERVER_END
+ ? (pThis->fRead ? FILE_PIPE_INBOUND : FILE_PIPE_OUTBOUND)
+ : (pThis->fRead ? FILE_PIPE_OUTBOUND : FILE_PIPE_INBOUND) )
+ || Info.NamedPipeConfiguration == FILE_PIPE_FULL_DUPLEX,
+ VERR_INVALID_HANDLE);
+ if ( RT_SUCCESS(rc)
+ && hNative2 == INVALID_HANDLE_VALUE
+ && !g_pfnSetHandleInformation(hNative,
+ HANDLE_FLAG_INHERIT /*dwMask*/,
+ fFlags & RTPIPE_N_INHERIT ? HANDLE_FLAG_INHERIT : 0))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%Rrc\n", rc));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Ok, we're good! If we replaced the handle, make sure it's not a standard
+ * handle if we think we need to close it.
+ */
+ if (hNative2 != INVALID_HANDLE_VALUE)
+ {
+ if ( !(fFlags & RTPIPE_N_LEAVE_OPEN)
+ && hNative != GetStdHandle(STD_INPUT_HANDLE)
+ && hNative != GetStdHandle(STD_OUTPUT_HANDLE)
+ && hNative != GetStdHandle(STD_ERROR_HANDLE) )
+ CloseHandle(hNative);
+ }
+ *phPipe = pThis;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* Bail out. */
+ if (hNative2 != INVALID_HANDLE_VALUE)
+ CloseHandle(hNative2);
+ CloseHandle(pThis->Overlapped.hEvent);
+ }
+ RTCritSectDelete(&pThis->CritSect);
+ }
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTDECL(RTHCINTPTR) RTPipeToNative(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hPipe;
+}
+
+
+RTDECL(int) RTPipeGetCreationInheritability(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, false);
+
+ return pThis->fCreatedInheritable;
+}
+
+
+RTDECL(int) RTPipeRead(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbRead);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent readers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /*
+ * Kick off a an overlapped read. It should return immediately if
+ * there are bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ if ( cbToRead == 0
+ || ReadFile(pThis->hPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Overlapped))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ /* We use NtCancelIoFile here because the CancelIo API
+ providing access to it wasn't available till NT4. This
+ code needs to work (or at least load) with NT 3.1 */
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = NtCancelIoFile(pThis->hPipe, &Ios);
+ if (!NT_SUCCESS(rcNt))
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
+ {
+ *pcbRead = cbRead;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_OPERATION_ABORTED)
+ {
+ *pcbRead = 0;
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeReadBlocking(RTPIPE hPipe, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent readers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ size_t cbTotalRead = 0;
+ while (cbToRead > 0)
+ {
+ /*
+ * Kick of a an overlapped read. It should return immediately if
+ * there is bytes in the buffer. If not, we'll cancel it and see
+ * what we get back.
+ */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ if (ReadFile(pThis->hPipe, pvBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->Overlapped))
+ rc = VINF_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* advance */
+ cbToRead -= cbRead;
+ cbTotalRead += cbRead;
+ pvBuf = (uint8_t *)pvBuf + cbRead;
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ if (pcbRead)
+ {
+ *pcbRead = cbTotalRead;
+ if ( RT_FAILURE(rc)
+ && cbTotalRead
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWrite(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pcbWritten);
+ AssertPtr(pvBuf);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent writers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /* If I/O is pending, check if it has completed. */
+ if (pThis->fIOPending)
+ rc = rtPipeWriteCheckCompletion(pThis);
+ else
+ rc = VINF_SUCCESS;
+ if (rc == VINF_SUCCESS)
+ {
+ Assert(!pThis->fIOPending);
+
+ /* Adjust the number of bytes to write to fit into the current
+ buffer quota, unless we've promised stuff in RTPipeSelectOne.
+ WriteQuotaAvailable better not be zero when it shouldn't!! */
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if ( !pThis->fPromisedWritable
+ && cbToWrite > 0
+ && rtPipeQueryNtInfo(pThis, &Info))
+ {
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ rc = VERR_BROKEN_PIPE;
+ /** @todo fixme: To get the pipe writing support to work the
+ * block below needs to be commented out until a
+ * way is found to address the problem of the incorrectly
+ * set field Info.WriteQuotaAvailable.
+ * Update: We now just write up to RTPIPE_NT_SIZE more. This is quite
+ * possibely what lead to the misunderstanding here wrt to
+ * WriteQuotaAvailable updating. */
+#if 0
+ else if ( cbToWrite >= Info.WriteQuotaAvailable
+ && Info.OutboundQuota != 0
+ && (Info.WriteQuotaAvailable || pThis->cbBounceBufAlloc)
+ )
+ {
+ cbToWrite = Info.WriteQuotaAvailable;
+ if (!cbToWrite)
+ rc = VINF_TRY_AGAIN;
+ }
+#endif
+ }
+ pThis->fPromisedWritable = false;
+
+ /* Do the bounce buffering. */
+ if ( pThis->cbBounceBufAlloc < cbToWrite
+ && pThis->cbBounceBufAlloc < RTPIPE_NT_SIZE)
+ {
+ if (cbToWrite > RTPIPE_NT_SIZE)
+ cbToWrite = RTPIPE_NT_SIZE;
+ void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
+ if (pv)
+ {
+ pThis->pbBounceBuf = (uint8_t *)pv;
+ pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (cbToWrite > RTPIPE_NT_SIZE)
+ cbToWrite = RTPIPE_NT_SIZE;
+ if (RT_SUCCESS(rc) && cbToWrite)
+ {
+ memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
+ pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
+
+ /* Submit the write. */
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hPipe, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->Overlapped))
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ *pcbWritten = cbToWrite;
+ pThis->fIOPending = true;
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+}
+
+
+RTDECL(int) RTPipeWriteBlocking(RTPIPE hPipe, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+ AssertPtr(pvBuf);
+ AssertPtrNull(pcbWritten);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /* No concurrent writers, sorry. */
+ if (pThis->cUsers == 0)
+ {
+ pThis->cUsers++;
+
+ /*
+ * If I/O is pending, wait for it to complete.
+ */
+ if (pThis->fIOPending)
+ {
+ rc = rtPipeWriteCheckCompletion(pThis);
+ while (rc == VINF_TRY_AGAIN)
+ {
+ Assert(pThis->fIOPending);
+ HANDLE hEvent = pThis->Overlapped.hEvent;
+ RTCritSectLeave(&pThis->CritSect);
+ WaitForSingleObject(hEvent, INFINITE);
+ RTCritSectEnter(&pThis->CritSect);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ Assert(!pThis->fIOPending);
+ pThis->fPromisedWritable = false;
+
+ /*
+ * Try write everything.
+ * No bounce buffering, cUsers protects us.
+ */
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ pThis->fIOPending = true;
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD cbWritten = 0;
+ DWORD const cbToWriteInThisIteration = cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0;
+ if (WriteFile(pThis->hPipe, pvBuf, cbToWriteInThisIteration, &cbWritten, &pThis->Overlapped))
+ rc = VINF_SUCCESS;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ WaitForSingleObject(pThis->Overlapped.hEvent, INFINITE);
+ if (GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbWritten, TRUE /*fWait*/))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (GetLastError() == ERROR_NO_DATA)
+ rc = VERR_BROKEN_PIPE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectEnter(&pThis->CritSect);
+ pThis->fIOPending = false;
+ if (RT_FAILURE(rc))
+ break;
+
+ /* advance */
+ if (cbWritten > cbToWriteInThisIteration) /* paranoia^3 */
+ cbWritten = cbToWriteInThisIteration;
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ pThis->cUsers--;
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+ RTCritSectLeave(&pThis->CritSect);
+ }
+ return rc;
+
+#if 0 /** @todo r=bird: What's this? */
+ int rc = rtPipeTryBlocking(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cbTotalWritten = 0;
+ while (cbToWrite > 0)
+ {
+ ssize_t cbWritten = write(pThis->fd, pvBuf, RT_MIN(cbToWrite, SSIZE_MAX));
+ if (cbWritten < 0)
+ {
+ rc = RTErrConvertFromErrno(errno);
+ break;
+ }
+
+ /* advance */
+ pvBuf = (char const *)pvBuf + cbWritten;
+ cbTotalWritten += cbWritten;
+ cbToWrite -= cbWritten;
+ }
+
+ if (pcbWritten)
+ {
+ *pcbWritten = cbTotalWritten;
+ if ( RT_FAILURE(rc)
+ && cbTotalWritten
+ && rc != VERR_INVALID_POINTER)
+ rc = VINF_SUCCESS;
+ }
+
+ ASMAtomicDecU32(&pThis->u32State);
+ }
+ return rc;
+#endif
+}
+
+
+RTDECL(int) RTPipeFlush(RTPIPE hPipe)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->fRead, VERR_ACCESS_DENIED);
+
+ if (!FlushFileBuffers(pThis->hPipe))
+ {
+ int rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTPipeSelectOne(RTPIPE hPipe, RTMSINTERVAL cMillies)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ uint64_t const StartMsTS = RTTimeMilliTS();
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+ for (unsigned iLoop = 0;; iLoop++)
+ {
+ HANDLE hWait = INVALID_HANDLE_VALUE;
+ if (pThis->fRead)
+ {
+ if (pThis->fIOPending)
+ hWait = pThis->Overlapped.hEvent;
+ else
+ {
+ /* Peek at the pipe buffer and see how many bytes it contains. */
+ DWORD cbAvailable;
+ if ( PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL)
+ && cbAvailable > 0)
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* Start a zero byte read operation that we can wait on. */
+ if (cMillies == 0)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ AssertBreakStmt(pThis->cUsers == 0, rc = VERR_INTERNAL_ERROR_5);
+ rc = ResetEvent(pThis->Overlapped.hEvent); Assert(rc == TRUE);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
+ {
+ rc = VINF_SUCCESS;
+ if (iLoop > 10)
+ RTThreadYield();
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->cUsers++;
+ pThis->fIOPending = true;
+ pThis->fZeroByteRead = true;
+ hWait = pThis->Overlapped.hEvent;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ {
+ if (pThis->fIOPending)
+ {
+ rc = rtPipeWriteCheckCompletion(pThis);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ if (pThis->fIOPending)
+ hWait = pThis->Overlapped.hEvent;
+ else
+ {
+ FILE_PIPE_LOCAL_INFORMATION Info;
+#if 1
+ /* We can always write one bounce buffer full of data regardless of
+ the pipe buffer state. We must of course take this into account,
+ or code like "Full write buffer" test in tstRTPipe gets confused. */
+ rc = VINF_SUCCESS;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState != FILE_PIPE_CLOSING_STATE)
+ pThis->fPromisedWritable = true;
+ else
+ rc = VERR_BROKEN_PIPE;
+ }
+ else
+ pThis->fPromisedWritable = true;
+ break;
+
+#else /* old code: */
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ {
+ rc = VERR_BROKEN_PIPE;
+ break;
+ }
+
+ /* Check for available write buffer space. */
+ if (Info.WriteQuotaAvailable > 0)
+ {
+ pThis->fPromisedWritable = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* delayed buffer alloc or timeout: phony promise
+ later: See if we still can associate a semaphore with
+ the pipe, like on OS/2. */
+ if (Info.OutboundQuota == 0 || cMillies)
+ {
+ pThis->fPromisedWritable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ else
+ {
+ pThis->fPromisedWritable = true;
+ rc = VINF_SUCCESS;
+ break;
+ }
+#endif
+ }
+ }
+ if (RT_FAILURE(rc))
+ break;
+
+ /*
+ * Check for timeout.
+ */
+ DWORD cMsMaxWait = INFINITE;
+ if ( cMillies != RT_INDEFINITE_WAIT
+ && ( hWait != INVALID_HANDLE_VALUE
+ || iLoop > 10)
+ )
+ {
+ uint64_t cElapsed = RTTimeMilliTS() - StartMsTS;
+ if (cElapsed >= cMillies)
+ {
+ rc = VERR_TIMEOUT;
+ break;
+ }
+ cMsMaxWait = cMillies - (uint32_t)cElapsed;
+ }
+
+ /*
+ * Wait.
+ */
+ if (hWait != INVALID_HANDLE_VALUE)
+ {
+ RTCritSectLeave(&pThis->CritSect);
+
+ DWORD dwRc = WaitForSingleObject(hWait, cMsMaxWait);
+ if (dwRc == WAIT_OBJECT_0)
+ rc = VINF_SUCCESS;
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if ( RT_FAILURE(rc)
+ && pThis->u32Magic != RTPIPE_MAGIC)
+ return rc;
+
+ RTCritSectEnter(&pThis->CritSect);
+ if (pThis->fZeroByteRead)
+ {
+ pThis->cUsers--;
+ pThis->fIOPending = false;
+ if (rc != VINF_SUCCESS)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hPipe, &Ios);
+ }
+ DWORD cbRead = 0;
+ GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/);
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ if (rc == VERR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryReadable(RTPIPE hPipe, size_t *pcbReadable)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->fRead, VERR_PIPE_NOT_READ);
+ AssertPtrReturn(pcbReadable, VERR_INVALID_POINTER);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /** @todo The file size should give the same info and be slightly faster... */
+ DWORD cbAvailable = 0;
+ if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+#if ARCH_BITS == 32
+ /*
+ * Kludge!
+ *
+ * Prior to XP SP1 (?), the returned cbAvailable value was not adjusted
+ * by the read position in the current message/buffer, so it could
+ * potentially be too high. This may cause the caller to try read more
+ * data than what's actually available, which may cause the read to
+ * block when the caller thought it wouldn't.
+ *
+ * To get an accurate readable size, we have to provide an output
+ * buffer and see how much we actually get back in it, as the data
+ * peeking works correctly (as you would expect).
+ */
+ if (cbAvailable == 0 || g_enmWinVer >= kRTWinOSType_XP64)
+ { /* No data available or kernel shouldn't be affected. */ }
+ else
+ {
+ for (unsigned i = 0; ; i++)
+ {
+ uint8_t abBufStack[_16K];
+ void *pvBufFree = NULL;
+ void *pvBuf;
+ DWORD cbBuf = RT_ALIGN_32(cbAvailable + i * 256, 64);
+ if (cbBuf <= sizeof(abBufStack))
+ {
+ pvBuf = abBufStack;
+ /* No cbBuf = sizeof(abBufStack) here! PeekNamedPipe bounce buffers the request on the heap. */
+ }
+ else
+ {
+ pvBufFree = pvBuf = RTMemTmpAlloc(cbBuf);
+ if (!pvBuf)
+ {
+ rc = VERR_NO_TMP_MEMORY;
+ cbAvailable = 1;
+ break;
+ }
+ }
+
+ DWORD cbAvailable2 = 0;
+ DWORD cbRead = 0;
+ BOOL fRc = PeekNamedPipe(pThis->hPipe, pvBuf, cbBuf, &cbRead, &cbAvailable2, NULL);
+ Log(("RTPipeQueryReadable: #%u: cbAvailable=%#x cbRead=%#x cbAvailable2=%#x (cbBuf=%#x)\n",
+ i, cbAvailable, cbRead, cbAvailable2, cbBuf));
+
+ RTMemTmpFree(pvBufFree);
+
+ if (fRc)
+ {
+ if (cbAvailable2 <= cbBuf || i >= 10)
+ cbAvailable = cbRead;
+ else
+ {
+ cbAvailable = cbAvailable2;
+ continue;
+ }
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ cbAvailable = 1;
+ }
+ break;
+ }
+ }
+#endif
+ *pcbReadable = cbAvailable;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTCritSectLeave(&pThis->CritSect);
+ return rc;
+}
+
+
+RTDECL(int) RTPipeQueryInfo(RTPIPE hPipe, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttr)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ rtPipeFakeQueryInfo(pObjInfo, enmAddAttr, pThis->fRead);
+
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ pObjInfo->cbAllocated = pThis->fRead ? Info.InboundQuota : Info.OutboundQuota;
+ pObjInfo->cbObject = pThis->fRead ? Info.ReadDataAvailable : Info.WriteQuotaAvailable;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+int rtPipePollGetHandle(RTPIPE hPipe, uint32_t fEvents, PRTHCINTPTR phNative)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, VERR_INVALID_HANDLE);
+
+ AssertReturn(!(fEvents & RTPOLL_EVT_READ) || pThis->fRead, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fEvents & RTPOLL_EVT_WRITE) || !pThis->fRead, VERR_INVALID_PARAMETER);
+
+ /* Later: Try register an event handle with the pipe like on OS/2, there is
+ a file control for doing this obviously intended for the OS/2 subsys.
+ The question is whether this still exists on Vista and W7. */
+ *phNative = (RTHCINTPTR)pThis->Overlapped.hEvent;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks for pending events.
+ *
+ * @returns Event mask or 0.
+ * @param pThis The pipe handle.
+ * @param fEvents The desired events.
+ */
+static uint32_t rtPipePollCheck(RTPIPEINTERNAL *pThis, uint32_t fEvents)
+{
+ uint32_t fRetEvents = 0;
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ else if (pThis->fRead)
+ {
+ if (!pThis->fIOPending)
+ {
+ DWORD cbAvailable;
+ if (PeekNamedPipe(pThis->hPipe, NULL, 0, NULL, &cbAvailable, NULL))
+ {
+ if ( (fEvents & RTPOLL_EVT_READ)
+ && cbAvailable > 0)
+ fRetEvents |= RTPOLL_EVT_READ;
+ }
+ else
+ {
+ if (GetLastError() == ERROR_BROKEN_PIPE)
+ pThis->fBrokenPipe = true;
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ }
+ }
+ else
+ {
+ if (pThis->fIOPending)
+ {
+ rtPipeWriteCheckCompletion(pThis);
+ if (pThis->fBrokenPipe)
+ fRetEvents |= RTPOLL_EVT_ERROR;
+ }
+ if ( !pThis->fIOPending
+ && !fRetEvents)
+ {
+ FILE_PIPE_LOCAL_INFORMATION Info;
+ if (rtPipeQueryNtInfo(pThis, &Info))
+ {
+ /* Check for broken pipe. */
+ if (Info.NamedPipeState == FILE_PIPE_CLOSING_STATE)
+ {
+ fRetEvents = RTPOLL_EVT_ERROR;
+ pThis->fBrokenPipe = true;
+ }
+
+ /* Check if there is available buffer space. */
+ if ( !fRetEvents
+ && (fEvents & RTPOLL_EVT_WRITE)
+ && ( Info.WriteQuotaAvailable > 0
+ || Info.OutboundQuota == 0)
+ )
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ else if (fEvents & RTPOLL_EVT_WRITE)
+ fRetEvents |= RTPOLL_EVT_WRITE;
+ }
+ }
+
+ return fRetEvents;
+}
+
+
+/**
+ * Internal RTPoll helper that polls the pipe handle and, if @a fNoWait is
+ * clear, starts whatever actions we've got running during the poll call.
+ *
+ * @returns 0 if no pending events, actions initiated if @a fNoWait is clear.
+ * Event mask (in @a fEvents) and no actions if the handle is ready
+ * already.
+ * UINT32_MAX (asserted) if the pipe handle is busy in I/O or a
+ * different poll set.
+ *
+ * @param hPipe The pipe handle.
+ * @param hPollSet The poll set handle (for access checks).
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries.
+ * @param fNoWait Set if it's a zero-wait poll call. Clear if
+ * we'll wait for an event to occur.
+ */
+uint32_t rtPipePollStart(RTPIPE hPipe, RTPOLLSET hPollSet, uint32_t fEvents, bool fFinalEntry, bool fNoWait)
+{
+ /** @todo All this polling code could be optimized to make fewer system
+ * calls; like for instance the ResetEvent calls. */
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, UINT32_MAX);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, UINT32_MAX);
+ RT_NOREF_PV(fFinalEntry);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, UINT32_MAX);
+
+ /* Check that this is the only current use of this pipe. */
+ uint32_t fRetEvents;
+ if ( pThis->cUsers == 0
+ || pThis->hPollSet == hPollSet)
+ {
+ /* Check what the current events are. */
+ fRetEvents = rtPipePollCheck(pThis, fEvents);
+ if ( !fRetEvents
+ && !fNoWait)
+ {
+ /* Make sure the event semaphore has been reset. */
+ if (!pThis->fIOPending)
+ {
+ rc = ResetEvent(pThis->Overlapped.hEvent);
+ Assert(rc == TRUE);
+ }
+
+ /* Kick off the zero byte read thing if applicable. */
+ if ( !pThis->fIOPending
+ && pThis->fRead
+ && (fEvents & RTPOLL_EVT_READ)
+ )
+ {
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hPipe, pThis->abBuf, 0, &cbRead, &pThis->Overlapped))
+ fRetEvents = rtPipePollCheck(pThis, fEvents);
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ pThis->fIOPending = true;
+ pThis->fZeroByteRead = true;
+ }
+ else
+ fRetEvents = RTPOLL_EVT_ERROR;
+ }
+
+ /* If we're still set for the waiting, record the poll set and
+ mark the pipe used. */
+ if (!fRetEvents)
+ {
+ pThis->cUsers++;
+ pThis->hPollSet = hPollSet;
+ }
+ }
+ }
+ else
+ {
+ AssertFailed();
+ fRetEvents = UINT32_MAX;
+ }
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
+
+/**
+ * Called after a WaitForMultipleObjects returned in order to check for pending
+ * events and stop whatever actions that rtPipePollStart() initiated.
+ *
+ * @returns Event mask or 0.
+ *
+ * @param hPipe The pipe handle.
+ * @param fEvents The events we're polling for.
+ * @param fFinalEntry Set if this is the final entry for this handle
+ * in this poll set. This can be used for dealing
+ * with duplicate entries. Only keep in mind that
+ * this method is called in reverse order, so the
+ * first call will have this set (when the entire
+ * set was processed).
+ * @param fHarvestEvents Set if we should check for pending events.
+ */
+uint32_t rtPipePollDone(RTPIPE hPipe, uint32_t fEvents, bool fFinalEntry, bool fHarvestEvents)
+{
+ RTPIPEINTERNAL *pThis = hPipe;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTPIPE_MAGIC, 0);
+ RT_NOREF_PV(fFinalEntry);
+ RT_NOREF_PV(fHarvestEvents);
+
+ int rc = RTCritSectEnter(&pThis->CritSect);
+ AssertRCReturn(rc, 0);
+
+ Assert(pThis->cUsers > 0);
+
+
+ /* Cancel the zero byte read. */
+ uint32_t fRetEvents = 0;
+ if (pThis->fZeroByteRead)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NtCancelIoFile(pThis->hPipe, &Ios);
+
+ DWORD cbRead = 0;
+ if ( !GetOverlappedResult(pThis->hPipe, &pThis->Overlapped, &cbRead, TRUE /*fWait*/)
+ && GetLastError() != ERROR_OPERATION_ABORTED)
+ fRetEvents = RTPOLL_EVT_ERROR;
+
+ pThis->fIOPending = false;
+ pThis->fZeroByteRead = false;
+ }
+
+ /* harvest events. */
+ fRetEvents |= rtPipePollCheck(pThis, fEvents);
+
+ /* update counters. */
+ pThis->cUsers--;
+ /** @todo This isn't sane, or is it? See OS/2 impl. */
+ if (!pThis->cUsers)
+ pThis->hPollSet = NIL_RTPOLLSET;
+
+ RTCritSectLeave(&pThis->CritSect);
+ return fRetEvents;
+}
+
diff --git a/src/VBox/Runtime/r3/win/process-win.cpp b/src/VBox/Runtime/r3/win/process-win.cpp
new file mode 100644
index 00000000..18ed9ab0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/process-win.cpp
@@ -0,0 +1,2750 @@
+/* $Id: process-win.cpp $ */
+/** @file
+ * IPRT - Process, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/asm.h> /* hack */
+
+#include <iprt/nt/nt-and-windows.h>
+#include <Userenv.h>
+#include <tlhelp32.h>
+#ifndef IPRT_NO_CRT
+# include <process.h>
+# include <errno.h>
+# include <Strsafe.h>
+#endif
+#include <LsaLookup.h>
+#include <Lmcons.h>
+
+#define _NTDEF_ /* Prevents redefining (P)UNICODE_STRING. */
+#include <Ntsecapi.h>
+
+#include <iprt/process.h>
+#include "internal-r3-win.h"
+
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/env.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/string.h>
+#include <iprt/socket.h>
+#include <iprt/utf16.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/* kernel32.dll: */
+//typedef DWORD (WINAPI *PFNWTSGETACTIVECONSOLESESSIONID)(VOID);
+typedef HANDLE (WINAPI *PFNCREATETOOLHELP32SNAPSHOT)(DWORD, DWORD);
+typedef BOOL (WINAPI *PFNPROCESS32FIRSTW)(HANDLE, LPPROCESSENTRY32W);
+typedef BOOL (WINAPI *PFNPROCESS32NEXTW)(HANDLE, LPPROCESSENTRY32W);
+
+/* psapi.dll: */
+typedef BOOL (WINAPI *PFNENUMPROCESSES)(LPDWORD, DWORD, LPDWORD);
+typedef DWORD (WINAPI *PFNGETMODULEBASENAMEW)(HANDLE, HMODULE, LPWSTR, DWORD);
+
+/* advapi32.dll: */
+typedef BOOL (WINAPI *PFNCREATEPROCESSWITHLOGON)(LPCWSTR, LPCWSTR, LPCWSTR, DWORD, LPCWSTR, LPWSTR, DWORD,
+ LPVOID, LPCWSTR, LPSTARTUPINFOW, LPPROCESS_INFORMATION);
+typedef NTSTATUS (NTAPI *PFNLSALOOKUPNAMES2)(LSA_HANDLE, ULONG, ULONG, PLSA_UNICODE_STRING,
+ PLSA_REFERENCED_DOMAIN_LIST*, PLSA_TRANSLATED_SID2*);
+
+/* userenv.dll: */
+typedef BOOL (WINAPI *PFNCREATEENVIRONMENTBLOCK)(LPVOID *, HANDLE, BOOL);
+typedef BOOL (WINAPI *PFNPFNDESTROYENVIRONMENTBLOCK)(LPVOID);
+typedef BOOL (WINAPI *PFNLOADUSERPROFILEW)(HANDLE, LPPROFILEINFOW);
+typedef BOOL (WINAPI *PFNUNLOADUSERPROFILE)(HANDLE, HANDLE);
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once structure. */
+static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
+/** Critical section protecting the process array. */
+static RTCRITSECT g_CritSect;
+/** The number of processes in the array. */
+static uint32_t g_cProcesses;
+/** The current allocation size. */
+static uint32_t g_cProcessesAlloc;
+/** Array containing the live or non-reaped child processes. */
+static struct RTPROCWINENTRY
+{
+ /** The process ID. */
+ ULONG_PTR pid;
+ /** The process handle. */
+ HANDLE hProcess;
+} *g_paProcesses;
+
+/** Structure for storing a user's account info.
+ * Must be free'd with rtProcWinFreeAccountInfo(). */
+typedef struct RTPROCWINACCOUNTINFO
+{
+ /** User name. */
+ PRTUTF16 pwszUserName;
+ /** Domain this account is tied to. Can be NULL if no domain is being used. */
+ PRTUTF16 pwszDomain;
+} RTPROCWINACCOUNTINFO, *PRTPROCWINACCOUNTINFO;
+
+/** @name userenv.dll imports (we don't unload it).
+ * They're all optional. So in addition to using g_rtProcWinResolveOnce, the
+ * caller must also check if any of the necessary APIs are NULL pointers.
+ * @{ */
+/** Init once structure for run-as-user functions we need. */
+static RTONCE g_rtProcWinResolveOnce = RTONCE_INITIALIZER;
+/* kernel32.dll: */
+static PFNCREATETOOLHELP32SNAPSHOT g_pfnCreateToolhelp32Snapshot = NULL;
+static PFNPROCESS32FIRSTW g_pfnProcess32FirstW = NULL;
+static PFNPROCESS32NEXTW g_pfnProcess32NextW = NULL;
+/* psapi.dll: */
+static PFNGETMODULEBASENAMEW g_pfnGetModuleBaseNameW = NULL;
+static PFNENUMPROCESSES g_pfnEnumProcesses = NULL;
+/* advapi32.dll: */
+static PFNCREATEPROCESSWITHLOGON g_pfnCreateProcessWithLogonW = NULL;
+static decltype(LogonUserW) *g_pfnLogonUserW = NULL;
+static decltype(CreateProcessAsUserW) *g_pfnCreateProcessAsUserW = NULL;
+/* user32.dll: */
+static decltype(OpenWindowStationW) *g_pfnOpenWindowStationW = NULL;
+static decltype(CloseWindowStation) *g_pfnCloseWindowStation = NULL;
+/* userenv.dll: */
+static PFNCREATEENVIRONMENTBLOCK g_pfnCreateEnvironmentBlock = NULL;
+static PFNPFNDESTROYENVIRONMENTBLOCK g_pfnDestroyEnvironmentBlock = NULL;
+static PFNLOADUSERPROFILEW g_pfnLoadUserProfileW = NULL;
+static PFNUNLOADUSERPROFILE g_pfnUnloadUserProfile = NULL;
+/** @} */
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec);
+static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
+ PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec);
+
+
+/**
+ * Clean up the globals.
+ *
+ * @param enmReason Ignored.
+ * @param iStatus Ignored.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
+{
+ NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
+
+ RTCritSectDelete(&g_CritSect);
+
+ size_t i = g_cProcesses;
+ while (i-- > 0)
+ {
+ CloseHandle(g_paProcesses[i].hProcess);
+ g_paProcesses[i].hProcess = NULL;
+ }
+ RTMemFree(g_paProcesses);
+
+ g_paProcesses = NULL;
+ g_cProcesses = 0;
+ g_cProcessesAlloc = 0;
+}
+
+
+/**
+ * Initialize the globals.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser)
+{
+ NOREF(pvUser);
+
+ g_cProcesses = 0;
+ g_cProcessesAlloc = 0;
+ g_paProcesses = NULL;
+ int rc = RTCritSectInit(&g_CritSect);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo init once, terminate once - this is a generic thing which should
+ * have some kind of static and simpler setup! */
+ rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
+ if (RT_SUCCESS(rc))
+ return rc;
+ RTCritSectDelete(&g_CritSect);
+ }
+ return rc;
+}
+
+
+/**
+ * Gets the process handle for a process from g_paProcesses.
+ *
+ * @returns Process handle if found, NULL if not.
+ * @param pid The process to remove (pid).
+ */
+static HANDLE rtProcWinFindPid(RTPROCESS pid)
+{
+ HANDLE hProcess = NULL;
+
+ RTCritSectEnter(&g_CritSect);
+ uint32_t i = g_cProcesses;
+ while (i-- > 0)
+ if (g_paProcesses[i].pid == pid)
+ {
+ hProcess = g_paProcesses[i].hProcess;
+ break;
+ }
+ RTCritSectLeave(&g_CritSect);
+
+ return hProcess;
+}
+
+
+/**
+ * Removes a process from g_paProcesses and closes the process handle.
+ *
+ * @param pid The process to remove (pid).
+ */
+static void rtProcWinRemovePid(RTPROCESS pid)
+{
+ RTCritSectEnter(&g_CritSect);
+ uint32_t i = g_cProcesses;
+ while (i-- > 0)
+ if (g_paProcesses[i].pid == pid)
+ {
+ HANDLE hProcess = g_paProcesses[i].hProcess;
+
+ g_cProcesses--;
+ uint32_t cToMove = g_cProcesses - i;
+ if (cToMove)
+ memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
+
+ RTCritSectLeave(&g_CritSect);
+ CloseHandle(hProcess);
+ return;
+ }
+ RTCritSectLeave(&g_CritSect);
+}
+
+
+/**
+ * Adds a process to g_paProcesses.
+ *
+ * @returns IPRT status code.
+ * @param pid The process id.
+ * @param hProcess The process handle.
+ */
+static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
+{
+ RTCritSectEnter(&g_CritSect);
+
+ uint32_t i = g_cProcesses;
+ if (i >= g_cProcessesAlloc)
+ {
+ void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
+ if (RT_UNLIKELY(!pvNew))
+ {
+ RTCritSectLeave(&g_CritSect);
+ return VERR_NO_MEMORY;
+ }
+ g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
+ g_cProcessesAlloc = i + 16;
+ }
+
+ g_paProcesses[i].pid = pid;
+ g_paProcesses[i].hProcess = hProcess;
+ g_cProcesses = i + 1;
+
+ RTCritSectLeave(&g_CritSect);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Initialize the import APIs for run-as-user and special environment support.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int) rtProcWinResolveOnce(void *pvUser)
+{
+ int rc;
+ RTLDRMOD hMod;
+ RT_NOREF_PV(pvUser);
+
+ /*
+ * kernel32.dll APIs introduced after NT4.
+ */
+ g_pfnCreateToolhelp32Snapshot = (PFNCREATETOOLHELP32SNAPSHOT)GetProcAddress(g_hModKernel32, "CreateToolhelp32Snapshot");
+ g_pfnProcess32FirstW = (PFNPROCESS32FIRSTW )GetProcAddress(g_hModKernel32, "Process32FirstW");
+ g_pfnProcess32NextW = (PFNPROCESS32NEXTW )GetProcAddress(g_hModKernel32, "Process32NextW");
+
+ /*
+ * psapi.dll APIs, if none of the above are available.
+ */
+ if ( !g_pfnCreateToolhelp32Snapshot
+ || !g_pfnProcess32FirstW
+ || !g_pfnProcess32NextW)
+ {
+ Assert(!g_pfnCreateToolhelp32Snapshot && !g_pfnProcess32FirstW && !g_pfnProcess32NextW);
+
+ rc = RTLdrLoadSystem("psapi.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "GetModuleBaseNameW", (void **)&g_pfnGetModuleBaseNameW);
+ AssertStmt(RT_SUCCESS(rc), g_pfnGetModuleBaseNameW = NULL);
+
+ rc = RTLdrGetSymbol(hMod, "EnumProcesses", (void **)&g_pfnEnumProcesses);
+ AssertStmt(RT_SUCCESS(rc), g_pfnEnumProcesses = NULL);
+
+ RTLdrClose(hMod);
+ }
+ }
+
+ /*
+ * advapi32.dll APIs.
+ */
+ rc = RTLdrLoadSystem("advapi32.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "CreateProcessWithLogonW", (void **)&g_pfnCreateProcessWithLogonW);
+ if (RT_FAILURE(rc)) { g_pfnCreateProcessWithLogonW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "LogonUserW", (void **)&g_pfnLogonUserW);
+ if (RT_FAILURE(rc)) { g_pfnLogonUserW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT350); }
+
+ rc = RTLdrGetSymbol(hMod, "CreateProcessAsUserW", (void **)&g_pfnCreateProcessAsUserW);
+ if (RT_FAILURE(rc)) { g_pfnCreateProcessAsUserW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT350); }
+
+ RTLdrClose(hMod);
+ }
+
+ /*
+ * user32.dll APIs.
+ */
+ rc = RTLdrLoadSystem("user32.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "OpenWindowStationW", (void **)&g_pfnOpenWindowStationW);
+ if (RT_FAILURE(rc)) { g_pfnOpenWindowStationW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT310); }
+
+ rc = RTLdrGetSymbol(hMod, "CloseWindowStation", (void **)&g_pfnCloseWindowStation);
+ if (RT_FAILURE(rc)) { g_pfnCloseWindowStation = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT310); }
+
+ RTLdrClose(hMod);
+ }
+
+ /*
+ * userenv.dll APIs.
+ */
+ rc = RTLdrLoadSystem("userenv.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "LoadUserProfileW", (void **)&g_pfnLoadUserProfileW);
+ if (RT_FAILURE(rc)) { g_pfnLoadUserProfileW = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "UnloadUserProfile", (void **)&g_pfnUnloadUserProfile);
+ if (RT_FAILURE(rc)) { g_pfnUnloadUserProfile = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "CreateEnvironmentBlock", (void **)&g_pfnCreateEnvironmentBlock);
+ if (RT_FAILURE(rc)) { g_pfnCreateEnvironmentBlock = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ rc = RTLdrGetSymbol(hMod, "DestroyEnvironmentBlock", (void **)&g_pfnDestroyEnvironmentBlock);
+ if (RT_FAILURE(rc)) { g_pfnDestroyEnvironmentBlock = NULL; Assert(g_enmWinVer <= kRTWinOSType_NT4); }
+
+ RTLdrClose(hMod);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
+{
+ return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
+ NULL, NULL, NULL, /* standard handles */
+ NULL /*pszAsUser*/, NULL /* pszPassword*/,
+ NULL /*pvExtraData*/, pProcess);
+}
+
+
+/**
+ * The following NT call is for v3.51 and does the equivalent of:
+ * DuplicateTokenEx(hSrcToken, MAXIMUM_ALLOWED, NULL,
+ * SecurityIdentification, TokenPrimary, phToken);
+ */
+static int rtProcWinDuplicateToken(HANDLE hSrcToken, PHANDLE phToken)
+{
+ int rc;
+ if (g_pfnNtDuplicateToken)
+ {
+ SECURITY_QUALITY_OF_SERVICE SecQoS;
+ SecQoS.Length = sizeof(SecQoS);
+ SecQoS.ImpersonationLevel = SecurityIdentification;
+ SecQoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
+ SecQoS.EffectiveOnly = FALSE;
+
+ OBJECT_ATTRIBUTES ObjAttr;
+ InitializeObjectAttributes(&ObjAttr, NULL /*Name*/, 0 /*OBJ_XXX*/, NULL /*Root*/, NULL /*SecDesc*/);
+ ObjAttr.SecurityQualityOfService = &SecQoS;
+
+ NTSTATUS rcNt = g_pfnNtDuplicateToken(hSrcToken, MAXIMUM_ALLOWED, &ObjAttr, FALSE, TokenPrimary, phToken);
+ if (NT_SUCCESS(rcNt))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+ }
+ else
+ rc = VERR_SYMBOL_NOT_FOUND; /** @todo do we really need to duplicate the token? */
+ return rc;
+}
+
+
+/**
+ * Get the token assigned to the thread indicated by @a hThread.
+ *
+ * Only used when RTPROC_FLAGS_AS_IMPERSONATED_TOKEN is in effect and the
+ * purpose is to get a duplicate the impersonated token of the current thread.
+ *
+ * @returns IPRT status code.
+ * @param hThread The thread handle (current thread).
+ * @param phToken Where to return the a duplicate of the thread token
+ * handle on success. (The caller closes it.)
+ */
+static int rtProcWinGetThreadTokenHandle(HANDLE hThread, PHANDLE phToken)
+{
+ AssertPtr(phToken);
+
+ int rc;
+ HANDLE hTokenThread;
+ if (OpenThreadToken(hThread,
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
+ | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
+ TRUE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
+ &hTokenThread))
+ {
+ rc = rtProcWinDuplicateToken(hTokenThread, phToken);
+ CloseHandle(hTokenThread);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Get the token assigned the process indicated by @a hProcess.
+ *
+ * Only used when pwszUser is NULL and RTPROC_FLAGS_AS_IMPERSONATED_TOKEN isn't
+ * set.
+ *
+ * @returns IPRT status code.
+ * @param hProcess The process handle (current process).
+ * @param phToken Where to return the a duplicate of the thread token
+ * handle on success. (The caller closes it.)
+ */
+static int rtProcWinGetProcessTokenHandle(HANDLE hProcess, PHANDLE phToken)
+{
+ AssertPtr(phToken);
+
+ int rc;
+ HANDLE hTokenProcess;
+ if (OpenProcessToken(hProcess,
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
+ | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
+ &hTokenProcess))
+ {
+ rc = rtProcWinDuplicateToken(hTokenProcess, phToken); /* not sure if this is strictly necessary */
+ CloseHandle(hTokenProcess);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Get the process token of the process indicated by @a dwPID if the @a pSid and
+ * @a idSessionDesired matches.
+ *
+ * @returns IPRT status code.
+ * @param dwPid The process identifier.
+ * @param pSid The secure identifier of the user.
+ * @param idDesiredSession The session the process candidate should
+ * preferably belong to, UINT32_MAX if anything
+ * goes.
+ * @param phToken Where to return the a duplicate of the process token
+ * handle on success. (The caller closes it.)
+ */
+static int rtProcWinGetProcessTokenHandle(DWORD dwPid, PSID pSid, DWORD idDesiredSession, PHANDLE phToken)
+{
+ AssertPtr(pSid);
+ AssertPtr(phToken);
+
+ int rc;
+ HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPid);
+ if (hProc != NULL)
+ {
+ HANDLE hTokenProc;
+ if (OpenProcessToken(hProc,
+ TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE
+ | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
+ &hTokenProc))
+ {
+ /*
+ * Query the user SID from the token.
+ */
+ SetLastError(NO_ERROR);
+ DWORD dwSize = 0;
+ BOOL fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
+ DWORD dwErr = GetLastError();
+ if ( !fRc
+ && dwErr == ERROR_INSUFFICIENT_BUFFER
+ && dwSize > 0)
+ {
+ PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemTmpAllocZ(dwSize);
+ if (pTokenUser)
+ {
+ if (GetTokenInformation(hTokenProc, TokenUser, pTokenUser, dwSize, &dwSize))
+ {
+ /*
+ * Match token user with the user we're want to create a process as.
+ */
+ if ( IsValidSid(pTokenUser->User.Sid)
+ && EqualSid(pTokenUser->User.Sid, pSid))
+ {
+ /*
+ * Do we need to match the session ID?
+ */
+ rc = VINF_SUCCESS;
+ if (idDesiredSession != UINT32_MAX)
+ {
+ DWORD idCurSession = UINT32_MAX;
+ if (GetTokenInformation(hTokenProc, TokenSessionId, &idCurSession, sizeof(DWORD), &dwSize))
+ rc = idDesiredSession == idCurSession ? VINF_SUCCESS : VERR_NOT_FOUND;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Got a match. Duplicate the token. This duplicated token will
+ * be used for the actual CreateProcessAsUserW() call then.
+ */
+ rc = rtProcWinDuplicateToken(hTokenProc, phToken);
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTMemTmpFree(pTokenUser);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (fRc || dwErr == NO_ERROR)
+ rc = VERR_IPE_UNEXPECTED_STATUS;
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ CloseHandle(hTokenProc);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ CloseHandle(hProc);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+/**
+ * Fallback method for rtProcWinFindTokenByProcess that uses the older NT4
+ * PSAPI.DLL API.
+ *
+ * @returns Success indicator.
+ * @param papszNames The process candidates, in prioritized order.
+ * @param pSid The secure identifier of the user.
+ * @param phToken Where to return the token handle - duplicate,
+ * caller closes it on success.
+ *
+ * @remarks NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
+ * part of the OS) in order to get a lookup. If we don't have this DLL
+ * we are not able to get a token and therefore no UI will be visible.
+ */
+static bool rtProcWinFindTokenByProcessAndPsApi(const char * const *papszNames, PSID pSid, PHANDLE phToken)
+{
+ /*
+ * Load PSAPI.DLL and resolve the two symbols we need.
+ */
+ if ( !g_pfnGetModuleBaseNameW
+ || !g_pfnEnumProcesses)
+ return false;
+
+ /*
+ * Get a list of PID. We retry if it looks like there are more PIDs
+ * to be returned than what we supplied buffer space for.
+ */
+ bool fFound = false;
+ int rc = VINF_SUCCESS;
+ DWORD cbPidsAllocated = 4096;
+ DWORD cbPidsReturned = 0; /* (MSC maybe used uninitialized) */
+ DWORD *paPids;
+ for (;;)
+ {
+ paPids = (DWORD *)RTMemTmpAlloc(cbPidsAllocated);
+ AssertBreakStmt(paPids, rc = VERR_NO_TMP_MEMORY);
+ cbPidsReturned = 0;
+ if (!g_pfnEnumProcesses(paPids, cbPidsAllocated, &cbPidsReturned))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc\n", rc));
+ }
+ if ( cbPidsReturned < cbPidsAllocated
+ || cbPidsAllocated >= _512K)
+ break;
+ RTMemTmpFree(paPids);
+ cbPidsAllocated *= 2;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Search for the process.
+ *
+ * We ASSUME that the caller won't be specifying any names longer
+ * than RTPATH_MAX.
+ */
+ PRTUTF16 pwszProcName = (PRTUTF16)RTMemTmpAllocZ(RTPATH_MAX * sizeof(pwszProcName[0]));
+ if (pwszProcName)
+ {
+ for (size_t i = 0; papszNames[i] && !fFound; i++)
+ {
+ const DWORD cPids = cbPidsReturned / sizeof(DWORD);
+ for (DWORD iPid = 0; iPid < cPids && !fFound; iPid++)
+ {
+ HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, paPids[iPid]);
+ if (hProc)
+ {
+ *pwszProcName = '\0';
+ DWORD cbRet = g_pfnGetModuleBaseNameW(hProc, 0 /*hModule = exe */, pwszProcName, RTPATH_MAX);
+ if ( cbRet > 0
+ && RTUtf16ICmpAscii(pwszProcName, papszNames[i]) == 0
+ && RT_SUCCESS(rtProcWinGetProcessTokenHandle(paPids[iPid], pSid, UINT32_MAX, phToken)))
+ fFound = true;
+ CloseHandle(hProc);
+ }
+ }
+ }
+ RTMemTmpFree(pwszProcName);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ RTMemTmpFree(paPids);
+
+ return fFound;
+}
+
+
+/**
+ * Finds a one of the processes in @a papszNames running with user @a pSid and possibly
+ * in the required windows session. Returns a duplicate handle to its token.
+ *
+ * @returns Success indicator.
+ * @param papszNames The process candidates, in prioritized order.
+ * @param pSid The secure identifier of the user.
+ * @param idDesiredSession The session the process candidate should
+ * belong to if possible, UINT32_MAX if anything
+ * goes.
+ * @param phToken Where to return the token handle - duplicate,
+ * caller closes it on success.
+ */
+static bool rtProcWinFindTokenByProcess(const char * const *papszNames, PSID pSid, uint32_t idDesiredSession, PHANDLE phToken)
+{
+ AssertPtr(papszNames);
+ AssertPtr(pSid);
+ AssertPtr(phToken);
+
+ bool fFound = false;
+
+ /*
+ * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
+ * and reliable. Fallback to EnumProcess on NT4.
+ */
+ bool fFallback = true;
+ if (g_pfnProcess32NextW && g_pfnProcess32FirstW && g_pfnCreateToolhelp32Snapshot)
+ {
+ HANDLE hSnap = g_pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
+ Assert(hSnap != INVALID_HANDLE_VALUE);
+ if (hSnap != INVALID_HANDLE_VALUE)
+ {
+ fFallback = false;
+ for (size_t i = 0; papszNames[i] && !fFound; i++)
+ {
+ PROCESSENTRY32W ProcEntry;
+ ProcEntry.dwSize = sizeof(PROCESSENTRY32);
+ ProcEntry.szExeFile[0] = '\0';
+ if (g_pfnProcess32FirstW(hSnap, &ProcEntry))
+ {
+ do
+ {
+ if (RTUtf16ICmpAscii(ProcEntry.szExeFile, papszNames[i]) == 0)
+ {
+ int rc = rtProcWinGetProcessTokenHandle(ProcEntry.th32ProcessID, pSid, idDesiredSession, phToken);
+ if (RT_SUCCESS(rc))
+ {
+ fFound = true;
+ break;
+ }
+ }
+ } while (g_pfnProcess32NextW(hSnap, &ProcEntry));
+ }
+ else
+ AssertMsgFailed(("dwErr=%u (%x)\n", GetLastError(), GetLastError()));
+ }
+ CloseHandle(hSnap);
+ }
+ }
+
+ /* If we couldn't take a process snapshot for some reason or another, fall
+ back on the NT4 compatible API. */
+ if (fFallback)
+ fFound = rtProcWinFindTokenByProcessAndPsApi(papszNames, pSid, phToken);
+ return fFound;
+}
+
+
+/**
+ * Logs on a specified user and returns its primary token.
+ *
+ * @returns IPRT status code.
+ * @param pwszUser User name. A domain name can be specified (as part of a UPN, User Principal Name),
+ * e.g. "joedoe@example.com".
+ * @param pwszPassword Password.
+ * @param phToken Pointer to store the logon token.
+ */
+static int rtProcWinUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, HANDLE *phToken)
+{
+ AssertPtrReturn(pwszUser, VERR_INVALID_POINTER);
+ AssertPtrReturn(pwszPassword, VERR_INVALID_POINTER);
+ AssertPtrReturn(phToken, VERR_INVALID_POINTER);
+ if (!g_pfnLogonUserW)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Because we have to deal with http://support.microsoft.com/kb/245683
+ * for NULL domain names when running on NT4 here, pass an empty string if so.
+ * However, passing FQDNs should work!
+ *
+ * The SE_TCB_NAME (Policy: Act as part of the operating system) right
+ * is required on older windows versions (NT4, W2K, possibly XP).
+ */
+ PCRTUTF16 pwszDomainNone = g_enmWinVer < kRTWinOSType_2K ? L"" /* NT4 and older */ : NULL /* Windows 2000 and up */;
+ BOOL fRc = g_pfnLogonUserW(pwszUser,
+ /* The domain always is passed as part of the UPN (user name). */
+ pwszDomainNone,
+ pwszPassword,
+ LOGON32_LOGON_INTERACTIVE,
+ LOGON32_PROVIDER_DEFAULT,
+ phToken);
+ if (fRc)
+ return VINF_SUCCESS;
+
+ DWORD dwErr = GetLastError();
+ int rc = dwErr == ERROR_PRIVILEGE_NOT_HELD ? VERR_PROC_TCB_PRIV_NOT_HELD : RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_UNRESOLVED_ERROR)
+ LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
+ return rc;
+}
+
+
+/**
+ * Returns the environment to use for the child process.
+ *
+ * This implements the RTPROC_FLAGS_ENV_CHANGE_RECORD and environment related
+ * parts of RTPROC_FLAGS_PROFILE.
+ *
+ * @returns IPRT status code.
+ * @param hToken The user token to use if RTPROC_FLAGS_PROFILE is given.
+ * The caller must have loaded profile for this.
+ * @param hEnv The environment passed in by the RTProcCreateEx caller.
+ * @param fFlags The process creation flags passed in by the
+ * RTProcCreateEx caller (RTPROC_FLAGS_XXX).
+ * @param phEnv Where to return the environment to use. This can either
+ * be a newly created environment block or @a hEnv. In the
+ * former case, the caller must destroy it.
+ */
+static int rtProcWinCreateEnvFromToken(HANDLE hToken, RTENV hEnv, uint32_t fFlags, PRTENV phEnv)
+{
+ int rc;
+
+ /*
+ * Query the environment from the user profile associated with the token if
+ * the caller has specified it directly or indirectly.
+ */
+ if ( (fFlags & RTPROC_FLAGS_PROFILE)
+ && ( hEnv == RTENV_DEFAULT
+ || (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
+ {
+ if (g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock)
+ {
+ LPVOID pvEnvBlockProfile = NULL;
+ if (g_pfnCreateEnvironmentBlock(&pvEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
+ {
+ rc = RTEnvCloneUtf16Block(phEnv, (PCRTUTF16)pvEnvBlockProfile, 0 /*fFlags*/);
+ if ( (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+ && RT_SUCCESS(rc)
+ && hEnv != RTENV_DEFAULT)
+ {
+ rc = RTEnvApplyChanges(*phEnv, hEnv);
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(*phEnv);
+ }
+ g_pfnDestroyEnvironmentBlock(pvEnvBlockProfile);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ }
+ /*
+ * We we've got an incoming change record, combine it with the default environment.
+ */
+ else if (hEnv != RTENV_DEFAULT && (fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD))
+ {
+ rc = RTEnvClone(phEnv, RTENV_DEFAULT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvApplyChanges(*phEnv, hEnv);
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(*phEnv);
+ }
+ }
+ /*
+ * Otherwise we can return the incoming environment directly.
+ */
+ else
+ {
+ *phEnv = hEnv;
+ rc = VINF_SUCCESS;
+ }
+
+ return rc;
+}
+
+
+/**
+ * Figures which privilege we're missing for success application of
+ * CreateProcessAsUserW.
+ *
+ * @returns IPRT error status.
+ */
+static int rtProcWinFigureWhichPrivilegeNotHeld2(void)
+{
+ HANDLE hToken;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &hToken))
+ {
+ static struct
+ {
+ const char *pszName;
+ int rc;
+ } const s_aPrivileges[] =
+ {
+ { SE_TCB_NAME, VERR_PROC_TCB_PRIV_NOT_HELD },
+ { SE_ASSIGNPRIMARYTOKEN_NAME, VERR_PROC_APT_PRIV_NOT_HELD },
+ { SE_INCREASE_QUOTA_NAME, VERR_PROC_IQ_PRIV_NOT_HELD },
+ };
+ for (uint32_t i = 0; i < RT_ELEMENTS(s_aPrivileges); i++)
+ {
+ union
+ {
+ TOKEN_PRIVILEGES TokPriv;
+ char abAlloced[sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES)];
+ } uNew, uOld;
+ uNew.TokPriv.PrivilegeCount = 1;
+ uNew.TokPriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ AssertContinue(LookupPrivilegeValue(NULL, s_aPrivileges[i].pszName, &uNew.TokPriv.Privileges[0].Luid));
+ uOld = uNew;
+ SetLastError(NO_ERROR);
+ DWORD cbActual = RT_UOFFSETOF(TOKEN_PRIVILEGES, Privileges[1]);
+ AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uNew.TokPriv, cbActual, &uOld.TokPriv, &cbActual);
+ if (GetLastError() != NO_ERROR)
+ {
+ CloseHandle(hToken);
+ return s_aPrivileges[i].rc;
+ }
+ if (uOld.TokPriv.Privileges[0].Attributes == 0)
+ AdjustTokenPrivileges(hToken, FALSE /*fDisableAllPrivileges*/, &uOld.TokPriv, 0, NULL, NULL);
+ }
+ AssertFailed();
+ CloseHandle(hToken);
+ }
+ else
+ AssertFailed();
+ return VERR_PRIVILEGE_NOT_HELD;
+}
+
+#if 0 /* debug code */
+
+static char *rtProcWinSidToString(char *psz, PSID pSid)
+{
+ char *pszRet = psz;
+
+ *psz++ = 'S';
+ *psz++ = '-';
+ *psz++ = '1';
+ *psz++ = '-';
+
+ PISID pISid = (PISID)pSid;
+
+ psz += RTStrFormatU32(psz, 32, RT_MAKE_U32_FROM_U8(pISid->IdentifierAuthority.Value[5],
+ pISid->IdentifierAuthority.Value[4],
+ pISid->IdentifierAuthority.Value[3],
+ pISid->IdentifierAuthority.Value[2]),
+ 10, 0, 0, 0);
+ for (unsigned i = 0; i < pISid->SubAuthorityCount; i++)
+ {
+ *psz++ = '-';
+ psz += RTStrFormatU32(psz, 32, pISid->SubAuthority[i], 10, 0, 0, 0);
+ }
+ *psz++ = '\0';
+ return pszRet;
+}
+
+static void rtProcWinLogAcl(PACL pAcl)
+{
+ if (!pAcl)
+ RTAssertMsg2("ACL is NULL\n");
+ else
+ {
+ RTAssertMsg2("AceCount=%d AclSize=%#x AclRevision=%d\n", pAcl->AceCount, pAcl->AclSize, pAcl->AclRevision);
+ for (uint32_t i = 0; i < pAcl->AceCount; i++)
+ {
+ PACE_HEADER pAceHdr = NULL;
+ if (GetAce(pAcl, i, (PVOID *)&pAceHdr))
+ {
+ RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize);
+ char szTmp[256];
+ if (pAceHdr->AceType == ACCESS_ALLOWED_ACE_TYPE)
+ RTAssertMsg2(" Mask=%#x %s\n", ((ACCESS_ALLOWED_ACE *)pAceHdr)->Mask,
+ rtProcWinSidToString(szTmp, &((ACCESS_ALLOWED_ACE *)pAceHdr)->SidStart));
+ else
+ RTAssertMsg2(" ACE[%u]: Flags=%#x Type=%#x Size=%#x\n", i, pAceHdr->AceFlags, pAceHdr->AceType, pAceHdr->AceSize);
+ }
+ }
+ }
+}
+
+static bool rtProcWinLogSecAttr(HANDLE hUserObj)
+{
+ /*
+ * Get the security descriptor for the user interface object.
+ */
+ uint32_t cbSecDesc = _64K;
+ PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ DWORD cbNeeded;
+ AssertReturn(pSecDesc, false);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, false);
+ cbSecDesc = cbNeeded + 128;
+ pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ AssertReturn(pSecDesc, false);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertFailedReturn(false);
+ }
+ }
+
+ /*
+ * Get the discretionary access control list (if we have one).
+ */
+ BOOL fDaclDefaulted;
+ BOOL fDaclPresent;
+ PACL pDacl;
+ if (GetSecurityDescriptorDacl(pSecDesc, &fDaclPresent, &pDacl, &fDaclDefaulted))
+ rtProcWinLogAcl(pDacl);
+ else
+ RTAssertMsg2("GetSecurityDescriptorDacl failed\n");
+
+ RTMemFree(pSecDesc);
+ return true;
+}
+
+#endif /* debug */
+
+/**
+ * Get the user SID from a token.
+ *
+ * @returns Pointer to the SID on success. Free by calling RTMemFree.
+ * @param hToken The token..
+ * @param prc Optional return code.
+ */
+static PSID rtProcWinGetTokenUserSid(HANDLE hToken, int *prc)
+{
+ int rcIgn;
+ if (!prc)
+ prc = &rcIgn;
+ *prc = VERR_NO_MEMORY;
+
+ /*
+ * Get the groups associated with the token. We just try a size first then
+ * reallocates if it's insufficient.
+ */
+ DWORD cbUser = _1K;
+ PTOKEN_USER pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser);
+ AssertReturn(pUser, NULL);
+ DWORD cbNeeded = 0;
+ if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded))
+ {
+ DWORD dwErr = GetLastError();
+ RTMemTmpFree(pUser);
+ AssertLogRelMsgReturnStmt(dwErr == ERROR_INSUFFICIENT_BUFFER,
+ ("rtProcWinGetTokenUserSid: GetTokenInformation failed with %u\n", dwErr),
+ *prc = RTErrConvertFromWin32(dwErr), NULL);
+ cbUser = cbNeeded + 128;
+ pUser = (PTOKEN_USER)RTMemTmpAlloc(cbUser);
+ AssertReturn(pUser, NULL);
+ if (!GetTokenInformation(hToken, TokenUser, pUser, cbUser, &cbNeeded))
+ {
+ dwErr = GetLastError();
+ *prc = RTErrConvertFromWin32(dwErr);
+ RTMemTmpFree(pUser);
+ AssertLogRelMsgFailedReturn(("rtProcWinGetTokenUserSid: GetTokenInformation failed with %u\n", dwErr), NULL);
+ }
+ }
+
+ DWORD cbSid = GetLengthSid(pUser->User.Sid);
+ PSID pSidRet = RTMemDup(pUser->User.Sid, cbSid);
+ Assert(pSidRet);
+ RTMemTmpFree(pUser);
+ *prc = VINF_SUCCESS;
+ return pSidRet;
+}
+
+
+#if 0 /* not used */
+/**
+ * Get the login SID from a token.
+ *
+ * @returns Pointer to the SID on success. Free by calling RTMemFree.
+ * @param hToken The token..
+ */
+static PSID rtProcWinGetTokenLogonSid(HANDLE hToken)
+{
+ /*
+ * Get the groups associated with the token. We just try a size first then
+ * reallocates if it's insufficient.
+ */
+ DWORD cbGroups = _1K;
+ PTOKEN_GROUPS pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups);
+ AssertReturn(pGroups, NULL);
+ DWORD cbNeeded = 0;
+ if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded))
+ {
+ RTMemTmpFree(pGroups);
+ AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
+ cbGroups = cbNeeded + 128;
+ pGroups = (PTOKEN_GROUPS)RTMemTmpAlloc(cbGroups);
+ AssertReturn(pGroups, NULL);
+ if (!GetTokenInformation(hToken, TokenGroups, pGroups, cbGroups, &cbNeeded))
+ {
+ RTMemTmpFree(pGroups);
+ AssertFailedReturn(NULL);
+ }
+ }
+
+ /*
+ * Locate the logon sid.
+ */
+ PSID pSidRet = NULL;
+ uint32_t i = pGroups->GroupCount;
+ while (i-- > 0)
+ if ((pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == SE_GROUP_LOGON_ID)
+ {
+ DWORD cbSid = GetLengthSid(pGroups->Groups[i].Sid);
+ pSidRet = RTMemDup(pGroups->Groups[i].Sid, cbSid);
+ break;
+ }
+
+ RTMemTmpFree(pGroups);
+ Assert(pSidRet);
+ return pSidRet;
+}
+#endif /* unused */
+
+
+/**
+ * Retrieves the DACL security descriptor of the give GUI object.
+ *
+ * @returns Pointer to the security descriptor.
+ * @param hUserObj The GUI object handle.
+ * @param pcbSecDesc Where to return the size of the security descriptor.
+ * @param ppDacl Where to return the DACL pointer.
+ * @param pfDaclPresent Where to return the DACL-present indicator.
+ * @param pDaclSizeInfo Where to return the DACL size information.
+ */
+static PSECURITY_DESCRIPTOR rtProcWinGetUserObjDacl(HANDLE hUserObj, uint32_t *pcbSecDesc, PACL *ppDacl,
+ BOOL *pfDaclPresent, ACL_SIZE_INFORMATION *pDaclSizeInfo)
+{
+ /*
+ * Get the security descriptor for the user interface object.
+ */
+ uint32_t cbSecDesc = _1K;
+ PSECURITY_DESCRIPTOR pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ DWORD cbNeeded;
+ AssertReturn(pSecDesc, NULL);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertReturn(GetLastError() == ERROR_INSUFFICIENT_BUFFER, NULL);
+ cbSecDesc = cbNeeded + 128;
+ pSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ AssertReturn(pSecDesc, NULL);
+ if (!GetUserObjectSecurity(hUserObj, &SecInfo, pSecDesc, cbSecDesc, &cbNeeded))
+ {
+ RTMemTmpFree(pSecDesc);
+ AssertFailedReturn(NULL);
+ }
+ }
+ *pcbSecDesc = cbNeeded;
+
+ /*
+ * Get the discretionary access control list (if we have one).
+ */
+ BOOL fDaclDefaulted;
+ if (GetSecurityDescriptorDacl(pSecDesc, pfDaclPresent, ppDacl, &fDaclDefaulted))
+ {
+ RT_ZERO(*pDaclSizeInfo);
+ pDaclSizeInfo->AclBytesInUse = sizeof(ACL);
+ if ( !*ppDacl
+ || GetAclInformation(*ppDacl, pDaclSizeInfo, sizeof(*pDaclSizeInfo), AclSizeInformation))
+ return pSecDesc;
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pSecDesc);
+ return NULL;
+}
+
+
+/**
+ * Copy ACEs from one ACL to another.
+ *
+ * @returns true on success, false on failure.
+ * @param pDst The destination ACL.
+ * @param pSrc The source ACL.
+ * @param cAces The number of ACEs to copy.
+ */
+static bool rtProcWinCopyAces(PACL pDst, PACL pSrc, uint32_t cAces)
+{
+ for (uint32_t i = 0; i < cAces; i++)
+ {
+ PACE_HEADER pAceHdr;
+ AssertReturn(GetAce(pSrc, i, (PVOID *)&pAceHdr), false);
+ AssertReturn(AddAce(pDst, ACL_REVISION, MAXDWORD, pAceHdr, pAceHdr->AceSize), false);
+ }
+ return true;
+}
+
+
+/**
+ * Adds an access-allowed access control entry to an ACL.
+ *
+ * @returns true on success, false on failure.
+ * @param pDstAcl The ACL.
+ * @param fAceFlags The ACE flags.
+ * @param fMask The ACE access mask.
+ * @param pSid The SID to go with the ACE.
+ * @param cbSid The size of the SID.
+ */
+static bool rtProcWinAddAccessAllowedAce(PACL pDstAcl, uint32_t fAceFlags, uint32_t fMask, PSID pSid, uint32_t cbSid)
+{
+ struct
+ {
+ ACCESS_ALLOWED_ACE Core;
+ DWORD abPadding[128]; /* More than enough, AFAIK. */
+ } AceBuf;
+ RT_ZERO(AceBuf);
+ uint32_t const cbAllowedAce = RT_UOFFSETOF(ACCESS_ALLOWED_ACE, SidStart) + cbSid;
+ AssertReturn(cbAllowedAce <= sizeof(AceBuf), false);
+
+ AceBuf.Core.Header.AceSize = cbAllowedAce;
+ AceBuf.Core.Header.AceType = ACCESS_ALLOWED_ACE_TYPE;
+ AceBuf.Core.Header.AceFlags = fAceFlags;
+ AceBuf.Core.Mask = fMask;
+ AssertReturn(CopySid(cbSid, &AceBuf.Core.SidStart, pSid), false);
+
+ uint32_t i = pDstAcl->AceCount;
+ while (i-- > 0)
+ {
+ PACE_HEADER pAceHdr;
+ AssertContinue(GetAce(pDstAcl, i, (PVOID *)&pAceHdr));
+ if ( pAceHdr->AceSize == cbAllowedAce
+ && memcmp(pAceHdr, &AceBuf.Core, cbAllowedAce) == 0)
+ return true;
+
+ }
+ AssertMsgReturn(AddAce(pDstAcl, ACL_REVISION, MAXDWORD, &AceBuf.Core, cbAllowedAce), ("%u\n", GetLastError()), false);
+ return true;
+}
+
+
+/** All window station rights we know about */
+#define MY_WINSTATION_ALL_RIGHTS ( WINSTA_ACCESSCLIPBOARD | WINSTA_ACCESSGLOBALATOMS | WINSTA_CREATEDESKTOP \
+ | WINSTA_ENUMDESKTOPS | WINSTA_ENUMERATE | WINSTA_EXITWINDOWS | WINSTA_READATTRIBUTES \
+ | WINSTA_READSCREEN | WINSTA_WRITEATTRIBUTES | DELETE | READ_CONTROL | WRITE_DAC | WRITE_OWNER )
+/** All desktop rights we know about */
+#define MY_DESKTOP_ALL_RIGHTS ( DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW | DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL \
+ | DESKTOP_JOURNALPLAYBACK | DESKTOP_JOURNALRECORD | DESKTOP_READOBJECTS \
+ | DESKTOP_SWITCHDESKTOP | DESKTOP_WRITEOBJECTS | DELETE | READ_CONTROL | WRITE_DAC \
+ | WRITE_OWNER )
+/** Generic rights. */
+#define MY_GENERIC_ALL_RIGHTS ( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )
+
+
+/**
+ * Grants the given SID full access to the given window station.
+ *
+ * @returns true on success, false on failure.
+ * @param hWinStation The window station.
+ * @param pSid The SID.
+ */
+static bool rtProcWinAddSidToWinStation(HWINSTA hWinStation, PSID pSid)
+{
+ bool fRet = false;
+
+ /*
+ * Get the current DACL.
+ */
+ uint32_t cbSecDesc;
+ PACL pDacl;
+ ACL_SIZE_INFORMATION DaclSizeInfo;
+ BOOL fDaclPresent;
+ PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hWinStation, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo);
+ if (pSecDesc)
+ {
+ /*
+ * Create a new DACL. This will contain two extra ACEs.
+ */
+ PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ if ( pNewSecDesc
+ && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION))
+ {
+ uint32_t const cbSid = GetLengthSid(pSid);
+ uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 2;
+ PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl);
+ if ( pNewDacl
+ && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION)
+ && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0))
+ {
+ /*
+ * Add the two new SID ACEs.
+ */
+ if ( rtProcWinAddAccessAllowedAce(pNewDacl, CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE,
+ MY_GENERIC_ALL_RIGHTS, pSid, cbSid)
+ && rtProcWinAddAccessAllowedAce(pNewDacl, NO_PROPAGATE_INHERIT_ACE, MY_WINSTATION_ALL_RIGHTS, pSid, cbSid))
+ {
+ /*
+ * Now mate the new DECL with the security descriptor and set it.
+ */
+ if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/))
+ {
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ if (SetUserObjectSecurity(hWinStation, &SecInfo, pNewSecDesc))
+ fRet = true;
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewDacl);
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewSecDesc);
+ RTMemTmpFree(pSecDesc);
+ }
+ return fRet;
+}
+
+
+/**
+ * Grants the given SID full access to the given desktop.
+ *
+ * @returns true on success, false on failure.
+ * @param hDesktop The desktop handle.
+ * @param pSid The SID.
+ */
+static bool rtProcWinAddSidToDesktop(HDESK hDesktop, PSID pSid)
+{
+ bool fRet = false;
+
+ /*
+ * Get the current DACL.
+ */
+ uint32_t cbSecDesc;
+ PACL pDacl;
+ ACL_SIZE_INFORMATION DaclSizeInfo;
+ BOOL fDaclPresent;
+ PSECURITY_DESCRIPTOR pSecDesc = rtProcWinGetUserObjDacl(hDesktop, &cbSecDesc, &pDacl, &fDaclPresent, &DaclSizeInfo);
+ if (pSecDesc)
+ {
+ /*
+ * Create a new DACL. This will contain one extra ACE.
+ */
+ PSECURITY_DESCRIPTOR pNewSecDesc = (PSECURITY_DESCRIPTOR)RTMemTmpAlloc(cbSecDesc);
+ if ( pNewSecDesc
+ && InitializeSecurityDescriptor(pNewSecDesc, SECURITY_DESCRIPTOR_REVISION))
+ {
+ uint32_t const cbSid = GetLengthSid(pSid);
+ uint32_t const cbNewDacl = DaclSizeInfo.AclBytesInUse + (sizeof(ACCESS_ALLOWED_ACE) + cbSid) * 1;
+ PACL pNewDacl = (PACL)RTMemTmpAlloc(cbNewDacl);
+ if ( pNewDacl
+ && InitializeAcl(pNewDacl, cbNewDacl, ACL_REVISION)
+ && rtProcWinCopyAces(pNewDacl, pDacl, fDaclPresent ? DaclSizeInfo.AceCount : 0))
+ {
+ /*
+ * Add the new SID ACE.
+ */
+ if (rtProcWinAddAccessAllowedAce(pNewDacl, 0 /*fAceFlags*/, MY_DESKTOP_ALL_RIGHTS, pSid, cbSid))
+ {
+ /*
+ * Now mate the new DECL with the security descriptor and set it.
+ */
+ if (SetSecurityDescriptorDacl(pNewSecDesc, TRUE /*fDaclPresent*/, pNewDacl, FALSE /*fDaclDefaulted*/))
+ {
+ SECURITY_INFORMATION SecInfo = DACL_SECURITY_INFORMATION;
+ if (SetUserObjectSecurity(hDesktop, &SecInfo, pNewSecDesc))
+ fRet = true;
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewDacl);
+ }
+ else
+ AssertFailed();
+ RTMemTmpFree(pNewSecDesc);
+ RTMemTmpFree(pSecDesc);
+ }
+ return fRet;
+}
+
+
+/**
+ * Preps the window station and desktop for the new app.
+ *
+ * EXPERIMENTAL. Thus no return code.
+ *
+ * @param hTokenToUse The access token of the new process.
+ * @param pStartupInfo The startup info (we'll change lpDesktop, maybe).
+ * @param phWinStationOld Where to return an window station handle to restore.
+ * Pass this to SetProcessWindowStation if not NULL.
+ */
+static void rtProcWinStationPrep(HANDLE hTokenToUse, STARTUPINFOW *pStartupInfo, HWINSTA *phWinStationOld)
+{
+ /** @todo Always mess with the interactive one? Maybe it's not there... */
+ *phWinStationOld = GetProcessWindowStation();
+ HWINSTA hWinStation0;
+ if (g_pfnOpenWindowStationW)
+ hWinStation0 = g_pfnOpenWindowStationW(L"winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC);
+ else
+ hWinStation0 = OpenWindowStationA("winsta0", FALSE /*fInherit*/, READ_CONTROL | WRITE_DAC); /* (for NT3.1) */
+ if (hWinStation0)
+ {
+ if (SetProcessWindowStation(hWinStation0))
+ {
+ HDESK hDesktop = OpenDesktop("default", 0 /*fFlags*/, FALSE /*fInherit*/,
+ READ_CONTROL | WRITE_DAC | DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS);
+ if (hDesktop)
+ {
+ /*PSID pSid = rtProcWinGetTokenLogonSid(hTokenToUse); - Better to use the user SID. Avoid overflowing the ACL. */
+ PSID pSid = rtProcWinGetTokenUserSid(hTokenToUse, NULL /*prc*/);
+ if (pSid)
+ {
+ if ( rtProcWinAddSidToWinStation(hWinStation0, pSid)
+ && rtProcWinAddSidToDesktop(hDesktop, pSid))
+ {
+ pStartupInfo->lpDesktop = L"winsta0\\default";
+ }
+ RTMemFree(pSid);
+ }
+ CloseDesktop(hDesktop);
+ }
+ else
+ AssertFailed();
+ }
+ else
+ AssertFailed();
+ if (g_pfnCloseWindowStation)
+ g_pfnCloseWindowStation(hWinStation0);
+ }
+ else
+ AssertFailed();
+}
+
+
+/**
+ * Extracts the user name + domain from a given UPN (User Principal Name, "joedoe@example.com") or
+ * Down-Level Logon Name format ("example.com\\joedoe") string.
+ *
+ * @return IPRT status code.
+ * @param pwszString Pointer to string to extract the account info from.
+ * @param pAccountInfo Where to store the parsed account info.
+ * Must be free'd with rtProcWinFreeAccountInfo().
+ */
+static int rtProcWinParseAccountInfo(PRTUTF16 pwszString, PRTPROCWINACCOUNTINFO pAccountInfo)
+{
+ AssertPtrReturn(pwszString, VERR_INVALID_POINTER);
+ AssertPtrReturn(pAccountInfo, VERR_INVALID_POINTER);
+
+ /*
+ * Note: UPN handling is defined in RFC 822. We only implement very rudimentary parsing for the user
+ * name and domain fields though.
+ */
+ char *pszString;
+ int rc = RTUtf16ToUtf8(pwszString, &pszString);
+ if (RT_SUCCESS(rc))
+ {
+ do
+ {
+ /* UPN or FQDN handling needed? */
+ /** @todo Add more validation here as needed. Regular expressions would be nice. */
+ char *pszDelim = strchr(pszString, '@');
+ if (pszDelim) /* UPN name? */
+ {
+ rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszUserName, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszDomain, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else if (pszDelim = strchr(pszString, '\\')) /* FQDN name? */
+ {
+ rc = RTStrToUtf16Ex(pszString, pszDelim - pszString, &pAccountInfo->pwszDomain, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+
+ rc = RTStrToUtf16Ex(pszDelim + 1, RTSTR_MAX, &pAccountInfo->pwszUserName, 0, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ } while (0);
+
+ RTStrFree(pszString);
+ }
+
+#ifdef DEBUG
+ LogRelFunc(("Name : %ls\n", pAccountInfo->pwszUserName));
+ LogRelFunc(("Domain: %ls\n", pAccountInfo->pwszDomain));
+#endif
+
+ if (RT_FAILURE(rc))
+ LogRelFunc(("Parsing \"%ls\" failed with rc=%Rrc\n", pwszString, rc));
+ return rc;
+}
+
+
+static void rtProcWinFreeAccountInfo(PRTPROCWINACCOUNTINFO pAccountInfo)
+{
+ if (!pAccountInfo)
+ return;
+
+ if (pAccountInfo->pwszUserName)
+ {
+ RTUtf16Free(pAccountInfo->pwszUserName);
+ pAccountInfo->pwszUserName = NULL;
+ }
+
+ if (pAccountInfo->pwszDomain)
+ {
+ RTUtf16Free(pAccountInfo->pwszDomain);
+ pAccountInfo->pwszDomain = NULL;
+ }
+}
+
+
+/**
+ * Tries to resolve the name of the SID.
+ *
+ * @returns IPRT status code.
+ * @param pSid The SID to resolve.
+ * @param ppwszName Where to return the name. Use RTUtf16Free to free.
+ */
+static int rtProcWinSidToName(PSID pSid, PRTUTF16 *ppwszName)
+{
+ *ppwszName = NULL;
+
+ /*
+ * Use large initial buffers here to try avoid having to repeat the call.
+ */
+ DWORD cwcAllocated = 512;
+ while (cwcAllocated < _32K)
+ {
+ PRTUTF16 pwszName = RTUtf16Alloc(cwcAllocated * sizeof(RTUTF16));
+ AssertReturn(pwszName, VERR_NO_UTF16_MEMORY);
+ PRTUTF16 pwszDomain = RTUtf16Alloc(cwcAllocated * sizeof(RTUTF16));
+ AssertReturnStmt(pwszDomain, RTUtf16Free(pwszName), VERR_NO_UTF16_MEMORY);
+
+ DWORD cwcName = cwcAllocated;
+ DWORD cwcDomain = cwcAllocated;
+ SID_NAME_USE SidNameUse = SidTypeUser;
+ if (LookupAccountSidW(NULL /*lpSystemName*/, pSid, pwszName, &cwcName, pwszDomain, &cwcDomain, &SidNameUse))
+ {
+ *ppwszName = pwszName;
+ RTUtf16Free(pwszDomain); /* may need this later. */
+ return VINF_SUCCESS;
+ }
+
+ DWORD const dwErr = GetLastError();
+ RTUtf16Free(pwszName);
+ RTUtf16Free(pwszDomain);
+ if (dwErr != ERROR_INSUFFICIENT_BUFFER)
+ return RTErrConvertFromWin32(dwErr);
+ cwcAllocated = RT_MAX(cwcName, cwcDomain) + 1;
+ }
+
+ return RTErrConvertFromWin32(ERROR_INSUFFICIENT_BUFFER);
+}
+
+
+/**
+ * Tries to resolve the user name for the token.
+ *
+ * @returns IPRT status code.
+ * @param hToken The token.
+ * @param ppwszUser Where to return the username. Use RTUtf16Free to free.
+ */
+static int rtProcWinTokenToUsername(HANDLE hToken, PRTUTF16 *ppwszUser)
+{
+ int rc = VINF_SUCCESS;
+ PSID pSid = rtProcWinGetTokenUserSid(hToken, &rc);
+ if (pSid)
+ {
+ rc = rtProcWinSidToName(pSid, ppwszUser);
+ RTMemFree(pSid);
+ }
+ else
+ *ppwszUser = NULL;
+ return rc;
+}
+
+
+/**
+ * Method \#2.
+ *
+ * @note pwszUser can be NULL when RTPROC_FLAGS_AS_IMPERSONATED_TOKEN is set.
+ */
+static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
+ RTENV hEnv, DWORD dwCreationFlags,
+ STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
+ uint32_t fFlags, const char *pszExec, uint32_t idDesiredSession,
+ HANDLE hUserToken)
+{
+ /*
+ * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
+ * we have to do the following:
+ * - Check the credentials supplied and get the user SID.
+ * - If valid get the correct Explorer/VBoxTray instance corresponding to that
+ * user. This of course is only possible if that user is logged in (over
+ * physical console or terminal services).
+ * - If we found the user's Explorer/VBoxTray app, use and modify the token to
+ * use it in order to allow the newly started process to access the user's
+ * desktop. If there's no Explorer/VBoxTray app we cannot display the started
+ * process (but run it without UI).
+ *
+ * The following restrictions apply:
+ * - A process only can show its UI when the user the process should run
+ * under is logged in (has a desktop).
+ * - We do not want to display a process of user A run on the desktop
+ * of user B on multi session systems.
+ *
+ * The following rights are needed in order to use LogonUserW and
+ * CreateProcessAsUserW, so the local policy has to be modified to:
+ * - SE_TCB_NAME = Act as part of the operating system
+ * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a (process) token object
+ * - SE_INCREASE_QUOTA_NAME = Increase quotas
+ *
+ * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
+ */
+ DWORD dwErr = NO_ERROR;
+ HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
+ int rc = VINF_SUCCESS;
+ if (fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED)
+ hTokenLogon = hUserToken;
+ else if (fFlags & RTPROC_FLAGS_AS_IMPERSONATED_TOKEN)
+ rc = rtProcWinGetThreadTokenHandle(GetCurrentThread(), &hTokenLogon);
+ else if (pwszUser == NULL)
+ rc = rtProcWinGetProcessTokenHandle(GetCurrentProcess(), &hTokenLogon);
+ else
+ rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon);
+ if (RT_SUCCESS(rc))
+ {
+ BOOL fRc;
+ bool fFound = false;
+ HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
+
+ /*
+ * If the SERVICE flag is specified, we do something rather ugly to
+ * make things work at all. We search for a known desktop process
+ * belonging to the user, grab its token and use it for launching
+ * the new process. That way the process will have desktop access.
+ */
+ if (fFlags & RTPROC_FLAGS_SERVICE)
+ {
+ /*
+ * For the token search we need a SID.
+ */
+ PSID pSid = rtProcWinGetTokenUserSid(hTokenLogon, &rc);
+
+ /*
+ * If we got a valid SID, search the running processes.
+ */
+ /*
+ * If we got a valid SID, search the running processes.
+ */
+ if (pSid)
+ {
+ if (IsValidSid(pSid))
+ {
+ /* Array of process names we want to look for. */
+ static const char * const s_papszProcNames[] =
+ {
+#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
+ { "VBoxTray.exe" },
+# ifndef IN_GUEST
+ { "VirtualBox.exe" },
+# endif
+#endif
+ { "explorer.exe" },
+ NULL
+ };
+ fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, idDesiredSession, &hTokenUserDesktop);
+ dwErr = 0;
+ }
+ else
+ {
+ dwErr = GetLastError();
+ LogRelFunc(("SID is invalid: %ld\n", dwErr));
+ rc = dwErr != NO_ERROR ? RTErrConvertFromWin32(dwErr) : VERR_INTERNAL_ERROR_3;
+ }
+
+ RTMemFree(pSid);
+ }
+ }
+ /* else: !RTPROC_FLAGS_SERVICE: Nothing to do here right now. */
+
+#if 0
+ /*
+ * If we make LogonUserW to return an impersonation token, enable this
+ * to convert it into a primary token.
+ */
+ if (!fFound && detect-impersonation-token)
+ {
+ HANDLE hNewToken;
+ if (DuplicateTokenEx(hTokenLogon, MAXIMUM_ALLOWED, NULL /*SecurityAttribs*/,
+ SecurityIdentification, TokenPrimary, &hNewToken))
+ {
+ CloseHandle(hTokenLogon);
+ hTokenLogon = hNewToken;
+ }
+ else
+ AssertMsgFailed(("%d\n", GetLastError()));
+ }
+#endif
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * If we didn't find a matching VBoxTray, just use the token we got
+ * above from LogonUserW(). This enables us to at least run processes
+ * with desktop interaction without UI.
+ */
+ HANDLE hTokenToUse = fFound ? hTokenUserDesktop : hTokenLogon;
+ if ( !(fFlags & RTPROC_FLAGS_PROFILE)
+ || (g_pfnUnloadUserProfile && g_pfnLoadUserProfileW) )
+ {
+ /*
+ * Load the profile, if requested. (Must be done prior to creating the enviornment.)
+ *
+ * Note! We don't have sufficient rights when impersonating a user, but we can
+ * ASSUME the user is logged on and has its profile loaded into HKEY_USERS already.
+ */
+ PROFILEINFOW ProfileInfo;
+ PRTUTF16 pwszUserFree = NULL;
+ RT_ZERO(ProfileInfo);
+ /** @todo r=bird: We probably don't need to load anything if pwszUser is NULL... */
+ if ((fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN)) == RTPROC_FLAGS_PROFILE)
+ {
+ if (!pwszUser)
+ {
+ Assert(fFlags & RTPROC_FLAGS_AS_IMPERSONATED_TOKEN);
+ rc = rtProcWinTokenToUsername(hTokenToUse, &pwszUserFree);
+ pwszUser = pwszUserFree;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ ProfileInfo.dwSize = sizeof(ProfileInfo);
+ ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
+ ProfileInfo.lpUserName = pwszUser;
+ if (!g_pfnLoadUserProfileW(hTokenToUse, &ProfileInfo))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the environment.
+ */
+ RTENV hEnvFinal;
+ rc = rtProcWinCreateEnvFromToken(hTokenToUse, hEnv, fFlags, &hEnvFinal);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszzBlock;
+ rc = RTEnvQueryUtf16Block(hEnvFinal, &pwszzBlock);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
+ if (RT_SUCCESS(rc))
+ {
+ HWINSTA hOldWinStation = NULL;
+ if ( !fFound
+ && g_enmWinVer <= kRTWinOSType_NT4) /** @todo test newer versions... */
+ rtProcWinStationPrep(hTokenToUse, pStartupInfo, &hOldWinStation);
+
+ /*
+ * Useful KB articles:
+ * http://support.microsoft.com/kb/165194/
+ * http://support.microsoft.com/kb/184802/
+ * http://support.microsoft.com/kb/327618/
+ */
+ if (g_pfnCreateProcessAsUserW)
+ {
+ fRc = g_pfnCreateProcessAsUserW(hTokenToUse,
+ *ppwszExec,
+ pwszCmdLine,
+ NULL, /* pProcessAttributes */
+ NULL, /* pThreadAttributes */
+ TRUE, /* fInheritHandles */
+ dwCreationFlags,
+ /** @todo Warn about exceeding 8192 bytes
+ * on XP and up. */
+ pwszzBlock, /* lpEnvironment */
+ NULL, /* pCurrentDirectory */
+ pStartupInfo,
+ pProcInfo);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ {
+ dwErr = GetLastError();
+ if (dwErr == ERROR_PRIVILEGE_NOT_HELD)
+ rc = rtProcWinFigureWhichPrivilegeNotHeld2();
+ else
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+
+ if (hOldWinStation)
+ SetProcessWindowStation(hOldWinStation);
+ }
+ RTEnvFreeUtf16Block(pwszzBlock);
+ }
+
+ if (hEnvFinal != hEnv)
+ RTEnvDestroy(hEnvFinal);
+ }
+
+ if ((fFlags & RTPROC_FLAGS_PROFILE) && ProfileInfo.hProfile)
+ {
+ fRc = g_pfnUnloadUserProfile(hTokenToUse, ProfileInfo.hProfile);
+#ifdef RT_STRICT
+ if (!fRc)
+ {
+ DWORD dwErr2 = GetLastError();
+ AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
+ dwErr2, dwErr2, dwErr));
+ }
+#endif
+ }
+ if (pwszUserFree)
+ RTUtf16Free(pwszUserFree);
+ }
+ }
+ else
+ rc = VERR_SYMBOL_NOT_FOUND;
+ } /* Account lookup succeeded? */
+
+ if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
+ CloseHandle(hTokenUserDesktop);
+ if ( !(fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED)
+ && hTokenLogon != INVALID_HANDLE_VALUE)
+ CloseHandle(hTokenLogon);
+
+ if (rc == VERR_UNRESOLVED_ERROR)
+ LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
+ }
+
+ return rc;
+}
+
+
+/**
+ * Plants a standard handle into a child process on older windows versions.
+ *
+ * This is only needed when using CreateProcessWithLogonW on older windows
+ * versions. It would appear that newer versions of windows does this for us.
+ *
+ * @param hSrcHandle The source handle.
+ * @param hDstProcess The child process handle.
+ * @param offProcParamMember The offset to RTL_USER_PROCESS_PARAMETERS.
+ * @param ppvDstProcParamCache Where where cached the address of
+ * RTL_USER_PROCESS_PARAMETERS in the child.
+ */
+static void rtProcWinDupStdHandleIntoChild(HANDLE hSrcHandle, HANDLE hDstProcess, uint32_t offProcParamMember,
+ PVOID *ppvDstProcParamCache)
+{
+ if (hSrcHandle != NULL && hSrcHandle != INVALID_HANDLE_VALUE)
+ {
+ HANDLE hDstHandle;
+ if (DuplicateHandle(GetCurrentProcess(), hSrcHandle, hDstProcess, &hDstHandle,
+ 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS))
+ {
+ if (hSrcHandle == hDstHandle)
+ return;
+
+ if (!*ppvDstProcParamCache)
+ {
+ PROCESS_BASIC_INFORMATION BasicInfo;
+ ULONG cbIgn;
+ NTSTATUS rcNt = NtQueryInformationProcess(hDstProcess, ProcessBasicInformation,
+ &BasicInfo, sizeof(BasicInfo), &cbIgn);
+ if (NT_SUCCESS(rcNt))
+ {
+ SIZE_T cbCopied = 0;
+ if (!ReadProcessMemory(hDstProcess,
+ (char *)BasicInfo.PebBaseAddress + RT_UOFFSETOF(PEB_COMMON, ProcessParameters),
+ ppvDstProcParamCache, sizeof(*ppvDstProcParamCache), &cbCopied))
+ {
+ AssertMsgFailed(("PebBaseAddress=%p %d\n", BasicInfo.PebBaseAddress, GetLastError()));
+ *ppvDstProcParamCache = NULL;
+ }
+ }
+ else
+ AssertMsgFailed(("rcNt=%#x\n", rcNt));
+ }
+ if (*ppvDstProcParamCache)
+ {
+ if (WriteProcessMemory(hDstProcess, (char *)*ppvDstProcParamCache + offProcParamMember,
+ &hDstHandle, sizeof(hDstHandle), NULL))
+ return;
+ }
+
+ /*
+ * Close the handle.
+ */
+ HANDLE hSrcHandle2;
+ if (DuplicateHandle(hDstProcess, hDstHandle, GetCurrentProcess(), &hSrcHandle2,
+ 0 /*IgnoredDesiredAccess*/, FALSE /*fInherit*/, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ CloseHandle(hSrcHandle2);
+ else
+ AssertMsgFailed(("hDstHandle=%p %u\n", hDstHandle, GetLastError()));
+ }
+ else
+ AssertMsg(GetLastError() == ERROR_INVALID_PARAMETER, ("%u\n", GetLastError()));
+ }
+}
+
+
+/**
+ * Method \#1.
+ *
+ * This method requires Windows 2000 or later. It may fail if the process is
+ * running under the SYSTEM account (like a service, ERROR_ACCESS_DENIED) on
+ * newer platforms (however, this works on W2K!).
+ */
+static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
+ RTENV hEnv, DWORD dwCreationFlags,
+ STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
+ uint32_t fFlags, const char *pszExec)
+{
+ /* The CreateProcessWithLogonW API was introduced with W2K and later. It uses a service
+ for launching the process. */
+ if (!g_pfnCreateProcessWithLogonW)
+ return VERR_SYMBOL_NOT_FOUND;
+
+ /*
+ * Create the environment block and find the executable first.
+ *
+ * We try to skip this when RTPROC_FLAGS_PROFILE is set so we can sidestep
+ * potential missing TCB privilege issues when calling UserLogonW. At least
+ * NT4 and W2K requires the trusted code base (TCB) privilege for logon use.
+ * Passing pwszzBlock=NULL and LOGON_WITH_PROFILE means the child process
+ * gets the environment specified by the user profile.
+ */
+ int rc;
+ PRTUTF16 pwszzBlock = NULL;
+
+ /* Eliminating the path search flags simplifies things a little. */
+ if ( (fFlags & RTPROC_FLAGS_SEARCH_PATH)
+ && (RTPathHasPath(pszExec) || RTPathExists(pszExec)))
+ fFlags &= ~RTPROC_FLAGS_SEARCH_PATH;
+
+ /*
+ * No profile is simple, as is a user specified environment (no change record).
+ */
+ if ( !(fFlags & RTPROC_FLAGS_PROFILE)
+ || ( !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)
+ && hEnv != RTENV_DEFAULT))
+ rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, ppwszExec);
+ /*
+ * Default profile environment without changes or path searching we leave
+ * to the service that implements the API.
+ */
+ else if ( hEnv == RTENV_DEFAULT
+ && !(fFlags & (RTPROC_FLAGS_ENV_CHANGE_RECORD | RTPROC_FLAGS_SEARCH_PATH)))
+ {
+ pwszzBlock = NULL;
+ rc = VINF_SUCCESS;
+ }
+ /*
+ * Otherwise, we need to get the user profile environment.
+ */
+ else
+ {
+ RTENV hEnvToUse = NIL_RTENV;
+ HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
+ rc = rtProcWinUserLogon(pwszUser, pwszPassword, &hTokenLogon);
+ if (RT_SUCCESS(rc))
+ {
+ /* CreateEnvFromToken docs says we should load the profile, though
+ we haven't observed any difference when not doing it. Maybe it's
+ only an issue with roaming profiles or something similar... */
+ PROFILEINFOW ProfileInfo;
+ RT_ZERO(ProfileInfo);
+ ProfileInfo.dwSize = sizeof(ProfileInfo);
+ ProfileInfo.lpUserName = pwszUser;
+ ProfileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
+
+ if (g_pfnLoadUserProfileW(hTokenLogon, &ProfileInfo))
+ {
+ /*
+ * Do what we need to do. Don't keep any temp environment object.
+ */
+ rc = rtProcWinCreateEnvFromToken(hTokenLogon, hEnv, fFlags, &hEnvToUse);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtProcWinFindExe(fFlags, hEnv, pszExec, ppwszExec);
+ if (RT_SUCCESS(rc))
+ rc = RTEnvQueryUtf16Block(hEnvToUse, &pwszzBlock);
+ if (hEnvToUse != hEnv)
+ RTEnvDestroy(hEnvToUse);
+ }
+
+ if (!g_pfnUnloadUserProfile(hTokenLogon, ProfileInfo.hProfile))
+ AssertFailed();
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (hTokenLogon != INVALID_HANDLE_VALUE)
+ CloseHandle(hTokenLogon);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the process.
+ */
+ Assert(!(dwCreationFlags & CREATE_SUSPENDED));
+ bool const fCreatedSuspended = g_enmWinVer < kRTWinOSType_XP;
+ BOOL fRc = g_pfnCreateProcessWithLogonW(pwszUser,
+ NULL, /* lpDomain*/
+ pwszPassword,
+ fFlags & RTPROC_FLAGS_PROFILE ? 1 /*LOGON_WITH_PROFILE*/ : 0,
+ *ppwszExec,
+ pwszCmdLine,
+ dwCreationFlags | (fCreatedSuspended ? CREATE_SUSPENDED : 0),
+ pwszzBlock,
+ NULL, /* pCurrentDirectory */
+ pStartupInfo,
+ pProcInfo);
+ if (fRc)
+ {
+ if (!fCreatedSuspended)
+ rc = VINF_SUCCESS;
+ else
+ {
+ /*
+ * Duplicate standard handles into the child process, we ignore failures here as it's
+ * legal to have bad standard handle values and we cannot dup console I/O handles.*
+ */
+ PVOID pvDstProcParamCache = NULL;
+ rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdInput, pProcInfo->hProcess,
+ RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardInput), &pvDstProcParamCache);
+ rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdOutput, pProcInfo->hProcess,
+ RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardOutput), &pvDstProcParamCache);
+ rtProcWinDupStdHandleIntoChild(pStartupInfo->hStdError, pProcInfo->hProcess,
+ RT_UOFFSETOF(RTL_USER_PROCESS_PARAMETERS, StandardError), &pvDstProcParamCache);
+
+ if (ResumeThread(pProcInfo->hThread) != ~(DWORD)0)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (RT_FAILURE(rc))
+ {
+ TerminateProcess(pProcInfo->hProcess, 127);
+ CloseHandle(pProcInfo->hThread);
+ CloseHandle(pProcInfo->hProcess);
+ }
+ }
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ if (rc == VERR_UNRESOLVED_ERROR)
+ LogRelFunc(("CreateProcessWithLogonW failed: dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
+ }
+ if (pwszzBlock)
+ RTEnvFreeUtf16Block(pwszzBlock);
+ }
+ return rc;
+}
+
+
+static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 *ppwszExec, PRTUTF16 pwszCmdLine,
+ RTENV hEnv, DWORD dwCreationFlags,
+ STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo,
+ uint32_t fFlags, const char *pszExec, uint32_t idDesiredSession,
+ HANDLE hUserToken)
+{
+ /*
+ * If we run as a service CreateProcessWithLogon will fail, so don't even
+ * try it (because of Local System context). If we got an impersonated token
+ * we should use, we also have to have to skip over this approach.
+ * Note! This method is very slow on W2K.
+ */
+ if (!(fFlags & (RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_TOKEN_SUPPLIED)))
+ {
+ AssertPtr(pwszUser);
+ int rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, ppwszExec, pwszCmdLine,
+ hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags, pszExec);
+ if (RT_SUCCESS(rc))
+ return rc;
+ }
+ return rtProcWinCreateAsUser2(pwszUser, pwszPassword, ppwszExec, pwszCmdLine, hEnv, dwCreationFlags,
+ pStartupInfo, pProcInfo, fFlags, pszExec, idDesiredSession, hUserToken);
+}
+
+
+/**
+ * RTPathTraverseList callback used by rtProcWinFindExe to locate the
+ * executable.
+ */
+static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
+{
+ const char *pszExec = (const char *)pvUser1;
+ char *pszRealExec = (char *)pvUser2;
+ int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX, RTPATH_STR_F_STYLE_HOST);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (RTFileExists(pszRealExec))
+ return VINF_SUCCESS;
+ return VERR_TRY_AGAIN;
+}
+
+
+/**
+ * Locate the executable file if necessary.
+ *
+ * @returns IPRT status code.
+ * @param pszExec The UTF-8 executable string passed in by the user.
+ * @param fFlags The process creation flags pass in by the user.
+ * @param hEnv The environment to get the path variabel from.
+ * @param ppwszExec Pointer to the variable pointing to the UTF-16
+ * converted string. If we find something, the current
+ * pointer will be free (RTUtf16Free) and
+ * replaced by a new one.
+ */
+static int rtProcWinFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec, PRTUTF16 *ppwszExec)
+{
+ /*
+ * Return immediately if we're not asked to search, or if the file has a
+ * path already or if it actually exists in the current directory.
+ */
+ if ( !(fFlags & RTPROC_FLAGS_SEARCH_PATH)
+ || RTPathHavePath(pszExec)
+ || RTPathExists(pszExec) )
+ return VINF_SUCCESS;
+
+ /*
+ * Search the Path or PATH variable for the file.
+ */
+ char *pszPath;
+ if (RTEnvExistEx(hEnv, "PATH"))
+ pszPath = RTEnvDupEx(hEnv, "PATH");
+ else if (RTEnvExistEx(hEnv, "Path"))
+ pszPath = RTEnvDupEx(hEnv, "Path");
+ else
+ return VERR_FILE_NOT_FOUND;
+
+ char szRealExec[RTPATH_MAX];
+ int rc = RTPathTraverseList(pszPath, ';', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
+ RTStrFree(pszPath);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Replace the executable string.
+ */
+ RTPathWinFree(*ppwszExec);
+ *ppwszExec = NULL;
+ rc = RTPathWinFromUtf8(ppwszExec, szRealExec, 0 /*fFlags*/);
+ }
+ else if (rc == VERR_END_OF_STRING)
+ rc = VERR_FILE_NOT_FOUND;
+ return rc;
+}
+
+
+/**
+ * Creates the UTF-16 environment block and, if necessary, find the executable.
+ *
+ * @returns IPRT status code.
+ * @param fFlags The process creation flags pass in by the user.
+ * @param hEnv The environment handle passed by the user.
+ * @param pszExec See rtProcWinFindExe.
+ * @param ppwszzBlock Where RTEnvQueryUtf16Block returns the block.
+ * @param ppwszExec See rtProcWinFindExe.
+ */
+static int rtProcWinCreateEnvBlockAndFindExe(uint32_t fFlags, RTENV hEnv, const char *pszExec,
+ PRTUTF16 *ppwszzBlock, PRTUTF16 *ppwszExec)
+{
+ int rc;
+
+ /*
+ * In most cases, we just need to convert the incoming enviornment to a
+ * UTF-16 environment block.
+ */
+ RTENV hEnvToUse = NIL_RTENV; /* (MSC maybe used uninitialized) */
+ if ( !(fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_ENV_CHANGE_RECORD))
+ || (hEnv == RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_PROFILE))
+ || (hEnv != RTENV_DEFAULT && !(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD)) )
+ {
+ hEnvToUse = hEnv;
+ rc = VINF_SUCCESS;
+ }
+ else if (fFlags & RTPROC_FLAGS_PROFILE)
+ {
+ /*
+ * We need to get the profile environment for the current user.
+ */
+ Assert((fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD) || hEnv == RTENV_DEFAULT);
+ AssertReturn(g_pfnCreateEnvironmentBlock && g_pfnDestroyEnvironmentBlock, VERR_SYMBOL_NOT_FOUND);
+ AssertReturn(g_pfnLoadUserProfileW && g_pfnUnloadUserProfile, VERR_SYMBOL_NOT_FOUND);
+ HANDLE hToken;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_IMPERSONATE, &hToken))
+ {
+ rc = rtProcWinCreateEnvFromToken(hToken, hEnv, fFlags, &hEnvToUse);
+ CloseHandle(hToken);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ /*
+ * Apply hEnv as a change record on top of the default environment.
+ */
+ Assert(fFlags & RTPROC_FLAGS_ENV_CHANGE_RECORD);
+ rc = RTEnvClone(&hEnvToUse, RTENV_DEFAULT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTEnvApplyChanges(hEnvToUse, hEnv);
+ if (RT_FAILURE(rc))
+ RTEnvDestroy(hEnvToUse);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Query the UTF-16 environment block and locate the executable (if needed).
+ */
+ rc = RTEnvQueryUtf16Block(hEnvToUse, ppwszzBlock);
+ if (RT_SUCCESS(rc))
+ rc = rtProcWinFindExe(fFlags, hEnvToUse, pszExec, ppwszExec);
+
+ if (hEnvToUse != hEnv)
+ RTEnvDestroy(hEnvToUse);
+ }
+
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
+ PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
+ const char *pszPassword, void *pvExtraData, PRTPROCESS phProcess)
+{
+ /*
+ * Input validation
+ */
+ AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
+ AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTPROC_FLAGS_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
+ AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
+ AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
+ AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
+
+ /* Extra data: */
+ uint32_t idDesiredSession = UINT32_MAX;
+ if ( (fFlags & (RTPROC_FLAGS_DESIRED_SESSION_ID | RTPROC_FLAGS_SERVICE))
+ == (RTPROC_FLAGS_DESIRED_SESSION_ID | RTPROC_FLAGS_SERVICE))
+ {
+ AssertPtrReturn(pvExtraData, VERR_INVALID_POINTER);
+ idDesiredSession = *(uint32_t *)pvExtraData;
+ }
+ else
+ AssertReturn(!(fFlags & RTPROC_FLAGS_DESIRED_SESSION_ID), VERR_INVALID_FLAGS);
+
+ HANDLE hUserToken = NULL;
+ if (fFlags & RTPROC_FLAGS_TOKEN_SUPPLIED)
+ hUserToken = *(HANDLE *)pvExtraData;
+
+ /*
+ * Initialize the globals.
+ */
+ int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+ if ( pszAsUser
+ || (fFlags & (RTPROC_FLAGS_PROFILE | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN
+ | RTPROC_FLAGS_TOKEN_SUPPLIED)))
+ {
+ rc = RTOnce(&g_rtProcWinResolveOnce, rtProcWinResolveOnce, NULL);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * Get the file descriptors for the handles we've been passed.
+ *
+ * It seems there is no point in trying to convince a child process's CRT
+ * that any of the standard file handles is non-TEXT. So, we don't...
+ */
+ STARTUPINFOW StartupInfo;
+ RT_ZERO(StartupInfo);
+ StartupInfo.cb = sizeof(StartupInfo);
+ StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+#if 1 /* The CRT should keep the standard handles up to date. */
+ StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+#else
+ StartupInfo.hStdInput = _get_osfhandle(0);
+ StartupInfo.hStdOutput = _get_osfhandle(1);
+ StartupInfo.hStdError = _get_osfhandle(2);
+#endif
+ /* If we want to have a hidden process (e.g. not visible to
+ * to the user) use the STARTUPINFO flags. */
+ if (fFlags & RTPROC_FLAGS_HIDDEN)
+ {
+ StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
+ StartupInfo.wShowWindow = SW_HIDE;
+ }
+
+ PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
+ HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
+ DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
+ HANDLE ahStdDups[3] = { INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE };
+ for (int i = 0; i < 3; i++)
+ {
+ if (paHandles[i])
+ {
+ AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
+ switch (paHandles[i]->enmType)
+ {
+ case RTHANDLETYPE_FILE:
+ {
+ HANDLE hNativeFile = paHandles[i]->u.hFile != NIL_RTFILE
+ ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
+ : INVALID_HANDLE_VALUE;
+ if ( hNativeFile == *aphStds[i]
+ && g_enmWinVer == kRTWinOSType_NT310)
+ continue;
+ *aphStds[i] = hNativeFile;
+ break;
+ }
+
+ case RTHANDLETYPE_PIPE:
+ *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
+ ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
+ : INVALID_HANDLE_VALUE;
+ if ( g_enmWinVer == kRTWinOSType_NT310
+ && *aphStds[i] == INVALID_HANDLE_VALUE)
+ {
+ AssertMsgReturn(RTPipeGetCreationInheritability(paHandles[i]->u.hPipe), ("%Rrc %p\n", rc, *aphStds[i]),
+ VERR_INVALID_STATE);
+ continue;
+ }
+ break;
+
+ case RTHANDLETYPE_SOCKET:
+ *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
+ ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
+ : INVALID_HANDLE_VALUE;
+ break;
+
+ default:
+ AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
+ }
+
+ /* Get the inheritability of the handle. */
+ if (*aphStds[i] != INVALID_HANDLE_VALUE)
+ {
+ if (!g_pfnGetHandleInformation)
+ afInhStds[i] = 0; /* No handle info on NT 3.1, so ASSUME it is not inheritable. */
+ else if (!g_pfnGetHandleInformation(*aphStds[i], &afInhStds[i]))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedReturn(("%Rrc aphStds[%d] => %p paHandles[%d]={%d,%p}\n",
+ rc, i, *aphStds[i], i, paHandles[i]->enmType, paHandles[i]->u.uInt),
+ rc);
+ }
+ }
+ }
+ }
+
+ /*
+ * Set the inheritability any handles we're handing the child.
+ *
+ * Note! On NT 3.1 there is no SetHandleInformation, so we have to duplicate
+ * the handles to make sure they are inherited by the child.
+ */
+ rc = VINF_SUCCESS;
+ for (int i = 0; i < 3; i++)
+ if ( (afInhStds[i] != 0xffffffff)
+ && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
+ {
+ if (!g_pfnSetHandleInformation)
+ {
+ if (DuplicateHandle(GetCurrentProcess(), *aphStds[i], GetCurrentProcess(), &ahStdDups[i],
+ i == 0 ? GENERIC_READ : GENERIC_WRITE, TRUE /*fInheritHandle*/, DUPLICATE_SAME_ACCESS))
+ *aphStds[i] = ahStdDups[i];
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailedBreak(("%Rrc aphStds[%u] => %p\n", rc, i, *aphStds[i]));
+ }
+ }
+ else if (!g_pfnSetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ if (rc == VERR_INVALID_FUNCTION && g_enmWinVer == kRTWinOSType_NT310)
+ rc = VINF_SUCCESS;
+ else
+ AssertMsgFailedBreak(("%Rrc aphStds[%u] => %p\n", rc, i, *aphStds[i]));
+ }
+ }
+
+ /*
+ * Create the command line and convert the executable name.
+ */
+ PRTUTF16 pwszCmdLine = NULL; /* Shut up, MSC! */
+ if (RT_SUCCESS(rc))
+ rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs,
+ !(fFlags & RTPROC_FLAGS_UNQUOTED_ARGS)
+ ? RTGETOPTARGV_CNV_QUOTE_MS_CRT : RTGETOPTARGV_CNV_UNQUOTED);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszExec;
+ rc = RTPathWinFromUtf8(&pwszExec, pszExec, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Get going...
+ */
+ PROCESS_INFORMATION ProcInfo;
+ RT_ZERO(ProcInfo);
+ DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
+ if (fFlags & RTPROC_FLAGS_DETACHED)
+ dwCreationFlags |= DETACHED_PROCESS;
+ if (fFlags & RTPROC_FLAGS_NO_WINDOW)
+ dwCreationFlags |= CREATE_NO_WINDOW;
+
+ /*
+ * Only use the normal CreateProcess stuff if we have no user name
+ * and we are not running from a (Windows) service. Otherwise use
+ * the more advanced version in rtProcWinCreateAsUser().
+ */
+ if ( pszAsUser == NULL
+ && !(fFlags & (RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_AS_IMPERSONATED_TOKEN | RTPROC_FLAGS_TOKEN_SUPPLIED)))
+ {
+ /* Create the environment block first. */
+ PRTUTF16 pwszzBlock;
+ rc = rtProcWinCreateEnvBlockAndFindExe(fFlags, hEnv, pszExec, &pwszzBlock, &pwszExec);
+ if (RT_SUCCESS(rc))
+ {
+ if (CreateProcessW(pwszExec,
+ pwszCmdLine,
+ NULL, /* pProcessAttributes */
+ NULL, /* pThreadAttributes */
+ TRUE, /* fInheritHandles */
+ dwCreationFlags,
+ pwszzBlock,
+ NULL, /* pCurrentDirectory */
+ &StartupInfo,
+ &ProcInfo))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTEnvFreeUtf16Block(pwszzBlock);
+ }
+ }
+ else
+ {
+ /*
+ * Convert the additional parameters and use a helper
+ * function to do the actual work.
+ */
+ PRTUTF16 pwszUser = NULL;
+ if (pszAsUser)
+ rc = RTStrToUtf16(pszAsUser, &pwszUser);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszPassword;
+ rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rtProcWinCreateAsUser(pwszUser, pwszPassword, &pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
+ &StartupInfo, &ProcInfo, fFlags, pszExec, idDesiredSession,
+ hUserToken);
+
+ if (pwszPassword && *pwszPassword)
+ RTMemWipeThoroughly(pwszPassword, RTUtf16Len(pwszPassword), 5);
+ RTUtf16Free(pwszPassword);
+ }
+ RTUtf16Free(pwszUser);
+ }
+ }
+ if (RT_SUCCESS(rc))
+ {
+ CloseHandle(ProcInfo.hThread);
+ if (phProcess)
+ {
+ /*
+ * Add the process to the child process list so RTProcWait can reuse and close
+ * the process handle, unless, of course, the caller has no intention waiting.
+ */
+ if (!(fFlags & RTPROC_FLAGS_NO_WAIT))
+ rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
+ else
+ CloseHandle(ProcInfo.hProcess);
+ *phProcess = ProcInfo.dwProcessId;
+ }
+ else
+ CloseHandle(ProcInfo.hProcess);
+ rc = VINF_SUCCESS;
+ }
+ RTPathWinFree(pwszExec);
+ }
+ RTUtf16Free(pwszCmdLine);
+ }
+
+ if (g_pfnSetHandleInformation)
+ {
+ /* Undo any handle inherit changes. */
+ for (int i = 0; i < 3; i++)
+ if ( (afInhStds[i] != 0xffffffff)
+ && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
+ {
+ if ( !g_pfnSetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0)
+ && ( GetLastError() != ERROR_INVALID_FUNCTION
+ || g_enmWinVer != kRTWinOSType_NT310) )
+ AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
+ }
+ }
+ else
+ {
+ /* Close handles duplicated for correct inheritance. */
+ for (int i = 0; i < 3; i++)
+ if (ahStdDups[i] != INVALID_HANDLE_VALUE)
+ CloseHandle(ahStdDups[i]);
+ }
+
+ return rc;
+}
+
+
+
+RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
+{
+ AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
+ int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Try find the process among the ones we've spawned, otherwise, attempt
+ * opening the specified process.
+ */
+ HANDLE hOpenedProc = NULL;
+ HANDLE hProcess = rtProcWinFindPid(Process);
+ if (hProcess == NULL)
+ {
+ hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
+ if (hProcess == NULL)
+ {
+ DWORD dwErr = GetLastError();
+ if (dwErr == ERROR_INVALID_PARAMETER)
+ return VERR_PROCESS_NOT_FOUND;
+ return RTErrConvertFromWin32(dwErr);
+ }
+ }
+
+ /*
+ * Wait for it to terminate.
+ */
+ DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
+ DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
+ while (WaitRc == WAIT_IO_COMPLETION)
+ WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
+ switch (WaitRc)
+ {
+ /*
+ * It has terminated.
+ */
+ case WAIT_OBJECT_0:
+ {
+ DWORD dwExitCode;
+ if (GetExitCodeProcess(hProcess, &dwExitCode))
+ {
+ /** @todo the exit code can be special statuses. */
+ if (pProcStatus)
+ {
+ pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
+ pProcStatus->iStatus = (int)dwExitCode;
+ }
+ if (hOpenedProc == NULL)
+ rtProcWinRemovePid(Process);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+ }
+
+ /*
+ * It hasn't terminated just yet.
+ */
+ case WAIT_TIMEOUT:
+ rc = VERR_PROCESS_RUNNING;
+ break;
+
+ /*
+ * Something went wrong...
+ */
+ case WAIT_FAILED:
+ rc = RTErrConvertFromWin32(GetLastError());
+ break;
+
+ case WAIT_ABANDONED:
+ AssertFailed();
+ rc = VERR_GENERAL_FAILURE;
+ break;
+
+ default:
+ AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
+ rc = VERR_GENERAL_FAILURE;
+ break;
+ }
+
+ if (hOpenedProc != NULL)
+ CloseHandle(hOpenedProc);
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
+{
+ /** @todo this isn't quite right. */
+ return RTProcWait(Process, fFlags, pProcStatus);
+}
+
+
+RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
+{
+ if (Process == NIL_RTPROCESS)
+ return VINF_SUCCESS;
+
+ int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Try find the process among the ones we've spawned, otherwise, attempt
+ * opening the specified process.
+ */
+ HANDLE hProcess = rtProcWinFindPid(Process);
+ if (hProcess != NULL)
+ {
+ if (!TerminateProcess(hProcess, 127))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
+ if (hProcess != NULL)
+ {
+ BOOL fRc = TerminateProcess(hProcess, 127);
+ DWORD dwErr = GetLastError();
+ CloseHandle(hProcess);
+ if (!fRc)
+ rc = RTErrConvertFromWin32(dwErr);
+ }
+ }
+ return rc;
+}
+
+
+RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
+{
+ DWORD_PTR dwProcessAffinityMask = 0xffffffff;
+ DWORD_PTR dwSystemAffinityMask;
+
+ BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
+ Assert(fRc); NOREF(fRc);
+
+ return dwProcessAffinityMask;
+}
+
+
+RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser, size_t *pcbUser)
+{
+ AssertReturn( (pszUser && cbUser > 0)
+ || (!pszUser && !cbUser), VERR_INVALID_PARAMETER);
+ AssertReturn(pcbUser || pszUser, VERR_INVALID_PARAMETER);
+
+ int rc;
+ if ( hProcess == NIL_RTPROCESS
+ || hProcess == RTProcSelf())
+ {
+ RTUTF16 wszUsername[UNLEN + 1];
+ DWORD cwcUsername = RT_ELEMENTS(wszUsername);
+ if (GetUserNameW(&wszUsername[0], &cwcUsername))
+ {
+ if (pszUser)
+ {
+ rc = RTUtf16ToUtf8Ex(wszUsername, cwcUsername, &pszUser, cbUser, pcbUser);
+ if (pcbUser)
+ *pcbUser += 1;
+ }
+ else
+ {
+ *pcbUser = RTUtf16CalcUtf8Len(wszUsername) + 1;
+ rc = VERR_BUFFER_OVERFLOW;
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
+
+RTR3DECL(int) RTProcQueryUsernameA(RTPROCESS hProcess, char **ppszUser)
+{
+ AssertPtrReturn(ppszUser, VERR_INVALID_POINTER);
+ int rc;
+ if ( hProcess == NIL_RTPROCESS
+ || hProcess == RTProcSelf())
+ {
+ RTUTF16 wszUsername[UNLEN + 1];
+ DWORD cwcUsername = RT_ELEMENTS(wszUsername);
+ if (GetUserNameW(&wszUsername[0], &cwcUsername))
+ rc = RTUtf16ToUtf8(wszUsername, ppszUser);
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_NOT_SUPPORTED;
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp b/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp
new file mode 100644
index 00000000..f0f478f6
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/rtProcInitExePath-win.cpp
@@ -0,0 +1,72 @@
+/* $Id: rtProcInitExePath-win.cpp $ */
+/** @file
+ * IPRT - rtProcInitName, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_PROCESS
+#include <iprt/win/windows.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/path.h>
+#include <iprt/param.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/process.h"
+
+
+DECLHIDDEN(int) rtProcInitExePath(char *pszPath, size_t cchPath)
+{
+ /*
+ * Query the image name from the dynamic linker, convert and return it.
+ */
+ WCHAR wsz[RTPATH_MAX];
+ HMODULE hExe = GetModuleHandle(NULL);
+ if (GetModuleFileNameW(hExe, wsz, RTPATH_MAX))
+ {
+ int rc = RTUtf16ToUtf8Ex(wsz, RTSTR_MAX, &pszPath, cchPath, NULL);
+ AssertRCReturn(rc, rc);
+ return VINF_SUCCESS;
+ }
+
+ DWORD err = GetLastError();
+ int rc = RTErrConvertFromWin32(err);
+ AssertMsgFailed(("%Rrc %d\n", rc, err));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/sched-win.cpp b/src/VBox/Runtime/r3/win/sched-win.cpp
new file mode 100644
index 00000000..7b8397e0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/sched-win.cpp
@@ -0,0 +1,333 @@
+/* $Id: sched-win.cpp $ */
+/** @file
+ * IPRT - Scheduling, Win32.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+/** @def WIN32_SCHED_ENABLED
+ * Enables the priority scheme. */
+#define WIN32_SCHED_ENABLED
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/win/windows.h>
+
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/sched.h"
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Configuration of one priority.
+ */
+typedef struct
+{
+ /** The priority. */
+ RTPROCPRIORITY enmPriority;
+ /** The name of this priority. */
+ const char *pszName;
+ /** The Win32 process priority class. If ANY_PROCESS_PRIORITY_CLASS the
+ * process priority class is left unchanged. */
+ DWORD dwProcessPriorityClass;
+ /** Array scheduler attributes corresponding to each of the thread types. */
+ struct
+ {
+ /** For sanity include the array index. */
+ RTTHREADTYPE enmType;
+ /** The Win32 thread priority. */
+ int iThreadPriority;
+ } aTypes[RTTHREADTYPE_END];
+} PROCPRIORITY;
+
+/** Matches any process priority class. */
+#define ANY_PROCESS_PRIORITY_CLASS (~0U)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Array of static priority configurations.
+ */
+static const PROCPRIORITY g_aPriorities[] =
+{
+ {
+ RTPROCPRIORITY_FLAT, "Flat", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_NORMAL }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low - Below Normal", BELOW_NORMAL_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_LOW, "Low", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_NORMAL }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal - Normal", NORMAL_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_NORMAL, "Normal", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High - High", HIGH_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High - Above Normal", ABOVE_NORMAL_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ },
+ {
+ RTPROCPRIORITY_HIGH, "High", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+ }
+};
+
+/**
+ * The dynamic default priority configuration.
+ *
+ * This can be recalulated at runtime depending on what the
+ * system allow us to do. Presently we don't do this as it's
+ * generally not a bit issue on Win32 hosts.
+ */
+static PROCPRIORITY g_aDefaultPriority =
+{
+ RTPROCPRIORITY_LOW, "Default", ANY_PROCESS_PRIORITY_CLASS,
+ {
+ { RTTHREADTYPE_INVALID, ~0 },
+ { RTTHREADTYPE_INFREQUENT_POLLER, THREAD_PRIORITY_LOWEST },
+ { RTTHREADTYPE_MAIN_HEAVY_WORKER, THREAD_PRIORITY_BELOW_NORMAL },
+ { RTTHREADTYPE_EMULATION, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEFAULT, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_GUI, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_MAIN_WORKER, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_VRDP_IO, THREAD_PRIORITY_NORMAL },
+ { RTTHREADTYPE_DEBUGGER, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_MSG_PUMP, THREAD_PRIORITY_ABOVE_NORMAL },
+ { RTTHREADTYPE_IO, THREAD_PRIORITY_HIGHEST },
+ { RTTHREADTYPE_TIMER, THREAD_PRIORITY_HIGHEST }
+ }
+};
+
+
+/** Pointer to the current priority configuration. */
+static const PROCPRIORITY *g_pProcessPriority = &g_aDefaultPriority;
+
+
+/**
+ * Calculate the scheduling properties for all the threads in the default
+ * process priority, assuming the current thread have the type enmType.
+ *
+ * @returns iprt status code.
+ * @param enmType The thread type to be assumed for the current thread.
+ */
+DECLHIDDEN(int) rtSchedNativeCalcDefaultPriority(RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END); RT_NOREF_PV(enmType);
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(int) rtProcNativeSetPriority(RTPROCPRIORITY enmPriority)
+{
+ Assert(enmPriority > RTPROCPRIORITY_INVALID && enmPriority < RTPROCPRIORITY_LAST); RT_NOREF_PV(enmPriority);
+
+ if (enmPriority == RTPROCPRIORITY_DEFAULT)
+ {
+ g_pProcessPriority = &g_aDefaultPriority;
+ return VINF_SUCCESS;
+ }
+
+ for (size_t i = 0; i < RT_ELEMENTS(g_aPriorities); i++)
+ if ( g_aPriorities[i].enmPriority == enmPriority
+ && g_aPriorities[i].dwProcessPriorityClass == ANY_PROCESS_PRIORITY_CLASS)
+ {
+ g_pProcessPriority = &g_aPriorities[i];
+ return VINF_SUCCESS;
+ }
+
+ AssertFailedReturn(VERR_INTERNAL_ERROR);
+}
+
+
+/**
+ * Gets the win32 thread handle.
+ *
+ * @returns Valid win32 handle for the specified thread.
+ * @param pThread The thread.
+ */
+DECLINLINE(HANDLE) rtThreadNativeGetHandle(PRTTHREADINT pThread)
+{
+ if ((uintptr_t)pThread->Core.Key == GetCurrentThreadId())
+ return GetCurrentThread();
+ return (HANDLE)pThread->hThread;
+}
+
+
+DECLHIDDEN(int) rtThreadNativeSetPriority(PRTTHREADINT pThread, RTTHREADTYPE enmType)
+{
+ Assert(enmType > RTTHREADTYPE_INVALID && enmType < RTTHREADTYPE_END);
+ AssertMsg(g_pProcessPriority && g_pProcessPriority->aTypes[enmType].enmType == enmType,
+ ("enmType=%d entry=%d\n", enmType, g_pProcessPriority->aTypes[enmType].enmType));
+
+#ifdef WIN32_SCHED_ENABLED
+ HANDLE hThread = rtThreadNativeGetHandle(pThread);
+ if ( hThread == NULL /* No handle for alien threads. */
+ || SetThreadPriority(hThread, g_pProcessPriority->aTypes[enmType].iThreadPriority))
+ return VINF_SUCCESS;
+
+ DWORD dwLastError = GetLastError();
+ int rc = RTErrConvertFromWin32(dwLastError);
+ AssertMsgFailed(("SetThreadPriority(%p, %d) failed, dwLastError=%d rc=%Rrc\n",
+ rtThreadNativeGetHandle(pThread), g_pProcessPriority->aTypes[enmType].iThreadPriority, dwLastError, rc));
+ return rc;
+#else
+ return VINF_SUCCESS;
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/semevent-win.cpp b/src/VBox/Runtime/r3/win/semevent-win.cpp
new file mode 100644
index 00000000..be086941
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/semevent-win.cpp
@@ -0,0 +1,315 @@
+/* $Id: semevent-win.cpp $ */
+/** @file
+ * IPRT - Event Semaphore, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SEMAPHORE
+#include <iprt/win/windows.h>
+
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+#include "internal/mem.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+struct RTSEMEVENTINTERNAL
+{
+ /** Magic value (RTSEMEVENT_MAGIC). */
+ uint32_t u32Magic;
+ /** The event handle. */
+ HANDLE hev;
+#ifdef RTSEMEVENT_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+ /** The creation flags. */
+ uint32_t fFlags;
+};
+
+
+
+RTDECL(int) RTSemEventCreate(PRTSEMEVENT phEventSem)
+{
+ return RTSemEventCreateEx(phEventSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventCreateEx(PRTSEMEVENT phEventSem, uint32_t fFlags, RTLOCKVALCLASS hClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~(RTSEMEVENT_FLAGS_NO_LOCK_VAL | RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)), VERR_INVALID_PARAMETER);
+ Assert(!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK) || (fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL));
+
+ struct RTSEMEVENTINTERNAL *pThis;
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ pThis = (struct RTSEMEVENTINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ else
+ pThis = (struct RTSEMEVENTINTERNAL *)rtMemBaseAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Create the semaphore.
+ * (Auto reset, not signaled, private event object.)
+ */
+ pThis->hev = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (pThis->hev != NULL) /* not INVALID_HANDLE_VALUE */
+ {
+ pThis->u32Magic = RTSEMEVENT_MAGIC;
+ pThis->fFlags = fFlags;
+#ifdef RTSEMEVENT_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ "RTSemEvent-%u", ASMAtomicIncU32(&s_iSemEventAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENT_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phEventSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ DWORD dwErr = GetLastError();
+ if (!(fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTSemEventDestroy(RTSEMEVENT hEventSem)
+{
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ if (pThis == NIL_RTSEMEVENT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the handle and close the semaphore.
+ */
+ int rc = VINF_SUCCESS;
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMEVENT_MAGIC, RTSEMEVENT_MAGIC), VERR_INVALID_HANDLE);
+ if (CloseHandle(pThis->hev))
+ {
+#ifdef RTSEMEVENT_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ if (!(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK))
+ RTMemFree(pThis);
+ else
+ rtMemBaseFree(pThis);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ AssertMsgFailed(("Destroy hEventSem %p failed, lasterr=%u (%Rrc)\n", pThis, dwErr, rc));
+ /* Leak it. */
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventSignal(RTSEMEVENT hEventSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Signal the object.
+ */
+ if (SetEvent(pThis->hev))
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("Signaling hEventSem %p failed, lasterr=%d\n", pThis, dwErr));
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+/** Goto avoidance. */
+DECL_FORCE_INLINE(int) rtSemEventWaitHandleStatus(struct RTSEMEVENTINTERNAL *pThis, DWORD rc)
+{
+ switch (rc)
+ {
+ case WAIT_OBJECT_0: return VINF_SUCCESS;
+ case WAIT_TIMEOUT: return VERR_TIMEOUT;
+ case WAIT_IO_COMPLETION: return VERR_INTERRUPTED;
+ case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED;
+ default:
+ AssertMsgFailed(("%u\n", rc));
+ case WAIT_FAILED:
+ {
+ int rc2 = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("Wait on hEventSem %p failed, rc=%d lasterr=%d\n", pThis, rc, GetLastError()));
+ if (rc2)
+ return rc2;
+
+ AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2));
+ RT_NOREF_PV(pThis);
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+}
+
+
+#undef RTSemEventWaitNoResume
+RTDECL(int) RTSemEventWaitNoResume(RTSEMEVENT hEventSem, RTMSINTERVAL cMillies)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Wait for condition.
+ */
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = !(pThis->fFlags & RTSEMEVENT_FLAGS_BOOTSTRAP_HACK)
+ ? RTThreadSelfAutoAdopt()
+ : RTThreadSelf();
+ if (pThis->fEverHadSignallers)
+ {
+ DWORD rc = WaitForSingleObjectEx(pThis->hev,
+ 0 /*Timeout*/,
+ TRUE /*fAlertable*/);
+ if (rc != WAIT_TIMEOUT || cMillies == 0)
+ return rtSemEventWaitHandleStatus(pThis, rc);
+ int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, NULL /*pSrcPos*/, false,
+ cMillies, RTTHREADSTATE_EVENT, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT, true);
+ DWORD rc = WaitForSingleObjectEx(pThis->hev,
+ cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
+ TRUE /*fAlertable*/);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT);
+ return rtSemEventWaitHandleStatus(pThis, rc);
+}
+
+
+RTDECL(void) RTSemEventSetSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventAddSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventRemoveSignaller(RTSEMEVENT hEventSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTINTERNAL *pThis = hEventSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENT_MAGIC);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF_PV(hEventSem); RT_NOREF_PV(hThread);
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/semeventmulti-win.cpp b/src/VBox/Runtime/r3/win/semeventmulti-win.cpp
new file mode 100644
index 00000000..ed37f656
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/semeventmulti-win.cpp
@@ -0,0 +1,389 @@
+/* $Id: semeventmulti-win.cpp $ */
+/** @file
+ * IPRT - Multiple Release Event Semaphore, Windows.
+ *
+ * @remarks This file is identical to semevent-win.cpp except for the 2nd
+ * CreateEvent parameter, the reset function and the "Multi" infix.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SEMAPHORE
+#include <iprt/win/windows.h>
+
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+struct RTSEMEVENTMULTIINTERNAL
+{
+ /** Magic value (RTSEMEVENTMULTI_MAGIC). */
+ uint32_t u32Magic;
+ /** The event handle. */
+ HANDLE hev;
+#ifdef RTSEMEVENT_STRICT
+ /** Signallers. */
+ RTLOCKVALRECSHRD Signallers;
+ /** Indicates that lock validation should be performed. */
+ bool volatile fEverHadSignallers;
+#endif
+};
+
+
+
+RTDECL(int) RTSemEventMultiCreate(PRTSEMEVENTMULTI phEventMultiSem)
+{
+ return RTSemEventMultiCreateEx(phEventMultiSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, NULL);
+}
+
+
+RTDECL(int) RTSemEventMultiCreateEx(PRTSEMEVENTMULTI phEventMultiSem, uint32_t fFlags, RTLOCKVALCLASS hClass,
+ const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ struct RTSEMEVENTMULTIINTERNAL *pThis = (struct RTSEMEVENTMULTIINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ if (!pThis)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Create the semaphore.
+ * (Manual reset, not signaled, private event object.)
+ */
+ pThis->hev = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (pThis->hev != NULL) /* not INVALID_HANDLE_VALUE */
+ {
+ pThis->u32Magic = RTSEMEVENTMULTI_MAGIC;
+#ifdef RTSEMEVENT_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iSemEventMultiAnon = 0;
+ RTLockValidatorRecSharedInit(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ "RTSemEventMulti-%u", ASMAtomicIncU32(&s_iSemEventMultiAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecSharedInitV(&pThis->Signallers, hClass, RTLOCKVAL_SUB_CLASS_ANY, pThis,
+ true /*fSignaller*/, !(fFlags & RTSEMEVENTMULTI_FLAGS_NO_LOCK_VAL),
+ pszNameFmt, va);
+ va_end(va);
+ }
+ pThis->fEverHadSignallers = false;
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(pszNameFmt);
+#endif
+
+ *phEventMultiSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ DWORD dwErr = GetLastError();
+ RTMemFree(pThis);
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTSemEventMultiDestroy(RTSEMEVENTMULTI hEventMultiSem)
+{
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ if (pThis == NIL_RTSEMEVENT) /* don't bitch */
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Invalidate the handle and close the semaphore.
+ */
+ int rc = VINF_SUCCESS;
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, ~RTSEMEVENTMULTI_MAGIC, RTSEMEVENTMULTI_MAGIC), VERR_INVALID_HANDLE);
+ if (CloseHandle(pThis->hev))
+ {
+#ifdef RTSEMEVENT_STRICT
+ RTLockValidatorRecSharedDelete(&pThis->Signallers);
+#endif
+ RTMemFree(pThis);
+ }
+ else
+ {
+ DWORD dwErr = GetLastError();
+ rc = RTErrConvertFromWin32(dwErr);
+ AssertMsgFailed(("Destroy hEventMultiSem %p failed, lasterr=%u (%Rrc)\n", pThis, dwErr, rc));
+ /* Leak it. */
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSemEventMultiSignal(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+#ifdef RTSEMEVENT_STRICT
+ if (pThis->fEverHadSignallers)
+ {
+ int rc9 = RTLockValidatorRecSharedCheckSignaller(&pThis->Signallers, NIL_RTTHREAD);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#endif
+
+ /*
+ * Signal the object.
+ */
+ if (SetEvent(pThis->hev))
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("Signaling hEventMultiSem %p failed, lasterr=%d\n", pThis, dwErr));
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+RTDECL(int) RTSemEventMultiReset(RTSEMEVENTMULTI hEventMultiSem)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Reset the object.
+ */
+ if (ResetEvent(pThis->hev))
+ return VINF_SUCCESS;
+ DWORD dwErr = GetLastError();
+ AssertMsgFailed(("Resetting hEventMultiSem %p failed, lasterr=%d\n", pThis, dwErr));
+ return RTErrConvertFromWin32(dwErr);
+}
+
+
+/** Goto avoidance. */
+DECL_FORCE_INLINE(int)
+rtSemEventWaitHandleStatus(struct RTSEMEVENTMULTIINTERNAL *pThis, uint32_t fFlags, DWORD rc)
+{
+ switch (rc)
+ {
+ case WAIT_OBJECT_0: return VINF_SUCCESS;
+ case WAIT_TIMEOUT: return VERR_TIMEOUT;
+ case WAIT_IO_COMPLETION: return fFlags & RTSEMWAIT_FLAGS_RESUME ? VERR_TIMEOUT : VERR_INTERRUPTED;
+ case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED;
+ default:
+ AssertMsgFailed(("%u\n", rc));
+ case WAIT_FAILED:
+ {
+ int rc2 = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("Wait on hEventMultiSem %p failed, rc=%d lasterr=%d\n", pThis, rc, GetLastError()));
+ if (rc2)
+ return rc2;
+
+ AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2));
+ RT_NOREF_PV(pThis);
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+}
+
+
+DECLINLINE(int) rtSemEventMultiWinWait(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate input.
+ */
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(RTSEMWAIT_FLAGS_ARE_VALID(fFlags), VERR_INVALID_PARAMETER);
+
+ /*
+ * Convert the timeout to a millisecond count.
+ */
+ uint64_t uAbsDeadline;
+ DWORD dwMsTimeout;
+ if (fFlags & RTSEMWAIT_FLAGS_INDEFINITE)
+ {
+ dwMsTimeout = INFINITE;
+ uAbsDeadline = UINT64_MAX;
+ }
+ else
+ {
+ if (fFlags & RTSEMWAIT_FLAGS_NANOSECS)
+ uTimeout = uTimeout < UINT64_MAX - UINT32_C(1000000) / 2
+ ? (uTimeout + UINT32_C(1000000) / 2) / UINT32_C(1000000)
+ : UINT64_MAX / UINT32_C(1000000);
+ if (fFlags & RTSEMWAIT_FLAGS_ABSOLUTE)
+ {
+ uAbsDeadline = uTimeout;
+ uint64_t u64Now = RTTimeSystemMilliTS();
+ if (u64Now < uTimeout)
+ uTimeout -= u64Now;
+ else
+ uTimeout = 0;
+ }
+ else if (fFlags & RTSEMWAIT_FLAGS_RESUME)
+ uAbsDeadline = RTTimeSystemMilliTS() + uTimeout;
+ else
+ uAbsDeadline = UINT64_MAX;
+
+ dwMsTimeout = uTimeout < UINT32_MAX
+ ? (DWORD)uTimeout
+ : INFINITE;
+ }
+
+ /*
+ * Do the wait.
+ */
+ DWORD rc;
+#ifdef RTSEMEVENT_STRICT
+ RTTHREAD hThreadSelf = RTThreadSelfAutoAdopt();
+ if (pThis->fEverHadSignallers)
+ {
+ do
+ rc = WaitForSingleObjectEx(pThis->hev, 0 /*Timeout*/, TRUE /*fAlertable*/);
+ while (rc == WAIT_IO_COMPLETION && (fFlags & RTSEMWAIT_FLAGS_RESUME));
+ if (rc != WAIT_TIMEOUT || dwMsTimeout == 0)
+ return rtSemEventWaitHandleStatus(pThis, fFlags, rc);
+ int rc9 = RTLockValidatorRecSharedCheckBlocking(&pThis->Signallers, hThreadSelf, pSrcPos, false,
+ dwMsTimeout, RTTHREADSTATE_EVENT_MULTI, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+ }
+#else
+ RTTHREAD hThreadSelf = RTThreadSelf();
+ RT_NOREF_PV(pSrcPos);
+#endif
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_EVENT_MULTI, true);
+ rc = WaitForSingleObjectEx(pThis->hev, dwMsTimeout, TRUE /*fAlertable*/);
+ if (rc == WAIT_IO_COMPLETION && (fFlags & RTSEMWAIT_FLAGS_RESUME))
+ {
+ while ( rc == WAIT_IO_COMPLETION
+ && RTTimeSystemMilliTS() < uAbsDeadline)
+ rc = WaitForSingleObjectEx(pThis->hev, dwMsTimeout, TRUE /*fAlertable*/);
+
+ }
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_EVENT_MULTI);
+ return rtSemEventWaitHandleStatus(pThis, fFlags, rc);
+}
+
+
+
+#undef RTSemEventMultiWaitEx
+RTDECL(int) RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout)
+{
+#ifndef RTSEMEVENT_STRICT
+ return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout,
+ RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemEventMultiWinWait(hEventMultiSem, fFlags, uTimeout, &SrcPos);
+}
+
+
+
+RTDECL(void) RTSemEventMultiSetSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedResetOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiAddSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ ASMAtomicWriteBool(&pThis->fEverHadSignallers, true);
+ RTLockValidatorRecSharedAddOwner(&pThis->Signallers, hThread, NULL);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
+
+RTDECL(void) RTSemEventMultiRemoveSignaller(RTSEMEVENTMULTI hEventMultiSem, RTTHREAD hThread)
+{
+#ifdef RTSEMEVENT_STRICT
+ struct RTSEMEVENTMULTIINTERNAL *pThis = hEventMultiSem;
+ AssertPtrReturnVoid(pThis);
+ AssertReturnVoid(pThis->u32Magic == RTSEMEVENTMULTI_MAGIC);
+
+ RTLockValidatorRecSharedRemoveOwner(&pThis->Signallers, hThread);
+#else
+ RT_NOREF_PV(hEventMultiSem); RT_NOREF_PV(hThread);
+#endif
+}
+
diff --git a/src/VBox/Runtime/r3/win/semmutex-win.cpp b/src/VBox/Runtime/r3/win/semmutex-win.cpp
new file mode 100644
index 00000000..34aa8bc5
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/semmutex-win.cpp
@@ -0,0 +1,356 @@
+/* $Id: semmutex-win.cpp $ */
+/** @file
+ * IPRT - Mutex Semaphores, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SEMAPHORE
+#include <iprt/win/windows.h>
+
+#include <iprt/semaphore.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/lockvalidator.h>
+#include <iprt/mem.h>
+#include <iprt/thread.h>
+#include "internal/magics.h"
+#include "internal/strict.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Posix internal representation of a Mutex semaphore. */
+struct RTSEMMUTEXINTERNAL
+{
+ /** Magic value (RTSEMMUTEX_MAGIC). */
+ uint32_t u32Magic;
+ /** Recursion count. */
+ uint32_t volatile cRecursions;
+ /** The owner thread. */
+ RTNATIVETHREAD volatile hNativeOwner;
+ /** The mutex handle. */
+ HANDLE hMtx;
+#ifdef RTSEMMUTEX_STRICT
+ /** Lock validator record associated with this mutex. */
+ RTLOCKVALRECEXCL ValidatorRec;
+#endif
+};
+
+
+
+#undef RTSemMutexCreate
+RTDECL(int) RTSemMutexCreate(PRTSEMMUTEX phMutexSem)
+{
+ return RTSemMutexCreateEx(phMutexSem, 0 /*fFlags*/, NIL_RTLOCKVALCLASS, RTLOCKVAL_SUB_CLASS_NONE, NULL);
+}
+
+
+RTDECL(int) RTSemMutexCreateEx(PRTSEMMUTEX phMutexSem, uint32_t fFlags,
+ RTLOCKVALCLASS hClass, uint32_t uSubClass, const char *pszNameFmt, ...)
+{
+ AssertReturn(!(fFlags & ~RTSEMMUTEX_FLAGS_NO_LOCK_VAL), VERR_INVALID_PARAMETER);
+
+ /*
+ * Create the semaphore.
+ */
+ int rc;
+ HANDLE hMtx = CreateMutex(NULL, FALSE, NULL);
+ if (hMtx)
+ {
+ RTSEMMUTEXINTERNAL *pThis = (RTSEMMUTEXINTERNAL *)RTMemAlloc(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSEMMUTEX_MAGIC;
+ pThis->hMtx = hMtx;
+ pThis->hNativeOwner = NIL_RTNATIVETHREAD;
+ pThis->cRecursions = 0;
+#ifdef RTSEMMUTEX_STRICT
+ if (!pszNameFmt)
+ {
+ static uint32_t volatile s_iMutexAnon = 0;
+ RTLockValidatorRecExclInit(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL),
+ "RTSemMutex-%u", ASMAtomicIncU32(&s_iMutexAnon) - 1);
+ }
+ else
+ {
+ va_list va;
+ va_start(va, pszNameFmt);
+ RTLockValidatorRecExclInitV(&pThis->ValidatorRec, hClass, uSubClass, pThis,
+ !(fFlags & RTSEMMUTEX_FLAGS_NO_LOCK_VAL), pszNameFmt, va);
+ va_end(va);
+ }
+#else
+ RT_NOREF_PV(hClass); RT_NOREF_PV(uSubClass); RT_NOREF_PV(pszNameFmt);
+#endif
+ *phMutexSem = pThis;
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ return rc;
+}
+
+
+RTDECL(int) RTSemMutexDestroy(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ if (pThis == NIL_RTSEMMUTEX)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Close semaphore handle.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSEMMUTEX_MAGIC_DEAD, RTSEMMUTEX_MAGIC), VERR_INVALID_HANDLE);
+ HANDLE hMtx = pThis->hMtx;
+ ASMAtomicWritePtr(&pThis->hMtx, INVALID_HANDLE_VALUE);
+
+ int rc = VINF_SUCCESS;
+ if (!CloseHandle(hMtx))
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%p rc=%d lasterr=%d\n", pThis->hMtx, rc, GetLastError()));
+ }
+
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclDelete(&pThis->ValidatorRec);
+#endif
+ RTMemFree(pThis);
+ return rc;
+}
+
+
+RTDECL(uint32_t) RTSemMutexSetSubClass(RTSEMMUTEX hMutexSem, uint32_t uSubClass)
+{
+#ifdef RTSEMMUTEX_STRICT
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, RTLOCKVAL_SUB_CLASS_INVALID);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, RTLOCKVAL_SUB_CLASS_INVALID);
+
+ return RTLockValidatorRecExclSetSubClass(&pThis->ValidatorRec, uSubClass);
+#else
+ RT_NOREF_PV(hMutexSem); RT_NOREF_PV(uSubClass);
+ return RTLOCKVAL_SUB_CLASS_INVALID;
+#endif
+}
+
+
+/**
+ * Internal worker for RTSemMutexRequestNoResume and it's debug companion.
+ *
+ * @returns Same as RTSEmMutexRequestNoResume
+ * @param hMutexSem The mutex handle.
+ * @param cMillies The number of milliseconds to wait.
+ * @param pSrcPos The source position of the caller.
+ */
+DECL_FORCE_INLINE(int) rtSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, PCRTLOCKVALSRCPOS pSrcPos)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check for recursive entry.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeOwner;
+ ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner);
+ if (hNativeOwner == hNativeSelf)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclRecursion(&pThis->ValidatorRec, pSrcPos);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicIncU32(&pThis->cRecursions);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Lock mutex semaphore.
+ */
+ RTTHREAD hThreadSelf = NIL_RTTHREAD;
+ if (cMillies > 0)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ hThreadSelf = RTThreadSelfAutoAdopt();
+ int rc9 = RTLockValidatorRecExclCheckOrderAndBlocking(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true,
+ cMillies, RTTHREADSTATE_MUTEX, true);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#else
+ hThreadSelf = RTThreadSelf();
+ RTThreadBlocking(hThreadSelf, RTTHREADSTATE_MUTEX, true);
+ RT_NOREF_PV(pSrcPos);
+#endif
+ }
+ DWORD rc = WaitForSingleObjectEx(pThis->hMtx,
+ cMillies == RT_INDEFINITE_WAIT ? INFINITE : cMillies,
+ TRUE /*fAlertable*/);
+ RTThreadUnblocked(hThreadSelf, RTTHREADSTATE_MUTEX);
+ switch (rc)
+ {
+ case WAIT_OBJECT_0:
+#ifdef RTSEMMUTEX_STRICT
+ RTLockValidatorRecExclSetOwner(&pThis->ValidatorRec, hThreadSelf, pSrcPos, true);
+#endif
+ ASMAtomicWriteHandle(&pThis->hNativeOwner, hNativeSelf);
+ ASMAtomicWriteU32(&pThis->cRecursions, 1);
+ return VINF_SUCCESS;
+
+ case WAIT_TIMEOUT: return VERR_TIMEOUT;
+ case WAIT_IO_COMPLETION: return VERR_INTERRUPTED;
+ case WAIT_ABANDONED: return VERR_SEM_OWNER_DIED;
+ default:
+ AssertMsgFailed(("%u\n", rc));
+ case WAIT_FAILED:
+ {
+ int rc2 = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("Wait on hMutexSem %p failed, rc=%d lasterr=%d\n", hMutexSem, rc, GetLastError()));
+ if (rc2 != VINF_SUCCESS)
+ return rc2;
+
+ AssertMsgFailed(("WaitForSingleObject(event) -> rc=%d while converted lasterr=%d\n", rc, rc2));
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+}
+
+
+#undef RTSemMutexRequestNoResume
+RTDECL(int) RTSemMutexRequestNoResume(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies)
+{
+#ifndef RTSEMMUTEX_STRICT
+ return rtSemMutexRequestNoResume(hMutexSem, cMillies, NULL);
+#else
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_NORMAL_API();
+ return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos);
+#endif
+}
+
+
+RTDECL(int) RTSemMutexRequestNoResumeDebug(RTSEMMUTEX hMutexSem, RTMSINTERVAL cMillies, RTHCUINTPTR uId, RT_SRC_POS_DECL)
+{
+ RTLOCKVALSRCPOS SrcPos = RTLOCKVALSRCPOS_INIT_DEBUG_API();
+ return rtSemMutexRequestNoResume(hMutexSem, cMillies, &SrcPos);
+}
+
+
+RTDECL(int) RTSemMutexRelease(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Check ownership and recursions.
+ */
+ RTNATIVETHREAD hNativeSelf = RTThreadNativeSelf();
+ RTNATIVETHREAD hNativeOwner;
+ ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner);
+ if (RT_UNLIKELY(hNativeOwner != hNativeSelf))
+ {
+ AssertMsgFailed(("Not owner of mutex %p!! hNativeSelf=%RTntrd Owner=%RTntrd cRecursions=%d\n",
+ pThis, hNativeSelf, hNativeOwner, pThis->cRecursions));
+ return VERR_NOT_OWNER;
+ }
+ if (pThis->cRecursions > 1)
+ {
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclUnwind(&pThis->ValidatorRec);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicDecU32(&pThis->cRecursions);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Unlock mutex semaphore.
+ */
+#ifdef RTSEMMUTEX_STRICT
+ int rc9 = RTLockValidatorRecExclReleaseOwner(&pThis->ValidatorRec, false);
+ if (RT_FAILURE(rc9))
+ return rc9;
+#endif
+ ASMAtomicWriteU32(&pThis->cRecursions, 0);
+ ASMAtomicWriteHandle(&pThis->hNativeOwner, NIL_RTNATIVETHREAD);
+
+ if (ReleaseMutex(pThis->hMtx))
+ return VINF_SUCCESS;
+
+ int rc = RTErrConvertFromWin32(GetLastError());
+ AssertMsgFailed(("%p/%p, rc=%Rrc lasterr=%d\n", pThis, pThis->hMtx, rc, GetLastError()));
+ return rc;
+}
+
+
+RTDECL(bool) RTSemMutexIsOwned(RTSEMMUTEX hMutexSem)
+{
+ /*
+ * Validate.
+ */
+ RTSEMMUTEXINTERNAL *pThis = hMutexSem;
+ AssertPtrReturn(pThis, false);
+ AssertReturn(pThis->u32Magic == RTSEMMUTEX_MAGIC, false);
+
+ RTNATIVETHREAD hNativeOwner;
+ ASMAtomicReadHandle(&pThis->hNativeOwner, &hNativeOwner);
+ return hNativeOwner != NIL_RTNATIVETHREAD;
+}
+
diff --git a/src/VBox/Runtime/r3/win/serialport-win.cpp b/src/VBox/Runtime/r3/win/serialport-win.cpp
new file mode 100644
index 00000000..0f6f04c2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/serialport-win.cpp
@@ -0,0 +1,1056 @@
+/* $Id: serialport-win.cpp $ */
+/** @file
+ * IPRT - Serial Port API, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2017-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/serialport.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include "internal/magics.h"
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal serial port state.
+ */
+typedef struct RTSERIALPORTINTERNAL
+{
+ /** Magic value (RTSERIALPORT_MAGIC). */
+ uint32_t u32Magic;
+ /** Flags given while opening the serial port. */
+ uint32_t fOpenFlags;
+ /** The device handle. */
+ HANDLE hDev;
+ /** The overlapped write structure. */
+ OVERLAPPED OverlappedWrite;
+ /** The overlapped read structure. */
+ OVERLAPPED OverlappedRead;
+ /** The overlapped I/O structure when waiting on events. */
+ OVERLAPPED OverlappedEvt;
+ /** The event handle to wait on for the overlapped event operations of the device. */
+ HANDLE hEvtDev;
+ /** The event handle to wait on for the overlapped write operations of the device. */
+ HANDLE hEvtWrite;
+ /** The event handle to wait on for the overlapped read operations of the device. */
+ HANDLE hEvtRead;
+ /** The event handle to wait on for waking up waiting threads externally. */
+ HANDLE hEvtIntr;
+ /** Events currently waited for. */
+ uint32_t fEvtMask;
+ /** Event mask as received by WaitCommEvent(). */
+ DWORD dwEventMask;
+ /** Flag whether a write is currently pending. */
+ bool fWritePending;
+ /** Event query pending. */
+ bool fEvtQueryPending;
+ /** Bounce buffer for writes. */
+ uint8_t *pbBounceBuf;
+ /** Amount of used buffer space. */
+ size_t cbBounceBufUsed;
+ /** Amount of allocated buffer space. */
+ size_t cbBounceBufAlloc;
+ /** The current active port config. */
+ DCB PortCfg;
+} RTSERIALPORTINTERNAL;
+/** Pointer to the internal serial port state. */
+typedef RTSERIALPORTINTERNAL *PRTSERIALPORTINTERNAL;
+
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** The pipe buffer size we prefer. */
+#define RTSERIALPORT_NT_SIZE _32K
+
+
+
+/*********************************************************************************************************************************
+* Global variables *
+*********************************************************************************************************************************/
+
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Updatest the current event mask to wait for.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ * @param fEvtMask The new event mask to change to.
+ */
+static int rtSerialPortWinUpdateEvtMask(PRTSERIALPORTINTERNAL pThis, uint32_t fEvtMask)
+{
+ DWORD dwEvtMask = EV_ERR;
+
+ if (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
+ dwEvtMask |= EV_RXCHAR;
+ if (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
+ dwEvtMask |= EV_TXEMPTY;
+ if (fEvtMask & RTSERIALPORT_EVT_F_BREAK_DETECTED)
+ dwEvtMask |= EV_BREAK;
+ if (fEvtMask & RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED)
+ dwEvtMask |= EV_CTS | EV_DSR | EV_RING | EV_RLSD;
+
+ int rc = VINF_SUCCESS;
+ if (!SetCommMask(pThis->hDev, dwEvtMask))
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ pThis->fEvtMask = fEvtMask;
+
+ return rc;
+}
+
+
+/**
+ * Tries to set the default config on the given serial port.
+ *
+ * @returns IPRT status code.
+ * @param pThis The internal serial port instance data.
+ */
+static int rtSerialPortSetDefaultCfg(PRTSERIALPORTINTERNAL pThis)
+{
+ if (!PurgeComm(pThis->hDev, PURGE_RXABORT | PURGE_RXCLEAR | PURGE_TXABORT | PURGE_TXCLEAR))
+ return RTErrConvertFromWin32(GetLastError());
+
+ pThis->PortCfg.DCBlength = sizeof(pThis->PortCfg);
+ if (!GetCommState(pThis->hDev, &pThis->PortCfg))
+ return RTErrConvertFromWin32(GetLastError());
+
+ pThis->PortCfg.BaudRate = CBR_9600;
+ pThis->PortCfg.fBinary = TRUE;
+ pThis->PortCfg.fParity = TRUE;
+ pThis->PortCfg.fDtrControl = DTR_CONTROL_DISABLE;
+ pThis->PortCfg.ByteSize = 8;
+ pThis->PortCfg.Parity = NOPARITY;
+ pThis->PortCfg.fOutxCtsFlow = FALSE;
+ pThis->PortCfg.fOutxDsrFlow = FALSE;
+ pThis->PortCfg.fDsrSensitivity = FALSE;
+ pThis->PortCfg.fTXContinueOnXoff = TRUE;
+ pThis->PortCfg.fOutX = FALSE;
+ pThis->PortCfg.fInX = FALSE;
+ pThis->PortCfg.fErrorChar = FALSE;
+ pThis->PortCfg.fNull = FALSE;
+ pThis->PortCfg.fRtsControl = RTS_CONTROL_DISABLE;
+ pThis->PortCfg.fAbortOnError = FALSE;
+ pThis->PortCfg.wReserved = 0;
+ pThis->PortCfg.XonLim = 5;
+ pThis->PortCfg.XoffLim = 5;
+
+ int rc = VINF_SUCCESS;
+ if (!SetCommState(pThis->hDev, &pThis->PortCfg))
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set timeouts for non blocking mode.
+ * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/ns-winbase-_commtimeouts .
+ */
+ COMMTIMEOUTS ComTimeouts;
+ RT_ZERO(ComTimeouts);
+ ComTimeouts.ReadIntervalTimeout = MAXDWORD;
+ if (!SetCommTimeouts(pThis->hDev, &ComTimeouts))
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+
+ return rc;
+}
+
+
+/**
+ * Common worker for handling I/O completion.
+ *
+ * This is used by RTSerialPortClose, RTSerialPortWrite and RTPipeSerialPortNB.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ */
+static int rtSerialPortWriteCheckCompletion(PRTSERIALPORTINTERNAL pThis)
+{
+ int rc = VINF_SUCCESS;
+ DWORD dwRc = WaitForSingleObject(pThis->OverlappedWrite.hEvent, 0);
+ if (dwRc == WAIT_OBJECT_0)
+ {
+ DWORD cbWritten = 0;
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE))
+ {
+ for (;;)
+ {
+ if (cbWritten >= pThis->cbBounceBufUsed)
+ {
+ pThis->fWritePending = false;
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /* resubmit the remainder of the buffer - can this actually happen? */
+ memmove(&pThis->pbBounceBuf[0], &pThis->pbBounceBuf[cbWritten], pThis->cbBounceBufUsed - cbWritten);
+ rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
+ if (!WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->OverlappedWrite))
+ {
+ if (GetLastError() == ERROR_IO_PENDING)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fWritePending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ break;
+ }
+ Assert(cbWritten > 0);
+ }
+ }
+ else
+ {
+ pThis->fWritePending = false;
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else if (dwRc == WAIT_TIMEOUT)
+ rc = VINF_TRY_AGAIN;
+ else
+ {
+ pThis->fWritePending = false;
+ if (dwRc == WAIT_ABANDONED)
+ rc = VERR_INVALID_HANDLE;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ return rc;
+}
+
+
+/**
+ * Processes the received Windows comm events and converts them to our format.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ * @param dwEventMask Event mask received.
+ * @param pfEvtsRecv Where to store the converted events.
+ */
+static int rtSerialPortEvtFlagsProcess(PRTSERIALPORTINTERNAL pThis, DWORD dwEventMask, uint32_t *pfEvtsRecv)
+{
+ int rc = VINF_SUCCESS;
+
+ if (dwEventMask & EV_RXCHAR)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
+ if (dwEventMask & EV_TXEMPTY)
+ {
+ if (pThis->fWritePending)
+ {
+ rc = rtSerialPortWriteCheckCompletion(pThis);
+ if (rc == VINF_SUCCESS)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+ else
+ rc = VINF_SUCCESS;
+ }
+ else
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+ }
+ if (dwEventMask & EV_BREAK)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_BREAK_DETECTED;
+ if (dwEventMask & (EV_CTS | EV_DSR | EV_RING | EV_RLSD))
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_STATUS_LINE_CHANGED;
+
+ return rc;
+}
+
+
+/**
+ * The internal comm event wait worker.
+ *
+ * @returns IPRT status code.
+ * @param pThis The pipe instance handle.
+ * @param msTimeout The timeout to wait for.
+ */
+static int rtSerialPortEvtWaitWorker(PRTSERIALPORTINTERNAL pThis, RTMSINTERVAL msTimeout)
+{
+ int rc = VINF_SUCCESS;
+ HANDLE ahWait[2];
+ ahWait[0] = pThis->hEvtDev;
+ ahWait[1] = pThis->hEvtIntr;
+
+ DWORD dwRet = WaitForMultipleObjects(2, ahWait, FALSE, msTimeout == RT_INDEFINITE_WAIT ? INFINITE : msTimeout);
+ if (dwRet == WAIT_TIMEOUT)
+ rc = VERR_TIMEOUT;
+ else if (dwRet == WAIT_FAILED)
+ rc = RTErrConvertFromWin32(GetLastError());
+ else if (dwRet == WAIT_OBJECT_0)
+ rc = VINF_SUCCESS;
+ else
+ {
+ Assert(dwRet == WAIT_OBJECT_0 + 1);
+ rc = VERR_INTERRUPTED;
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortOpen(PRTSERIALPORT phSerialPort, const char *pszPortAddress, uint32_t fFlags)
+{
+ AssertPtrReturn(phSerialPort, VERR_INVALID_POINTER);
+ AssertReturn(RT_VALID_PTR(pszPortAddress) && *pszPortAddress != '\0', VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSERIALPORT_OPEN_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn((fFlags & RTSERIALPORT_OPEN_F_READ) || (fFlags & RTSERIALPORT_OPEN_F_WRITE),
+ VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ PRTSERIALPORTINTERNAL pThis = (PRTSERIALPORTINTERNAL)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ pThis->u32Magic = RTSERIALPORT_MAGIC;
+ pThis->fOpenFlags = fFlags;
+ pThis->fEvtMask = 0;
+ pThis->fWritePending = false;
+ pThis->fEvtQueryPending = false;
+ pThis->pbBounceBuf = NULL;
+ pThis->cbBounceBufUsed = 0;
+ pThis->cbBounceBufAlloc = 0;
+ RT_ZERO(pThis->OverlappedEvt);
+ RT_ZERO(pThis->OverlappedWrite);
+ RT_ZERO(pThis->OverlappedRead);
+ pThis->hEvtDev = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (pThis->hEvtDev)
+ {
+ pThis->OverlappedEvt.hEvent = pThis->hEvtDev,
+ pThis->hEvtIntr = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (pThis->hEvtIntr)
+ {
+ pThis->hEvtWrite = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (pThis->hEvtWrite)
+ {
+ pThis->OverlappedWrite.hEvent = pThis->hEvtWrite;
+ pThis->hEvtRead = CreateEvent(NULL, TRUE, TRUE, NULL);
+ if (pThis->hEvtRead)
+ {
+ pThis->OverlappedRead.hEvent = pThis->hEvtRead;
+ DWORD fWinFlags = 0;
+
+ if (fFlags & RTSERIALPORT_OPEN_F_WRITE)
+ fWinFlags |= GENERIC_WRITE;
+ if (fFlags & RTSERIALPORT_OPEN_F_READ)
+ fWinFlags |= GENERIC_READ;
+
+ pThis->hDev = CreateFile(pszPortAddress,
+ fWinFlags,
+ 0, /* Must be opened with exclusive access. */
+ NULL, /* No SECURITY_ATTRIBUTES structure. */
+ OPEN_EXISTING, /* Must use OPEN_EXISTING. */
+ FILE_FLAG_OVERLAPPED, /* Overlapped I/O. */
+ NULL);
+ if (pThis->hDev)
+ {
+ rc = rtSerialPortSetDefaultCfg(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ *phSerialPort = pThis;
+ return rc;
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ CloseHandle(pThis->hEvtRead);
+ }
+
+ CloseHandle(pThis->hEvtWrite);
+ }
+
+ CloseHandle(pThis->hEvtIntr);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ CloseHandle(pThis->hEvtDev);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortClose(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ if (pThis == NIL_RTSERIALPORT)
+ return VINF_SUCCESS;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ /*
+ * Do the cleanup.
+ */
+ AssertReturn(ASMAtomicCmpXchgU32(&pThis->u32Magic, RTSERIALPORT_MAGIC_DEAD, RTSERIALPORT_MAGIC), VERR_INVALID_HANDLE);
+
+ if (pThis->fWritePending)
+ rtSerialPortWriteCheckCompletion(pThis);
+
+ CloseHandle(pThis->hDev);
+ CloseHandle(pThis->hEvtDev);
+ CloseHandle(pThis->hEvtWrite);
+ CloseHandle(pThis->hEvtRead);
+ CloseHandle(pThis->hEvtIntr);
+ pThis->hDev = NULL;
+ pThis->hEvtDev = NULL;
+ pThis->hEvtWrite = NULL;
+ pThis->hEvtRead = NULL;
+ pThis->hEvtIntr = NULL;
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTHCINTPTR) RTSerialPortToNative(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, -1);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, -1);
+
+ return (RTHCINTPTR)pThis->hDev;
+}
+
+
+RTDECL(int) RTSerialPortRead(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+
+ /*
+ * Kick of an overlapped read.
+ */
+ int rc = VINF_SUCCESS;
+ uint8_t *pbBuf = (uint8_t *)pvBuf;
+
+ while ( cbToRead > 0
+ && RT_SUCCESS(rc))
+ {
+ BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
+ DWORD cbRead = 0;
+ if (ReadFile(pThis->hDev, pbBuf,
+ cbToRead <= ~(DWORD)0 ? (DWORD)cbToRead : ~(DWORD)0,
+ &cbRead, &pThis->OverlappedRead))
+ {
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ DWORD dwWait = WaitForSingleObject(pThis->OverlappedRead.hEvent, INFINITE);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
+ {
+ if (pcbRead)
+ {
+ *pcbRead = cbRead;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ Assert(dwWait == WAIT_FAILED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (RT_SUCCESS(rc))
+ {
+ cbToRead -= cbRead;
+ pbBuf += cbRead;
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortReadNB(RTSERIALPORT hSerialPort, void *pvBuf, size_t cbToRead, size_t *pcbRead)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToRead > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbRead, VERR_INVALID_POINTER);
+
+ *pcbRead = 0;
+
+ /* Check whether there is data waiting in the input queue. */
+ int rc = VINF_SUCCESS;
+ COMSTAT ComStat; RT_ZERO(ComStat);
+ if (ClearCommError(pThis->hDev, NULL, &ComStat))
+ {
+ if (ComStat.cbInQue > 0)
+ {
+ DWORD dwToRead = RT_MIN(ComStat.cbInQue, (DWORD)cbToRead);
+ /* Kick of an overlapped read. It should return immediately */
+ BOOL fSucc = ResetEvent(pThis->OverlappedRead.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
+ DWORD cbRead = 0;
+ if ( cbToRead == 0
+ || ReadFile(pThis->hDev, pvBuf, dwToRead,
+ &cbRead, &pThis->OverlappedRead))
+ *pcbRead = cbRead;
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ /* This shouldn't actually happen, so turn this into a synchronous read. */
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedRead, &cbRead, TRUE /*fWait*/))
+ *pcbRead = cbRead;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VINF_TRY_AGAIN;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWrite(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+
+ /* If I/O is pending, check if it has completed. */
+ int rc = VINF_SUCCESS;
+ if (pThis->fWritePending)
+ rc = rtSerialPortWriteCheckCompletion(pThis);
+ if (rc == VINF_SUCCESS)
+ {
+ const uint8_t *pbBuf = (const uint8_t *)pvBuf;
+
+ while ( cbToWrite > 0
+ && RT_SUCCESS(rc))
+ {
+ BOOL fSucc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(fSucc == TRUE); RT_NOREF(fSucc);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hDev, pbBuf,
+ cbToWrite <= ~(DWORD)0 ? (DWORD)cbToWrite : ~(DWORD)0,
+ &cbWritten, &pThis->OverlappedWrite))
+ {
+ if (pcbWritten)
+ {
+ *pcbWritten = cbWritten;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ DWORD dwWait = WaitForSingleObject(pThis->OverlappedWrite.hEvent, INFINITE);
+ if (dwWait == WAIT_OBJECT_0)
+ {
+ if (GetOverlappedResult(pThis->hDev, &pThis->OverlappedWrite, &cbWritten, TRUE /*fWait*/))
+ {
+ if (pcbWritten)
+ {
+ *pcbWritten = cbWritten;
+ break;
+ }
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ {
+ Assert(dwWait == WAIT_FAILED);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ if (RT_SUCCESS(rc))
+ {
+ cbToWrite -= cbWritten;
+ pbBuf += cbWritten;
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortWriteNB(RTSERIALPORT hSerialPort, const void *pvBuf, size_t cbToWrite, size_t *pcbWritten)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
+ AssertReturn(cbToWrite > 0, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbWritten, VERR_INVALID_POINTER);
+
+ /* If I/O is pending, check if it has completed. */
+ int rc = VINF_SUCCESS;
+ if (pThis->fWritePending)
+ rc = rtSerialPortWriteCheckCompletion(pThis);
+ if (rc == VINF_SUCCESS)
+ {
+ Assert(!pThis->fWritePending);
+
+ /* Do the bounce buffering. */
+ if ( pThis->cbBounceBufAlloc < cbToWrite
+ && pThis->cbBounceBufAlloc < RTSERIALPORT_NT_SIZE)
+ {
+ if (cbToWrite > RTSERIALPORT_NT_SIZE)
+ cbToWrite = RTSERIALPORT_NT_SIZE;
+ void *pv = RTMemRealloc(pThis->pbBounceBuf, RT_ALIGN_Z(cbToWrite, _1K));
+ if (pv)
+ {
+ pThis->pbBounceBuf = (uint8_t *)pv;
+ pThis->cbBounceBufAlloc = RT_ALIGN_Z(cbToWrite, _1K);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else if (cbToWrite > RTSERIALPORT_NT_SIZE)
+ cbToWrite = RTSERIALPORT_NT_SIZE;
+ if (RT_SUCCESS(rc) && cbToWrite)
+ {
+ memcpy(pThis->pbBounceBuf, pvBuf, cbToWrite);
+ pThis->cbBounceBufUsed = (uint32_t)cbToWrite;
+
+ /* Submit the write. */
+ rc = ResetEvent(pThis->OverlappedWrite.hEvent); Assert(rc == TRUE);
+ DWORD cbWritten = 0;
+ if (WriteFile(pThis->hDev, pThis->pbBounceBuf, (DWORD)pThis->cbBounceBufUsed,
+ &cbWritten, &pThis->OverlappedWrite))
+ {
+ *pcbWritten = RT_MIN(cbWritten, cbToWrite); /* paranoia^3 */
+ rc = VINF_SUCCESS;
+ }
+ else if (GetLastError() == ERROR_IO_PENDING)
+ {
+ *pcbWritten = cbToWrite;
+ pThis->fWritePending = true;
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+ }
+ else if (RT_SUCCESS(rc))
+ *pcbWritten = 0;
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortCfgQueryCurrent(RTSERIALPORT hSerialPort, PRTSERIALPORTCFG pCfg)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ pCfg->uBaudRate = pThis->PortCfg.BaudRate;
+ switch (pThis->PortCfg.Parity)
+ {
+ case NOPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_NONE;
+ break;
+ case EVENPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_EVEN;
+ break;
+ case ODDPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_ODD;
+ break;
+ case MARKPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_MARK;
+ break;
+ case SPACEPARITY:
+ pCfg->enmParity = RTSERIALPORTPARITY_SPACE;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ switch (pThis->PortCfg.ByteSize)
+ {
+ case 5:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_5BITS;
+ break;
+ case 6:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_6BITS;
+ break;
+ case 7:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_7BITS;
+ break;
+ case 8:
+ pCfg->enmDataBitCount = RTSERIALPORTDATABITS_8BITS;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ switch (pThis->PortCfg.StopBits)
+ {
+ case ONESTOPBIT:
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONE;
+ break;
+ case ONE5STOPBITS:
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_ONEPOINTFIVE;
+ break;
+ case TWOSTOPBITS:
+ pCfg->enmStopBitCount = RTSERIALPORTSTOPBITS_TWO;
+ break;
+ default:
+ AssertFailed();
+ return VERR_INTERNAL_ERROR;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortCfgSet(RTSERIALPORT hSerialPort, PCRTSERIALPORTCFG pCfg, PRTERRINFO pErrInfo)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ RT_NOREF(pErrInfo);
+
+ DCB DcbNew;
+ memcpy(&DcbNew, &pThis->PortCfg, sizeof(DcbNew));
+ DcbNew.BaudRate = pCfg->uBaudRate;
+
+ switch (pCfg->enmParity)
+ {
+ case RTSERIALPORTPARITY_NONE:
+ DcbNew.Parity = NOPARITY;
+ break;
+ case RTSERIALPORTPARITY_EVEN:
+ DcbNew.Parity = EVENPARITY;
+ break;
+ case RTSERIALPORTPARITY_ODD:
+ DcbNew.Parity = ODDPARITY;
+ break;
+ case RTSERIALPORTPARITY_MARK:
+ DcbNew.Parity = MARKPARITY;
+ break;
+ case RTSERIALPORTPARITY_SPACE:
+ DcbNew.Parity = SPACEPARITY;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ switch (pCfg->enmDataBitCount)
+ {
+ case RTSERIALPORTDATABITS_5BITS:
+ DcbNew.ByteSize = 5;
+ break;
+ case RTSERIALPORTDATABITS_6BITS:
+ DcbNew.ByteSize = 6;
+ break;
+ case RTSERIALPORTDATABITS_7BITS:
+ DcbNew.ByteSize = 7;
+ break;
+ case RTSERIALPORTDATABITS_8BITS:
+ DcbNew.ByteSize = 8;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ switch (pCfg->enmStopBitCount)
+ {
+ case RTSERIALPORTSTOPBITS_ONE:
+ DcbNew.StopBits = ONESTOPBIT;
+ break;
+ case RTSERIALPORTSTOPBITS_ONEPOINTFIVE:
+ AssertReturn(pCfg->enmDataBitCount == RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
+ DcbNew.StopBits = ONE5STOPBITS;
+ break;
+ case RTSERIALPORTSTOPBITS_TWO:
+ AssertReturn(pCfg->enmDataBitCount != RTSERIALPORTDATABITS_5BITS, VERR_INVALID_PARAMETER);
+ DcbNew.StopBits = TWOSTOPBITS;
+ break;
+ default:
+ AssertFailedReturn(VERR_INVALID_PARAMETER);
+ }
+
+ int rc = VINF_SUCCESS;
+ if (!SetCommState(pThis->hDev, &DcbNew))
+ rc = RTErrConvertFromWin32(GetLastError());
+ else
+ memcpy(&pThis->PortCfg, &DcbNew, sizeof(DcbNew));
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPoll(RTSERIALPORT hSerialPort, uint32_t fEvtMask, uint32_t *pfEvtsRecv,
+ RTMSINTERVAL msTimeout)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!(fEvtMask & ~RTSERIALPORT_EVT_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pfEvtsRecv, VERR_INVALID_POINTER);
+
+ *pfEvtsRecv = 0;
+
+ int rc = VINF_SUCCESS;
+ if (fEvtMask != pThis->fEvtMask)
+ {
+ rc = rtSerialPortWinUpdateEvtMask(pThis, fEvtMask);
+ if ( RT_SUCCESS(rc)
+ && pThis->fEvtQueryPending)
+ {
+ /*
+ * Setting a new event mask lets the WaitCommEvent() call finish immediately,
+ * so clean up and process any events here.
+ */
+ rc = rtSerialPortEvtWaitWorker(pThis, 1);
+ AssertRC(rc);
+
+ if (pThis->dwEventMask != 0)
+ {
+ pThis->fEvtQueryPending = false;
+ return rtSerialPortEvtFlagsProcess(pThis, pThis->dwEventMask, pfEvtsRecv);
+ }
+ }
+ }
+
+ /*
+ * EV_RXCHAR is triggered only if a byte is received after the event mask is set,
+ * not if there is already something in the input buffer. Thatswhy we check the input
+ * buffer for any stored data and the output buffer whether it is empty and return
+ * the appropriate flags.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ COMSTAT ComStat; RT_ZERO(ComStat);
+ if (!ClearCommError(pThis->hDev, NULL, &ComStat))
+ return RTErrConvertFromWin32(GetLastError());
+
+ /* Check whether data is already waiting in the input buffer. */
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_RX)
+ && ComStat.cbInQue > 0)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_RX;
+
+ /* Check whether the output buffer is empty. */
+ if ( (fEvtMask & RTSERIALPORT_EVT_F_DATA_TX)
+ && ComStat.cbOutQue == 0)
+ *pfEvtsRecv |= RTSERIALPORT_EVT_F_DATA_TX;
+
+ /* Return if there is at least one event. */
+ if (*pfEvtsRecv != 0)
+ return VINF_SUCCESS;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Set up a new event wait if there is none pending. */
+ if (!pThis->fEvtQueryPending)
+ {
+ RT_ZERO(pThis->OverlappedEvt);
+ pThis->OverlappedEvt.hEvent = pThis->hEvtDev;
+
+ pThis->dwEventMask = 0;
+ pThis->fEvtQueryPending = true;
+ if (!WaitCommEvent(pThis->hDev, &pThis->dwEventMask, &pThis->OverlappedEvt))
+ {
+ DWORD dwRet = GetLastError();
+ if (dwRet == ERROR_IO_PENDING)
+ rc = VINF_SUCCESS;
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ pThis->fEvtQueryPending = false;
+ }
+ }
+ else
+ pThis->fEvtQueryPending = false;
+ }
+
+ if ( RT_SUCCESS(rc)
+ && pThis->fEvtQueryPending)
+ rc = rtSerialPortEvtWaitWorker(pThis, msTimeout);
+
+ if (RT_SUCCESS(rc))
+ {
+ pThis->fEvtQueryPending = false;
+ rc = rtSerialPortEvtFlagsProcess(pThis, pThis->dwEventMask, pfEvtsRecv);
+ }
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortEvtPollInterrupt(RTSERIALPORT hSerialPort)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ if (!SetEvent(pThis->hEvtIntr))
+ return RTErrConvertFromWin32(GetLastError());
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTSerialPortChgBreakCondition(RTSERIALPORT hSerialPort, bool fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ BOOL fSucc = FALSE;
+ if (fSet)
+ fSucc = SetCommBreak(pThis->hDev);
+ else
+ fSucc = ClearCommBreak(pThis->hDev);
+
+ int rc = VINF_SUCCESS;
+ if (!fSucc)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortChgStatusLines(RTSERIALPORT hSerialPort, uint32_t fClear, uint32_t fSet)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+
+ BOOL fSucc = TRUE;
+ if (fSet & RTSERIALPORT_CHG_STS_LINES_F_DTR)
+ fSucc = EscapeCommFunction(pThis->hDev, SETDTR);
+ if ( fSucc
+ && (fSet & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ fSucc = EscapeCommFunction(pThis->hDev, SETRTS);
+
+ if ( fSucc
+ && (fClear & RTSERIALPORT_CHG_STS_LINES_F_DTR))
+ fSucc = EscapeCommFunction(pThis->hDev, CLRDTR);
+ if ( fSucc
+ && (fClear & RTSERIALPORT_CHG_STS_LINES_F_RTS))
+ fSucc = EscapeCommFunction(pThis->hDev, CLRRTS);
+
+ int rc = VINF_SUCCESS;
+ if (!fSucc)
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTSerialPortQueryStatusLines(RTSERIALPORT hSerialPort, uint32_t *pfStsLines)
+{
+ PRTSERIALPORTINTERNAL pThis = hSerialPort;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSERIALPORT_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pfStsLines, VERR_INVALID_POINTER);
+
+ *pfStsLines = 0;
+
+ int rc = VINF_SUCCESS;
+ DWORD fStsLinesQueried = 0;
+
+ /* Get the new state */
+ if (GetCommModemStatus(pThis->hDev, &fStsLinesQueried))
+ {
+ *pfStsLines |= (fStsLinesQueried & MS_RLSD_ON) ? RTSERIALPORT_STS_LINE_DCD : 0;
+ *pfStsLines |= (fStsLinesQueried & MS_RING_ON) ? RTSERIALPORT_STS_LINE_RI : 0;
+ *pfStsLines |= (fStsLinesQueried & MS_DSR_ON) ? RTSERIALPORT_STS_LINE_DSR : 0;
+ *pfStsLines |= (fStsLinesQueried & MS_CTS_ON) ? RTSERIALPORT_STS_LINE_CTS : 0;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/shmem-win.cpp b/src/VBox/Runtime/r3/win/shmem-win.cpp
new file mode 100644
index 00000000..b97fc796
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/shmem-win.cpp
@@ -0,0 +1,473 @@
+/* $Id: shmem-win.cpp $ */
+/** @file
+ * IPRT - Named shared memory object, Windows Implementation.
+ */
+
+/*
+ * Copyright (C) 2018-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/shmem.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/cdefs.h>
+#include <iprt/err.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/magics.h"
+#include "internal/path.h"
+#include "internal-r3-win.h" /* For g_enmWinVer + kRTWinOSType_XXX */
+
+/*
+ * Define values ourselves in case the compiling host is too old.
+ * See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createfilemappinga
+ * for when these were introduced.
+ */
+#ifndef PAGE_EXECUTE_READ
+# define PAGE_EXECUTE_READ 0x20
+#endif
+#ifndef PAGE_EXECUTE_READWRITE
+# define PAGE_EXECUTE_READWRITE 0x40
+#endif
+#ifndef PAGE_EXECUTE_WRITECOPY
+# define PAGE_EXECUTE_WRITECOPY 0x80
+#endif
+#ifndef FILE_MAP_EXECUTE
+# define FILE_MAP_EXECUTE 0x20
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Shared memory object mapping descriptor.
+ */
+typedef struct RTSHMEMMAPPINGDESC
+{
+ /** Number of references held to this mapping, 0 if the descriptor is free. */
+ volatile uint32_t cMappings;
+ /** Pointer to the region mapping. */
+ void *pvMapping;
+ /** Start offset */
+ size_t offRegion;
+ /** Size of the region. */
+ size_t cbRegion;
+ /** Access flags for this region .*/
+ uint32_t fFlags;
+} RTSHMEMMAPPINGDESC;
+/** Pointer to a shared memory object mapping descriptor. */
+typedef RTSHMEMMAPPINGDESC *PRTSHMEMMAPPINGDESC;
+/** Pointer to a constant shared memory object mapping descriptor. */
+typedef const RTSHMEMMAPPINGDESC *PCRTSHMEMMAPPINGDESC;
+
+
+/**
+ * Internal shared memory object state.
+ */
+typedef struct RTSHMEMINT
+{
+ /** Magic value (RTSHMEM_MAGIC). */
+ uint32_t u32Magic;
+ /** Flag whether this instance created the named shared memory object. */
+ bool fCreate;
+ /** Handle to the underlying mapping object. */
+ HANDLE hShmObj;
+ /** Size of the mapping object in bytes. */
+ size_t cbMax;
+ /** Overall number of mappings active for this shared memory object. */
+ volatile uint32_t cMappings;
+ /** Maximum number of mapping descriptors allocated. */
+ uint32_t cMappingDescsMax;
+ /** Number of mapping descriptors used. */
+ volatile uint32_t cMappingDescsUsed;
+ /** Array of mapping descriptors - variable in size. */
+ RTSHMEMMAPPINGDESC aMappingDescs[1];
+} RTSHMEMINT;
+/** Pointer to the internal shared memory object state. */
+typedef RTSHMEMINT *PRTSHMEMINT;
+
+
+
+
+/**
+ * Returns a mapping descriptor matching the given region properties or NULL if none was found.
+ *
+ * @returns Pointer to the matching mapping descriptor or NULL if not found.
+ * @param pThis Pointer to the shared memory object instance.
+ * @param offRegion Offset into the shared memory object to start mapping at.
+ * @param cbRegion Size of the region to map.
+ * @param fFlags Desired properties of the mapped region, combination of RTSHMEM_MAP_F_* defines.
+ */
+DECLINLINE(PRTSHMEMMAPPINGDESC) rtShMemMappingDescFindByProp(PRTSHMEMINT pThis, size_t offRegion, size_t cbRegion, uint32_t fFlags)
+{
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax; i++)
+ {
+ if ( pThis->aMappingDescs[i].offRegion == offRegion
+ && pThis->aMappingDescs[i].cbRegion == cbRegion
+ && pThis->aMappingDescs[i].fFlags == fFlags)
+ return &pThis->aMappingDescs[i];
+ }
+
+ return NULL;
+}
+
+
+RTDECL(int) RTShMemOpen(PRTSHMEM phShMem, const char *pszName, uint32_t fFlags, size_t cbMax, uint32_t cMappingsHint)
+{
+ AssertPtrReturn(phShMem, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszName, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSHMEM_O_F_VALID_MASK), VERR_INVALID_PARAMETER);
+ AssertReturn(cMappingsHint < 64, VERR_OUT_OF_RANGE);
+ AssertReturn(cbMax > 0 || !(fFlags & RTSHMEM_O_F_CREATE), VERR_NOT_SUPPORTED);
+
+ if (fFlags & RTSHMEM_O_F_TRUNCATE)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * The executable access was introduced with Windows XP SP2 and Windows Server 2003 SP1,
+ * PAGE_EXECUTE_WRITECOPY was not available until Windows Vista SP1.
+ * Allow execute mappings only starting from Windows 7 to keep the checks simple here (lazy coder).
+ */
+ if ( (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
+ && g_enmWinVer < kRTWinOSType_7)
+ return VERR_NOT_SUPPORTED;
+
+ cMappingsHint = cMappingsHint == 0 ? 5 : cMappingsHint;
+ int rc = VINF_SUCCESS;
+ PRTSHMEMINT pThis = (PRTSHMEMINT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTSHMEMINT, aMappingDescs[cMappingsHint]));
+ if (RT_LIKELY(pThis))
+ {
+ pThis->u32Magic = RTSHMEM_MAGIC;
+ /*pThis->fCreate = false; */
+ /*pThis->cMappings = 0; */
+ pThis->cMappingDescsMax = cMappingsHint;
+ /*pThis->cMappingDescsUsed = 0; */
+ /* Construct the filename, always use the local namespace, global requires special privileges. */
+ char szName[RTPATH_MAX];
+ ssize_t cch = RTStrPrintf2(&szName[0], sizeof(szName), "Local\\%s", pszName);
+ if (cch > 0)
+ {
+ PRTUTF16 pwszName = NULL;
+ rc = RTStrToUtf16Ex(&szName[0], RTSTR_MAX, &pwszName, 0, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (fFlags & RTSHMEM_O_F_CREATE)
+ {
+#if HC_ARCH_BITS == 64
+ DWORD dwSzMaxHigh = cbMax >> 32;
+#elif HC_ARCH_BITS == 32
+ DWORD dwSzMaxHigh = 0;
+#else
+# error "Port me"
+#endif
+ DWORD dwSzMaxLow = cbMax & UINT32_C(0xffffffff);
+ DWORD fProt = 0;
+
+ if (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
+ {
+ if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ)
+ fProt |= PAGE_EXECUTE_READ;
+ else
+ fProt |= PAGE_EXECUTE_READWRITE;
+ }
+ else
+ {
+ if ((fFlags & RTSHMEM_O_F_READWRITE) == RTSHMEM_O_F_READ)
+ fProt |= PAGE_READONLY;
+ else
+ fProt |= PAGE_READWRITE;
+ }
+ pThis->hShmObj = CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, fProt,
+ dwSzMaxHigh, dwSzMaxLow, pwszName);
+ }
+ else
+ {
+ DWORD fProt = SECTION_QUERY;
+ if (fFlags & RTSHMEM_O_F_MAYBE_EXEC)
+ fProt |= FILE_MAP_EXECUTE;
+ if (fFlags & RTSHMEM_O_F_READ)
+ fProt |= FILE_MAP_READ;
+ if (fFlags & RTSHMEM_O_F_WRITE)
+ fProt |= FILE_MAP_WRITE;
+
+ pThis->hShmObj = OpenFileMappingW(fProt, FALSE, pwszName);
+ }
+ RTUtf16Free(pwszName);
+ if (pThis->hShmObj != NULL)
+ {
+ *phShMem = pThis;
+ return VINF_SUCCESS;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemClose(RTSHMEM hShMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
+
+ int rc = VINF_SUCCESS;
+ if (CloseHandle(pThis->hShmObj))
+ {
+ pThis->u32Magic = RTSHMEM_MAGIC_DEAD;
+ RTMemFree(pThis);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemDelete(const char *pszName)
+{
+ AssertPtrReturn(pszName, VERR_INVALID_POINTER);
+ AssertReturn(*pszName != '\0', VERR_INVALID_PARAMETER);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(uint32_t) RTShMemRefCount(RTSHMEM hShMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, 0);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, 0);
+
+ return pThis->cMappings;
+}
+
+
+RTDECL(int) RTShMemSetSize(RTSHMEM hShMem, size_t cbMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertReturn(!pThis->cMappings, VERR_INVALID_STATE);
+ AssertReturn(cbMem, VERR_NOT_SUPPORTED);
+
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTDECL(int) RTShMemQuerySize(RTSHMEM hShMem, size_t *pcbMem)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pcbMem, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ SECTION_BASIC_INFORMATION SecInf;
+ SIZE_T cbRet;
+ NTSTATUS rcNt = NtQuerySection(pThis->hShmObj, SectionBasicInformation, &SecInf, sizeof(SecInf), &cbRet);
+ if (NT_SUCCESS(rcNt))
+ {
+ AssertReturn(cbRet == sizeof(SecInf), VERR_INTERNAL_ERROR);
+#if HC_ARCH_BITS == 32
+ AssertReturn(SecInf.MaximumSize.HighPart == 0, VERR_INTERNAL_ERROR_2);
+ *pcbMem = SecInf.MaximumSize.LowPart;
+#elif HC_ARCH_BITS == 64
+ *pcbMem = SecInf.MaximumSize.QuadPart;
+#else
+# error "Port me"
+#endif
+ }
+ else
+ rc = RTErrConvertFromNtStatus(rcNt);
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemMapRegion(RTSHMEM hShMem, size_t offRegion, size_t cbRegion, uint32_t fFlags, void **ppv)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(ppv, VERR_INVALID_PARAMETER);
+ AssertReturn(!(fFlags & ~RTSHMEM_MAP_F_VALID_MASK), VERR_INVALID_PARAMETER);
+
+ /* See comment in RTShMemOpen(). */
+ if ( (fFlags & RTSHMEM_MAP_F_EXEC)
+ && g_enmWinVer < kRTWinOSType_7)
+ return VERR_NOT_SUPPORTED;
+
+ /* Try to find a mapping with compatible parameters first. */
+ PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
+ for (uint32_t iTry = 0; iTry < 10; iTry++)
+ {
+ pMappingDesc = rtShMemMappingDescFindByProp(pThis, offRegion, cbRegion, fFlags);
+ if (!pMappingDesc)
+ break;
+
+ /* Increase the mapping count and check that the region is still accessible by us. */
+ if ( ASMAtomicIncU32(&pMappingDesc->cMappings) > 1
+ && pMappingDesc->offRegion == offRegion
+ && pMappingDesc->cbRegion == cbRegion
+ && pMappingDesc->fFlags == fFlags)
+ break;
+ /* Mapping was freed inbetween, next round. */
+ }
+
+ int rc = VINF_SUCCESS;
+ if (!pMappingDesc)
+ {
+ /* Find an empty region descriptor and map the region. */
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
+ {
+ if (!pThis->aMappingDescs[i].cMappings)
+ {
+ pMappingDesc = &pThis->aMappingDescs[i];
+
+ /* Try to grab this one. */
+ if (ASMAtomicIncU32(&pMappingDesc->cMappings) == 1)
+ break;
+
+ /* Somebody raced us, drop reference and continue. */
+ ASMAtomicDecU32(&pMappingDesc->cMappings);
+ pMappingDesc = NULL;
+ }
+ }
+
+ if (RT_LIKELY(pMappingDesc))
+ {
+ /* Try to map it. */
+ DWORD fProt = 0;
+ DWORD offLow = offRegion & UINT32_C(0xffffffff);
+#if HC_ARCH_BITS == 64
+ DWORD offHigh = offRegion >> 32;
+#elif HC_ARCH_BITS == 32
+ DWORD offHigh = 0;
+#else
+# error "Port me"
+#endif
+ if (fFlags & RTSHMEM_MAP_F_READ)
+ fProt |= FILE_MAP_READ;
+ if (fFlags & RTSHMEM_MAP_F_WRITE)
+ fProt |= FILE_MAP_WRITE;
+ if (fFlags & RTSHMEM_MAP_F_EXEC)
+ fProt |= FILE_MAP_EXECUTE;
+ if (fFlags & RTSHMEM_MAP_F_COW)
+ fProt |= FILE_MAP_COPY;
+
+ void *pv = MapViewOfFile(pThis->hShmObj, fProt, offHigh, offLow, cbRegion);
+ if (pv != NULL)
+ {
+ pMappingDesc->pvMapping = pv;
+ pMappingDesc->offRegion = offRegion;
+ pMappingDesc->cbRegion = cbRegion;
+ pMappingDesc->fFlags = fFlags;
+ }
+ else
+ {
+ rc = RTErrConvertFromWin32(GetLastError());
+ ASMAtomicDecU32(&pMappingDesc->cMappings);
+ }
+ }
+ else
+ rc = VERR_SHMEM_MAXIMUM_MAPPINGS_REACHED;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppv = pMappingDesc->pvMapping;
+ ASMAtomicIncU32(&pThis->cMappings);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTShMemUnmapRegion(RTSHMEM hShMem, void *pv)
+{
+ PRTSHMEMINT pThis = hShMem;
+ AssertPtrReturn(pThis, VERR_INVALID_PARAMETER);
+ AssertReturn(pThis->u32Magic == RTSHMEM_MAGIC, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pv, VERR_INVALID_PARAMETER);
+
+ /* Find the mapping descriptor by the given region address. */
+ PRTSHMEMMAPPINGDESC pMappingDesc = NULL;
+ for (uint32_t i = 0; i < pThis->cMappingDescsMax && !pMappingDesc; i++)
+ {
+ if (pThis->aMappingDescs[i].pvMapping == pv)
+ {
+ pMappingDesc = &pThis->aMappingDescs[i];
+ break;
+ }
+ }
+
+ AssertPtrReturn(pMappingDesc, VERR_INVALID_PARAMETER);
+
+ int rc = VINF_SUCCESS;
+ if (!ASMAtomicDecU32(&pMappingDesc->cMappings))
+ {
+ /* Last mapping of this region was unmapped, so do the real unmapping now. */
+ if (UnmapViewOfFile(pv))
+ {
+ ASMAtomicDecU32(&pThis->cMappingDescsUsed);
+ ASMAtomicDecU32(&pThis->cMappings);
+ }
+ else
+ {
+ ASMAtomicIncU32(&pMappingDesc->cMappings);
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ }
+
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/symlink-win.cpp b/src/VBox/Runtime/r3/win/symlink-win.cpp
new file mode 100644
index 00000000..a0c5f1e2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/symlink-win.cpp
@@ -0,0 +1,367 @@
+/* $Id: symlink-win.cpp $ */
+/** @file
+ * IPRT - Symbolic Links, Windows.
+ */
+
+/*
+ * Copyright (C) 2010-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_SYMLINK
+#include <iprt/win/windows.h>
+
+#include <iprt/symlink.h>
+#include "internal-r3-win.h"
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/log.h>
+#include <iprt/path.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/utf16.h>
+#include "internal/path.h"
+
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct MY_REPARSE_DATA_BUFFER
+{
+ ULONG ReparseTag;
+#define MY_IO_REPARSE_TAG_SYMLINK 0xa000000c
+#define MY_IO_REPARSE_TAG_MOUNT_POINT 0xa0000003
+
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union
+ {
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+#define MY_SYMLINK_FLAG_RELATIVE 1
+ WCHAR PathBuffer[1];
+ } SymbolicLinkReparseBuffer;
+ struct
+ {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPointReparseBuffer;
+ struct
+ {
+ UCHAR DataBuffer[1];
+ } GenericReparseBuffer;
+ };
+} MY_REPARSE_DATA_BUFFER;
+#define MY_FSCTL_GET_REPARSE_POINT CTL_CODE(FILE_DEVICE_FILE_SYSTEM, 42, METHOD_BUFFERED, FILE_ANY_ACCESS)
+
+
+RTDECL(bool) RTSymlinkExists(const char *pszSymlink)
+{
+ bool fRc = false;
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
+
+ LogFlow(("RTSymlinkExists(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
+ return fRc;
+}
+
+
+RTDECL(bool) RTSymlinkIsDangling(const char *pszSymlink)
+{
+ bool fRc = false;
+ RTFSOBJINFO ObjInfo;
+ int rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
+ if (RT_SUCCESS(rc))
+ {
+ fRc = RTFS_IS_SYMLINK(ObjInfo.Attr.fMode);
+ if (fRc)
+ {
+ rc = RTPathQueryInfoEx(pszSymlink, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
+ fRc = !RT_SUCCESS_NP(rc);
+ }
+ }
+
+ LogFlow(("RTSymlinkIsDangling(%p={%s}): returns %RTbool\n", pszSymlink, pszSymlink, fRc));
+ return fRc;
+}
+
+
+RTDECL(int) RTSymlinkCreate(const char *pszSymlink, const char *pszTarget, RTSYMLINKTYPE enmType, uint32_t fCreate)
+{
+ /*
+ * Validate the input.
+ */
+ AssertReturn(enmType > RTSYMLINKTYPE_INVALID && enmType < RTSYMLINKTYPE_END, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszSymlink, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszTarget, VERR_INVALID_POINTER);
+ RT_NOREF_PV(fCreate);
+
+ /*
+ * Resolve the API.
+ */
+ typedef BOOLEAN (WINAPI *PFNCREATESYMBOLICLINKW)(LPCWSTR, LPCWSTR, DWORD);
+ static PFNCREATESYMBOLICLINKW s_pfnCreateSymbolicLinkW = NULL;
+ static bool s_fTried = FALSE;
+ if (!s_fTried)
+ {
+ PFNCREATESYMBOLICLINKW pfn = (PFNCREATESYMBOLICLINKW)GetProcAddress(g_hModKernel32, "CreateSymbolicLinkW");
+ if (pfn)
+ s_pfnCreateSymbolicLinkW = pfn;
+ s_fTried = true;
+ }
+ if (!s_pfnCreateSymbolicLinkW)
+ {
+ LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns VERR_NOT_SUPPORTED - Windows API not found\n",
+ pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate));
+ return VERR_NOT_SUPPORTED;
+ }
+
+ /*
+ * Convert the paths.
+ */
+ PRTUTF16 pwszNativeSymlink;
+ int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ PRTUTF16 pwszNativeTarget;
+ rc = RTPathWinFromUtf8(&pwszNativeTarget, pszTarget, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /* The link target path must use backslashes to work reliably. */
+ RTUTF16 wc;
+ PRTUTF16 pwsz = pwszNativeTarget;
+ while ((wc = *pwsz) != '\0')
+ {
+ if (wc == '/')
+ *pwsz = '\\';
+ pwsz++;
+ }
+
+ /*
+ * Massage the target path, determin the link type.
+ */
+ size_t cchTarget = strlen(pszTarget);
+ size_t cchVolSpecTarget = rtPathVolumeSpecLen(pszTarget);
+#if 0 /* looks like this isn't needed after all. That makes everything much simper :-) */
+ if ( cchTarget > RT_MIN(cchVolSpecTarget, 1)
+ && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
+ {
+ size_t cwcNativeTarget = RTUtf16Len(pwszNativeTarget);
+ size_t offFromEnd = 1;
+ while ( offFromEnd < cchTarget
+ && cchTarget - offFromEnd >= cchVolSpecTarget
+ && RTPATH_IS_SLASH(pszTarget[cchTarget - offFromEnd]))
+ {
+ Assert(offFromEnd < cwcNativeTarget);
+ pwszNativeTarget[cwcNativeTarget - offFromEnd] = 0;
+ offFromEnd++;
+ }
+ }
+#endif
+
+ if (enmType == RTSYMLINKTYPE_UNKNOWN)
+ {
+ if ( cchTarget > cchVolSpecTarget
+ && RTPATH_IS_SLASH(pszTarget[cchTarget - 1]))
+ enmType = RTSYMLINKTYPE_DIR;
+ else if (cchVolSpecTarget)
+ {
+ /** @todo this is subject to sharing violations. */
+ DWORD dwAttr = GetFileAttributesW(pwszNativeTarget);
+ if ( dwAttr != INVALID_FILE_ATTRIBUTES
+ && (dwAttr & FILE_ATTRIBUTE_DIRECTORY))
+ enmType = RTSYMLINKTYPE_DIR;
+ }
+ else
+ {
+ /** @todo Join the symlink directory with the target and
+ * look up the attributes on that. -lazy bird. */
+ }
+ }
+
+ /*
+ * Create the link.
+ */
+ if (s_pfnCreateSymbolicLinkW(pwszNativeSymlink, pwszNativeTarget, enmType == RTSYMLINKTYPE_DIR))
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+
+ RTPathWinFree(pwszNativeTarget);
+ }
+ RTPathWinFree(pwszNativeSymlink);
+ }
+
+ LogFlow(("RTSymlinkCreate(%p={%s}, %p={%s}, %d, %#x): returns %Rrc\n", pszSymlink, pszSymlink, pszTarget, pszTarget, enmType, fCreate, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkDelete(const char *pszSymlink, uint32_t fDelete)
+{
+ RT_NOREF_PV(fDelete);
+
+ /*
+ * Convert the path.
+ */
+ PRTUTF16 pwszNativeSymlink;
+ int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We have to use different APIs depending on whether this is a
+ * directory or file link. This means we're subject to one more race
+ * than on posix at the moment. We could probably avoid this though,
+ * if we wanted to go talk with the native API layer below Win32...
+ */
+ DWORD dwAttr = GetFileAttributesW(pwszNativeSymlink);
+ if (dwAttr != INVALID_FILE_ATTRIBUTES)
+ {
+ if (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT)
+ {
+ BOOL fRc;
+ if (dwAttr & FILE_ATTRIBUTE_DIRECTORY)
+ fRc = RemoveDirectoryW(pwszNativeSymlink);
+ else
+ fRc = DeleteFileW(pwszNativeSymlink);
+ if (fRc)
+ rc = VINF_SUCCESS;
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ }
+ else
+ rc = VERR_NOT_SYMLINK;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszNativeSymlink);
+ }
+
+ LogFlow(("RTSymlinkDelete(%p={%s}, %#x): returns %Rrc\n", pszSymlink, pszSymlink, fDelete, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkRead(const char *pszSymlink, char *pszTarget, size_t cbTarget, uint32_t fRead)
+{
+ RT_NOREF_PV(fRead);
+
+ char *pszMyTarget;
+ int rc = RTSymlinkReadA(pszSymlink, &pszMyTarget);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTStrCopy(pszTarget, cbTarget, pszMyTarget);
+ RTStrFree(pszMyTarget);
+ }
+ LogFlow(("RTSymlinkRead(%p={%s}): returns %Rrc\n", pszSymlink, pszSymlink, rc));
+ return rc;
+}
+
+
+RTDECL(int) RTSymlinkReadA(const char *pszSymlink, char **ppszTarget)
+{
+ AssertPtr(ppszTarget);
+ PRTUTF16 pwszNativeSymlink;
+ int rc = RTPathWinFromUtf8(&pwszNativeSymlink, pszSymlink, 0 /*fFlags*/);
+ if (RT_SUCCESS(rc))
+ {
+ HANDLE hSymlink = CreateFileW(pwszNativeSymlink,
+ GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ NULL,
+ OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS,
+ NULL);
+ if (hSymlink != INVALID_HANDLE_VALUE)
+ {
+ DWORD cbReturned = 0;
+ union
+ {
+ MY_REPARSE_DATA_BUFFER Buf;
+ uint8_t abBuf[16*_1K + sizeof(WCHAR)];
+ } u;
+ if (DeviceIoControl(hSymlink,
+ MY_FSCTL_GET_REPARSE_POINT,
+ NULL /*pInBuffer */,
+ 0 /*cbInBuffer */,
+ &u.Buf,
+ sizeof(u) - sizeof(WCHAR),
+ &cbReturned,
+ NULL /*pOverlapped*/))
+ {
+ if (u.Buf.ReparseTag == MY_IO_REPARSE_TAG_SYMLINK)
+ {
+ PWCHAR pwszTarget = &u.Buf.SymbolicLinkReparseBuffer.PathBuffer[0];
+ pwszTarget += u.Buf.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2;
+ pwszTarget[u.Buf.SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0;
+ if ( !(u.Buf.SymbolicLinkReparseBuffer.Flags & MY_SYMLINK_FLAG_RELATIVE)
+ && pwszTarget[0] == '\\'
+ && pwszTarget[1] == '?'
+ && pwszTarget[2] == '?'
+ && pwszTarget[3] == '\\'
+ && pwszTarget[4] != 0
+ )
+ pwszTarget += 4;
+ rc = RTUtf16ToUtf8(pwszTarget, ppszTarget);
+ }
+ else
+ rc = VERR_NOT_SYMLINK;
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ CloseHandle(hSymlink);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTPathWinFree(pwszNativeSymlink);
+ }
+
+ if (RT_SUCCESS(rc))
+ LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc *ppszTarget=%p:{%s}\n", pszSymlink, pszSymlink, ppszTarget, rc, *ppszTarget, *ppszTarget));
+ else
+ LogFlow(("RTSymlinkReadA(%p={%s},%p): returns %Rrc\n", pszSymlink, pszSymlink, ppszTarget, rc));
+ return rc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp b/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp
new file mode 100644
index 00000000..30088667
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/system-get-nt-xxx-win.cpp
@@ -0,0 +1,68 @@
+/* $Id: system-get-nt-xxx-win.cpp $ */
+/** @file
+ * IPRT - RTSystemQueryOSInfo, generic stub.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/win/windows.h>
+
+#include "internal-r3-win.h"
+#include <iprt/system.h>
+#include <iprt/assert.h>
+
+
+RTDECL(uint32_t) RTSystemGetNtBuildNo(void)
+{
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0);
+ return g_WinOsInfoEx.dwBuildNumber;
+}
+
+
+RTDECL(uint64_t) RTSystemGetNtVersion(void)
+{
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0);
+ return RTSYSTEM_MAKE_NT_VERSION(g_WinOsInfoEx.dwMajorVersion, g_WinOsInfoEx.dwMinorVersion, g_WinOsInfoEx.dwBuildNumber);
+}
+
+
+RTDECL(uint8_t) RTSystemGetNtProductType(void)
+{
+ Assert(g_WinOsInfoEx.dwOSVersionInfoSize > 0);
+ return g_WinOsInfoEx.wProductType; /* It's a byte, not a word as 'w' normally indicates. (Baka Maikurosofuto!) */
+}
+
diff --git a/src/VBox/Runtime/r3/win/thread-win.cpp b/src/VBox/Runtime/r3/win/thread-win.cpp
new file mode 100644
index 00000000..80bd3879
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/thread-win.cpp
@@ -0,0 +1,588 @@
+/* $Id: thread-win.cpp $ */
+/** @file
+ * IPRT - Threads, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/nt/nt-and-windows.h>
+
+#ifndef IPRT_NO_CRT
+# include <errno.h>
+# include <process.h>
+#endif
+
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/assert.h>
+#include <iprt/cpuset.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include "internal/thread.h"
+#include "internal-r3-win.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/** SetThreadDescription */
+typedef HRESULT (WINAPI *PFNSETTHREADDESCRIPTION)(HANDLE hThread, WCHAR *pwszName); /* Since W10 1607 */
+
+/** CoInitializeEx */
+typedef HRESULT (WINAPI *PFNCOINITIALIZEEX)(LPVOID, DWORD);
+/** CoUninitialize */
+typedef void (WINAPI *PFNCOUNINITIALIZE)(void);
+/** OleUninitialize */
+typedef void (WINAPI *PFNOLEUNINITIALIZE)(void);
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The TLS index allocated for storing the RTTHREADINT pointer. */
+static DWORD g_dwSelfTLS = TLS_OUT_OF_INDEXES;
+/** Pointer to SetThreadDescription (KERNEL32.DLL) if available. */
+static PFNSETTHREADDESCRIPTION g_pfnSetThreadDescription = NULL;
+
+/** Pointer to CoInitializeEx (OLE32.DLL / combase.dll) if available. */
+static PFNCOINITIALIZEEX volatile g_pfnCoInitializeEx = NULL;
+/** Pointer to CoUninitialize (OLE32.DLL / combase.dll) if available. */
+static PFNCOUNINITIALIZE volatile g_pfnCoUninitialize = NULL;
+/** Pointer to OleUninitialize (OLE32.DLL / combase.dll) if available. */
+static PFNOLEUNINITIALIZE volatile g_pfnOleUninitialize = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName);
+DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread);
+
+
+DECLHIDDEN(int) rtThreadNativeInit(void)
+{
+ g_dwSelfTLS = TlsAlloc();
+ if (g_dwSelfTLS == TLS_OUT_OF_INDEXES)
+ return VERR_NO_TLS_FOR_SELF;
+
+ g_pfnSetThreadDescription = (PFNSETTHREADDESCRIPTION)GetProcAddress(g_hModKernel32, "SetThreadDescription");
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeReInitObtrusive(void)
+{
+ /* nothing to do here. */
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDetach(void)
+{
+ /*
+ * Deal with alien threads.
+ */
+ PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS);
+ if ( pThread
+ && (pThread->fIntFlags & RTTHREADINT_FLAGS_ALIEN))
+ {
+ rtThreadTerminate(pThread, 0);
+ TlsSetValue(g_dwSelfTLS, NULL);
+ }
+}
+
+
+DECLHIDDEN(void) rtThreadNativeDestroy(PRTTHREADINT pThread)
+{
+ if (pThread == (PRTTHREADINT)TlsGetValue(g_dwSelfTLS))
+ TlsSetValue(g_dwSelfTLS, NULL);
+
+ if ((HANDLE)pThread->hThread != INVALID_HANDLE_VALUE)
+ {
+ CloseHandle((HANDLE)pThread->hThread);
+ pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE;
+ }
+}
+
+
+DECLHIDDEN(int) rtThreadNativeAdopt(PRTTHREADINT pThread)
+{
+ if (!TlsSetValue(g_dwSelfTLS, pThread))
+ return VERR_FAILED_TO_SET_SELF_TLS;
+ rtThreadWinSetThreadName(pThread, GetCurrentThreadId());
+ return VINF_SUCCESS;
+}
+
+
+DECLHIDDEN(void) rtThreadNativeInformDebugger(PRTTHREADINT pThread)
+{
+ rtThreadWinTellDebuggerThreadName((uint32_t)(uintptr_t)pThread->Core.Key, pThread->szName);
+}
+
+
+/**
+ * Communicates the thread name to the debugger, if we're begin debugged that
+ * is.
+ *
+ * See http://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx for debugger
+ * interface details.
+ *
+ * @param idThread The thread ID. UINT32_MAX for current thread.
+ * @param pszName The name.
+ */
+static void rtThreadWinTellDebuggerThreadName(uint32_t idThread, const char *pszName)
+{
+ struct
+ {
+ uint32_t uType;
+ const char *pszName;
+ uint32_t idThread;
+ uint32_t fFlags;
+ } Pkg = { 0x1000, pszName, idThread, 0 };
+ __try
+ {
+ RaiseException(0x406d1388, 0, sizeof(Pkg)/sizeof(ULONG_PTR), (ULONG_PTR *)&Pkg);
+ }
+ __except(EXCEPTION_CONTINUE_EXECUTION)
+ {
+
+ }
+}
+
+
+/**
+ * Sets the thread name as best as we can.
+ */
+DECLINLINE(void) rtThreadWinSetThreadName(PRTTHREADINT pThread, DWORD idThread)
+{
+ if (g_pfnIsDebuggerPresent && g_pfnIsDebuggerPresent())
+ rtThreadWinTellDebuggerThreadName(idThread, &pThread->szName[0]);
+
+ /* The SetThreadDescription API introduced in windows 10 1607 / server 2016
+ allows setting the thread name while the debugger isn't attached. Works
+ with WinDbgX, VisualStudio 2017 v15.6+, and presumeably some recent windbg
+ version. */
+ if (g_pfnSetThreadDescription)
+ {
+ /* The name should be ASCII, so we just need to expand 'char' to 'WCHAR'. */
+ WCHAR wszName[RTTHREAD_NAME_LEN];
+ for (size_t i = 0; i < RTTHREAD_NAME_LEN; i++)
+ wszName[i] = pThread->szName[i];
+
+ HRESULT hrc = g_pfnSetThreadDescription(GetCurrentThread(), wszName);
+ Assert(SUCCEEDED(hrc)); RT_NOREF(hrc);
+ }
+}
+
+
+/**
+ * Bitch about dangling COM and OLE references, dispose of them
+ * afterwards so we don't end up deadlocked somewhere below
+ * OLE32!DllMain.
+ */
+static void rtThreadNativeUninitComAndOle(void)
+{
+#if 1 /* experimental code */
+ /*
+ * Read the counters.
+ */
+ struct MySOleTlsData
+ {
+ void *apvReserved0[2]; /**< x86=0x00 W7/64=0x00 */
+ DWORD adwReserved0[3]; /**< x86=0x08 W7/64=0x10 */
+ void *apvReserved1[1]; /**< x86=0x14 W7/64=0x20 */
+ DWORD cComInits; /**< x86=0x18 W7/64=0x28 */
+ DWORD cOleInits; /**< x86=0x1c W7/64=0x2c */
+ DWORD dwReserved1; /**< x86=0x20 W7/64=0x30 */
+ void *apvReserved2[4]; /**< x86=0x24 W7/64=0x38 */
+ DWORD adwReserved2[1]; /**< x86=0x34 W7/64=0x58 */
+ void *pvCurrentCtx; /**< x86=0x38 W7/64=0x60 */
+ IUnknown *pCallState; /**< x86=0x3c W7/64=0x68 */
+ } *pOleTlsData = NULL; /* outside the try/except for debugging */
+ DWORD cComInits = 0;
+ DWORD cOleInits = 0;
+ __try
+ {
+ void *pvTeb = NtCurrentTeb();
+# ifdef RT_ARCH_AMD64
+ pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x1758); /*TEB.ReservedForOle*/
+# elif RT_ARCH_X86
+ pOleTlsData = *(struct MySOleTlsData **)((uintptr_t)pvTeb + 0x0f80); /*TEB.ReservedForOle*/
+# else
+# error "Port me!"
+# endif
+ if (pOleTlsData)
+ {
+ cComInits = pOleTlsData->cComInits;
+ cOleInits = pOleTlsData->cOleInits;
+ }
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ AssertFailedReturnVoid();
+ }
+
+ /*
+ * Assert sanity. If any of these breaks, the structure layout above is
+ * probably not correct any longer.
+ */
+ AssertMsgReturnVoid(cComInits < 1000, ("%u (%#x)\n", cComInits, cComInits));
+ AssertMsgReturnVoid(cOleInits < 1000, ("%u (%#x)\n", cOleInits, cOleInits));
+ AssertMsgReturnVoid(cComInits >= cOleInits, ("cComInits=%#x cOleInits=%#x\n", cComInits, cOleInits));
+
+ /*
+ * Do the uninitializing.
+ */
+ if (cComInits)
+ {
+ AssertMsgFailed(("cComInits=%u (%#x) cOleInits=%u (%#x) - dangling COM/OLE inits!\n",
+ cComInits, cComInits, cOleInits, cOleInits));
+
+ PFNOLEUNINITIALIZE pfnOleUninitialize = g_pfnOleUninitialize;
+ PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize;
+ if (pfnCoUninitialize && pfnOleUninitialize)
+ { /* likely */ }
+ else
+ {
+ HMODULE hOle32 = GetModuleHandle("ole32.dll");
+ AssertReturnVoid(hOle32 != NULL);
+
+ pfnOleUninitialize = (PFNOLEUNINITIALIZE)GetProcAddress(hOle32, "OleUninitialize");
+ AssertReturnVoid(pfnOleUninitialize);
+
+ pfnCoUninitialize = (PFNCOUNINITIALIZE)GetProcAddress(hOle32, "CoUninitialize");
+ AssertReturnVoid(pfnCoUninitialize);
+ }
+
+ while (cOleInits-- > 0)
+ {
+ pfnOleUninitialize();
+ cComInits--;
+ }
+
+ while (cComInits-- > 0)
+ pfnCoUninitialize();
+ }
+#endif
+}
+
+
+/**
+ * Implements the RTTHREADFLAGS_COM_MTA and RTTHREADFLAGS_COM_STA flags.
+ *
+ * @returns true if COM uninitialization should be done, false if not.
+ * @param fFlags The thread flags.
+ */
+static bool rtThreadNativeWinCoInitialize(unsigned fFlags)
+{
+ /*
+ * Resolve the ole32 init and uninit functions dynamically.
+ */
+ PFNCOINITIALIZEEX pfnCoInitializeEx = g_pfnCoInitializeEx;
+ PFNCOUNINITIALIZE pfnCoUninitialize = g_pfnCoUninitialize;
+ if (pfnCoInitializeEx && pfnCoUninitialize)
+ { /* likely */ }
+ else
+ {
+ RTLDRMOD hModOle32 = NIL_RTLDRMOD;
+ int rc = RTLdrLoadSystem("ole32.dll", true /*fNoUnload*/, &hModOle32);
+ AssertRCReturn(rc, false);
+
+ PFNOLEUNINITIALIZE pfnOleUninitialize;
+ pfnOleUninitialize = (PFNOLEUNINITIALIZE)RTLdrGetFunction(hModOle32, "OleUninitialize");
+ pfnCoUninitialize = (PFNCOUNINITIALIZE )RTLdrGetFunction(hModOle32, "CoUninitialize");
+ pfnCoInitializeEx = (PFNCOINITIALIZEEX )RTLdrGetFunction(hModOle32, "CoInitializeEx");
+
+ RTLdrClose(hModOle32);
+ AssertReturn(pfnCoInitializeEx && pfnCoUninitialize, false);
+
+ if (pfnOleUninitialize && !g_pfnOleUninitialize)
+ g_pfnOleUninitialize = pfnOleUninitialize;
+ g_pfnCoInitializeEx = pfnCoInitializeEx;
+ g_pfnCoUninitialize = pfnCoUninitialize;
+ }
+
+ /*
+ * Do the initializating.
+ */
+ DWORD fComInit;
+ if (fFlags & RTTHREADFLAGS_COM_MTA)
+ fComInit = COINIT_MULTITHREADED | COINIT_SPEED_OVER_MEMORY | COINIT_DISABLE_OLE1DDE;
+ else
+ fComInit = COINIT_APARTMENTTHREADED | COINIT_SPEED_OVER_MEMORY;
+ HRESULT hrc = pfnCoInitializeEx(NULL, fComInit);
+ AssertMsg(SUCCEEDED(hrc), ("%Rhrc fComInit=%#x\n", hrc, fComInit));
+ return SUCCEEDED(hrc);
+}
+
+
+/**
+ * Wrapper which unpacks the param stuff and calls thread function.
+ */
+#ifndef IPRT_NO_CRT
+static unsigned __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF
+#else
+static DWORD __stdcall rtThreadNativeMain(void *pvArgs) RT_NOTHROW_DEF
+#endif
+{
+ DWORD dwThreadId = GetCurrentThreadId();
+ PRTTHREADINT pThread = (PRTTHREADINT)pvArgs;
+
+ if (!TlsSetValue(g_dwSelfTLS, pThread))
+ AssertReleaseMsgFailed(("failed to set self TLS. lasterr=%d thread '%s'\n", GetLastError(), pThread->szName));
+ rtThreadWinSetThreadName(pThread, dwThreadId);
+
+ bool fUninitCom = (pThread->fFlags & (RTTHREADFLAGS_COM_MTA | RTTHREADFLAGS_COM_STA)) != 0;
+ if (fUninitCom)
+ fUninitCom = rtThreadNativeWinCoInitialize(pThread->fFlags);
+
+ int rc = rtThreadMain(pThread, dwThreadId, &pThread->szName[0]);
+
+ TlsSetValue(g_dwSelfTLS, NULL); /* rtThreadMain already released the structure. */
+
+ if (fUninitCom && g_pfnCoUninitialize)
+ g_pfnCoUninitialize();
+
+ rtThreadNativeUninitComAndOle();
+#ifndef IPRT_NO_CRT
+ _endthreadex(rc);
+ return rc; /* not reached */
+#else
+ for (;;)
+ ExitThread(rc);
+#endif
+}
+
+
+DECLHIDDEN(int) rtThreadNativeCreate(PRTTHREADINT pThread, PRTNATIVETHREAD pNativeThread)
+{
+ AssertReturn(pThread->cbStack < ~(unsigned)0, VERR_INVALID_PARAMETER);
+
+ /*
+ * If a stack size is given, make sure it's not a multiple of 64KB so that we
+ * get one or more pages for overflow protection. (ASSUMES 64KB alloc align.)
+ */
+ unsigned cbStack = (unsigned)pThread->cbStack;
+ if (cbStack > 0 && RT_ALIGN_T(cbStack, _64K, unsigned) == cbStack)
+ cbStack += PAGE_SIZE;
+
+ /*
+ * Create the thread.
+ */
+ pThread->hThread = (uintptr_t)INVALID_HANDLE_VALUE;
+#ifndef IPRT_NO_CRT
+ unsigned uThreadId = 0;
+ uintptr_t hThread = _beginthreadex(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &uThreadId);
+ if (hThread != 0 && hThread != ~0U)
+ {
+ pThread->hThread = hThread;
+ *pNativeThread = uThreadId;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromErrno(errno);
+#else
+ DWORD idThread = 0;
+ HANDLE hThread = CreateThread(NULL /*pSecAttrs*/, cbStack, rtThreadNativeMain, pThread, 0 /*fFlags*/, &idThread);
+ if (hThread != NULL)
+ {
+ pThread->hThread = (uintptr_t)hThread;
+ *pNativeThread = idThread;
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+#endif
+}
+
+
+DECLHIDDEN(bool) rtThreadNativeIsAliveKludge(PRTTHREADINT pThread)
+{
+ PPEB_COMMON pPeb = NtCurrentPeb();
+ if (!pPeb || !pPeb->Ldr || !pPeb->Ldr->ShutdownInProgress)
+ return true;
+ DWORD rcWait = WaitForSingleObject((HANDLE)pThread->hThread, 0);
+ return rcWait != WAIT_OBJECT_0;
+}
+
+
+RTDECL(RTTHREAD) RTThreadSelf(void)
+{
+ PRTTHREADINT pThread = (PRTTHREADINT)TlsGetValue(g_dwSelfTLS);
+ /** @todo import alien threads ? */
+ return pThread;
+}
+
+
+#if 0 /* noone is using this ... */
+/**
+ * Returns the processor number the current thread was running on during this call
+ *
+ * @returns processor nr
+ */
+static int rtThreadGetCurrentProcessorNumber(void)
+{
+ static bool fInitialized = false;
+ static DWORD (WINAPI *pfnGetCurrentProcessorNumber)(void) = NULL;
+ if (!fInitialized)
+ {
+ HMODULE hmodKernel32 = GetModuleHandle("kernel32.dll");
+ if (hmodKernel32)
+ pfnGetCurrentProcessorNumber = (DWORD (WINAPI*)(void))GetProcAddress(hmodKernel32, "GetCurrentProcessorNumber");
+ fInitialized = true;
+ }
+ if (pfnGetCurrentProcessorNumber)
+ return pfnGetCurrentProcessorNumber();
+ return -1;
+}
+#endif
+
+
+RTR3DECL(int) RTThreadSetAffinity(PCRTCPUSET pCpuSet)
+{
+ /* The affinity functionality was added in NT 3.50, so we resolve the APIs
+ dynamically to be able to run on NT 3.1. */
+ if (g_pfnSetThreadAffinityMask)
+ {
+ DWORD_PTR fNewMask = pCpuSet ? RTCpuSetToU64(pCpuSet) : ~(DWORD_PTR)0;
+ DWORD_PTR dwRet = g_pfnSetThreadAffinityMask(GetCurrentThread(), fNewMask);
+ if (dwRet)
+ return VINF_SUCCESS;
+
+ int iLastError = GetLastError();
+ AssertMsgFailed(("SetThreadAffinityMask failed, LastError=%d\n", iLastError));
+ return RTErrConvertFromWin32(iLastError);
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR3DECL(int) RTThreadGetAffinity(PRTCPUSET pCpuSet)
+{
+ /* The affinity functionality was added in NT 3.50, so we resolve the APIs
+ dynamically to be able to run on NT 3.1. */
+ if ( g_pfnSetThreadAffinityMask
+ && g_pfnGetProcessAffinityMask)
+ {
+ /*
+ * Haven't found no query api, but the set api returns the old mask, so let's use that.
+ */
+ DWORD_PTR dwIgnored;
+ DWORD_PTR dwProcAff = 0;
+ if (g_pfnGetProcessAffinityMask(GetCurrentProcess(), &dwProcAff, &dwIgnored))
+ {
+ HANDLE hThread = GetCurrentThread();
+ DWORD_PTR dwRet = g_pfnSetThreadAffinityMask(hThread, dwProcAff);
+ if (dwRet)
+ {
+ DWORD_PTR dwSet = g_pfnSetThreadAffinityMask(hThread, dwRet);
+ Assert(dwSet == dwProcAff); NOREF(dwRet);
+
+ RTCpuSetFromU64(pCpuSet, (uint64_t)dwSet);
+ return VINF_SUCCESS;
+ }
+ }
+
+ int iLastError = GetLastError();
+ AssertMsgFailed(("SetThreadAffinityMask or GetProcessAffinityMask failed, LastError=%d\n", iLastError));
+ return RTErrConvertFromWin32(iLastError);
+ }
+ return VERR_NOT_SUPPORTED;
+}
+
+
+RTR3DECL(int) RTThreadGetExecutionTimeMilli(uint64_t *pKernelTime, uint64_t *pUserTime)
+{
+ uint64_t u64CreationTime, u64ExitTime, u64KernelTime, u64UserTime;
+
+ if (GetThreadTimes(GetCurrentThread(), (LPFILETIME)&u64CreationTime, (LPFILETIME)&u64ExitTime, (LPFILETIME)&u64KernelTime, (LPFILETIME)&u64UserTime))
+ {
+ *pKernelTime = u64KernelTime / 10000; /* GetThreadTimes returns time in 100 ns units */
+ *pUserTime = u64UserTime / 10000; /* GetThreadTimes returns time in 100 ns units */
+ return VINF_SUCCESS;
+ }
+
+ int iLastError = GetLastError();
+ AssertMsgFailed(("GetThreadTimes failed, LastError=%d\n", iLastError));
+ return RTErrConvertFromWin32(iLastError);
+}
+
+
+/**
+ * Gets the native thread handle for a IPRT thread.
+ *
+ * @returns The thread handle. INVALID_HANDLE_VALUE on failure.
+ * @param hThread The IPRT thread handle.
+ *
+ * @note Windows only.
+ * @note Only valid after parent returns from the thread creation call.
+ */
+RTDECL(uintptr_t) RTThreadGetNativeHandle(RTTHREAD hThread)
+{
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ if (pThread)
+ {
+ uintptr_t hHandle = pThread->hThread;
+ rtThreadRelease(pThread);
+ return hHandle;
+ }
+ return (uintptr_t)INVALID_HANDLE_VALUE;
+}
+RT_EXPORT_SYMBOL(RTThreadGetNativeHandle);
+
+
+RTDECL(int) RTThreadPoke(RTTHREAD hThread)
+{
+ AssertReturn(hThread != RTThreadSelf(), VERR_INVALID_PARAMETER);
+ if (g_pfnNtAlertThread)
+ {
+ PRTTHREADINT pThread = rtThreadGet(hThread);
+ AssertReturn(pThread, VERR_INVALID_HANDLE);
+
+ NTSTATUS rcNt = g_pfnNtAlertThread((HANDLE)pThread->hThread);
+
+ rtThreadRelease(pThread);
+ if (NT_SUCCESS(rcNt))
+ return VINF_SUCCESS;
+ return RTErrConvertFromNtStatus(rcNt);
+ }
+ return VERR_NOT_IMPLEMENTED;
+}
+
diff --git a/src/VBox/Runtime/r3/win/thread2-win.cpp b/src/VBox/Runtime/r3/win/thread2-win.cpp
new file mode 100644
index 00000000..f9874c96
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/thread2-win.cpp
@@ -0,0 +1,84 @@
+/* $Id: thread2-win.cpp $ */
+/** @file
+ * IPRT - Threads part 2, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/win/windows.h>
+
+#include <iprt/thread.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm-amd64-x86.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include "internal/thread.h"
+
+
+RTDECL(RTNATIVETHREAD) RTThreadNativeSelf(void)
+{
+ return (RTNATIVETHREAD)GetCurrentThreadId();
+}
+
+
+RTR3DECL(int) RTThreadSleep(RTMSINTERVAL cMillies)
+{
+ LogFlow(("RTThreadSleep: cMillies=%d\n", cMillies));
+ Sleep(cMillies);
+ LogFlow(("RTThreadSleep: returning %Rrc (cMillies=%d)\n", VINF_SUCCESS, cMillies));
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(int) RTThreadSleepNoLog(RTMSINTERVAL cMillies)
+{
+ Sleep(cMillies);
+ return VINF_SUCCESS;
+}
+
+
+RTR3DECL(bool) RTThreadYield(void)
+{
+ uint64_t u64TS = ASMReadTSC();
+ Sleep(0);
+ u64TS = ASMReadTSC() - u64TS;
+ bool fRc = u64TS > 1500;
+ LogFlow(("RTThreadYield: returning %d (%llu ticks)\n", fRc, u64TS));
+ return fRc;
+}
+
diff --git a/src/VBox/Runtime/r3/win/time-win.cpp b/src/VBox/Runtime/r3/win/time-win.cpp
new file mode 100644
index 00000000..09e42d8e
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/time-win.cpp
@@ -0,0 +1,228 @@
+/* $Id: time-win.cpp $ */
+/** @file
+ * IPRT - Time, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/nt/nt-and-windows.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/time.h"
+#include "internal-r3-win.h"
+
+/*
+ * Note! The selected time source be the exact same one as we use in kernel land!
+ */
+//#define USE_TICK_COUNT
+//#define USE_PERFORMANCE_COUNTER
+//# define USE_FILE_TIME
+//#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+# define USE_INTERRUPT_TIME
+//#else
+//# define USE_TICK_COUNT
+//#endif
+
+
+
+DECLINLINE(uint64_t) rtTimeGetSystemNanoTS(void)
+{
+#if defined USE_TICK_COUNT
+ /*
+ * This would work if it didn't flip over every 49 (or so) days.
+ */
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+
+#elif defined USE_PERFORMANCE_COUNTER
+ /*
+ * Slow and not derived from InterruptTime.
+ */
+ static LARGE_INTEGER llFreq;
+ static unsigned uMult;
+ if (!llFreq.QuadPart)
+ {
+ if (!QueryPerformanceFrequency(&llFreq))
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+ llFreq.QuadPart /= 1000;
+ uMult = 1000000; /* no math genius, but this seemed to help avoiding floating point. */
+ }
+
+ LARGE_INTEGER ll;
+ if (QueryPerformanceCounter(&ll))
+ return (ll.QuadPart * uMult) / llFreq.QuadPart;
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+
+#elif defined USE_FILE_TIME
+ /*
+ * This is SystemTime not InterruptTime.
+ */
+ uint64_t u64; /* manual say larger integer, should be safe to assume it's the same. */
+ GetSystemTimeAsFileTime((LPFILETIME)&u64);
+ return u64 * 100;
+
+#elif defined USE_INTERRUPT_TIME
+ /*
+ * Use interrupt time if we can (not possible on NT 3.1).
+ * Note! We cannot entirely depend on g_enmWinVer here as we're likely to
+ * get called before IPRT is initialized. Ditto g_hModNtDll.
+ */
+ static PFNRTLGETINTERRUPTTIMEPRECISE s_pfnRtlGetInterruptTimePrecise = NULL;
+ static int volatile s_iCanUseUserSharedData = -1;
+ int iCanUseUserSharedData = s_iCanUseUserSharedData;
+ if (iCanUseUserSharedData != -1)
+ { /* likely */ }
+ else
+ {
+ /* We may be called before g_enmWinVer has been initialized. */
+ if (g_enmWinVer != kRTWinOSType_UNKNOWN)
+ iCanUseUserSharedData = g_enmWinVer > kRTWinOSType_NT310;
+ else
+ {
+ DWORD dwVer = GetVersion();
+ iCanUseUserSharedData = (dwVer & 0xff) != 3 || ((dwVer >> 8) & 0xff) >= 50;
+ }
+ if (iCanUseUserSharedData != 0)
+ {
+ FARPROC pfn = GetProcAddress(g_hModNtDll ? g_hModNtDll : GetModuleHandleW(L"ntdll"), "RtlGetInterruptTimePrecise");
+ if (pfn != NULL)
+ {
+ ASMAtomicWritePtr(&s_pfnRtlGetInterruptTimePrecise, pfn);
+ iCanUseUserSharedData = 42;
+ }
+ }
+ s_iCanUseUserSharedData = iCanUseUserSharedData;
+ }
+
+ if (iCanUseUserSharedData != 0)
+ {
+ LARGE_INTEGER Time;
+ if (iCanUseUserSharedData == 42)
+ {
+ uint64_t iIgnored;
+ Time.QuadPart = s_pfnRtlGetInterruptTimePrecise(&iIgnored);
+ }
+ else
+ {
+ PKUSER_SHARED_DATA pUserSharedData = (PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA;
+ do
+ {
+ Time.HighPart = pUserSharedData->InterruptTime.High1Time;
+ Time.LowPart = pUserSharedData->InterruptTime.LowPart;
+ } while (pUserSharedData->InterruptTime.High2Time != Time.HighPart);
+ }
+
+ return (uint64_t)Time.QuadPart * 100;
+ }
+
+ return (uint64_t)GetTickCount() * RT_NS_1MS_64;
+
+#else
+# error "Must select a method bright guy!"
+#endif
+}
+
+
+RTDECL(uint64_t) RTTimeSystemNanoTS(void)
+{
+ return rtTimeGetSystemNanoTS();
+}
+
+
+RTDECL(uint64_t) RTTimeSystemMilliTS(void)
+{
+ return rtTimeGetSystemNanoTS() / RT_NS_1MS;
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeNow(PRTTIMESPEC pTime)
+{
+ uint64_t u64;
+ AssertCompile(sizeof(u64) == sizeof(FILETIME));
+ if (g_pfnGetSystemTimeAsFileTime)
+ g_pfnGetSystemTimeAsFileTime((LPFILETIME)&u64);
+ else
+ {
+ SYSTEMTIME SysTime = {0};
+ GetSystemTime(&SysTime);
+ BOOL fRet = SystemTimeToFileTime(&SysTime, (LPFILETIME)&u64);
+ Assert(fRet); RT_NOREF(fRet);
+ }
+ return RTTimeSpecSetNtTime(pTime, u64);
+
+}
+
+
+RTDECL(PRTTIMESPEC) RTTimeLocalNow(PRTTIMESPEC pTime)
+{
+ uint64_t u64Local;
+ if (g_pfnGetSystemTimeAsFileTime)
+ {
+ uint64_t u64;
+ AssertCompile(sizeof(u64) == sizeof(FILETIME));
+ g_pfnGetSystemTimeAsFileTime((LPFILETIME)&u64);
+ if (!FileTimeToLocalFileTime((FILETIME const *)&u64, (LPFILETIME)&u64Local))
+ u64Local = u64;
+ }
+ else
+ {
+ SYSTEMTIME SysTime = {0};
+ GetLocalTime(&SysTime);
+ BOOL fRet = SystemTimeToFileTime(&SysTime, (LPFILETIME)&u64Local);
+ Assert(fRet); RT_NOREF(fRet);
+ }
+ return RTTimeSpecSetNtTime(pTime, u64Local);
+}
+
+
+RTDECL(int64_t) RTTimeLocalDeltaNano(void)
+{
+ /*
+ * UTC = local + Tzi.Bias;
+ * The bias is given in minutes.
+ */
+ TIME_ZONE_INFORMATION Tzi;
+ Tzi.Bias = 0;
+ if (GetTimeZoneInformation(&Tzi) != TIME_ZONE_ID_INVALID)
+ return -(int64_t)Tzi.Bias * 60 * RT_NS_1SEC_64;
+ return 0;
+}
+
diff --git a/src/VBox/Runtime/r3/win/time2-win.cpp b/src/VBox/Runtime/r3/win/time2-win.cpp
new file mode 100644
index 00000000..d4f0db06
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/time2-win.cpp
@@ -0,0 +1,164 @@
+/* $Id: time2-win.cpp $ */
+/** @file
+ * IPRT - Time, Windows.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIME
+#include <iprt/win/windows.h>
+
+#include <iprt/time.h>
+#include "internal/iprt.h"
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include "internal/time.h"
+
+#include "internal-r3-win.h"
+
+
+RTDECL(int) RTTimeSet(PCRTTIMESPEC pTime)
+{
+ FILETIME FileTime;
+ SYSTEMTIME SysTime;
+ if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTime, &FileTime), &SysTime))
+ {
+ if (SetSystemTime(&SysTime))
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTDECL(PRTTIME) RTTimeLocalExplode(PRTTIME pTime, PCRTTIMESPEC pTimeSpec)
+{
+ RTTIMESPEC LocalTime;
+ if (g_pfnSystemTimeToTzSpecificLocalTime)
+ {
+ /*
+ * FileTimeToLocalFileTime does not do the right thing, so we'll have
+ * to convert to system time and SystemTimeToTzSpecificLocalTime instead.
+ *
+ * Note! FileTimeToSystemTime drops resoultion down to milliseconds, thus
+ * we have to do the offUTC calculation using milliseconds and adjust
+ * u32Nanosecons by sub milliseconds digits.
+ */
+ SYSTEMTIME SystemTimeIn;
+ FILETIME FileTime;
+ if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn))
+ {
+ SYSTEMTIME SystemTimeOut;
+ if (g_pfnSystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, &SystemTimeIn, &SystemTimeOut))
+ {
+ if (SystemTimeToFileTime(&SystemTimeOut, &FileTime))
+ {
+ RTTimeSpecSetNtFileTime(&LocalTime, &FileTime);
+ pTime = RTTimeExplode(pTime, &LocalTime);
+ if (pTime)
+ {
+ pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = (RTTimeSpecGetMilli(&LocalTime) - RTTimeSpecGetMilli(pTimeSpec)) / RT_MS_1MIN;
+ pTime->u32Nanosecond += RTTimeSpecGetNano(pTimeSpec) % RT_NS_1MS;
+ }
+ return pTime;
+ }
+ }
+ }
+ }
+
+ /*
+ * The fallback is to use the current offset.
+ * (A better fallback would be to use the offset of the same time of the year.)
+ */
+ LocalTime = *pTimeSpec;
+ int64_t cNsUtcOffset = RTTimeLocalDeltaNano();
+ RTTimeSpecAddNano(&LocalTime, cNsUtcOffset);
+ pTime = RTTimeExplode(pTime, &LocalTime);
+ if (pTime)
+ {
+ pTime->fFlags = (pTime->fFlags & ~RTTIME_FLAGS_TYPE_MASK) | RTTIME_FLAGS_TYPE_LOCAL;
+ pTime->offUTC = cNsUtcOffset / RT_NS_1MIN;
+ }
+ return pTime;
+}
+
+
+/**
+ * Gets the delta between UTC and local time at the given time.
+ *
+ * @code
+ * RTTIMESPEC LocalTime;
+ * RTTimeNow(&LocalTime);
+ * RTTimeSpecAddNano(&LocalTime, RTTimeLocalDeltaNanoFor(&LocalTime));
+ * @endcode
+ *
+ * @param pTimeSpec The time spec giving the time to get the delta for.
+ * @returns Returns the nanosecond delta between UTC and local time.
+ */
+RTDECL(int64_t) RTTimeLocalDeltaNanoFor(PCRTTIMESPEC pTimeSpec)
+{
+ RTTIMESPEC LocalTime;
+ if (g_pfnSystemTimeToTzSpecificLocalTime)
+ {
+ /*
+ * FileTimeToLocalFileTime does not do the right thing, so we'll have
+ * to convert to system time and SystemTimeToTzSpecificLocalTime instead.
+ *
+ * Note! FileTimeToSystemTime drops resoultion down to milliseconds, thus
+ * we have to do the offUTC calculation using milliseconds and adjust
+ * u32Nanosecons by sub milliseconds digits.
+ */
+ SYSTEMTIME SystemTimeIn;
+ FILETIME FileTime;
+ if (FileTimeToSystemTime(RTTimeSpecGetNtFileTime(pTimeSpec, &FileTime), &SystemTimeIn))
+ {
+ SYSTEMTIME SystemTimeOut;
+ if (g_pfnSystemTimeToTzSpecificLocalTime(NULL /* use current TZI */, &SystemTimeIn, &SystemTimeOut))
+ {
+ if (SystemTimeToFileTime(&SystemTimeOut, &FileTime))
+ {
+ RTTimeSpecSetNtFileTime(&LocalTime, &FileTime);
+
+ return (RTTimeSpecGetMilli(&LocalTime) - RTTimeSpecGetMilli(pTimeSpec)) * RT_NS_1MS;
+ }
+ }
+ }
+ }
+
+ return RTTimeLocalDeltaNano();
+}
+
diff --git a/src/VBox/Runtime/r3/win/timer-win.cpp b/src/VBox/Runtime/r3/win/timer-win.cpp
new file mode 100644
index 00000000..f8d4a520
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/timer-win.cpp
@@ -0,0 +1,520 @@
+/* $Id: timer-win.cpp $ */
+/** @file
+ * IPRT - Timer.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_TIMER
+#define _WIN32_WINNT 0x0500
+#include <iprt/win/windows.h>
+
+#include <iprt/timer.h>
+#ifdef USE_CATCH_UP
+# include <iprt/time.h>
+#endif
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/thread.h>
+#include <iprt/log.h>
+#include <iprt/asm.h>
+#include <iprt/semaphore.h>
+#include <iprt/err.h>
+#include "internal/magics.h"
+#include "internal-r3-win.h"
+
+
+/** Define the flag for creating a manual reset timer if not available in the SDK we are compiling with. */
+#ifndef CREATE_WAITABLE_TIMER_MANUAL_RESET
+# define CREATE_WAITABLE_TIMER_MANUAL_RESET 0x00000001
+#endif
+/** Define the flag for high resolution timers, available since Windows 10 RS4 if not available. */
+#ifndef CREATE_WAITABLE_TIMER_HIGH_RESOLUTION
+# define CREATE_WAITABLE_TIMER_HIGH_RESOLUTION 0x00000002
+#endif
+
+
+RT_C_DECLS_BEGIN
+/* from sysinternals. */
+NTSYSAPI LONG NTAPI NtSetTimerResolution(IN ULONG DesiredResolution, IN BOOLEAN SetResolution, OUT PULONG CurrentResolution);
+NTSYSAPI LONG NTAPI NtQueryTimerResolution(OUT PULONG MaximumResolution, OUT PULONG MinimumResolution, OUT PULONG CurrentResolution);
+RT_C_DECLS_END
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The internal representation of a timer handle.
+ */
+typedef struct RTTIMER
+{
+ /** Magic.
+ * This is RTTIMER_MAGIC, but changes to something else before the timer
+ * is destroyed to indicate clearly that thread should exit. */
+ uint32_t volatile u32Magic;
+ /** Flag indicating the timer is suspended. */
+ bool volatile fSuspended;
+ /** Flag indicating that the timer has been destroyed. */
+ bool volatile fDestroyed;
+ /** User argument. */
+ void *pvUser;
+ /** Callback. */
+ PFNRTTIMER pfnTimer;
+ /** The current tick. */
+ uint64_t iTick;
+ /** The timer interval. 0 if one-shot. */
+ uint64_t u64NanoInterval;
+ /** The first shot interval. 0 if ASAP. */
+ uint64_t volatile u64NanoFirst;
+ /** Time handle. */
+ HANDLE hTimer;
+ /** USE_CATCH_UP: ns time of the next tick.
+ * !USE_CATCH_UP: -uMilliesInterval * 10000 */
+ LARGE_INTEGER llNext;
+ /** The thread handle of the timer thread. */
+ RTTHREAD Thread;
+ /** Event semaphore on which the thread is blocked. */
+ RTSEMEVENT Event;
+ /** The error/status of the timer.
+ * Initially -1, set to 0 when the timer have been successfully started, and
+ * to errno on failure in starting the timer. */
+ volatile int iError;
+} RTTIMER;
+
+
+
+/**
+ * Timer thread.
+ */
+static DECLCALLBACK(int) rttimerCallback(RTTHREAD hThreadSelf, void *pvArg)
+{
+ PRTTIMER pTimer = (PRTTIMER)(void *)pvArg;
+ Assert(pTimer->u32Magic == RTTIMER_MAGIC);
+
+ /*
+ * Bounce our priority up quite a bit.
+ */
+ if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL))
+ {
+ int rc = GetLastError();
+ AssertMsgFailed(("Failed to set priority class lasterror %d.\n", rc));
+ pTimer->iError = RTErrConvertFromWin32(rc);
+ RTThreadUserSignal(hThreadSelf);
+ return rc;
+ }
+
+ /*
+ * The work loop.
+ */
+ RTThreadUserSignal(hThreadSelf);
+
+ while ( !pTimer->fDestroyed
+ && pTimer->u32Magic == RTTIMER_MAGIC)
+ {
+ /*
+ * Wait for a start or destroy event.
+ */
+ if (pTimer->fSuspended)
+ {
+ int rc = RTSemEventWait(pTimer->Event, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc) && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ if (pTimer->fDestroyed)
+ continue;
+ RTThreadSleep(1000); /* Don't cause trouble! */
+ }
+ if ( pTimer->fSuspended
+ || pTimer->fDestroyed)
+ continue;
+ }
+
+ /*
+ * Start the waitable timer.
+ */
+ pTimer->llNext.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100;
+ LARGE_INTEGER ll;
+ if (pTimer->u64NanoFirst)
+ {
+ GetSystemTimeAsFileTime((LPFILETIME)&ll);
+ ll.QuadPart += pTimer->u64NanoFirst / 100;
+ pTimer->u64NanoFirst = 0;
+ }
+ else
+ ll.QuadPart = -(int64_t)pTimer->u64NanoInterval / 100;
+ if (!SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE))
+ {
+ ASMAtomicXchgBool(&pTimer->fSuspended, true);
+ int rc = GetLastError();
+ AssertMsgFailed(("Failed to set timer, lasterr %d.\n", rc));
+ pTimer->iError = RTErrConvertFromWin32(rc);
+ RTThreadUserSignal(hThreadSelf);
+ continue; /* back to suspended mode. */
+ }
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+
+ /*
+ * Timer Service Loop.
+ */
+ do
+ {
+ int rc = WaitForSingleObjectEx(pTimer->hTimer, INFINITE, FALSE);
+ if (pTimer->u32Magic != RTTIMER_MAGIC)
+ break;
+ if (rc == WAIT_OBJECT_0)
+ {
+ /*
+ * Callback the handler.
+ */
+ pTimer->pfnTimer(pTimer, pTimer->pvUser, ++pTimer->iTick);
+
+ /*
+ * Rearm the timer handler.
+ */
+ ll = pTimer->llNext;
+ BOOL fRc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
+ AssertMsg(fRc || pTimer->u32Magic != RTTIMER_MAGIC, ("last error %d\n", GetLastError())); NOREF(fRc);
+ }
+ else
+ {
+ /*
+ * We failed during wait, so just signal the destructor and exit.
+ */
+ int rc2 = GetLastError();
+ RTThreadUserSignal(hThreadSelf);
+ AssertMsgFailed(("Wait on hTimer failed, rc=%d lasterr=%d\n", rc, rc2)); NOREF(rc2);
+ return -1;
+ }
+ } while (RT_LIKELY( !pTimer->fSuspended
+ && !pTimer->fDestroyed
+ && pTimer->u32Magic == RTTIMER_MAGIC));
+
+ /*
+ * Disable the timer.
+ */
+ int rc = CancelWaitableTimer (pTimer->hTimer); RT_NOREF(rc);
+ AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError()));
+
+ /*
+ * ACK any pending suspend request.
+ */
+ if (!pTimer->fDestroyed)
+ {
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+ }
+ }
+
+ /*
+ * Exit.
+ */
+ pTimer->iError = 0;
+ RTThreadUserSignal(hThreadSelf);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Tries to set the NT timer resolution to a value matching the given timer interval.
+ *
+ * @returns IPRT status code.
+ * @param u64NanoInterval The timer interval in nano seconds.
+ */
+static int rtTimerNtSetTimerResolution(uint64_t u64NanoInterval)
+{
+ /*
+ * On windows we'll have to set the timer resolution before
+ * we start the timer.
+ */
+ ULONG ulMax = UINT32_MAX;
+ ULONG ulMin = UINT32_MAX;
+ ULONG ulCur = UINT32_MAX;
+ ULONG ulReq = (ULONG)(u64NanoInterval / 100);
+ NtQueryTimerResolution(&ulMax, &ulMin, &ulCur);
+ Log(("NtQueryTimerResolution -> ulMax=%lu00ns ulMin=%lu00ns ulCur=%lu00ns\n", ulMax, ulMin, ulCur));
+ if (ulCur > ulMin && ulCur > ulReq)
+ {
+ ulReq = RT_MIN(ulMin, ulReq);
+ if (NtSetTimerResolution(ulReq, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to %lu*100ns.\n", ulReq));
+ else if (NtSetTimerResolution(10000, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to 1ms.\n"));
+ else if (NtSetTimerResolution(20000, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to 2ms.\n"));
+ else if (NtSetTimerResolution(40000, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to 4ms.\n"));
+ else if (ulMin <= 50000 && NtSetTimerResolution(ulMin, TRUE, &ulCur) >= 0)
+ Log(("Changed timer resolution to %lu *100ns.\n", ulMin));
+ else
+ {
+ AssertMsgFailed(("Failed to configure timer resolution!\n"));
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTimerCreateEx(PRTTIMER *ppTimer, uint64_t u64NanoInterval, uint32_t fFlags, PFNRTTIMER pfnTimer, void *pvUser)
+{
+ /*
+ * We don't support the fancy MP features.
+ */
+ if (fFlags & RTTIMER_FLAGS_CPU_SPECIFIC)
+ return VERR_NOT_SUPPORTED;
+
+ /*
+ * Create new timer.
+ */
+ int rc = VERR_IPE_UNINITIALIZED_STATUS;
+ PRTTIMER pTimer = (PRTTIMER)RTMemAlloc(sizeof(*pTimer));
+ if (pTimer)
+ {
+ pTimer->u32Magic = RTTIMER_MAGIC;
+ pTimer->fSuspended = true;
+ pTimer->fDestroyed = false;
+ pTimer->Thread = NIL_RTTHREAD;
+ pTimer->pfnTimer = pfnTimer;
+ pTimer->pvUser = pvUser;
+ pTimer->u64NanoInterval = u64NanoInterval;
+
+ rc = RTSemEventCreate(&pTimer->Event);
+ AssertRC(rc);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create Win32 waitable timer.
+ * We will first try the undocumented CREATE_WAITABLE_TIMER_HIGH_RESOLUTION which
+ * exists since some Windows 10 version (RS4). If this fails we resort to the old
+ * method of setting the timer resolution before creating a timer which will probably
+ * not give us the accuracy for intervals below the system tick resolution.
+ */
+ pTimer->iError = 0;
+ if (g_pfnCreateWaitableTimerExW)
+ pTimer->hTimer = g_pfnCreateWaitableTimerExW(NULL, NULL,
+ CREATE_WAITABLE_TIMER_MANUAL_RESET | CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
+ TIMER_ALL_ACCESS);
+ if (!pTimer->hTimer)
+ {
+ rc = rtTimerNtSetTimerResolution(u64NanoInterval);
+ if (RT_SUCCESS(rc))
+ pTimer->hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
+ }
+
+ if (pTimer->hTimer)
+ {
+ /*
+ * Kick off the timer thread.
+ */
+ rc = RTThreadCreate(&pTimer->Thread, rttimerCallback, pTimer, 0, RTTHREADTYPE_TIMER, RTTHREADFLAGS_WAITABLE, "Timer");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the timer to successfully create the timer
+ * If we don't get a response in 10 secs, then we assume we're screwed.
+ */
+ rc = RTThreadUserWait(pTimer->Thread, 10000);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pTimer->iError;
+ if (RT_SUCCESS(rc))
+ {
+ *ppTimer = pTimer;
+ return VINF_SUCCESS;
+ }
+ }
+
+ /* bail out */
+ ASMAtomicXchgBool(&pTimer->fDestroyed, true);
+ ASMAtomicXchgU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+ RTThreadWait(pTimer->Thread, 45*1000, NULL);
+ CancelWaitableTimer(pTimer->hTimer);
+ }
+ CloseHandle(pTimer->hTimer);
+ }
+ else
+ rc = RTErrConvertFromWin32(GetLastError());
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+ }
+
+ RTMemFree(pTimer);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ return rc;
+}
+
+
+RTR3DECL(int) RTTimerDestroy(PRTTIMER pTimer)
+{
+ /* NULL is ok. */
+ if (!pTimer)
+ return VINF_SUCCESS;
+
+ int rc = VINF_SUCCESS;
+ AssertPtrReturn(pTimer, VERR_INVALID_HANDLE);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+
+ /*
+ * Signal that we want the thread to exit.
+ */
+ ASMAtomicWriteBool(&pTimer->fDestroyed, true);
+ ASMAtomicWriteU32(&pTimer->u32Magic, ~RTTIMER_MAGIC);
+
+ /*
+ * Suspend the timer if it's running.
+ */
+ if (!pTimer->fSuspended)
+ {
+ LARGE_INTEGER ll = {0};
+ ll.LowPart = 100;
+ rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
+ AssertMsg(rc, ("CancelWaitableTimer lasterr=%d\n", GetLastError()));
+ }
+
+ rc = RTSemEventSignal(pTimer->Event);
+ AssertRC(rc);
+
+ /*
+ * Wait for the thread to exit.
+ * And if it don't wanna exit, we'll get kill it.
+ */
+ rc = RTThreadWait(pTimer->Thread, 30 * 1000, NULL);
+ if (RT_FAILURE(rc))
+ TerminateThread((HANDLE)RTThreadGetNative(pTimer->Thread), UINT32_MAX);
+
+ /*
+ * Free resource.
+ */
+ rc = CloseHandle(pTimer->hTimer);
+ AssertMsg(rc, ("CloseHandle lasterr=%d\n", GetLastError()));
+
+ RTSemEventDestroy(pTimer->Event);
+ pTimer->Event = NIL_RTSEMEVENT;
+
+ RTMemFree(pTimer);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerStart(PRTTIMER pTimer, uint64_t u64First)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ AssertReturn(pTimer->Thread != RTThreadSelf(), VERR_INTERNAL_ERROR);
+
+ RTThreadUserReset(pTimer->Thread);
+
+ /*
+ * Already running?
+ */
+ if (!ASMAtomicXchgBool(&pTimer->fSuspended, false))
+ return VERR_TIMER_ACTIVE;
+ LogFlow(("RTTimerStart: pTimer=%p u64First=%llu u64NanoInterval=%llu\n", pTimer, u64First, pTimer->u64NanoInterval));
+
+ /*
+ * Tell the thread to start servicing the timer.
+ * Wait for it to ACK the request to avoid reset races.
+ */
+ ASMAtomicUoWriteU64(&pTimer->u64NanoFirst, u64First);
+ ASMAtomicUoWriteU64(&pTimer->iTick, 0);
+ int rc = RTSemEventSignal(pTimer->Event);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ RTThreadUserReset(pTimer->Thread);
+ }
+ else
+ AssertRC(rc);
+
+ if (RT_FAILURE(rc))
+ ASMAtomicXchgBool(&pTimer->fSuspended, true);
+ return rc;
+}
+
+
+RTDECL(int) RTTimerStop(PRTTIMER pTimer)
+{
+ /*
+ * Validate input.
+ */
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+
+ RTThreadUserReset(pTimer->Thread);
+
+ /*
+ * Already running?
+ */
+ if (ASMAtomicXchgBool(&pTimer->fSuspended, true))
+ return VERR_TIMER_SUSPENDED;
+ LogFlow(("RTTimerStop: pTimer=%p\n", pTimer));
+
+ /*
+ * Tell the thread to stop servicing the timer.
+ */
+ int rc = VINF_SUCCESS;
+ if (RTThreadSelf() != pTimer->Thread)
+ {
+ LARGE_INTEGER ll = {0};
+ ll.LowPart = 100;
+ rc = SetWaitableTimer(pTimer->hTimer, &ll, 0, NULL, NULL, FALSE);
+ AssertMsg(rc, ("SetWaitableTimer lasterr=%d\n", GetLastError()));
+ rc = RTThreadUserWait(pTimer->Thread, 45*1000);
+ AssertRC(rc);
+ RTThreadUserReset(pTimer->Thread);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTTimerChangeInterval(PRTTIMER pTimer, uint64_t u64NanoInterval)
+{
+ AssertPtrReturn(pTimer, VERR_INVALID_POINTER);
+ AssertReturn(pTimer->u32Magic == RTTIMER_MAGIC, VERR_INVALID_MAGIC);
+ NOREF(u64NanoInterval);
+ return VERR_NOT_SUPPORTED;
+}
diff --git a/src/VBox/Runtime/r3/win/tls-win.cpp b/src/VBox/Runtime/r3/win/tls-win.cpp
new file mode 100644
index 00000000..5145a266
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/tls-win.cpp
@@ -0,0 +1,230 @@
+/* $Id: tls-win.cpp $ */
+/** @file
+ * IPRT - Thread Local Storage (TLS), Win32.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_THREAD
+#include <iprt/win/windows.h>
+
+#include <iprt/thread.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/errcore.h>
+#include <iprt/log.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+#include "internal/thread.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+typedef struct RTTLSWINDTOR
+{
+ RTLISTNODE ListEntry;
+ DWORD iTls;
+ PFNRTTLSDTOR pfnDestructor;
+} RTTLSWINDTOR;
+typedef RTTLSWINDTOR *PRTTLSWINDTOR;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once for the list and critical section. */
+static RTONCE g_Once = RTONCE_INITIALIZER;
+/** Critical section protecting the TLS destructor list. */
+static RTCRITSECTRW g_CritSect;
+/** List of TLS destrictors (RTTLSWINDTOR). */
+static RTLISTANCHOR g_TlsDtorHead;
+/** Number of desturctors in the list (helps putting of initialization). */
+static uint32_t volatile g_cTlsDtors = 0;
+
+
+/**
+ * @callback_method_impl{FNRTONCE}
+ */
+static DECLCALLBACK(int32_t) rtTlsWinInitLock(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTListInit(&g_TlsDtorHead);
+ return RTCritSectRwInit(&g_CritSect);
+}
+
+
+RTR3DECL(RTTLS) RTTlsAlloc(void)
+{
+ AssertCompile(sizeof(RTTLS) >= sizeof(DWORD));
+ DWORD iTls = TlsAlloc();
+ return iTls != TLS_OUT_OF_INDEXES ? (RTTLS)iTls : NIL_RTTLS;
+}
+
+
+RTR3DECL(int) RTTlsAllocEx(PRTTLS piTls, PFNRTTLSDTOR pfnDestructor)
+{
+ int rc;
+ if (!pfnDestructor)
+ {
+ DWORD iTls = TlsAlloc();
+ if (iTls != TLS_OUT_OF_INDEXES)
+ {
+ Assert((RTTLS)iTls != NIL_RTTLS);
+ *piTls = (RTTLS)iTls;
+ Assert((DWORD)*piTls == iTls);
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ {
+ rc = RTOnce(&g_Once, rtTlsWinInitLock, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ PRTTLSWINDTOR pDtor = (PRTTLSWINDTOR)RTMemAlloc(sizeof(*pDtor));
+ if (pDtor)
+ {
+ DWORD iTls = TlsAlloc();
+ if (iTls != TLS_OUT_OF_INDEXES)
+ {
+ Assert((RTTLS)iTls != NIL_RTTLS);
+ *piTls = (RTTLS)iTls;
+ Assert((DWORD)*piTls == iTls);
+
+ /*
+ * Add the destructor to the list. We keep it sorted.
+ */
+ pDtor->iTls = iTls;
+ pDtor->pfnDestructor = pfnDestructor;
+ RTCritSectRwEnterExcl(&g_CritSect);
+ RTListAppend(&g_TlsDtorHead, &pDtor->ListEntry);
+ ASMAtomicIncU32(&g_cTlsDtors);
+ RTCritSectRwLeaveExcl(&g_CritSect);
+
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTTlsFree(RTTLS iTls)
+{
+ if (iTls == NIL_RTTLS)
+ return VINF_SUCCESS;
+ if (TlsFree((DWORD)iTls))
+ {
+ if (ASMAtomicReadU32(&g_cTlsDtors) > 0)
+ {
+ RTCritSectRwEnterExcl(&g_CritSect);
+ PRTTLSWINDTOR pDtor;
+ RTListForEach(&g_TlsDtorHead, pDtor, RTTLSWINDTOR, ListEntry)
+ {
+ if (pDtor->iTls == (DWORD)iTls)
+ {
+ RTListNodeRemove(&pDtor->ListEntry);
+ ASMAtomicDecU32(&g_cTlsDtors);
+ RTMemFree(pDtor);
+ break;
+ }
+ }
+ RTCritSectRwLeaveExcl(&g_CritSect);
+ }
+ return VINF_SUCCESS;
+ }
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(void *) RTTlsGet(RTTLS iTls)
+{
+ return TlsGetValue((DWORD)iTls);
+}
+
+
+RTR3DECL(int) RTTlsGetEx(RTTLS iTls, void **ppvValue)
+{
+ void *pv = TlsGetValue((DWORD)iTls);
+ if (pv)
+ {
+ *ppvValue = pv;
+ return VINF_SUCCESS;
+ }
+
+ /* TlsGetValue always updates last error */
+ *ppvValue = NULL;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+RTR3DECL(int) RTTlsSet(RTTLS iTls, void *pvValue)
+{
+ if (TlsSetValue((DWORD)iTls, pvValue))
+ return VINF_SUCCESS;
+ return RTErrConvertFromWin32(GetLastError());
+}
+
+
+/**
+ * Called by dllmain-win.cpp when a thread detaches.
+ */
+DECLHIDDEN(void) rtThreadWinTlsDestruction(void)
+{
+ if (ASMAtomicReadU32(&g_cTlsDtors) > 0)
+ {
+ RTCritSectRwEnterShared(&g_CritSect);
+ PRTTLSWINDTOR pDtor;
+ RTListForEach(&g_TlsDtorHead, pDtor, RTTLSWINDTOR, ListEntry)
+ {
+ void *pvValue = TlsGetValue(pDtor->iTls);
+ if (pvValue != NULL)
+ {
+ pDtor->pfnDestructor(pvValue);
+ TlsSetValue(pDtor->iTls, NULL);
+ }
+ }
+ RTCritSectRwLeaveShared(&g_CritSect);
+ }
+}
+
diff --git a/src/VBox/Runtime/r3/win/tpm-win.cpp b/src/VBox/Runtime/r3/win/tpm-win.cpp
new file mode 100644
index 00000000..3ee0e46b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/tpm-win.cpp
@@ -0,0 +1,307 @@
+/* $Id: tpm-win.cpp $ */
+/** @file
+ * IPRT - Trusted Platform Module (TPM) access, Windows variant.
+ */
+
+/*
+ * Copyright (C) 2021-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_DEFAULT
+#include <iprt/tpm.h>
+
+#include <iprt/assertcompile.h>
+#include <iprt/asm.h>
+#include <iprt/err.h>
+#include <iprt/ldr.h>
+#include <iprt/mem.h>
+#include <iprt/once.h>
+
+#include "internal-r3-win.h"
+
+#include <iprt/win/windows.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/* tbs.dll: */
+typedef struct TBS_CONTEXT_PARAMS2
+{
+ UINT32 version;
+ union
+ {
+ struct
+ {
+ UINT32 requestRaw: 1;
+ UINT32 includeTpm12: 1;
+ UINT32 includeTpm20: 1;
+ } Fields;
+
+ UINT32 asUINT32;
+ } u;
+} TBS_CONTEXT_PARAMS2;
+
+typedef struct TBS_DEVICE_INFO
+{
+ UINT32 structVersion;
+ UINT32 tpmVersion;
+ UINT32 tpmInterfaceType;
+ UINT32 tpmImpRevision;
+} TBS_DEVICE_INFO;
+
+#define TPM_VERSION_12 1
+#define TPM_VERSION_20 2
+
+#define TBS_SUCCESS S_OK
+#define TBS_COMMAND_PRIORITY_NORMAL 200
+
+typedef UINT32 TBS_RESULT;
+typedef void *TBS_HCONTEXT;
+typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CREATE)(const TBS_CONTEXT_PARAMS2 *, TBS_HCONTEXT *);
+typedef TBS_RESULT (WINAPI *PFNTBSI_CONTEXT_CLOSE)(TBS_HCONTEXT);
+typedef TBS_RESULT (WINAPI *PFNTBSI_GET_DEVICE_INFO)(UINT32, TBS_DEVICE_INFO *);
+typedef TBS_RESULT (WINAPI *PFNTBSI_CANCEL_COMMANDS)(TBS_HCONTEXT);
+typedef TBS_RESULT (WINAPI *PFNTBSI_SUBMIT_COMMANDS)(TBS_HCONTEXT, UINT32, UINT32, const BYTE *, UINT32, PBYTE, PUINT32);
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Internal TPM instance data.
+ */
+typedef struct RTTPMINT
+{
+ /** Handle to the TPM context. */
+ TBS_HCONTEXT hCtx;
+ /** The deduced TPM version. */
+ RTTPMVERSION enmTpmVers;
+} RTTPMINT;
+/** Pointer to the internal TPM instance data. */
+typedef RTTPMINT *PRTTPMINT;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Init once structure. */
+static RTONCE g_rtTpmWinInitOnce = RTONCE_INITIALIZER;
+/* tbs.dll: */
+static PFNTBSI_CONTEXT_CREATE g_pfnTbsiContextCreate = NULL;
+static PFNTBSI_CONTEXT_CLOSE g_pfnTbsiContextClose = NULL;
+static PFNTBSI_GET_DEVICE_INFO g_pfnTbsiGetDeviceInfo = NULL;
+static PFNTBSI_CANCEL_COMMANDS g_pfnTbsiCancelCommands = NULL;
+static PFNTBSI_SUBMIT_COMMANDS g_pfnTbsiSubmitCommands = NULL;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * Initialize the globals.
+ *
+ * @returns IPRT status code.
+ * @param pvUser Ignored.
+ */
+static DECLCALLBACK(int32_t) rtTpmWinInitOnce(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ RTLDRMOD hMod;
+
+ int rc = RTLdrLoadSystem("tbs.dll", true /*fNoUnload*/, &hMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hMod, "Tbsi_Context_Create", (void **)&g_pfnTbsiContextCreate);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsip_Context_Close", (void **)&g_pfnTbsiContextClose);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsip_Cancel_Commands", (void **)&g_pfnTbsiCancelCommands);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsip_Submit_Command", (void **)&g_pfnTbsiSubmitCommands);
+ if (RT_FAILURE(rc)) return rc;
+
+ rc = RTLdrGetSymbol(hMod, "Tbsi_GetDeviceInfo", (void **)&g_pfnTbsiGetDeviceInfo);
+ if (RT_FAILURE(rc)) { g_pfnTbsiGetDeviceInfo = NULL; Assert(g_enmWinVer < kRTWinOSType_8); }
+
+ RTLdrClose(hMod);
+ }
+
+ return rc;
+}
+
+
+RTDECL(int) RTTpmOpen(PRTTPM phTpm, uint32_t idTpm)
+{
+ AssertPtrReturn(phTpm, VERR_INVALID_POINTER);
+ if (idTpm == RTTPM_ID_DEFAULT)
+ idTpm = 0;
+
+ AssertReturn(idTpm == 0, VERR_NOT_SUPPORTED);
+
+ /*
+ * Initialize the globals.
+ */
+ int rc = RTOnce(&g_rtTpmWinInitOnce, rtTpmWinInitOnce, NULL);
+ AssertRCReturn(rc, rc);
+
+ PRTTPMINT pThis = (PRTTPMINT)RTMemAllocZ(sizeof(*pThis));
+ if (pThis)
+ {
+ TBS_CONTEXT_PARAMS2 CtxParams; RT_ZERO(CtxParams);
+
+ CtxParams.version = TPM_VERSION_12;
+ if (g_pfnTbsiGetDeviceInfo)
+ {
+ /* TPM2 support is available starting with Win8 which has Tbsi_GetDeviceInfo available. */
+ TBS_DEVICE_INFO DevInfo; RT_ZERO(DevInfo);
+
+ DevInfo.structVersion = TPM_VERSION_20;
+ TBS_RESULT rcTbs = g_pfnTbsiGetDeviceInfo(sizeof(DevInfo), &DevInfo);
+ if (rcTbs == TBS_SUCCESS)
+ {
+ CtxParams.version = TPM_VERSION_20;
+ if (DevInfo.tpmVersion == TPM_VERSION_20)
+ {
+ pThis->enmTpmVers = RTTPMVERSION_2_0;
+ CtxParams.u.Fields.includeTpm20 = 1;
+ }
+ else
+ {
+ Assert(DevInfo.tpmVersion == TPM_VERSION_12);
+ pThis->enmTpmVers = RTTPMVERSION_1_2;
+ CtxParams.u.Fields.includeTpm12 = 1;
+ }
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+ else
+ pThis->enmTpmVers = RTTPMVERSION_1_2;
+
+ if (RT_SUCCESS(rc))
+ {
+ TBS_RESULT rcTbs = g_pfnTbsiContextCreate(&CtxParams, &pThis->hCtx);
+ if (rcTbs == TBS_SUCCESS)
+ {
+ *phTpm = pThis;
+ return VINF_SUCCESS;
+ }
+ else
+ rc = VERR_NOT_FOUND;
+ }
+
+ RTMemFree(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+
+ return rc;
+}
+
+
+RTDECL(int) RTTpmClose(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ TBS_RESULT rcTbs = g_pfnTbsiContextClose(pThis->hCtx);
+ Assert(rcTbs == TBS_SUCCESS); RT_NOREF(rcTbs);
+
+ RTMemFree(pThis);
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(RTTPMVERSION) RTTpmGetVersion(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, RTTPMVERSION_INVALID);
+ return pThis->enmTpmVers;
+}
+
+
+RTDECL(uint32_t) RTTpmGetLocalityMax(RTTPM hTpm)
+{
+ RT_NOREF(hTpm);
+ return 0; /* Only TPM locality 0 is supported. */
+}
+
+
+RTDECL(int) RTTpmReqCancel(RTTPM hTpm)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+
+ TBS_RESULT rcTbs = g_pfnTbsiCancelCommands(pThis->hCtx);
+ if (rcTbs != TBS_SUCCESS)
+ return VERR_DEV_IO_ERROR;
+
+ return VINF_SUCCESS;
+}
+
+
+RTDECL(int) RTTpmReqExec(RTTPM hTpm, uint8_t bLoc, const void *pvReq, size_t cbReq,
+ void *pvResp, size_t cbRespMax, size_t *pcbResp)
+{
+ PRTTPMINT pThis = hTpm;
+
+ AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
+ AssertPtrReturn(pvReq, VERR_INVALID_POINTER);
+ AssertPtrReturn(pvResp, VERR_INVALID_POINTER);
+ AssertReturn(cbReq && cbRespMax, VERR_INVALID_PARAMETER);
+ AssertReturn(cbReq == (UINT32)cbReq && cbRespMax == (UINT32)cbRespMax, VERR_BUFFER_OVERFLOW);
+ AssertReturn(bLoc == 0, VERR_NOT_SUPPORTED); /* TBS doesn't support another locality than 0. */
+
+ UINT32 cbResult = (UINT32)cbRespMax;
+ TBS_RESULT rcTbs = g_pfnTbsiSubmitCommands(pThis->hCtx, 0 /*Locality*/, TBS_COMMAND_PRIORITY_NORMAL,
+ (const BYTE *)pvReq, (UINT32)cbReq, (BYTE *)pvResp, &cbResult);
+ if (rcTbs == TBS_SUCCESS)
+ {
+ if (pcbResp)
+ *pcbResp = cbResult;
+ return VINF_SUCCESS;
+ }
+
+ return VERR_DEV_IO_ERROR;
+}
+
diff --git a/src/VBox/Runtime/r3/win/utf16locale-win.cpp b/src/VBox/Runtime/r3/win/utf16locale-win.cpp
new file mode 100644
index 00000000..d282572a
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/utf16locale-win.cpp
@@ -0,0 +1,58 @@
+/* $Id: utf16locale-win.cpp $ */
+/** @file
+ * IPRT - UTF-16 Locale Specific Manipulation, Win32.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UTF16
+#include <iprt/win/windows.h>
+
+#include <iprt/utf16.h>
+
+
+RTDECL(int) RTUtf16LocaleICmp(PCRTUTF16 pusz1, PCRTUTF16 pusz2)
+{
+ if (pusz1 == pusz2)
+ return 0;
+ if (pusz1 == NULL)
+ return -1;
+ if (pusz2 == NULL)
+ return 1;
+
+ return CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pusz1, -1, pusz2, -1) - 2;
+}
+
diff --git a/src/VBox/Runtime/r3/win/utf8-win.cpp b/src/VBox/Runtime/r3/win/utf8-win.cpp
new file mode 100644
index 00000000..7defa137
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/utf8-win.cpp
@@ -0,0 +1,204 @@
+/* $Id: utf8-win.cpp $ */
+/** @file
+ * IPRT - UTF8 helpers.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UTF8
+#include <iprt/win/windows.h>
+
+#include <iprt/string.h>
+#include <iprt/alloc.h>
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/utf16.h>
+
+
+
+RTR3DECL(int) RTStrUtf8ToCurrentCPTag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return RTStrUtf8ToCurrentCPExTag(ppszString, pszString, RTSTR_MAX, pszTag);
+}
+
+
+RTR3DECL(int) RTStrUtf8ToCurrentCPExTag(char **ppszString, const char *pszString, size_t cchString, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * If the ANSI codepage (CP_ACP) is UTF-8, no translation is needed.
+ * Same goes for empty strings.
+ */
+ if ( cchString == 0
+ || *pszString == '\0')
+ return RTStrDupNExTag(ppszString, pszString, 0, pszTag);
+ if (GetACP() == CP_UTF8)
+ {
+ int rc = RTStrValidateEncodingEx(pszString, cchString, 0);
+ AssertRCReturn(rc, rc);
+ return RTStrDupNExTag(ppszString, pszString, cchString, pszTag);
+ }
+
+ /*
+ * Convert to wide char first.
+ */
+ PRTUTF16 pwszString = NULL;
+ int rc = RTStrToUtf16Ex(pszString, cchString, &pwszString, 0, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * First calc result string length.
+ */
+ int cbResult = WideCharToMultiByte(CP_ACP, 0, pwszString, -1, NULL, 0, NULL, NULL);
+ if (cbResult > 0)
+ {
+ /*
+ * Alloc space for result buffer.
+ */
+ LPSTR lpString = (LPSTR)RTMemTmpAllocTag(cbResult, pszTag);
+ if (lpString)
+ {
+ /*
+ * Do the translation.
+ */
+ if (WideCharToMultiByte(CP_ACP, 0, pwszString, -1, lpString, cbResult, NULL, NULL) > 0)
+ {
+ /* ok */
+ *ppszString = lpString;
+ RTMemTmpFree(pwszString);
+ return VINF_SUCCESS;
+ }
+
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("Unicode to ACP translation failed. lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ RTMemTmpFree(lpString);
+ }
+ else
+ {
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("Unicode to ACP translation failed lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ RTMemTmpFree(pwszString);
+ return rc;
+}
+
+static int rtStrCPToUtf8Tag(char **ppszString, const char *pszString, uint32_t uCodePage, const char *pszTag)
+{
+ Assert(ppszString);
+ Assert(pszString);
+ *ppszString = NULL;
+
+ /*
+ * If the ANSI codepage (CP_ACP) is UTF-8, no translation is needed.
+ * Same goes for empty strings.
+ */
+ if (*pszString == '\0')
+ return RTStrDupExTag(ppszString, pszString, pszTag);
+ if (GetACP() == CP_UTF8)
+ {
+ int rc = RTStrValidateEncoding(pszString);
+ AssertRCReturn(rc, rc);
+ return RTStrDupExTag(ppszString, pszString, pszTag);
+ }
+
+ /** @todo is there a quicker way? Currently: ACP -> UTF-16 -> UTF-8 */
+
+ /*
+ * First calc result string length.
+ */
+ int rc;
+ int cwc = MultiByteToWideChar((UINT)uCodePage, 0, pszString, -1, NULL, 0);
+ if (cwc > 0)
+ {
+ /*
+ * Alloc space for result buffer.
+ */
+ PRTUTF16 pwszString = (PRTUTF16)RTMemTmpAlloc(cwc * sizeof(RTUTF16));
+ if (pwszString)
+ {
+ /*
+ * Do the translation.
+ */
+ if (MultiByteToWideChar((UINT)uCodePage, 0, pszString, -1, pwszString, cwc) > 0)
+ {
+ /*
+ * Now we got UTF-16, convert it to UTF-8
+ */
+ rc = RTUtf16ToUtf8(pwszString, ppszString);
+ RTMemTmpFree(pwszString);
+ return rc;
+ }
+ RTMemTmpFree(pwszString);
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("ACP to Unicode translation failed. lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ else
+ rc = VERR_NO_TMP_MEMORY;
+ }
+ else
+ {
+ /* translation error */
+ int iLastErr = GetLastError();
+ AssertMsgFailed(("Unicode to ACP translation failed lasterr=%d\n", iLastErr));
+ rc = RTErrConvertFromWin32(iLastErr);
+ }
+ return rc;
+}
+
+
+RTR3DECL(int) RTStrCurrentCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return rtStrCPToUtf8Tag(ppszString, pszString, CP_ACP, pszTag);
+}
+
+
+RTR3DECL(int) RTStrConsoleCPToUtf8Tag(char **ppszString, const char *pszString, const char *pszTag)
+{
+ return rtStrCPToUtf8Tag(ppszString, pszString, GetConsoleCP(), pszTag);
+}
diff --git a/src/VBox/Runtime/r3/win/uuid-win.cpp b/src/VBox/Runtime/r3/win/uuid-win.cpp
new file mode 100644
index 00000000..eebeb0c0
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/uuid-win.cpp
@@ -0,0 +1,195 @@
+/* $Id: uuid-win.cpp $ */
+/** @file
+ * IPRT - UUID, Windows implementation.
+ */
+
+/*
+ * 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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_UUID
+#include <iprt/win/windows.h>
+
+#include <iprt/uuid.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/errcore.h>
+
+
+RTDECL(int) RTUuidClear(PRTUUID pUuid)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+
+ return RTErrConvertFromWin32(UuidCreateNil((UUID *)pUuid));
+}
+
+
+RTDECL(bool) RTUuidIsNull(PCRTUUID pUuid)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, true);
+
+ RPC_STATUS status;
+ return !!UuidIsNil((UUID *)pUuid, &status);
+}
+
+
+RTDECL(int) RTUuidCompare(PCRTUUID pUuid1, PCRTUUID pUuid2)
+{
+ /*
+ * Special cases.
+ */
+ if (pUuid1 == pUuid2)
+ return 0;
+ if (!pUuid1)
+ return RTUuidIsNull(pUuid2) ? 0 : -1;
+ if (!pUuid2)
+ return RTUuidIsNull(pUuid1) ? 0 : 1;
+ AssertPtrReturn(pUuid1, -1);
+ AssertPtrReturn(pUuid2, 1);
+
+ /*
+ * Hand the rest to the Windows API.
+ */
+ RPC_STATUS status;
+ return UuidCompare((UUID *)pUuid1, (UUID *)pUuid2, &status);
+}
+
+
+RTDECL(int) RTUuidCompareStr(PCRTUUID pUuid1, const char *pszString2)
+{
+ /* check params */
+ AssertPtrReturn(pUuid1, -1);
+ AssertPtrReturn(pszString2, 1);
+
+ /*
+ * Try convert the string to a UUID and then compare the two.
+ */
+ RTUUID Uuid2;
+ int rc = RTUuidFromStr(&Uuid2, pszString2);
+ AssertRCReturn(rc, 1);
+
+ return RTUuidCompare(pUuid1, &Uuid2);
+}
+
+
+RTDECL(int) RTUuidCompare2Strs(const char *pszString1, const char *pszString2)
+{
+ RTUUID Uuid1;
+ RTUUID Uuid2;
+ int rc;
+
+ /* check params */
+ AssertPtrReturn(pszString1, -1);
+ AssertPtrReturn(pszString2, 1);
+
+ /*
+ * Try convert the strings to UUIDs and then compare them.
+ */
+ rc = RTUuidFromStr(&Uuid1, pszString1);
+ AssertRCReturn(rc, -1);
+
+ rc = RTUuidFromStr(&Uuid2, pszString2);
+ AssertRCReturn(rc, 1);
+
+ return RTUuidCompare(&Uuid1, &Uuid2);
+}
+
+
+RTDECL(int) RTUuidToStr(PCRTUUID pUuid, char *pszString, size_t cchString)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszString, VERR_INVALID_POINTER);
+ AssertReturn(cchString >= RTUUID_STR_LENGTH, VERR_INVALID_PARAMETER);
+
+ /*
+ * Try convert it.
+ *
+ * The API allocates a new string buffer for us, so we can do our own
+ * buffer overflow handling.
+ */
+ RPC_STATUS Status;
+ unsigned char *pszTmpStr = NULL;
+#ifdef RPC_UNICODE_SUPPORTED
+ /* always use ASCII version! */
+ Status = UuidToStringA((UUID *)pUuid, &pszTmpStr);
+#else
+ Status = UuidToString((UUID *)pUuid, &pszTmpStr);
+#endif
+ if (Status != RPC_S_OK)
+ return RTErrConvertFromWin32(Status);
+
+ /* copy it. */
+ int rc = VINF_SUCCESS;
+ size_t cchTmpStr = strlen((char *)pszTmpStr);
+ if (cchTmpStr < cchString)
+ memcpy(pszString, pszTmpStr, cchTmpStr + 1);
+ else
+ {
+ AssertFailed();
+ rc = ERROR_BUFFER_OVERFLOW;
+ }
+
+ /* free buffer */
+#ifdef RPC_UNICODE_SUPPORTED
+ /* always use ASCII version! */
+ RpcStringFreeA(&pszTmpStr);
+#else
+ RpcStringFree(&pszTmpStr);
+#endif
+
+ /* all done */
+ return rc;
+}
+
+
+RTDECL(int) RTUuidFromStr(PRTUUID pUuid, const char *pszString)
+{
+ /* check params */
+ AssertPtrReturn(pUuid, VERR_INVALID_POINTER);
+ AssertPtrReturn(pszString, VERR_INVALID_POINTER);
+
+ RPC_STATUS rc;
+#ifdef RPC_UNICODE_SUPPORTED
+ /* always use ASCII version! */
+ rc = UuidFromStringA((unsigned char *)pszString, (UUID *)pUuid);
+#else
+ rc = UuidFromString((unsigned char *)pszString, (UUID *)pUuid);
+#endif
+
+ return RTErrConvertFromWin32(rc);
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h
new file mode 100644
index 00000000..1708ee52
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-100.h
@@ -0,0 +1,43 @@
+
+COMMENT("XP SP2 / W2K3 SP1 / VISTA")
+MAKE_IMPORT_ENTRY(6,0, DecodePointer, 4)
+MAKE_IMPORT_ENTRY(6,0, EncodePointer, 4)
+COMMENT("XP")
+MAKE_IMPORT_ENTRY(5,1, CreateIoCompletionPort, 16)
+MAKE_IMPORT_ENTRY(5,1, GetQueuedCompletionStatus, 20)
+MAKE_IMPORT_ENTRY(5,1, HeapSetInformation, 16)
+MAKE_IMPORT_ENTRY(5,1, HeapQueryInformation, 20)
+MAKE_IMPORT_ENTRY(5,1, InitializeSListHead, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedFlushSList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPopEntrySList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPushEntrySList, 8)
+MAKE_IMPORT_ENTRY(5,1, PostQueuedCompletionStatus, 16)
+MAKE_IMPORT_ENTRY(5,1, QueryDepthSList, 4)
+COMMENT("W2K")
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueue, 0)
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueueTimer, 28)
+MAKE_IMPORT_ENTRY(5,0, DeleteTimerQueueTimer, 12)
+MAKE_IMPORT_ENTRY(5,0, VerSetConditionMask, 16)
+COMMENT("NT 4 SP4+")
+MAKE_IMPORT_ENTRY(5,0, VerifyVersionInfoA, 16)
+COMMENT("NT 4 SP3+")
+MAKE_IMPORT_ENTRY(5,0, InitializeCriticalSectionAndSpinCount, 8)
+COMMENT("NT 4")
+MAKE_IMPORT_ENTRY(4,0, IsProcessorFeaturePresent, 4)
+MAKE_IMPORT_ENTRY(4,0, CancelIo, 4)
+COMMENT("NT 3.51")
+MAKE_IMPORT_ENTRY(3,51, IsDebuggerPresent, 0)
+MAKE_IMPORT_ENTRY(3,51, GetSystemTimeAsFileTime, 4)
+COMMENT("NT 3.50")
+MAKE_IMPORT_ENTRY(3,50, GetVersionExA, 4)
+MAKE_IMPORT_ENTRY(3,50, GetVersionExW, 4)
+MAKE_IMPORT_ENTRY(3,50, GetEnvironmentStringsW, 0)
+MAKE_IMPORT_ENTRY(3,50, FreeEnvironmentStringsW, 4)
+MAKE_IMPORT_ENTRY(3,50, GetLocaleInfoA, 16)
+MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesA, 8)
+MAKE_IMPORT_ENTRY(3,50, IsValidLocale, 8)
+MAKE_IMPORT_ENTRY(3,50, SetThreadAffinityMask, 8)
+MAKE_IMPORT_ENTRY(3,50, GetProcessAffinityMask, 12)
+MAKE_IMPORT_ENTRY(3,50, GetHandleInformation, 8)
+MAKE_IMPORT_ENTRY(3,50, SetHandleInformation, 12)
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h
new file mode 100644
index 00000000..a65e6392
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-141.h
@@ -0,0 +1,42 @@
+MAKE_IMPORT_ENTRY(6,0, DecodePointer, 4)
+MAKE_IMPORT_ENTRY(6,0, EncodePointer, 4)
+MAKE_IMPORT_ENTRY(5,1, InitializeSListHead, 4)
+MAKE_IMPORT_ENTRY(5,1, GetModuleHandleExW, 12)
+MAKE_IMPORT_ENTRY(5,0, VerifyVersionInfoA, 16)
+MAKE_IMPORT_ENTRY(5,0, SetFilePointerEx, 20)
+MAKE_IMPORT_ENTRY(5,0, GetFileSizeEx, 8)
+MAKE_IMPORT_ENTRY(5,0, InitializeCriticalSectionAndSpinCount, 8)
+MAKE_IMPORT_ENTRY(4,0, FindFirstFileExW, 24)
+MAKE_IMPORT_ENTRY(4,0, IsProcessorFeaturePresent, 4)
+MAKE_IMPORT_ENTRY(4,0, CancelIo, 4)
+MAKE_IMPORT_ENTRY(3,51, FreeLibraryAndExitThread, 8)
+MAKE_IMPORT_ENTRY(3,51, IsDebuggerPresent, 0)
+MAKE_IMPORT_ENTRY(3,51, GetSystemTimeAsFileTime, 4)
+MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesW, 8)
+MAKE_IMPORT_ENTRY(3,50, GetVersionExA, 4)
+MAKE_IMPORT_ENTRY(3,50, GetVersionExW, 4)
+MAKE_IMPORT_ENTRY(3,50, GetEnvironmentStringsW, 0)
+MAKE_IMPORT_ENTRY(3,50, FreeEnvironmentStringsW, 4)
+MAKE_IMPORT_ENTRY(3,50, IsValidLocale, 8)
+MAKE_IMPORT_ENTRY(3,50, SetThreadAffinityMask, 8)
+MAKE_IMPORT_ENTRY(3,50, GetProcessAffinityMask, 12)
+MAKE_IMPORT_ENTRY(3,50, GetHandleInformation, 8)
+MAKE_IMPORT_ENTRY(3,50, SetHandleInformation, 12)
+
+COMMENT("Not needed (usually)")
+MAKE_IMPORT_ENTRY(5,1, HeapSetInformation, 16)
+MAKE_IMPORT_ENTRY(5,1, HeapQueryInformation, 20)
+MAKE_IMPORT_ENTRY(5,1, InterlockedFlushSList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPopEntrySList, 4)
+MAKE_IMPORT_ENTRY(5,1, InterlockedPushEntrySList, 8)
+MAKE_IMPORT_ENTRY(5,1, QueryDepthSList, 4)
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueue, 0)
+MAKE_IMPORT_ENTRY(5,0, CreateTimerQueueTimer, 28)
+MAKE_IMPORT_ENTRY(5,0, DeleteTimerQueueTimer, 12)
+MAKE_IMPORT_ENTRY(5,0, VerSetConditionMask, 16)
+MAKE_IMPORT_ENTRY(3,51, PostQueuedCompletionStatus, 16)
+MAKE_IMPORT_ENTRY(3,51, CreateIoCompletionPort, 16)
+MAKE_IMPORT_ENTRY(3,51, GetQueuedCompletionStatus, 20)
+MAKE_IMPORT_ENTRY(3,50, GetLocaleInfoA, 16)
+MAKE_IMPORT_ENTRY(3,50, EnumSystemLocalesA, 8)
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm
new file mode 100644
index 00000000..eb1449c3
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32-A.asm
@@ -0,0 +1,56 @@
+; $Id: vcc-fakes-kernel32-A.asm $
+;; @file
+; IPRT - Wrappers for kernel32 APIs missing in NT4 and earlier.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "vcc-fakes.mac"
+
+%define FAKE_MODULE_NAME kernel32
+
+BEGINDATA
+GLOBALNAME vcc100_kernel32_fakes_asm
+
+%ifdef VCC_FAKES_TARGET_VCC100
+ %include "vcc-fakes-kernel32-100.h"
+%elifdef VCC_FAKES_TARGET_VCC140
+ %include "vcc-fakes-kernel32-141.h"
+%elifdef VCC_FAKES_TARGET_VCC141
+ %include "vcc-fakes-kernel32-141.h"
+%elifdef VCC_FAKES_TARGET_VCC142
+ %include "vcc-fakes-kernel32-141.h"
+%else
+ %error "PORT ME!"
+%endif
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp
new file mode 100644
index 00000000..697f22ca
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-kernel32.cpp
@@ -0,0 +1,891 @@
+/* $Id: vcc-fakes-kernel32.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#ifdef DEBUG
+# include <stdio.h> /* _snprintf */
+#endif
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#include <iprt/nt/nt-and-windows.h>
+
+#include "vcc-fakes.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#ifndef HEAP_STANDARD
+# define HEAP_STANDARD 0
+#endif
+
+
+/** Declare a kernel32 API.
+ * @note We are not exporting them as that causes duplicate symbol troubles in
+ * the OpenGL bits. */
+#define DECL_KERNEL32(a_Type) extern "C" a_Type WINAPI
+
+#if defined(WDK_NTDDI_VERSION) && defined(NTDDI_WIN10)
+# if WDK_NTDDI_VERSION >= NTDDI_WIN10 /* In Windows 10 SDK the 'Sequence' field has been renamed to 'CpuId'. */
+# define SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+# endif
+#endif
+
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static volatile bool g_fInitialized = false;
+#define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cb) DECLARE_FUNCTION_POINTER(a_Name, a_cb)
+#ifdef VCC_FAKES_TARGET_VCC100
+# include "vcc-fakes-kernel32-100.h"
+#elif defined(VCC_FAKES_TARGET_VCC140)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC141)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC142)
+# include "vcc-fakes-kernel32-141.h"
+#else
+# error "Port me!"
+#endif
+
+
+static BOOL FakeSetLastErrorFromNtStatus(NTSTATUS rcNt)
+{
+ DWORD dwErr;
+ switch (rcNt)
+ {
+ case STATUS_INVALID_PARAMETER:
+ case STATUS_INVALID_PARAMETER_1:
+ case STATUS_INVALID_PARAMETER_2:
+ case STATUS_INVALID_PARAMETER_3:
+ case STATUS_INVALID_PARAMETER_4:
+ case STATUS_INVALID_PARAMETER_5:
+ case STATUS_INVALID_PARAMETER_6:
+ case STATUS_INVALID_PARAMETER_7:
+ case STATUS_INVALID_PARAMETER_8:
+ case STATUS_INVALID_PARAMETER_9:
+ case STATUS_INVALID_PARAMETER_10:
+ case STATUS_INVALID_PARAMETER_11:
+ case STATUS_INVALID_PARAMETER_12:
+ dwErr = ERROR_INVALID_PARAMETER;
+ break;
+
+ case STATUS_INVALID_HANDLE:
+ dwErr = ERROR_INVALID_HANDLE;
+ break;
+
+ case STATUS_ACCESS_DENIED:
+ dwErr = ERROR_ACCESS_DENIED;
+ break;
+
+ default:
+ dwErr = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ SetLastError(dwErr);
+ return FALSE;
+}
+
+
+
+DECL_KERNEL32(PVOID) Fake_DecodePointer(PVOID pvEncoded)
+{
+ return pvEncoded;
+}
+
+
+DECL_KERNEL32(PVOID) Fake_EncodePointer(PVOID pvNative)
+{
+ return pvNative;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION pCritSect, DWORD cSpin)
+{
+ RT_NOREF(cSpin);
+ InitializeCriticalSection(pCritSect);
+ return TRUE;
+}
+
+
+DECL_KERNEL32(HANDLE) Fake_CreateIoCompletionPort(HANDLE hFile, HANDLE hExistingCompletionPort, ULONG_PTR uCompletionKey,
+ DWORD cConcurrentThreads)
+{
+ RT_NOREF(hFile, hExistingCompletionPort, uCompletionKey, cConcurrentThreads);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return NULL;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetQueuedCompletionStatus(HANDLE hCompletionPort, PDWORD_PTR pcbTransfered, PULONG_PTR puCompletionKey,
+ LPOVERLAPPED *ppOverlapped, DWORD cMs)
+{
+ RT_NOREF(hCompletionPort, pcbTransfered, puCompletionKey, ppOverlapped, cMs);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_PostQueuedCompletionStatus(HANDLE hCompletionPort, DWORD cbTransfered, ULONG_PTR uCompletionKey,
+ LPOVERLAPPED pOverlapped)
+{
+ RT_NOREF(hCompletionPort, cbTransfered, uCompletionKey, pOverlapped);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_HeapSetInformation(HANDLE hHeap, HEAP_INFORMATION_CLASS enmInfoClass, PVOID pvBuf, SIZE_T cbBuf)
+{
+ RT_NOREF(hHeap);
+ if (enmInfoClass == HeapCompatibilityInformation)
+ {
+ if ( cbBuf != sizeof(ULONG)
+ || !pvBuf
+ || *(PULONG)pvBuf == HEAP_STANDARD
+ )
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_HeapQueryInformation(HANDLE hHeap, HEAP_INFORMATION_CLASS enmInfoClass,
+ PVOID pvBuf, SIZE_T cbBuf, PSIZE_T pcbRet)
+{
+ RT_NOREF(hHeap);
+ if (enmInfoClass == HeapCompatibilityInformation)
+ {
+ *pcbRet = sizeof(ULONG);
+ if (cbBuf < sizeof(ULONG) || !pvBuf)
+ {
+ SetLastError(ERROR_INSUFFICIENT_BUFFER);
+ return FALSE;
+ }
+ *(PULONG)pvBuf = HEAP_STANDARD;
+ return TRUE;
+ }
+
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+}
+
+
+/* These are used by INTEL\mt_obj\Timer.obj: */
+
+DECL_KERNEL32(HANDLE) Fake_CreateTimerQueue(void)
+{
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return NULL;
+}
+
+DECL_KERNEL32(BOOL) Fake_CreateTimerQueueTimer(PHANDLE phTimer, HANDLE hTimerQueue, WAITORTIMERCALLBACK pfnCallback, PVOID pvUser,
+ DWORD msDueTime, DWORD msPeriod, ULONG fFlags)
+{
+ NOREF(phTimer); NOREF(hTimerQueue); NOREF(pfnCallback); NOREF(pvUser); NOREF(msDueTime); NOREF(msPeriod); NOREF(fFlags);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+DECL_KERNEL32(BOOL) Fake_DeleteTimerQueueTimer(HANDLE hTimerQueue, HANDLE hTimer, HANDLE hEvtCompletion)
+{
+ NOREF(hTimerQueue); NOREF(hTimer); NOREF(hEvtCompletion);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+/* This is used by several APIs. */
+
+DECL_KERNEL32(VOID) Fake_InitializeSListHead(PSLIST_HEADER pHead)
+{
+ pHead->Alignment = 0;
+}
+
+
+DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedFlushSList(PSLIST_HEADER pHead)
+{
+ PSLIST_ENTRY pRet = NULL;
+ if (pHead->Next.Next)
+ {
+ for (;;)
+ {
+ SLIST_HEADER OldHead = *pHead;
+ SLIST_HEADER NewHead;
+ NewHead.Alignment = 0;
+#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+ NewHead.CpuId = OldHead.CpuId + 1;
+#else
+ NewHead.Sequence = OldHead.Sequence + 1;
+#endif
+ if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment))
+ {
+ pRet = OldHead.Next.Next;
+ break;
+ }
+ }
+ }
+ return pRet;
+}
+
+DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedPopEntrySList(PSLIST_HEADER pHead)
+{
+ PSLIST_ENTRY pRet = NULL;
+ for (;;)
+ {
+ SLIST_HEADER OldHead = *pHead;
+ pRet = OldHead.Next.Next;
+ if (pRet)
+ {
+ SLIST_HEADER NewHead;
+ __try
+ {
+ NewHead.Next.Next = pRet->Next;
+ }
+ __except(EXCEPTION_EXECUTE_HANDLER)
+ {
+ continue;
+ }
+ NewHead.Depth = OldHead.Depth - 1;
+#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+ NewHead.CpuId = OldHead.CpuId + 1;
+#else
+ NewHead.Sequence = OldHead.Sequence + 1;
+#endif
+ if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment))
+ break;
+ }
+ else
+ break;
+ }
+ return pRet;
+}
+
+DECL_KERNEL32(PSLIST_ENTRY) Fake_InterlockedPushEntrySList(PSLIST_HEADER pHead, PSLIST_ENTRY pEntry)
+{
+ PSLIST_ENTRY pRet = NULL;
+ for (;;)
+ {
+ SLIST_HEADER OldHead = *pHead;
+ pRet = OldHead.Next.Next;
+ pEntry->Next = pRet;
+ SLIST_HEADER NewHead;
+ NewHead.Next.Next = pEntry;
+ NewHead.Depth = OldHead.Depth + 1;
+#ifdef SLIST_HEADER_SEQUENCE_NOW_CALLED_CPUID
+ NewHead.CpuId = OldHead.CpuId + 1;
+#else
+ NewHead.Sequence = OldHead.Sequence + 1;
+#endif
+ if (ASMAtomicCmpXchgU64(&pHead->Alignment, NewHead.Alignment, OldHead.Alignment))
+ break;
+ }
+ return pRet;
+}
+
+DECL_KERNEL32(WORD) Fake_QueryDepthSList(PSLIST_HEADER pHead)
+{
+ return pHead->Depth;
+}
+
+
+/* curl drags these in: */
+DECL_KERNEL32(BOOL) Fake_VerifyVersionInfoA(LPOSVERSIONINFOEXA pInfo, DWORD fTypeMask, DWORDLONG fConditionMask)
+{
+ OSVERSIONINFOEXA VerInfo;
+ RT_ZERO(VerInfo);
+ VerInfo.dwOSVersionInfoSize = sizeof(VerInfo);
+ if (!GetVersionExA((OSVERSIONINFO *)&VerInfo))
+ {
+ RT_ZERO(VerInfo);
+ VerInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ BOOL fRet = GetVersionExA((OSVERSIONINFO *)&VerInfo);
+ if (fRet)
+ { /* likely */ }
+ else
+ {
+ MY_ASSERT(false, "VerifyVersionInfoA: #1");
+ return FALSE;
+ }
+ }
+
+ BOOL fRet = TRUE;
+ for (unsigned i = 0; i < 8 && fRet; i++)
+ if (fTypeMask & RT_BIT_32(i))
+ {
+ uint32_t uLeft, uRight;
+ switch (RT_BIT_32(i))
+ {
+#define MY_CASE(a_Num, a_Member) case a_Num: uLeft = VerInfo.a_Member; uRight = pInfo->a_Member; break
+ MY_CASE(VER_MINORVERSION, dwMinorVersion);
+ MY_CASE(VER_MAJORVERSION, dwMajorVersion);
+ MY_CASE(VER_BUILDNUMBER, dwBuildNumber);
+ MY_CASE(VER_PLATFORMID, dwPlatformId);
+ MY_CASE(VER_SERVICEPACKMINOR, wServicePackMinor);
+ MY_CASE(VER_SERVICEPACKMAJOR, wServicePackMinor);
+ MY_CASE(VER_SUITENAME, wSuiteMask);
+ MY_CASE(VER_PRODUCT_TYPE, wProductType);
+#undef MY_CASE
+ default: uLeft = uRight = 0; MY_ASSERT(false, "VerifyVersionInfoA: #2");
+ }
+ switch ((uint8_t)(fConditionMask >> (i*8)))
+ {
+ case VER_EQUAL: fRet = uLeft == uRight; break;
+ case VER_GREATER: fRet = uLeft > uRight; break;
+ case VER_GREATER_EQUAL: fRet = uLeft >= uRight; break;
+ case VER_LESS: fRet = uLeft < uRight; break;
+ case VER_LESS_EQUAL: fRet = uLeft <= uRight; break;
+ case VER_AND: fRet = (uLeft & uRight) == uRight; break;
+ case VER_OR: fRet = (uLeft & uRight) != 0; break;
+ default: fRet = FALSE; MY_ASSERT(false, "VerifyVersionInfoA: #3"); break;
+ }
+ }
+
+ return fRet;
+}
+
+
+DECL_KERNEL32(ULONGLONG) Fake_VerSetConditionMask(ULONGLONG fConditionMask, DWORD fTypeMask, BYTE bOperator)
+{
+ for (unsigned i = 0; i < 8; i++)
+ if (fTypeMask & RT_BIT_32(i))
+ {
+ uint64_t fMask = UINT64_C(0xff) << (i*8);
+ fConditionMask &= ~fMask;
+ fConditionMask |= (uint64_t)bOperator << (i*8);
+
+ }
+ return fConditionMask;
+}
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 5.0 (windows 2000) */
+DECL_KERNEL32(BOOL) Fake_GetModuleHandleExW(DWORD dwFlags, LPCWSTR pwszModuleName, HMODULE *phModule)
+{
+ HMODULE hmod;
+ if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS)
+ {
+ /** @todo search the loader list. */
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+ }
+ else
+ {
+ hmod = GetModuleHandleW(pwszModuleName);
+ if (!hmod)
+ return FALSE;
+ }
+
+ /*
+ * Get references to the module.
+ */
+ if ( !(dwFlags & (GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | GET_MODULE_HANDLE_EX_FLAG_PIN))
+ && GetModuleHandleW(NULL) != hmod)
+ {
+ WCHAR wszModule[MAX_PATH];
+ if (GetModuleFileNameW(hmod, wszModule, RT_ELEMENTS(wszModule)) > 0)
+ {
+ if (dwFlags & GET_MODULE_HANDLE_EX_FLAG_PIN)
+ {
+ uint32_t cRefs = 32;
+ while (cRefs-- > 0)
+ LoadLibraryW(wszModule);
+ }
+ else if (!LoadLibraryW(wszModule))
+ return FALSE;
+ }
+ }
+
+ *phModule = hmod;
+ return TRUE;
+}
+#endif /* VCC_FAKES_TARGET >= 141 */
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 5.0 (windows 2000) */
+DECL_KERNEL32(BOOL) Fake_SetFilePointerEx(HANDLE hFile, LARGE_INTEGER offDistanceToMove,
+ PLARGE_INTEGER pNewFilePointer, DWORD dwMoveMethod)
+{
+ NTSTATUS rcNt;
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+
+ FILE_POSITION_INFORMATION PosInfo;
+ switch (dwMoveMethod)
+ {
+ case FILE_BEGIN:
+ PosInfo.CurrentByteOffset = offDistanceToMove;
+ break;
+
+ case FILE_CURRENT:
+ PosInfo.CurrentByteOffset.QuadPart = INT64_MAX;
+ rcNt = NtQueryInformationFile(hFile, &Ios, &PosInfo, sizeof(PosInfo), FilePositionInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ PosInfo.CurrentByteOffset.QuadPart += offDistanceToMove.QuadPart;
+ break;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+
+ case FILE_END:
+ {
+ FILE_STANDARD_INFO StdInfo = {{0}};
+ rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ PosInfo.CurrentByteOffset.QuadPart = offDistanceToMove.QuadPart + StdInfo.EndOfFile.QuadPart;
+ break;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+ }
+
+ default:
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return FALSE;
+ }
+
+ rcNt = NtSetInformationFile(hFile, &Ios, &PosInfo, sizeof(PosInfo), FilePositionInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ if (pNewFilePointer)
+ *pNewFilePointer = PosInfo.CurrentByteOffset;
+ return TRUE;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 5.0 (windows 2000) */
+DECL_KERNEL32(BOOL) Fake_GetFileSizeEx(HANDLE hFile, PLARGE_INTEGER pcbFile)
+{
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ FILE_STANDARD_INFO StdInfo = {{0}};
+ NTSTATUS rcNt = NtQueryInformationFile(hFile, &Ios, &StdInfo, sizeof(StdInfo), FileStandardInformation);
+ if (NT_SUCCESS(rcNt))
+ {
+ *pcbFile = StdInfo.EndOfFile;
+ return TRUE;
+ }
+ return FakeSetLastErrorFromNtStatus(rcNt);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+
+
+/*
+ * NT 3.51 stuff.
+ */
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 4.0 */
+DECL_KERNEL32(HANDLE) Fake_FindFirstFileExW(LPCWSTR pwszFileName, FINDEX_INFO_LEVELS enmInfoLevel, LPVOID pvFindFileData,
+ FINDEX_SEARCH_OPS enmSearchOp, LPVOID pvSearchFilter, DWORD dwAdditionalFlags)
+{
+ // STL: FindFirstFileExW(, FindExInfoBasic, , FindExSearchNameMatch, NULL, 0);
+ // CRT/_findfile: FindFirstFileExW(, FindExInfoStandard, , FindExSearchNameMatch, NULL, 0);
+ // CRT/argv_wildcards: FindFirstFileExW(, FindExInfoStandard, , FindExSearchNameMatch, NULL, 0);
+ MY_ASSERT_STMT_RETURN(dwAdditionalFlags == 0, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+ MY_ASSERT_STMT_RETURN(pvSearchFilter == NULL, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+ MY_ASSERT_STMT_RETURN(enmSearchOp == FindExSearchNameMatch, SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+ MY_ASSERT_STMT_RETURN(enmInfoLevel == FindExInfoStandard || enmInfoLevel == FindExInfoBasic,
+ SetLastError(ERROR_INVALID_PARAMETER), INVALID_HANDLE_VALUE);
+
+ return FindFirstFileW(pwszFileName, (WIN32_FIND_DATAW *)pvFindFileData);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+DECL_KERNEL32(BOOL) Fake_IsProcessorFeaturePresent(DWORD enmProcessorFeature)
+{
+ /* Could make more of an effort here... */
+ RT_NOREF(enmProcessorFeature);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_CancelIo(HANDLE hHandle)
+{
+ /* All NT versions the NTDLL API this corresponds to. */
+ RESOLVE_NTDLL_API(NtCancelIoFile);
+ if (pfnNtCancelIoFile)
+ {
+ IO_STATUS_BLOCK Ios = RTNT_IO_STATUS_BLOCK_INITIALIZER;
+ NTSTATUS rcNt = pfnNtCancelIoFile(hHandle, &Ios);
+ if (RT_SUCCESS(rcNt))
+ return TRUE;
+ if (rcNt == STATUS_INVALID_HANDLE)
+ SetLastError(ERROR_INVALID_HANDLE);
+ else
+ SetLastError(ERROR_INVALID_FUNCTION);
+ }
+ else
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+/*
+ * NT 3.50 stuff.
+ */
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 3.51 */
+DECL_KERNEL32(VOID) Fake_FreeLibraryAndExitThread(HMODULE hLibModule, DWORD dwExitCode)
+{
+ if (hLibModule)
+ FreeModule(hLibModule);
+ ExitThread(dwExitCode);
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+DECL_KERNEL32(BOOL) Fake_IsDebuggerPresent(VOID)
+{
+ /* Fallback: */
+ return FALSE;
+}
+
+
+DECL_KERNEL32(VOID) Fake_GetSystemTimeAsFileTime(LPFILETIME pTime)
+{
+ DWORD dwVersion = GetVersion();
+ if ( (dwVersion & 0xff) > 3
+ || ( (dwVersion & 0xff) == 3
+ && ((dwVersion >> 8) & 0xff) >= 50) )
+ {
+ PKUSER_SHARED_DATA pUsd = (PKUSER_SHARED_DATA)MM_SHARED_USER_DATA_VA;
+
+ /* use interrupt time */
+ LARGE_INTEGER Time;
+ do
+ {
+ Time.HighPart = pUsd->SystemTime.High1Time;
+ Time.LowPart = pUsd->SystemTime.LowPart;
+ } while (pUsd->SystemTime.High2Time != Time.HighPart);
+
+ pTime->dwHighDateTime = Time.HighPart;
+ pTime->dwLowDateTime = Time.LowPart;
+ }
+ else
+ {
+ /* NT 3.1 didn't have a KUSER_SHARED_DATA nor a GetSystemTimeAsFileTime export. */
+ SYSTEMTIME SystemTime;
+ GetSystemTime(&SystemTime);
+ BOOL fRet = SystemTimeToFileTime(&SystemTime, pTime);
+ if (fRet)
+ { /* likely */ }
+ else
+ {
+ MY_ASSERT(false, "GetSystemTimeAsFileTime: #2");
+ pTime->dwHighDateTime = 0;
+ pTime->dwLowDateTime = 0;
+ }
+ }
+}
+
+
+/*
+ * NT 3.1 stuff.
+ */
+
+DECL_KERNEL32(BOOL) Fake_GetVersionExA(LPOSVERSIONINFOA pInfo)
+{
+ DWORD dwVersion = GetVersion();
+
+ /* Common fields: */
+ pInfo->dwMajorVersion = dwVersion & 0xff;
+ pInfo->dwMinorVersion = (dwVersion >> 8) & 0xff;
+ if (!(dwVersion & RT_BIT_32(31)))
+ pInfo->dwBuildNumber = dwVersion >> 16;
+ else
+ pInfo->dwBuildNumber = 511;
+ pInfo->dwPlatformId = VER_PLATFORM_WIN32_NT;
+/** @todo get CSD from registry. */
+ pInfo->szCSDVersion[0] = '\0';
+
+ /* OSVERSIONINFOEX fields: */
+ if (pInfo->dwOSVersionInfoSize > sizeof((*pInfo)))
+ {
+ LPOSVERSIONINFOEXA pInfoEx = (LPOSVERSIONINFOEXA)pInfo;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wServicePackMinor))
+ {
+ pInfoEx->wServicePackMajor = 0;
+ pInfoEx->wServicePackMinor = 0;
+ }
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wSuiteMask))
+ pInfoEx->wSuiteMask = 0;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXA, wProductType))
+ pInfoEx->wProductType = VER_NT_WORKSTATION;
+ if (pInfoEx->wReserved > RT_UOFFSETOF(OSVERSIONINFOEXA, wProductType))
+ pInfoEx->wReserved = 0;
+ }
+
+ return TRUE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetVersionExW(LPOSVERSIONINFOW pInfo)
+{
+ DWORD dwVersion = GetVersion();
+
+ /* Common fields: */
+ pInfo->dwMajorVersion = dwVersion & 0xff;
+ pInfo->dwMinorVersion = (dwVersion >> 8) & 0xff;
+ if (!(dwVersion & RT_BIT_32(31)))
+ pInfo->dwBuildNumber = dwVersion >> 16;
+ else
+ pInfo->dwBuildNumber = 511;
+ pInfo->dwPlatformId = VER_PLATFORM_WIN32_NT;
+/** @todo get CSD from registry. */
+ pInfo->szCSDVersion[0] = '\0';
+
+ /* OSVERSIONINFOEX fields: */
+ if (pInfo->dwOSVersionInfoSize > sizeof((*pInfo)))
+ {
+ LPOSVERSIONINFOEXW pInfoEx = (LPOSVERSIONINFOEXW)pInfo;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wServicePackMinor))
+ {
+ pInfoEx->wServicePackMajor = 0;
+ pInfoEx->wServicePackMinor = 0;
+ }
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wSuiteMask))
+ pInfoEx->wSuiteMask = 0;
+ if (pInfoEx->dwOSVersionInfoSize > RT_UOFFSETOF(OSVERSIONINFOEXW, wProductType))
+ pInfoEx->wProductType = VER_NT_WORKSTATION;
+ if (pInfoEx->wReserved > RT_UOFFSETOF(OSVERSIONINFOEXW, wProductType))
+ pInfoEx->wReserved = 0;
+ }
+
+ return TRUE;
+}
+
+
+DECL_KERNEL32(LPWCH) Fake_GetEnvironmentStringsW(void)
+{
+ /*
+ * Environment is ANSI in NT 3.1. We should only be here for NT 3.1.
+ * For now, don't try do a perfect job converting it, just do it.
+ */
+ char *pszzEnv = (char *)RTNtCurrentPeb()->ProcessParameters->Environment;
+ size_t offEnv = 0;
+ while (pszzEnv[offEnv] != '\0')
+ {
+ size_t cchLen = strlen(&pszzEnv[offEnv]);
+ offEnv += cchLen + 1;
+ }
+ size_t const cchEnv = offEnv + 1;
+
+ PRTUTF16 pwszzEnv = (PRTUTF16)HeapAlloc(GetProcessHeap(), 0, cchEnv * sizeof(RTUTF16));
+ if (!pwszzEnv)
+ return NULL;
+ for (offEnv = 0; offEnv < cchEnv; offEnv++)
+ {
+ unsigned char ch = pwszzEnv[offEnv];
+ if (!(ch & 0x80))
+ pwszzEnv[offEnv] = ch;
+ else
+ pwszzEnv[offEnv] = '_';
+ }
+ return pwszzEnv;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_FreeEnvironmentStringsW(LPWCH pwszzEnv)
+{
+ if (pwszzEnv)
+ HeapFree(GetProcessHeap(), 0, pwszzEnv);
+ return TRUE;
+}
+
+
+DECL_KERNEL32(int) Fake_GetLocaleInfoA(LCID idLocale, LCTYPE enmType, LPSTR pData, int cchData)
+{
+ NOREF(idLocale); NOREF(enmType); NOREF(pData); NOREF(cchData);
+ //MY_ASSERT(false, "GetLocaleInfoA: idLocale=%#x enmType=%#x cchData=%#x", idLocale, enmType, cchData);
+ MY_ASSERT(false, "GetLocaleInfoA");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return 0;
+}
+
+
+#if VCC_FAKES_TARGET >= 140
+/** @since 3.51 */
+DECL_KERNEL32(BOOL) Fake_EnumSystemLocalesW(LOCALE_ENUMPROCW pfnLocaleEnum, DWORD dwFlags)
+{
+ RT_NOREF(pfnLocaleEnum, dwFlags);
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+#endif /* VCC_FAKES_TARGET >= 140 */
+
+
+DECL_KERNEL32(BOOL) Fake_EnumSystemLocalesA(LOCALE_ENUMPROCA pfnCallback, DWORD fFlags)
+{
+ NOREF(pfnCallback); NOREF(fFlags);
+ //MY_ASSERT(false, "EnumSystemLocalesA: pfnCallback=%p fFlags=%#x", pfnCallback, fFlags);
+ MY_ASSERT(false, "EnumSystemLocalesA");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_IsValidLocale(LCID idLocale, DWORD fFlags)
+{
+ NOREF(idLocale); NOREF(fFlags);
+ //MY_ASSERT(false, "IsValidLocale: idLocale fFlags=%#x", idLocale, fFlags);
+ MY_ASSERT(false, "IsValidLocale");
+ SetLastError(ERROR_NOT_SUPPORTED);
+ return FALSE;
+}
+
+
+DECL_KERNEL32(DWORD_PTR) Fake_SetThreadAffinityMask(HANDLE hThread, DWORD_PTR fAffinityMask)
+{
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ //MY_ASSERT(false, "SetThreadAffinityMask: hThread=%p fAffinityMask=%p SysInfo.dwActiveProcessorMask=%p",
+ // hThread, fAffinityMask, SysInfo.dwActiveProcessorMask);
+ MY_ASSERT(false, "SetThreadAffinityMask");
+ if ( SysInfo.dwActiveProcessorMask == fAffinityMask
+ || fAffinityMask == ~(DWORD_PTR)0)
+ return fAffinityMask;
+
+ SetLastError(ERROR_NOT_SUPPORTED);
+ RT_NOREF(hThread);
+ return 0;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetProcessAffinityMask(HANDLE hProcess, PDWORD_PTR pfProcessAffinityMask, PDWORD_PTR pfSystemAffinityMask)
+{
+ SYSTEM_INFO SysInfo;
+ GetSystemInfo(&SysInfo);
+ //MY_ASSERT(false, "GetProcessAffinityMask: SysInfo.dwActiveProcessorMask=%p", SysInfo.dwActiveProcessorMask);
+ MY_ASSERT(false, "GetProcessAffinityMask");
+ if (pfProcessAffinityMask)
+ *pfProcessAffinityMask = SysInfo.dwActiveProcessorMask;
+ if (pfSystemAffinityMask)
+ *pfSystemAffinityMask = SysInfo.dwActiveProcessorMask;
+ RT_NOREF(hProcess);
+ return TRUE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_GetHandleInformation(HANDLE hObject, DWORD *pfFlags)
+{
+ OBJECT_HANDLE_FLAG_INFORMATION Info = { 0, 0 };
+ DWORD cbRet = sizeof(Info);
+ NTSTATUS rcNt = NtQueryObject(hObject, ObjectHandleFlagInformation, &Info, sizeof(Info), &cbRet);
+ if (NT_SUCCESS(rcNt))
+ {
+ *pfFlags = (Info.Inherit ? HANDLE_FLAG_INHERIT : 0)
+ | (Info.ProtectFromClose ? HANDLE_FLAG_PROTECT_FROM_CLOSE : 0);
+ return TRUE;
+ }
+ *pfFlags = 0;
+ //MY_ASSERT(rcNt == STATUS_INVALID_HANDLE, "rcNt=%#x", rcNt);
+ MY_ASSERT(rcNt == STATUS_INVALID_HANDLE || rcNt == STATUS_INVALID_INFO_CLASS, "GetHandleInformation");
+ SetLastError(rcNt == STATUS_INVALID_HANDLE ? ERROR_INVALID_HANDLE : ERROR_INVALID_FUNCTION); /* see also process-win.cpp */
+ return FALSE;
+}
+
+
+DECL_KERNEL32(BOOL) Fake_SetHandleInformation(HANDLE hObject, DWORD fMask, DWORD fFlags)
+{
+ NOREF(hObject); NOREF(fMask); NOREF(fFlags);
+ SetLastError(ERROR_INVALID_FUNCTION);
+ return FALSE;
+}
+
+
+
+/**
+ * Resolves all the APIs ones and for all, updating the fake IAT entries.
+ */
+DECLASM(void) FakeResolve_kernel32(void)
+{
+ CURRENT_VERSION_VARIABLE();
+
+ HMODULE hmod = GetModuleHandleW(L"kernel32");
+ MY_ASSERT(hmod != NULL, "kernel32");
+
+#undef MAKE_IMPORT_ENTRY
+#define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cb) RESOLVE_IMPORT(a_uMajorVer, a_uMinorVer, a_Name, a_cb)
+#ifdef VCC_FAKES_TARGET_VCC100
+# include "vcc-fakes-kernel32-100.h"
+#elif defined(VCC_FAKES_TARGET_VCC140)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC141)
+# include "vcc-fakes-kernel32-141.h"
+#elif defined(VCC_FAKES_TARGET_VCC142)
+# include "vcc-fakes-kernel32-141.h"
+#else
+# error "Port me!"
+#endif
+
+ g_fInitialized = true;
+}
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from kernel32. */
+extern "C" int vcc100_kernel32_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp
new file mode 100644
index 00000000..9aaf2d4d
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-msvcrt.cpp
@@ -0,0 +1,93 @@
+/* $Id: vcc-fakes-msvcrt.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/assert.h>
+#include <stdlib.h>
+
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+
+
+/* This one is static in libcmt, fortunately no rocket science. */
+extern "C" void __cdecl my_initterm(PFNRT *papfnStart, PFNRT *papfnEnd)
+{
+ for (; (uintptr_t)papfnStart < (uintptr_t)papfnEnd; papfnStart++)
+ if (*papfnStart)
+ (*papfnStart)();
+}
+
+extern "C" PFNRT __cdecl my_dllonexit(PFNRT pfnToAdd, PFNRT **ppapfnStart, PFNRT **ppapfnEnd)
+{
+ /* This is _very_ crude, but it'll probably do for our purposes... */
+ size_t cItems = *ppapfnEnd - *ppapfnStart;
+ *ppapfnStart = (PFNRT *)realloc(*ppapfnStart, (cItems + 1) * sizeof(**ppapfnStart));
+ (*ppapfnStart)[cItems] = pfnToAdd;
+ *ppapfnEnd = &(*ppapfnStart)[cItems + 1];
+ return pfnToAdd;
+}
+
+extern "C" int _newmode;
+extern "C" int __cdecl __setargv(void);
+extern "C" int __cdecl _setargv(void);
+
+extern "C" int __cdecl my_getmainargs(int *pcArgs, char ***ppapszArgs, char ***ppapszEnv, int fDoWildcardExp, int *pfNewMode)
+{
+ _newmode = *pfNewMode;
+
+ Assert(!fDoWildcardExp);
+ int rc = _setargv();
+ if (rc >= 0)
+ {
+ *pcArgs = __argc;
+ *ppapszArgs = __argv;
+ *ppapszEnv = _environ;
+ }
+ return rc;
+}
+
+extern "C" void __cdecl my_setusermatherr(PFNRT pfnIgnore)
+{
+ RT_NOREF(pfnIgnore);
+ /* pure stub. */
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm
new file mode 100644
index 00000000..4eb482b2
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll-A.asm
@@ -0,0 +1,57 @@
+; $Id: vcc-fakes-ntdll-A.asm $
+;; @file
+; IPRT - Wrappers for ntdll APIs misisng NT4.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+%macro MAKE_IMPORT_ENTRY 2
+extern _ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2
+__imp__ %+ %1 %+ @ %+ %2:
+ dd _ %+ %1 %+ @ %+ %2
+
+%endmacro
+
+
+BEGINDATA
+GLOBALNAME vcc100_ntdll_fakes_asm
+
+MAKE_IMPORT_ENTRY RtlGetLastWin32Error, 0
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp
new file mode 100644
index 00000000..9aa5dd01
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ntdll.cpp
@@ -0,0 +1,84 @@
+/* $Id: vcc-fakes-ntdll.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP - NTDLL.DLL.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#include <iprt/win/windows.h>
+
+
+
+/** Dynamically resolves an kernel32 API. */
+#define RESOLVE_ME(ApiNm) \
+ static bool volatile s_fInitialized = false; \
+ static decltype(ApiNm) *s_pfnApi = NULL; \
+ decltype(ApiNm) *pfnApi; \
+ if (s_fInitialized) \
+ pfnApi = s_pfnApi; \
+ else \
+ { \
+ pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"ntdll.dll"), #ApiNm); \
+ s_pfnApi = pfnApi; \
+ s_fInitialized = true; \
+ } do {} while (0) \
+
+
+extern "C"
+__declspec(dllexport)
+ULONG WINAPI RtlGetLastWin32Error(VOID)
+{
+ RESOLVE_ME(RtlGetLastWin32Error);
+ if (pfnApi)
+ return pfnApi();
+ return GetLastError();
+}
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from kernel32. */
+extern "C" int vcc100_ntdll_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm
new file mode 100644
index 00000000..ddf7491b
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-shell32-A.asm
@@ -0,0 +1,59 @@
+; $Id: vcc-fakes-shell32-A.asm $
+;; @file
+; IPRT - Wrappers for shell32 APIs missing in NT 4 and earlier.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+%macro MAKE_IMPORT_ENTRY 2
+extern _ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2
+__imp__ %+ %1 %+ @ %+ %2:
+ dd _ %+ %1 %+ @ %+ %2
+
+%endmacro
+
+
+BEGINDATA
+GLOBALNAME vcc100_shell32_fakes_asm
+
+; NT 3.1
+MAKE_IMPORT_ENTRY CommandLineToArgvW, 8
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp
new file mode 100644
index 00000000..bd82befa
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-shell32.cpp
@@ -0,0 +1,103 @@
+/* $Id: vcc-fakes-shell32.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define RT_NO_STRICT /* Minimal deps so that it works on NT 3.51 too. */
+#include <iprt/types.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#define CommandLineToArgvW Ignore_CommandLineToArgvW
+
+#include <iprt/nt/nt-and-windows.h>
+
+#undef CommandLineToArgvW
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** Dynamically resolves an kernel32 API. */
+#define RESOLVE_ME(ApiNm) \
+ static decltype(ShellExecuteW) * volatile s_pfnInitialized = NULL; \
+ static decltype(ApiNm) *s_pfnApi = NULL; \
+ decltype(ApiNm) *pfnApi; \
+ if (s_pfnInitialized) \
+ pfnApi = s_pfnApi; \
+ else \
+ { \
+ pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"shell32"), #ApiNm); \
+ s_pfnApi = pfnApi; \
+ s_pfnInitialized = ShellExecuteW; /* ensures shell32 is loaded */ \
+ } do {} while (0)
+
+
+/** Declare a shell32 API.
+ * @note We are not exporting them as that causes duplicate symbol troubles in
+ * the OpenGL bits. */
+#define DECL_SHELL32(a_Type) extern "C" a_Type WINAPI
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+DECL_SHELL32(LPWSTR *) CommandLineToArgvW(LPCWSTR pwszCmdLine, int *pcArgs)
+{
+ RESOLVE_ME(CommandLineToArgvW);
+ if (pfnApi)
+ return pfnApi(pwszCmdLine, pcArgs);
+
+ *pcArgs = 0;
+ return NULL;
+}
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from shell32. */
+extern "C" int vcc100_shell32_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm
new file mode 100644
index 00000000..2158917a
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32-A.asm
@@ -0,0 +1,58 @@
+; $Id: vcc-fakes-ws2_32-A.asm $
+;; @file
+; IPRT - Wrappers for ws2_32 APIs misisng NT4.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+%macro MAKE_IMPORT_ENTRY 2
+extern _ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2
+__imp__ %+ %1 %+ @ %+ %2:
+ dd _ %+ %1 %+ @ %+ %2
+
+%endmacro
+
+
+BEGINDATA
+GLOBALNAME vcc100_ws2_32_fakes_asm
+
+MAKE_IMPORT_ENTRY getaddrinfo, 16
+MAKE_IMPORT_ENTRY freeaddrinfo, 4
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp
new file mode 100644
index 00000000..b12f253e
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes-ws2_32.cpp
@@ -0,0 +1,113 @@
+/* $Id: vcc-fakes-ws2_32.cpp $ */
+/** @file
+ * IPRT - Tricks to make the Visual C++ 2010 CRT work on NT4, W2K and XP - WS2_32.DLL.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <iprt/cdefs.h>
+#include <iprt/types.h>
+#include <iprt/asm.h>
+#include <iprt/string.h>
+
+#ifndef RT_ARCH_X86
+# error "This code is X86 only"
+#endif
+
+#define getaddrinfo Ignore_getaddrinfo
+#define freeaddrinfo Ignore_freeaddrinfo
+
+#include <iprt/win/winsock2.h>
+#include <iprt/win/ws2tcpip.h>
+
+#undef getaddrinfo
+#undef freeaddrinfo
+
+
+/** Dynamically resolves an kernel32 API. */
+#define RESOLVE_ME(ApiNm) \
+ static bool volatile s_fInitialized = false; \
+ static decltype(ApiNm) *s_pfnApi = NULL; \
+ decltype(ApiNm) *pfnApi; \
+ if (s_fInitialized) \
+ pfnApi = s_pfnApi; \
+ else \
+ { \
+ pfnApi = (decltype(pfnApi))GetProcAddress(GetModuleHandleW(L"wc2_32.dll"), #ApiNm); \
+ s_pfnApi = pfnApi; \
+ s_fInitialized = true; \
+ } do {} while (0) \
+
+
+extern "C"
+__declspec(dllexport)
+int WINAPI getaddrinfo(const char *pszNodeName, const char *pszServiceName, const struct addrinfo *pHints,
+ struct addrinfo **ppResults)
+{
+ RESOLVE_ME(getaddrinfo);
+ if (pfnApi)
+ return pfnApi(pszNodeName, pszServiceName, pHints, ppResults);
+
+ /** @todo fallback */
+ WSASetLastError(WSAEAFNOSUPPORT);
+ return WSAEAFNOSUPPORT;
+}
+
+
+
+extern "C"
+__declspec(dllexport)
+void WINAPI freeaddrinfo(struct addrinfo *pResults)
+{
+ RESOLVE_ME(freeaddrinfo);
+ if (pfnApi)
+ pfnApi(pResults);
+ else
+ {
+ Assert(!pResults);
+ /** @todo fallback */
+ }
+}
+
+
+
+/* Dummy to force dragging in this object in the link, so the linker
+ won't accidentally use the symbols from kernel32. */
+extern "C" int vcc100_ws2_32_fakes_cpp(void)
+{
+ return 42;
+}
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes.h b/src/VBox/Runtime/r3/win/vcc-fakes.h
new file mode 100644
index 00000000..6e72e2b8
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes.h
@@ -0,0 +1,155 @@
+/* $Id: vcc-fakes.h $ */
+/** @file
+ * IPRT - Common macros for the Visual C++ 2010+ CRT import fakes.
+ */
+
+/*
+ * Copyright (C) 2012-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>.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+ * in the VirtualBox distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+ */
+
+#ifndef IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h
+#define IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#ifdef RT_STRICT
+# include <stdio.h> /* _snprintf */
+#endif
+
+
+/** @def MY_ASSERT
+ * We use a special assertion macro here to avoid dragging in IPRT bits in
+ * places which cannot handle it (direct GA 3D bits or something like that).
+ *
+ * Turns out snprintf is off limits too. Needs Locale info and runs out of
+ * stack
+ */
+#ifdef RT_STRICT
+# if 1
+# define MY_ASSERT(a_Expr, g_szMsg) \
+ do { \
+ if ((a_Expr)) \
+ { /*likely*/ } \
+ else \
+ { \
+ OutputDebugStringA("Assertion failed on line " RT_XSTR(__LINE__) ": " RT_XSTR(a_Expr) "\n"); \
+ OutputDebugStringA("Assertion message: " g_szMsg "\n"); \
+ RT_BREAKPOINT(); \
+ } \
+ } while (0)
+# define MY_ASSERT_STMT_RETURN(a_Expr, a_Stmt, a_rc) \
+ do { \
+ if (a_Expr) \
+ { /* likely */ } \
+ else \
+ { \
+ OutputDebugStringA("Assertion failed on line " RT_XSTR(__LINE__) ": " RT_XSTR(a_Expr) "\n"); \
+ RT_BREAKPOINT(); \
+ a_Stmt; \
+ return (a_rc); \
+ } \
+ } while (0)
+# else
+# define MY_ASSERT(a_Expr, ...) \
+ do { \
+ if ((a_Expr)) \
+ { /*likely*/ } \
+ else \
+ { \
+ char szTmp[256]; \
+ _snprintf(szTmp, sizeof(szTmp), "Assertion failed on line %u in '%s': %s", __LINE__, __PRETTY_FUNCTION__, #a_Expr); \
+ OutputDebugStringA(szTmp); \
+ _snprintf(szTmp, sizeof(szTmp), __VA_ARGS__); \
+ OutputDebugStringA(szTmp); \
+ RT_BREAKPOINT(); \
+ } \
+ } while (0)
+# endif
+#else
+# define MY_ASSERT(a_Expr, ...) do { } while (0)
+# define MY_ASSERT_STMT_RETURN(a_Expr, a_Stmt, a_rc) \
+ do { if (a_Expr) { /* likely */ } else { a_Stmt; return (a_rc); }} while (0)
+#endif
+
+
+/** Dynamically resolves an NTDLL API we need. */
+#define RESOLVE_NTDLL_API(ApiNm) \
+ static bool volatile s_fInitialized##ApiNm = false; \
+ static decltype(ApiNm) *s_pfn##ApiNm = NULL; \
+ decltype(ApiNm) *pfn##ApiNm; \
+ if (s_fInitialized##ApiNm) \
+ pfn##ApiNm = s_pfn##ApiNm; \
+ else \
+ { \
+ pfn##ApiNm = (decltype(pfn##ApiNm))GetProcAddress(GetModuleHandleW(L"ntdll"), #ApiNm); \
+ s_pfn##ApiNm = pfn##ApiNm; \
+ s_fInitialized##ApiNm = true; \
+ } do {} while (0)
+
+
+/** Declare a kernel32 API.
+ * @note We are not exporting them as that causes duplicate symbol troubles in
+ * the OpenGL bits. */
+#define DECL_KERNEL32(a_Type) extern "C" a_Type WINAPI
+
+
+/** Ignore comments. */
+#define COMMENT(a_Text)
+
+/** Used for MAKE_IMPORT_ENTRY when declaring external g_pfnXxxx variables. */
+#define DECLARE_FUNCTION_POINTER(a_Name, a_cb) extern "C" decltype(a_Name) *RT_CONCAT(g_pfn, a_Name);
+
+/** Used in the InitFakes method to decl are uCurVersion used by assertion. */
+#ifdef RT_STRICT
+# define CURRENT_VERSION_VARIABLE() \
+ unsigned uCurVersion = GetVersion(); \
+ uCurVersion = ((uCurVersion & 0xff) << 8) | ((uCurVersion >> 8) & 0xff)
+#else
+# define CURRENT_VERSION_VARIABLE() (void)0
+#endif
+
+
+/** Used for MAKE_IMPORT_ENTRY when resolving an import. */
+#define RESOLVE_IMPORT(a_uMajorVer, a_uMinorVer, a_Name, a_cb) \
+ do { \
+ FARPROC pfnApi = GetProcAddress(hmod, #a_Name); \
+ if (pfnApi) \
+ RT_CONCAT(g_pfn, a_Name) = (decltype(a_Name) *)pfnApi; \
+ else \
+ { \
+ MY_ASSERT(uCurVersion < (((a_uMajorVer) << 8) | (a_uMinorVer)), #a_Name); \
+ RT_CONCAT(g_pfn, a_Name) = RT_CONCAT(Fake_,a_Name); \
+ } \
+ } while (0);
+
+
+#endif /* !IPRT_INCLUDED_SRC_r3_win_vcc_fakes_h */
+
diff --git a/src/VBox/Runtime/r3/win/vcc-fakes.mac b/src/VBox/Runtime/r3/win/vcc-fakes.mac
new file mode 100644
index 00000000..37c51543
--- /dev/null
+++ b/src/VBox/Runtime/r3/win/vcc-fakes.mac
@@ -0,0 +1,76 @@
+; $Id: vcc-fakes.mac $
+;; @file
+; IPRT - Common macros for the Visual C++ 2010+ CRT import fakes.
+;
+
+;
+; 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>.
+;
+; The contents of this file may alternatively be used under the terms
+; of the Common Development and Distribution License Version 1.0
+; (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
+; in the VirtualBox distribution, in which case the provisions of the
+; CDDL are applicable instead of those of the GPL.
+;
+; You may elect to license modified versions of this file under the
+; terms and conditions of either the GPL or the CDDL or both.
+;
+; SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
+;
+
+
+%include "iprt/asmdefs.mac"
+
+%ifndef RT_ARCH_X86
+ %error "This is x86 only code.
+%endif
+
+
+;;
+; Fakes an API import table entry.
+;
+; @param 1 The function name
+; @param 2 Number of bytes of parameters for x86.
+%macro MAKE_IMPORT_ENTRY_INTERNAL 2
+
+BEGINDATA
+extern _FakeResolve_ %+ FAKE_MODULE_NAME
+
+extern _Fake_ %+ %1 %+ @ %+ %2
+global __imp__ %+ %1 %+ @ %+ %2 ; The import address table (IAT) entry name.
+__imp__ %+ %1 %+ @ %+ %2:
+global _g_pfn %+ %1 ; C accessible label.
+_g_pfn %+ %1:
+ dd FakeLazyInit_ %+ %1
+
+BEGINCODE
+FakeLazyInit_ %+ %1:
+ pusha
+ call _FakeResolve_ %+ FAKE_MODULE_NAME
+ popa
+global _ %+ %1 %+ @ %+ %2 ; The imported function stub.
+_ %+ %1 %+ @ %+ %2:
+ jmp [_g_pfn %+ %1]
+
+
+%endmacro
+
+%define MAKE_IMPORT_ENTRY(a_uMajorVer, a_uMinorVer, a_Name, a_cbParams) MAKE_IMPORT_ENTRY_INTERNAL a_Name, a_cbParams
+%define COMMENT(a_Text)
+