summaryrefslogtreecommitdiffstats
path: root/src/common/windows
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/windows')
-rwxr-xr-xsrc/common/windows/c-logo.sh10
-rw-r--r--src/common/windows/com.c67
-rw-r--r--src/common/windows/com.h23
-rw-r--r--src/common/windows/getline.c58
-rw-r--r--src/common/windows/getline.h6
-rw-r--r--src/common/windows/logo.icobin0 -> 107906 bytes
l---------src/common/windows/logo.svg1
-rw-r--r--src/common/windows/manifest.xml18
-rw-r--r--src/common/windows/nt.h1325
-rw-r--r--src/common/windows/perflib_.h134
-rw-r--r--src/common/windows/registry.c388
-rw-r--r--src/common/windows/registry.h66
-rw-r--r--src/common/windows/unicode.c44
-rw-r--r--src/common/windows/unicode.h46
-rw-r--r--src/common/windows/unicode.hpp27
-rw-r--r--src/common/windows/util.hpp21
-rw-r--r--src/common/windows/variant.cpp20
-rw-r--r--src/common/windows/variant.hpp164
-rw-r--r--src/common/windows/version.c77
-rw-r--r--src/common/windows/version.h14
-rw-r--r--src/common/windows/version.rc37
-rw-r--r--src/common/windows/wmi.h54
22 files changed, 2600 insertions, 0 deletions
diff --git a/src/common/windows/c-logo.sh b/src/common/windows/c-logo.sh
new file mode 100755
index 0000000..e4d7863
--- /dev/null
+++ b/src/common/windows/c-logo.sh
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Convert logo.svg to logo.ico
+rsvg-convert -w 16 -h 16 logo.svg > logo16.png
+rsvg-convert -w 32 -h 32 logo.svg > logo32.png
+rsvg-convert -w 48 -h 48 logo.svg > logo48.png
+rsvg-convert -w 64 -h 64 logo.svg > logo64.png
+rsvg-convert -w 128 -h 128 logo.svg > logo128.png
+rsvg-convert -w 256 -h 256 logo.svg > logo256.png
+convert logo16.png logo32.png logo48.png logo64.png logo128.png logo256.png logo.ico
+rm logo16.png logo32.png logo48.png logo64.png logo128.png logo256.png
diff --git a/src/common/windows/com.c b/src/common/windows/com.c
new file mode 100644
index 0000000..a400ca9
--- /dev/null
+++ b/src/common/windows/com.c
@@ -0,0 +1,67 @@
+#include "com.h"
+
+#include <stdlib.h>
+
+#if FF_HAVE_WINRT
+ #include <roapi.h>
+
+static void RoUninitializeWrap(void) {
+ RoUninitialize();
+}
+
+static const char* doInitCom() {
+ HRESULT res = RoInitialize(RO_INIT_MULTITHREADED);
+ if (FAILED(res)) {
+ switch (res) {
+ case E_INVALIDARG:
+ return "RoInitialize() failed: invalid argument";
+ case E_OUTOFMEMORY:
+ return "RoInitialize() failed: out of memory";
+ case E_UNEXPECTED:
+ return "RoInitialize() failed: unexpected error";
+ case RPC_E_CHANGED_MODE:
+ // COM was already initialized with a different concurrency model
+ return NULL;
+ default:
+ return "RoInitialize() failed: unknown error";
+ }
+ }
+
+ atexit(RoUninitializeWrap);
+ return NULL;
+}
+#else
+ #include <combaseapi.h>
+
+static void CoUninitializeWrap(void) {
+ CoUninitialize();
+}
+
+static const char* doInitCom() {
+ HRESULT res = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (FAILED(res)) {
+ switch (res) {
+ case E_INVALIDARG:
+ return "CoInitializeEx() failed: invalid argument";
+ case E_OUTOFMEMORY:
+ return "CoInitializeEx() failed: out of memory";
+ case RPC_E_CHANGED_MODE:
+ // COM was already initialized with a different concurrency model
+ return NULL;
+ default:
+ return "CoInitializeEx() failed: unknown error";
+ }
+ }
+
+ atexit(CoUninitializeWrap);
+ return NULL;
+}
+#endif
+
+const char* ffInitCom(void) {
+ static const char* error = "";
+ if (error && error[0] == '\0') {
+ error = doInitCom();
+ }
+ return error;
+}
diff --git a/src/common/windows/com.h b/src/common/windows/com.h
new file mode 100644
index 0000000..57b8c6d
--- /dev/null
+++ b/src/common/windows/com.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "common/attributes.h"
+#include <assert.h>
+#include <unknwn.h>
+
+// Initialize COM & WinRT
+const char* ffInitCom(void);
+
+static inline void ffReleaseComObject(void* ppUnknown) {
+ assert(ppUnknown);
+ IUnknown* pUnknown = *(IUnknown**) ppUnknown;
+ if (pUnknown) {
+#ifdef __cplusplus
+ pUnknown->Release();
+#else
+ pUnknown->lpVtbl->Release(pUnknown);
+#endif
+ *(IUnknown**) ppUnknown = NULL;
+ }
+}
+
+#define FF_AUTO_RELEASE_COM_OBJECT FF_A_CLEANUP(ffReleaseComObject)
diff --git a/src/common/windows/getline.c b/src/common/windows/getline.c
new file mode 100644
index 0000000..3d43d55
--- /dev/null
+++ b/src/common/windows/getline.c
@@ -0,0 +1,58 @@
+#include "getline.h"
+
+#include <stdlib.h>
+#include <errno.h>
+
+ssize_t getline(char** lineptr, size_t* n, FILE* stream) {
+ ssize_t pos = -1;
+ int c;
+
+ if (lineptr == NULL || stream == NULL || n == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ _lock_file(stream);
+
+ c = _getc_nolock(stream);
+ if (c == EOF) {
+ goto exit;
+ }
+
+ if (*lineptr == NULL) {
+ *lineptr = malloc(128);
+ if (*lineptr == NULL) {
+ goto exit;
+ }
+ *n = 128;
+ }
+
+ pos = 0;
+ while (c != EOF) {
+ if ((size_t) (pos + 1) >= *n) {
+ size_t new_size = *n + (*n >> 2);
+ if (new_size < 128) {
+ new_size = 128;
+ }
+ char* new_ptr = realloc(*lineptr, new_size);
+ if (new_ptr == NULL) {
+ pos = -1;
+ goto exit;
+ }
+ *n = new_size;
+ *lineptr = new_ptr;
+ }
+
+ ((char*) (*lineptr))[pos++] = (char) c;
+ if (c == '\n') {
+ break;
+ }
+ c = _getc_nolock(stream);
+ }
+
+ (*lineptr)[pos] = '\0';
+
+exit:
+ _unlock_file(stream);
+ return pos;
+}
diff --git a/src/common/windows/getline.h b/src/common/windows/getline.h
new file mode 100644
index 0000000..49ba5db
--- /dev/null
+++ b/src/common/windows/getline.h
@@ -0,0 +1,6 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdio.h>
+
+ssize_t getline(char** lineptr, size_t* n, FILE* stream);
diff --git a/src/common/windows/logo.ico b/src/common/windows/logo.ico
new file mode 100644
index 0000000..cdd89ad
--- /dev/null
+++ b/src/common/windows/logo.ico
Binary files differ
diff --git a/src/common/windows/logo.svg b/src/common/windows/logo.svg
new file mode 120000
index 0000000..068d3f6
--- /dev/null
+++ b/src/common/windows/logo.svg
@@ -0,0 +1 @@
+../../logo/logo.svg \ No newline at end of file
diff --git a/src/common/windows/manifest.xml b/src/common/windows/manifest.xml
new file mode 100644
index 0000000..2113c07
--- /dev/null
+++ b/src/common/windows/manifest.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
+ <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+ <application>
+ <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/><!-- 8.1 -->
+ <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/><!-- 10/11 -->
+ </application>
+ </compatibility>
+ <assemblyIdentity type="win32" name="com.github.fastfetch" version="0.0.0.0"/>
+ <asmv3:application>
+ <asmv3:windowsSettings>
+ <activeCodePage xmlns="http://schemas.microsoft.com/SMI/2019/WindowsSettings">UTF-8</activeCodePage>
+ <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true/pm</dpiAware>
+ <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitor</dpiAwareness>
+ <heapType xmlns="http://schemas.microsoft.com/SMI/2020/WindowsSettings">SegmentHeap</heapType>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+</assembly>
diff --git a/src/common/windows/nt.h b/src/common/windows/nt.h
new file mode 100644
index 0000000..2f476b8
--- /dev/null
+++ b/src/common/windows/nt.h
@@ -0,0 +1,1325 @@
+#pragma once
+
+#include <ntdef.h>
+#include <winternl.h>
+#include <winnt.h>
+#include <stdint.h>
+#include <assert.h>
+
+enum {
+ SystemModuleInformation = 11,
+ SystemFirmwareTableInformation = 76,
+ SystemBootEnvironmentInformation = 90,
+ SystemLogicalProcessorAndGroupInformation = 107,
+ SystemSecureBootInformation = 146,
+};
+
+typedef struct _PROCESSOR_POWER_INFORMATION {
+ ULONG Number;
+ ULONG MaxMhz;
+ ULONG CurrentMhz;
+ ULONG MhzLimit;
+ ULONG MaxIdleState;
+ ULONG CurrentIdleState;
+} PROCESSOR_POWER_INFORMATION, *PPROCESSOR_POWER_INFORMATION;
+
+NTSYSAPI NTSTATUS NTAPI NtPowerInformation(
+ IN POWER_INFORMATION_LEVEL InformationLevel,
+ IN PVOID InputBuffer OPTIONAL,
+ IN ULONG InputBufferLength,
+ OUT PVOID OutputBuffer OPTIONAL,
+ IN ULONG OutputBufferLength);
+
+NTSYSAPI NTSTATUS NTAPI RtlGetVersion(
+ _Inout_ PRTL_OSVERSIONINFOW lpVersionInformation);
+
+NTSYSAPI NTSTATUS NTAPI NtQueryDirectoryFile(
+ IN HANDLE FileHandle,
+ IN HANDLE Event OPTIONAL,
+ IN PIO_APC_ROUTINE ApcRoutine OPTIONAL,
+ IN PVOID ApcContext OPTIONAL,
+ OUT PIO_STATUS_BLOCK IoStatusBlock,
+ OUT PVOID FileInformation,
+ IN ULONG Length,
+ IN FILE_INFORMATION_CLASS FileInformationClass,
+ IN BOOLEAN ReturnSingleEntry,
+ IN PUNICODE_STRING FileName OPTIONAL,
+ IN BOOLEAN RestartScan);
+
+// https://ntdoc.m417z.com/process_devicemap_information_ex
+typedef struct _PROCESS_DEVICEMAP_INFORMATION_EX {
+ union {
+ struct
+ {
+ HANDLE DirectoryHandle; // A handle to a directory object that can be set as the new device map for the process. This handle must have DIRECTORY_TRAVERSE access.
+ } Set;
+ struct
+ {
+ ULONG DriveMap; // A bitmask that indicates which drive letters are currently in use in the process's device map.
+ UCHAR DriveType[32]; // A value that indicates the type of each drive (e.g., local disk, network drive, etc.). // DRIVE_* WinBase.h
+ } Query;
+ };
+ ULONG Flags; // PROCESS_LUID_DOSDEVICES_ONLY
+} PROCESS_DEVICEMAP_INFORMATION_EX, *PPROCESS_DEVICEMAP_INFORMATION_EX;
+
+#ifndef NtCurrentProcess
+ #define NtCurrentProcess() ((HANDLE) (LONG_PTR) - 1)
+#endif
+
+typedef struct _CURDIR {
+ UNICODE_STRING DosPath;
+ HANDLE Handle;
+} CURDIR, *PCURDIR;
+
+NTSYSAPI PIMAGE_NT_HEADERS NTAPI RtlImageNtHeader(IN PVOID BaseOfImage);
+
+/**
+ * The SECTION_IMAGE_INFORMATION structure contains detailed information about an image section.
+ */
+typedef struct _SECTION_IMAGE_INFORMATION {
+ PVOID TransferAddress; // The address of the image entry point function.
+ ULONG ZeroBits; // The number of high-order address bits that must be zero in the image base address.
+ SIZE_T MaximumStackSize; // The maximum stack size of threads from the PE file header.
+ SIZE_T CommittedStackSize; // The initial stack size of threads from the PE file header.
+ ULONG SubSystemType; // The image subsystem from the PE file header (e.g., Windows GUI, Windows CUI, POSIX).
+ union {
+ struct
+ {
+ USHORT SubSystemMinorVersion;
+ USHORT SubSystemMajorVersion;
+ };
+ ULONG SubSystemVersion;
+ };
+ union {
+ struct
+ {
+ USHORT MajorOperatingSystemVersion;
+ USHORT MinorOperatingSystemVersion;
+ };
+ ULONG OperatingSystemVersion;
+ };
+ USHORT ImageCharacteristics; // The image characteristics from the PE file header.
+ USHORT DllCharacteristics; // The DLL characteristics flags (e.g., ASLR, NX compatibility).
+ USHORT Machine; // The image architecture (e.g., x86, x64, ARM).
+ BOOLEAN ImageContainsCode; // The image contains native executable code.
+ union {
+ UCHAR ImageFlags;
+ struct
+ {
+ UCHAR ComPlusNativeReady : 1; // The image contains precompiled .NET assembly generated by NGEN (Native Image Generator).
+ UCHAR ComPlusILOnly : 1; // the image contains only Microsoft Intermediate Language (IL) assembly.
+ UCHAR ImageDynamicallyRelocated : 1; // The image was mapped using a random base address rather than the preferred base address.
+ UCHAR ImageMappedFlat : 1; // The image was mapped using a single contiguous region, rather than separate regions for each section.
+ UCHAR BaseBelow4gb : 1; // The image was mapped using a base address below the 4 GB boundary.
+ UCHAR ComPlusPrefer32bit : 1; // The image prefers to run as a 32-bit process, even on a 64-bit system.
+ UCHAR Reserved : 2;
+ };
+ };
+ ULONG LoaderFlags; // Reserved by ntdll.dll for the Windows loader.
+ ULONG ImageFileSize; // The size of the image, in bytes, including all headers.
+ ULONG CheckSum; // The image file checksum, from the PE optional header.
+} SECTION_IMAGE_INFORMATION, *PSECTION_IMAGE_INFORMATION;
+
+typedef struct _SYSTEM_BOOT_ENVIRONMENT_INFORMATION {
+ GUID BootIdentifier;
+ FIRMWARE_TYPE FirmwareType;
+ union {
+ ULONGLONG BootFlags;
+ struct
+ {
+ ULONGLONG DbgMenuOsSelection : 1; // REDSTONE4
+ ULONGLONG DbgHiberBoot : 1;
+ ULONGLONG DbgSoftBoot : 1;
+ ULONGLONG DbgMeasuredLaunch : 1;
+ ULONGLONG DbgMeasuredLaunchCapable : 1; // 19H1
+ ULONGLONG DbgSystemHiveReplace : 1;
+ ULONGLONG DbgMeasuredLaunchSmmProtections : 1;
+ ULONGLONG DbgMeasuredLaunchSmmLevel : 7; // 20H1
+ ULONGLONG DbgBugCheckRecovery : 1; // 24H2
+ ULONGLONG DbgFASR : 1;
+ ULONGLONG DbgUseCachedBcd : 1;
+ };
+ };
+} SYSTEM_BOOT_ENVIRONMENT_INFORMATION;
+
+typedef struct _RTL_PROCESS_MODULE_INFORMATION {
+ PVOID Section;
+ PVOID MappedBase;
+ PVOID ImageBase;
+ ULONG ImageSize;
+ ULONG Flags;
+ USHORT LoadOrderIndex;
+ USHORT InitOrderIndex;
+ USHORT LoadCount;
+ USHORT OffsetToFileName;
+ UCHAR FullPathName[256];
+} RTL_PROCESS_MODULE_INFORMATION, *PRTL_PROCESS_MODULE_INFORMATION;
+
+typedef struct _RTL_PROCESS_MODULES {
+ ULONG NumberOfModules;
+ _Field_size_(NumberOfModules) RTL_PROCESS_MODULE_INFORMATION Modules[1];
+} RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES;
+
+NTSTATUS NTAPI NtQuerySystemEnvironmentValueEx(
+ _In_ PCUNICODE_STRING VariableName,
+ _In_ const GUID* VendorGuid,
+ _Out_writes_bytes_opt_(*BufferLength) PVOID Buffer,
+ _Inout_ PULONG BufferLength,
+ _Out_opt_ PULONG Attributes // EFI_VARIABLE_*
+);
+
+NTSTATUS NTAPI RtlGUIDFromString(IN PCUNICODE_STRING GuidString, OUT GUID* Guid);
+NTSTATUS NTAPI RtlStringFromGUIDEx(IN GUID* Guid, OUT PCUNICODE_STRING GuidString, _In_ BOOLEAN AllocateGuidString);
+
+typedef struct _SYSTEM_SECUREBOOT_INFORMATION {
+ BOOLEAN SecureBootEnabled;
+ BOOLEAN SecureBootCapable;
+} SYSTEM_SECUREBOOT_INFORMATION, *PSYSTEM_SECUREBOOT_INFORMATION;
+
+NTSTATUS NTAPI NtQuerySystemInformationEx(
+ _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass,
+ _In_reads_bytes_(InputBufferLength) PVOID InputBuffer,
+ _In_ ULONG InputBufferLength,
+ _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation,
+ _In_ ULONG SystemInformationLength,
+ _Out_opt_ PULONG ReturnLength);
+
+typedef enum _SYSTEM_FIRMWARE_TABLE_ACTION {
+ SystemFirmwareTableEnumerate,
+ SystemFirmwareTableGet,
+ SystemFirmwareTableMax
+} SYSTEM_FIRMWARE_TABLE_ACTION;
+
+typedef struct _SYSTEM_FIRMWARE_TABLE_INFORMATION {
+ ULONG ProviderSignature; // (same as the GetSystemFirmwareTable function)
+ SYSTEM_FIRMWARE_TABLE_ACTION Action;
+ ULONG TableID;
+ ULONG TableBufferLength;
+ _Field_size_bytes_(TableBufferLength) UCHAR TableBuffer[];
+} SYSTEM_FIRMWARE_TABLE_INFORMATION, *PSYSTEM_FIRMWARE_TABLE_INFORMATION;
+
+NTSYSAPI NTSTATUS NTAPI NtDelayExecution(_In_ BOOLEAN Alertable, _In_ PLARGE_INTEGER DelayInterval);
+
+/**
+ * The KSYSTEM_TIME structure represents interrupt time, system time, and time zone bias.
+ */
+typedef struct _KSYSTEM_TIME {
+ ULONG LowPart;
+ LONG High1Time;
+ LONG High2Time;
+} KSYSTEM_TIME, *PKSYSTEM_TIME;
+
+/**
+ * PROCESSOR_FEATURE_MAX defines the maximum number of processor feature flags
+ * that may be reported by the system.
+ */
+#define PROCESSOR_FEATURE_MAX 64
+
+/**
+ * The ALTERNATIVE_ARCHITECTURE_TYPE enumeration specifies the hardware
+ * architecture variant used by the system.
+ *
+ * \remarks NEC98x86 represents the NEC PC-98 architecture,
+ * supported only on very early Windows releases.
+ */
+typedef enum _ALTERNATIVE_ARCHITECTURE_TYPE {
+ StandardDesign,
+ NEC98x86,
+ EndAlternatives
+} ALTERNATIVE_ARCHITECTURE_TYPE;
+
+/**
+ * The KUSER_SHARED_DATA structure contains information shared with user-mode.
+ *
+ * \sa https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/ns-ntddk-kuser_shared_data
+ */
+typedef struct _KUSER_SHARED_DATA {
+ //
+ // Current low 32-bit of tick count and tick count multiplier.
+ //
+ // N.B. The tick count is updated each time the clock ticks.
+ //
+
+ ULONG TickCountLowDeprecated;
+ ULONG TickCountMultiplier;
+
+ //
+ // Current 64-bit interrupt time in 100ns units.
+ //
+
+ volatile KSYSTEM_TIME InterruptTime;
+
+ //
+ // Current 64-bit system time in 100ns units.
+ //
+
+ volatile KSYSTEM_TIME SystemTime;
+
+ //
+ // Current 64-bit time zone bias.
+ //
+
+ volatile KSYSTEM_TIME TimeZoneBias;
+
+ //
+ // Support image magic number range for the host system.
+ //
+ // N.B. This is an inclusive range.
+ //
+
+ USHORT ImageNumberLow;
+ USHORT ImageNumberHigh;
+
+ //
+ // Copy of system root in unicode.
+ //
+ // N.B. This field must be accessed via the RtlGetNtSystemRoot API for
+ // an accurate result.
+ //
+
+ WCHAR NtSystemRoot[260];
+
+ //
+ // Maximum stack trace depth if tracing enabled.
+ //
+
+ ULONG MaxStackTraceDepth;
+
+ //
+ // Crypto exponent value.
+ //
+
+ ULONG CryptoExponent;
+
+ //
+ // Time zone ID.
+ //
+
+ ULONG TimeZoneId;
+
+ //
+ // Minimum size of a large page on the system, in bytes.
+ //
+ // N.B. Returned by GetLargePageMinimum() function.
+ //
+
+ ULONG LargePageMinimum;
+
+ //
+ // This value controls the Application Impact Telemetry (AIT) Sampling rate.
+ //
+ // This value determines how frequently the system records AIT events,
+ // which are used by the Application Experience and compatibility
+ // subsystems to evaluate application behavior, performance, and
+ // potential compatibility issues.
+ //
+ // Lower values increase sampling frequency, while higher values reduce it.
+ // The kernel updates this field as part of its internal telemetry and
+ // heuristics logic.
+ //
+
+ ULONG AitSamplingValue;
+
+ //
+ // This value controls Application Compatibility (AppCompat) switchback processing.
+ //
+
+ union {
+ ULONG AppCompatFlag;
+ struct
+ {
+ ULONG SwitchbackEnabled : 1; // Basic switchback processing
+ ULONG ExtendedHeuristics : 1; // Extended switchback heuristics
+ ULONG TelemetryFallback : 1; // Telemetry-driven fallback
+ ULONG Reserved : 29;
+ } AppCompatFlags;
+ };
+
+ //
+ // Current Kernel Root RNG state seed version
+ //
+
+ ULONGLONG RNGSeedVersion;
+
+ //
+ // This value controls assertion failure handling.
+ //
+ // Historically (prior to Windows 10), this value was also used by
+ // Code Integrity (CI), AppLocker, and related security components to
+ // determine the minimum validation requirements for executable images,
+ // drivers, and privileged operations.
+ //
+ // In modern Windows versions, this field is used primarily by the kernel's
+ // diagnostic and validation infrastructure to decide how assertion failures
+ // should be handled (e.g., logging, debugger break-in, or bugcheck).
+
+ ULONG GlobalValidationRunlevel;
+
+ //
+ // Monotonic stamp incremented by the kernel whenever the system's
+ // time zone bias value changes.
+ //
+ // N.B. This field must be accessed via the RtlGetSystemTimeAndBias API for
+ // an accurate result.
+ // This value is read before and after accessing the bias fields to determine
+ // whether the time zone data changed during the read. If the stamp differs,
+ // the caller must re-read the bias values to ensure consistency.
+ //
+
+ volatile LONG TimeZoneBiasStamp;
+
+ //
+ // The shared collective build number undecorated with C or F.
+ // GetVersionEx hides the real number
+ //
+
+ ULONG NtBuildNumber;
+
+ //
+ // Product type.
+ //
+ // N.B. This field must be accessed via the RtlGetNtProductType API for
+ // an accurate result.
+ //
+
+ NT_PRODUCT_TYPE NtProductType;
+ BOOLEAN ProductTypeIsValid;
+ BOOLEAN Reserved0[1];
+
+ //
+ // Native hardware processor architecture of the running system.
+ //
+ // N.B. User-mode components read this field to determine the true system
+ // architecture, especially in WOW64 scenarios where the process architecture
+ // differs from the native one.
+ //
+
+ USHORT NativeProcessorArchitecture;
+
+ //
+ // The NT Version.
+ //
+ // N. B. Note that each process sees a version from its PEB, but if the
+ // process is running with an altered view of the system version,
+ // the following two fields are used to correctly identify the
+ // version
+ //
+
+ ULONG NtMajorVersion;
+ ULONG NtMinorVersion;
+
+ //
+ // Processor features.
+ //
+
+ BOOLEAN ProcessorFeatures[PROCESSOR_FEATURE_MAX];
+
+ //
+ // Reserved fields - do not use.
+ //
+
+ ULONG MaximumUserModeAddressDeprecated; // Deprecated, use SystemBasicInformation instead.
+ ULONG SystemRangeStartDeprecated; // Deprecated, use SystemRangeStartInformation instead.
+
+ //
+ // Time slippage while in debugger.
+ //
+
+ volatile ULONG TimeSlip;
+
+ //
+ // Alternative system architecture, e.g., NEC PC98xx on x86.
+ //
+
+ ALTERNATIVE_ARCHITECTURE_TYPE AlternativeArchitecture;
+
+ //
+ // Boot sequence, incremented for each boot attempt by the OS loader.
+ //
+
+ ULONG BootId;
+
+ //
+ // If the system is an evaluation unit, the following field contains the
+ // date and time that the evaluation unit expires. A value of 0 indicates
+ // that there is no expiration. A non-zero value is the UTC absolute time
+ // that the system expires.
+ //
+
+ LARGE_INTEGER SystemExpirationDate;
+
+ //
+ // Suite support.
+ //
+ // N.B. This field must be accessed via the RtlGetSuiteMask API for
+ // an accurate result.
+ //
+
+ ULONG SuiteMask;
+
+ //
+ // TRUE if a kernel debugger is connected/enabled.
+ //
+
+ BOOLEAN KdDebuggerEnabled;
+
+ //
+ // Mitigation policies.
+ //
+
+ union {
+ UCHAR MitigationPolicies;
+ struct
+ {
+ UCHAR NXSupportPolicy : 2;
+ UCHAR SEHValidationPolicy : 2;
+ UCHAR CurDirDevicesSkippedForDlls : 2;
+ UCHAR Reserved : 2;
+ };
+ };
+
+ //
+ // Measured duration of a single processor yield, in cycles. This is used by
+ // lock packages to determine how many times to spin waiting for a state
+ // change before blocking.
+ //
+
+ USHORT CyclesPerYield;
+
+ //
+ // Current console session Id. Always zero on non-TS systems.
+ //
+ // N.B. This field must be accessed via the RtlGetActiveConsoleId API for an
+ // accurate result.
+ //
+
+ volatile ULONG ActiveConsoleId;
+
+ //
+ // Force-dismounts cause handles to become invalid. Rather than always
+ // probe handles, a serial number of dismounts is maintained that clients
+ // can use to see if they need to probe handles.
+ //
+
+ volatile ULONG DismountCount;
+
+ //
+ // This field indicates the status of the 64-bit COM+ package on the
+ // system. It indicates whether the Intermediate Language (IL) COM+
+ // images need to use the 64-bit COM+ runtime or the 32-bit COM+ runtime.
+ //
+
+ ULONG ComPlusPackage;
+
+ //
+ // Time in tick count for system-wide last user input across all terminal
+ // sessions. For MP performance, it is not updated all the time (e.g. once
+ // a minute per session). It is used for idle detection.
+ //
+
+ ULONG LastSystemRITEventTickCount;
+
+ //
+ // Number of physical pages in the system. This can dynamically change as
+ // physical memory can be added or removed from a running system. This
+ // cell is too small to hold the non-truncated value on very large memory
+ // machines so code that needs the full value should access
+ // FullNumberOfPhysicalPages instead.
+ //
+
+ ULONG NumberOfPhysicalPages;
+
+ //
+ // True if the system was booted in safe boot mode.
+ //
+
+ BOOLEAN SafeBootMode;
+
+ //
+ // Virtualization flags.
+ //
+
+ union {
+ UCHAR VirtualizationFlags;
+
+#if defined(_ARM64_)
+
+ //
+ // N.B. Keep this bitfield in sync with the one in arc.w.
+ //
+
+ struct
+ {
+ UCHAR ArchStartedInEl2 : 1;
+ UCHAR QcSlIsSupported : 1;
+ UCHAR : 6;
+ };
+
+#endif
+ };
+
+ //
+ // Reserved (available for reuse).
+ //
+
+ UCHAR Reserved12[2];
+
+ //
+ // This is a packed bitfield that contains various flags concerning
+ // the system state. They must be manipulated using interlocked
+ // operations.
+ //
+ // N.B. DbgMultiSessionSku must be accessed via the RtlIsMultiSessionSku
+ // API for an accurate result
+ //
+
+ union {
+ ULONG SharedDataFlags;
+ struct
+ {
+ //
+ // The following bit fields are for the debugger only. Do not use.
+ // Use the bit definitions instead.
+ //
+
+ ULONG DbgErrorPortPresent : 1;
+ ULONG DbgElevationEnabled : 1;
+ ULONG DbgVirtEnabled : 1;
+ ULONG DbgInstallerDetectEnabled : 1;
+ ULONG DbgLkgEnabled : 1;
+ ULONG DbgDynProcessorEnabled : 1;
+ ULONG DbgConsoleBrokerEnabled : 1;
+ ULONG DbgSecureBootEnabled : 1;
+ ULONG DbgMultiSessionSku : 1;
+ ULONG DbgMultiUsersInSessionSku : 1;
+ ULONG DbgStateSeparationEnabled : 1;
+ ULONG DbgSplitTokenEnabled : 1;
+ ULONG DbgShadowAdminEnabled : 1;
+ ULONG SpareBits : 19;
+ };
+ };
+
+ // ... more fields follow, but we don't need them
+} KUSER_SHARED_DATA, *PKUSER_SHARED_DATA;
+
+#ifdef __aarch64__
+ #define SharedUserData ({ \
+ __auto_type shared_user_data = (const volatile KUSER_SHARED_DATA*) (uintptr_t) 0x7FFE0000UL; \
+ __asm__("" : "+r"(shared_user_data)); /* https://github.com/lhmouse/mcfgthread/issues/330 */ \
+ shared_user_data; \
+ })
+#else
+ #define SharedUserData ((const volatile KUSER_SHARED_DATA*) (uintptr_t) 0x7FFE0000UL)
+#endif
+
+static inline uint64_t ffKSystemTimeToUInt64(const volatile KSYSTEM_TIME* pTime) {
+#if _WIN64
+
+ // This is safe even if pTime is not 8-byte aligned
+ // See https://learn.microsoft.com/en-us/windows/win32/winprog64/fault-alignments
+ return *(const volatile uint64_t*) pTime;
+
+#else
+
+ uint32_t low, high1, high2;
+
+ do {
+ high1 = (uint32_t) pTime->High1Time;
+ low = (uint32_t) pTime->LowPart;
+ high2 = (uint32_t) pTime->High2Time;
+ } while (high1 != high2);
+
+ return ((uint64_t) high1 << 32) | low;
+#endif
+}
+
+static inline bool ffIsWindows10OrGreater() {
+#if FF_WIN81_COMPAT
+ return SharedUserData->NtMajorVersion >= 10;
+#else
+ return true;
+#endif
+}
+
+static inline bool ffIsWindows11OrGreater() {
+ return SharedUserData->NtMajorVersion > 10 ||
+ (SharedUserData->NtMajorVersion == 10 && SharedUserData->NtBuildNumber >= 22000);
+}
+
+NTSYSAPI NTSTATUS NTAPI NtOpenProcessToken(
+ _In_ HANDLE ProcessHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _Out_ PHANDLE TokenHandle);
+NTSYSAPI NTSTATUS NTAPI NtAdjustPrivilegesToken(
+ _In_ HANDLE TokenHandle,
+ _In_ BOOLEAN DisableAllPrivileges,
+ _In_opt_ PTOKEN_PRIVILEGES NewState,
+ _In_ ULONG BufferLength,
+ _Out_writes_bytes_to_opt_(BufferLength, *ReturnLength) PTOKEN_PRIVILEGES PreviousState,
+ _Out_opt_ PULONG ReturnLength);
+NTSYSAPI NTSTATUS NTAPI NtQueryInformationToken(
+ _In_ HANDLE TokenHandle,
+ _In_ TOKEN_INFORMATION_CLASS TokenInformationClass,
+ _Out_writes_bytes_to_opt_(TokenInformationLength, *ReturnLength) PVOID TokenInformation,
+ _In_ ULONG TokenInformationLength,
+ _Out_ PULONG ReturnLength);
+#define NtCurrentProcessToken() ((HANDLE) (LONG_PTR) - 4) // for NtQueryInformationToken only; Windows 8+
+
+NTSYSAPI NTSTATUS NTAPI NtReadFile(
+ _In_ HANDLE FileHandle,
+ _In_opt_ HANDLE Event,
+ _In_opt_ PIO_APC_ROUTINE ApcRoutine,
+ _In_opt_ PVOID ApcContext,
+ _Out_ PIO_STATUS_BLOCK IoStatusBlock,
+ _Out_writes_bytes_(Length) PVOID Buffer,
+ _In_ ULONG Length,
+ _In_opt_ PLARGE_INTEGER ByteOffset,
+ _In_opt_ PULONG Key);
+
+NTSYSAPI NTSTATUS NTAPI NtCreateEvent(
+ _Out_ PHANDLE EventHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
+ _In_ EVENT_TYPE EventType,
+ _In_ BOOLEAN InitialState);
+
+NTSYSAPI NTSTATUS NTAPI NtQueryAttributesFile(
+ _In_ PCOBJECT_ATTRIBUTES ObjectAttributes,
+ _Out_ PFILE_BASIC_INFORMATION FileInformation);
+
+NTSYSAPI NTSTATUS NTAPI RtlUnicodeToUTF8N(
+ _Out_writes_bytes_to_(UTF8StringMaxByteCount, *UTF8StringActualByteCount) PCHAR UTF8StringDestination,
+ _In_ ULONG UTF8StringMaxByteCount,
+ _Out_opt_ PULONG UTF8StringActualByteCount,
+ _In_reads_bytes_(UnicodeStringByteCount) PCWCH UnicodeStringSource,
+ _In_ ULONG UnicodeStringByteCount);
+
+NTSYSAPI NTSTATUS NTAPI RtlUTF8ToUnicodeN(
+ _Out_writes_bytes_to_(UnicodeStringMaxByteCount, *UnicodeStringActualByteCount) PWSTR UnicodeStringDestination,
+ _In_ ULONG UnicodeStringMaxByteCount,
+ _Out_opt_ PULONG UnicodeStringActualByteCount,
+ _In_reads_bytes_(UTF8StringByteCount) PCCH UTF8StringSource,
+ _In_ ULONG UTF8StringByteCount);
+
+#define RTL_MAX_DRIVE_LETTERS 32
+typedef struct _RTL_DRIVE_LETTER_CURDIR {
+ USHORT Flags;
+ USHORT Length;
+ ULONG TimeStamp;
+ STRING DosPath;
+} RTL_DRIVE_LETTER_CURDIR, *PRTL_DRIVE_LETTER_CURDIR;
+
+typedef struct _RTL_USER_PROCESS_PARAMETERS_FULL {
+ ULONG MaximumLength;
+ ULONG Length;
+
+ ULONG Flags;
+ ULONG DebugFlags;
+
+ HANDLE ConsoleHandle;
+ ULONG ConsoleFlags;
+ HANDLE StandardInput;
+ HANDLE StandardOutput;
+ HANDLE StandardError;
+
+ CURDIR CurrentDirectory;
+ UNICODE_STRING DllPath;
+ UNICODE_STRING ImagePathName;
+ UNICODE_STRING CommandLine;
+ PVOID Environment;
+
+ ULONG StartingX;
+ ULONG StartingY;
+ ULONG CountX;
+ ULONG CountY;
+ ULONG CountCharsX;
+ ULONG CountCharsY;
+ ULONG FillAttribute;
+
+ ULONG WindowFlags;
+ ULONG ShowWindowFlags;
+ UNICODE_STRING WindowTitle;
+ UNICODE_STRING DesktopInfo;
+ UNICODE_STRING ShellInfo;
+ UNICODE_STRING RuntimeData;
+ RTL_DRIVE_LETTER_CURDIR CurrentDirectories[RTL_MAX_DRIVE_LETTERS];
+
+ // Windows Vista
+ ULONG_PTR EnvironmentSize;
+ // Windows 7
+ ULONG_PTR EnvironmentVersion;
+
+ // Windows 8
+ PVOID PackageDependencyData;
+ ULONG ProcessGroupId;
+
+ // ...
+} RTL_USER_PROCESS_PARAMETERS_FULL, *PRTL_USER_PROCESS_PARAMETERS_FULL;
+
+typedef struct KERNEL_CALLBACK_TABLE* PKERNEL_CALLBACK_TABLE;
+typedef struct API_SET_NAMESPACE* PAPI_SET_NAMESPACE;
+typedef struct RTL_BITMAP* PRTL_BITMAP;
+typedef struct SILO_USER_SHARED_DATA* PSILO_USER_SHARED_DATA;
+typedef struct CPTABLEINFO* PCPTABLEINFO;
+typedef struct NLSTABLEINFO* PNLSTABLEINFO;
+typedef struct GDI_HANDLE_ENTRY* PGDI_HANDLE_ENTRY;
+
+typedef struct _PEB_FULL {
+ //
+ // The process was cloned with an inherited address space.
+ //
+ BOOLEAN InheritedAddressSpace;
+
+ //
+ // The process has image file execution options (IFEO).
+ //
+ BOOLEAN ReadImageFileExecOptions;
+
+ //
+ // The process has a debugger attached.
+ //
+ BOOLEAN BeingDebugged;
+
+ union {
+ BOOLEAN BitField;
+ struct
+ {
+ BOOLEAN ImageUsesLargePages : 1; // The process uses large image regions (4 MB).
+ BOOLEAN IsProtectedProcess : 1; // The process is a protected process.
+ BOOLEAN IsImageDynamicallyRelocated : 1; // The process image base address was relocated.
+ BOOLEAN SkipPatchingUser32Forwarders : 1; // The process skipped forwarders for User32.dll functions. 1 for 64-bit, 0 for 32-bit.
+ BOOLEAN IsPackagedProcess : 1; // The process is a packaged store process (APPX/MSIX).
+ BOOLEAN IsAppContainerProcess : 1; // The process has an AppContainer token.
+ BOOLEAN IsProtectedProcessLight : 1; // The process is a protected process (light).
+ BOOLEAN IsLongPathAwareProcess : 1; // The process is long path aware.
+ };
+ };
+
+ //
+ // Handle to a mutex for synchronization.
+ //
+ HANDLE Mutant;
+
+ //
+ // Pointer to the base address of the process image.
+ //
+ PVOID ImageBaseAddress;
+
+ //
+ // Pointer to the process loader data.
+ //
+ PPEB_LDR_DATA Ldr;
+
+ //
+ // Pointer to the process parameters.
+ //
+ PRTL_USER_PROCESS_PARAMETERS_FULL ProcessParameters;
+
+ //
+ // Reserved.
+ //
+ PVOID SubSystemData;
+
+ //
+ // Pointer to the process default heap.
+ //
+ PVOID ProcessHeap;
+
+ //
+ // Pointer to a critical section used to synchronize access to the PEB.
+ //
+ PRTL_CRITICAL_SECTION FastPebLock;
+
+ //
+ // Pointer to a singly linked list used by ATL.
+ //
+ PSLIST_HEADER AtlThunkSListPtr;
+
+ //
+ // Handle to the Image File Execution Options key.
+ //
+ HANDLE IFEOKey;
+
+ //
+ // Cross process flags.
+ //
+ union {
+ ULONG CrossProcessFlags;
+ struct
+ {
+ ULONG ProcessInJob : 1; // The process is part of a job.
+ ULONG ProcessInitializing : 1; // The process is initializing.
+ ULONG ProcessUsingVEH : 1; // The process is using VEH.
+ ULONG ProcessUsingVCH : 1; // The process is using VCH.
+ ULONG ProcessUsingFTH : 1; // The process is using FTH.
+ ULONG ProcessPreviouslyThrottled : 1; // The process was previously throttled.
+ ULONG ProcessCurrentlyThrottled : 1; // The process is currently throttled.
+ ULONG ProcessImagesHotPatched : 1; // The process images are hot patched. // RS5
+ ULONG ReservedBits0 : 24;
+ };
+ };
+
+ //
+ // User32 KERNEL_CALLBACK_TABLE (ntuser.h)
+ //
+ union {
+ PKERNEL_CALLBACK_TABLE KernelCallbackTable;
+ PVOID UserSharedInfoPtr;
+ };
+
+ //
+ // Reserved.
+ //
+ ULONG SystemReserved;
+
+ //
+ // Pointer to the Active Template Library (ATL) singly linked list (32-bit)
+ //
+ ULONG AtlThunkSListPtr32;
+
+ //
+ // Pointer to the API Set Schema.
+ //
+ PAPI_SET_NAMESPACE ApiSetMap;
+
+ //
+ // Counter for TLS expansion.
+ //
+ ULONG TlsExpansionCounter;
+
+ //
+ // Pointer to the TLS bitmap.
+ //
+ PRTL_BITMAP TlsBitmap;
+
+ //
+ // Bits for the TLS bitmap.
+ //
+ ULONG TlsBitmapBits[2];
+
+ //
+ // Reserved for CSRSS.
+ //
+ PVOID ReadOnlySharedMemoryBase;
+
+ //
+ // Pointer to the USER_SHARED_DATA for the current SILO.
+ //
+ PSILO_USER_SHARED_DATA SharedData;
+
+ //
+ // Reserved for CSRSS.
+ //
+ PVOID* ReadOnlyStaticServerData;
+
+ //
+ // Pointer to the ANSI code page data.
+ //
+ PCPTABLEINFO AnsiCodePageData;
+
+ //
+ // Pointer to the OEM code page data.
+ //
+ PCPTABLEINFO OemCodePageData;
+
+ //
+ // Pointer to the Unicode case table data.
+ //
+ PNLSTABLEINFO UnicodeCaseTableData;
+
+ //
+ // The total number of system processors.
+ //
+ ULONG NumberOfProcessors;
+
+ //
+ // Global flags for the system.
+ //
+ union {
+ ULONG NtGlobalFlag;
+ struct
+ {
+ ULONG StopOnException : 1; // FLG_STOP_ON_EXCEPTION
+ ULONG ShowLoaderSnaps : 1; // FLG_SHOW_LDR_SNAPS
+ ULONG DebugInitialCommand : 1; // FLG_DEBUG_INITIAL_COMMAND
+ ULONG StopOnHungGUI : 1; // FLG_STOP_ON_HUNG_GUI
+ ULONG HeapEnableTailCheck : 1; // FLG_HEAP_ENABLE_TAIL_CHECK
+ ULONG HeapEnableFreeCheck : 1; // FLG_HEAP_ENABLE_FREE_CHECK
+ ULONG HeapValidateParameters : 1; // FLG_HEAP_VALIDATE_PARAMETERS
+ ULONG HeapValidateAll : 1; // FLG_HEAP_VALIDATE_ALL
+ ULONG ApplicationVerifier : 1; // FLG_APPLICATION_VERIFIER
+ ULONG MonitorSilentProcessExit : 1; // FLG_MONITOR_SILENT_PROCESS_EXIT
+ ULONG PoolEnableTagging : 1; // FLG_POOL_ENABLE_TAGGING
+ ULONG HeapEnableTagging : 1; // FLG_HEAP_ENABLE_TAGGING
+ ULONG UserStackTraceDb : 1; // FLG_USER_STACK_TRACE_DB
+ ULONG KernelStackTraceDb : 1; // FLG_KERNEL_STACK_TRACE_DB
+ ULONG MaintainObjectTypeList : 1; // FLG_MAINTAIN_OBJECT_TYPELIST
+ ULONG HeapEnableTagByDll : 1; // FLG_HEAP_ENABLE_TAG_BY_DLL
+ ULONG DisableStackExtension : 1; // FLG_DISABLE_STACK_EXTENSION
+ ULONG EnableCsrDebug : 1; // FLG_ENABLE_CSRDEBUG
+ ULONG EnableKDebugSymbolLoad : 1; // FLG_ENABLE_KDEBUG_SYMBOL_LOAD
+ ULONG DisablePageKernelStacks : 1; // FLG_DISABLE_PAGE_KERNEL_STACKS
+ ULONG EnableSystemCritBreaks : 1; // FLG_ENABLE_SYSTEM_CRIT_BREAKS
+ ULONG HeapDisableCoalescing : 1; // FLG_HEAP_DISABLE_COALESCING
+ ULONG EnableCloseExceptions : 1; // FLG_ENABLE_CLOSE_EXCEPTIONS
+ ULONG EnableExceptionLogging : 1; // FLG_ENABLE_EXCEPTION_LOGGING
+ ULONG EnableHandleTypeTagging : 1; // FLG_ENABLE_HANDLE_TYPE_TAGGING
+ ULONG HeapPageAllocs : 1; // FLG_HEAP_PAGE_ALLOCS
+ ULONG DebugInitialCommandEx : 1; // FLG_DEBUG_INITIAL_COMMAND_EX
+ ULONG DisableDbgPrint : 1; // FLG_DISABLE_DBGPRINT
+ ULONG CritSecEventCreation : 1; // FLG_CRITSEC_EVENT_CREATION
+ ULONG LdrTopDown : 1; // FLG_LDR_TOP_DOWN
+ ULONG EnableHandleExceptions : 1; // FLG_ENABLE_HANDLE_EXCEPTIONS
+ ULONG DisableProtDlls : 1; // FLG_DISABLE_PROTDLLS
+ } NtGlobalFlags;
+ };
+
+ //
+ // Timeout for critical sections.
+ //
+ LARGE_INTEGER CriticalSectionTimeout;
+
+ //
+ // Reserved size for heap segments.
+ //
+ SIZE_T HeapSegmentReserve;
+
+ //
+ // Committed size for heap segments.
+ //
+ SIZE_T HeapSegmentCommit;
+
+ //
+ // Threshold for decommitting total free heap.
+ //
+ SIZE_T HeapDeCommitTotalFreeThreshold;
+
+ //
+ // Threshold for decommitting free heap blocks.
+ //
+ SIZE_T HeapDeCommitFreeBlockThreshold;
+
+ //
+ // Number of process heaps.
+ //
+ ULONG NumberOfHeaps;
+
+ //
+ // Maximum number of process heaps.
+ //
+ ULONG MaximumNumberOfHeaps;
+
+ //
+ // Pointer to an array of process heaps. ProcessHeaps is initialized
+ // to point to the first free byte after the PEB and MaximumNumberOfHeaps
+ // is computed from the page size used to hold the PEB, less the fixed
+ // size of this data structure.
+ //
+ PVOID* ProcessHeaps;
+
+ //
+ // Pointer to the system GDI shared handle table.
+ //
+ PGDI_HANDLE_ENTRY GdiSharedHandleTable;
+
+ //
+ // Pointer to the process starter helper.
+ //
+ PVOID ProcessStarterHelper;
+
+ //
+ // The maximum number of GDI function calls during batch operations (GdiSetBatchLimit)
+ //
+ ULONG GdiDCAttributeList;
+
+ //
+ // Pointer to the loader lock critical section.
+ //
+ PRTL_CRITICAL_SECTION LoaderLock;
+
+ //
+ // Major version of the operating system.
+ //
+ ULONG OSMajorVersion;
+
+ //
+ // Minor version of the operating system.
+ //
+ ULONG OSMinorVersion;
+
+ //
+ // Build number of the operating system.
+ //
+ USHORT OSBuildNumber;
+
+ //
+ // CSD version of the operating system.
+ //
+ USHORT OSCSDVersion;
+
+ //
+ // Platform ID of the operating system.
+ //
+ ULONG OSPlatformId;
+
+ //
+ // Subsystem version of the current process image (PE Headers).
+ //
+ ULONG ImageSubsystem;
+
+ //
+ // Major version of the current process image subsystem (PE Headers).
+ //
+ ULONG ImageSubsystemMajorVersion;
+
+ //
+ // Minor version of the current process image subsystem (PE Headers).
+ //
+ ULONG ImageSubsystemMinorVersion;
+
+ // ...
+} PEB_FULL, *PPEB_FULL;
+
+typedef struct _TEB_FULL {
+ //
+ // Thread Information Block (TIB) contains the thread's stack, base and limit addresses, the current stack pointer, and the exception list.
+ //
+ NT_TIB NtTib;
+
+ //
+ // Reserved.
+ //
+ PVOID EnvironmentPointer;
+
+ //
+ // Client ID for this thread.
+ //
+ CLIENT_ID ClientId;
+
+ //
+ // A handle to an active Remote Procedure Call (RPC) if the thread is currently involved in an RPC operation.
+ //
+ PVOID ActiveRpcHandle;
+
+ //
+ // A pointer to the __declspec(thread) local storage array.
+ //
+ PVOID ThreadLocalStoragePointer;
+
+ //
+ // A pointer to the Process Environment Block (PEB), which contains information about the process.
+ //
+ PPEB_FULL ProcessEnvironmentBlock;
+
+ //
+ // The previous Win32 error value for this thread.
+ //
+ ULONG LastErrorValue;
+
+ //
+ // The number of critical sections currently owned by this thread.
+ //
+ ULONG CountOfOwnedCriticalSections;
+
+ //
+ // Reserved.
+ //
+ PVOID CsrClientThread;
+
+ //
+ // Reserved for win32k.sys
+ //
+ PVOID Win32ThreadInfo;
+
+ //
+ // Reserved for user32.dll
+ //
+ ULONG User32Reserved[26];
+
+ //
+ // Reserved for winsrv.dll
+ //
+ ULONG UserReserved[5];
+
+ //
+ // Reserved.
+ //
+ PVOID WOW32Reserved;
+
+ //
+ // The LCID of the current thread. (Kernel32!GetThreadLocale)
+ //
+ LCID CurrentLocale;
+} TEB_FULL, *PTEB_FULL;
+
+static inline PTEB_FULL ffGetTeb() {
+ return (PTEB_FULL) NtCurrentTeb();
+}
+
+static inline PPEB_FULL ffGetPeb() {
+ return ffGetTeb()->ProcessEnvironmentBlock;
+}
+
+NTSYSAPI NTSTATUS NTAPI RtlExpandEnvironmentStrings(
+ _In_opt_ PVOID Environment,
+ _In_reads_(SourceLength) PCWSTR Source,
+ _In_ SIZE_T SourceLength,
+ _Out_writes_(DestinationLength) PWSTR Destination,
+ _In_ SIZE_T DestinationLength,
+ _Out_opt_ PSIZE_T ReturnLength);
+
+NTSYSAPI NTSTATUS NTAPI NtOpenKey(
+ _Out_ PHANDLE KeyHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ POBJECT_ATTRIBUTES ObjectAttributes);
+
+typedef enum _KEY_VALUE_INFORMATION_CLASS {
+ KeyValueBasicInformation, // KEY_VALUE_BASIC_INFORMATION
+ KeyValueFullInformation, // KEY_VALUE_FULL_INFORMATION
+ KeyValuePartialInformation, // KEY_VALUE_PARTIAL_INFORMATION
+ KeyValueFullInformationAlign64, // KEY_VALUE_FULL_INFORMATION_ALIGN64
+ KeyValuePartialInformationAlign64, // KEY_VALUE_PARTIAL_INFORMATION_ALIGN64
+ KeyValueLayerInformation, // KEY_VALUE_LAYER_INFORMATION
+ MaxKeyValueInfoClass
+} KEY_VALUE_INFORMATION_CLASS;
+
+NTSYSAPI NTSTATUS NTAPI NtQueryValueKey(
+ _In_ HANDLE KeyHandle,
+ _In_ PCUNICODE_STRING ValueName,
+ _In_ KEY_VALUE_INFORMATION_CLASS KeyValueInformationClass,
+ _Out_writes_bytes_to_opt_(Length, *ResultLength) PVOID KeyValueInformation,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength);
+
+NTSYSAPI NTSTATUS NTAPI RtlOpenCurrentUser(
+ _In_ ACCESS_MASK DesiredAccess,
+ _Out_ PHANDLE CurrentUserKey);
+
+typedef struct _KEY_VALUE_PARTIAL_INFORMATION {
+ ULONG TitleIndex;
+ ULONG Type;
+ ULONG DataLength;
+ _Field_size_bytes_(DataLength) UCHAR Data[];
+} KEY_VALUE_PARTIAL_INFORMATION, *PKEY_VALUE_PARTIAL_INFORMATION;
+
+typedef enum _KEY_INFORMATION_CLASS {
+ KeyBasicInformation, // KEY_BASIC_INFORMATION
+ KeyNodeInformation, // KEY_NODE_INFORMATION
+ KeyFullInformation, // KEY_FULL_INFORMATION
+ KeyNameInformation, // KEY_NAME_INFORMATION
+ KeyCachedInformation, // KEY_CACHED_INFORMATION
+ KeyFlagsInformation, // KEY_FLAGS_INFORMATION
+ KeyVirtualizationInformation, // KEY_VIRTUALIZATION_INFORMATION
+ KeyHandleTagsInformation, // KEY_HANDLE_TAGS_INFORMATION
+ KeyTrustInformation, // KEY_TRUST_INFORMATION
+ KeyLayerInformation, // KEY_LAYER_INFORMATION
+ MaxKeyInfoClass
+} KEY_INFORMATION_CLASS;
+
+NTSYSAPI NTSTATUS NTAPI NtEnumerateKey(
+ _In_ HANDLE KeyHandle,
+ _In_ ULONG Index,
+ _In_ KEY_INFORMATION_CLASS KeyInformationClass,
+ _Out_writes_bytes_to_opt_(Length, *ResultLength) PVOID KeyInformation,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength);
+
+typedef struct _KEY_BASIC_INFORMATION {
+ LARGE_INTEGER LastWriteTime; // Number of 100-nanosecond intervals since this key or any of its values changed.
+ ULONG TitleIndex; // Reserved // A legacy field originally intended for use with localization such as an index of a resource table.
+ ULONG NameLength; // The size, in bytes, of the key name string in the Name array.
+ _Field_size_bytes_(NameLength) WCHAR Name[]; // The name of the registry key. This string is not null-terminated.
+} KEY_BASIC_INFORMATION, *PKEY_BASIC_INFORMATION;
+
+typedef struct _KEY_FULL_INFORMATION {
+ LARGE_INTEGER LastWriteTime;
+ ULONG TitleIndex;
+ ULONG ClassOffset;
+ ULONG ClassLength;
+ ULONG SubKeys;
+ ULONG MaxNameLength;
+ ULONG MaxClassLength;
+ ULONG Values;
+ ULONG MaxValueNameLength;
+ ULONG MaxValueDataLength;
+ WCHAR Class[];
+} KEY_FULL_INFORMATION, *PKEY_FULL_INFORMATION;
+
+NTSYSAPI NTSTATUS NTAPI NtQueryKey(
+ _In_ HANDLE KeyHandle,
+ _In_ KEY_INFORMATION_CLASS KeyInformationClass,
+ _Out_writes_bytes_to_opt_(Length, *ResultLength) PVOID KeyInformation,
+ _In_ ULONG Length,
+ _Out_ PULONG ResultLength);
+
+NTSYSAPI NTSTATUS NTAPI NtOpenProcess(
+ _Out_ PHANDLE ProcessHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_ PCOBJECT_ATTRIBUTES ObjectAttributes,
+ _In_opt_ PCLIENT_ID ClientId);
+
+NTSYSAPI NTSTATUS NTAPI LdrLoadDll(
+ _In_opt_ PCWSTR DllPath,
+ _In_opt_ PULONG DllCharacteristics,
+ _In_ PCUNICODE_STRING DllName,
+ _Out_ PVOID* DllHandle);
+
+NTSYSAPI NTSTATUS NTAPI LdrUnloadDll(
+ _In_ PVOID DllHandle);
+
+NTSYSAPI NTSTATUS NTAPI LdrGetDllHandle(
+ _In_opt_ PCWSTR DllPath,
+ _In_opt_ PULONG DllCharacteristics,
+ _In_ PCUNICODE_STRING DllName,
+ _Out_ PVOID* DllHandle);
+
+NTSYSAPI NTSTATUS NTAPI LdrGetProcedureAddress(
+ _In_ PVOID DllHandle,
+ _In_opt_ PCANSI_STRING ProcedureName,
+ _In_opt_ ULONG ProcedureNumber,
+ _Out_ PVOID* ProcedureAddress);
+
+typedef enum _SECTION_INHERIT {
+ ViewShare = 1,
+ ViewUnmap = 2
+} SECTION_INHERIT;
+
+NTSYSAPI NTSTATUS NTAPI NtCreateSection(
+ _Out_ PHANDLE SectionHandle,
+ _In_ ACCESS_MASK DesiredAccess,
+ _In_opt_ POBJECT_ATTRIBUTES ObjectAttributes,
+ _In_opt_ PLARGE_INTEGER MaximumSize,
+ _In_ ULONG SectionPageProtection,
+ _In_ ULONG AllocationAttributes,
+ _In_opt_ HANDLE FileHandle);
+
+NTSYSAPI NTSTATUS NTAPI NtMapViewOfSection(
+ _In_ HANDLE SectionHandle,
+ _In_ HANDLE ProcessHandle,
+ _Inout_ _At_(*BaseAddress, _Readable_bytes_(*ViewSize) _Writable_bytes_(*ViewSize) _Post_readable_byte_size_(*ViewSize)) PVOID* BaseAddress,
+ _In_ ULONG_PTR ZeroBits,
+ _In_ SIZE_T CommitSize,
+ _Inout_opt_ PLARGE_INTEGER SectionOffset,
+ _Inout_ PSIZE_T ViewSize,
+ _In_ SECTION_INHERIT InheritDisposition,
+ _In_ ULONG AllocationType,
+ _In_ ULONG PageProtection);
+
+NTSYSAPI NTSTATUS NTAPI NtUnmapViewOfSection(
+ _In_ HANDLE ProcessHandle,
+ _In_opt_ PVOID BaseAddress);
+
+NTSYSAPI LOGICAL NTAPI RtlQueryPerformanceCounter(
+ _Out_ PLARGE_INTEGER PerformanceCounter);
+
+NTSYSAPI LOGICAL NTAPI RtlQueryPerformanceFrequency(
+ _Out_ PLARGE_INTEGER PerformanceFrequency);
+
+NTSYSAPI NTSTATUS NTAPI NtCancelIoFileEx(
+ _In_ HANDLE FileHandle,
+ _In_opt_ PIO_STATUS_BLOCK IoRequestToCancel,
+ _Out_ PIO_STATUS_BLOCK IoStatusBlock);
+
+NTSYSAPI NTSTATUS NTAPI NtTerminateProcess(
+ _In_opt_ HANDLE ProcessHandle,
+ _In_ NTSTATUS ExitStatus);
diff --git a/src/common/windows/perflib_.h b/src/common/windows/perflib_.h
new file mode 100644
index 0000000..f30ed54
--- /dev/null
+++ b/src/common/windows/perflib_.h
@@ -0,0 +1,134 @@
+#pragma once
+
+#include <windows.h>
+#include <perflib.h>
+
+// Missing from <perflib.h> of MinGW-w64 SDK
+
+#define PERF_WILDCARD_COUNTER 0xFFFFFFFF
+#define PERF_WILDCARD_INSTANCE L"*"
+#define PERF_AGGREGATE_INSTANCE L"_Total"
+#define PERF_MAX_INSTANCE_NAME 1024
+
+typedef struct _PERF_INSTANCE_HEADER {
+ ULONG Size; // = sizeof(PERF_INSTANCE_HEADER) + sizeof(InstanceName) + sizeof(Padding)
+ ULONG InstanceId; // Instance ID.
+ // Followed by:
+ // WCHAR InstanceName[]; // Nul-terminated.
+ // WCHAR Padding[]; // Pad to a multiple of 8 bytes
+} PERF_INSTANCE_HEADER, *PPERF_INSTANCE_HEADER;
+
+typedef struct _PERF_COUNTER_IDENTIFIER {
+ GUID CounterSetGuid; // The GUID of the counterset.
+ ULONG Status; // Win32 error code indicating success/failure of the add/delete operation.
+ ULONG Size; // sizeof(PERF_COUNTER_IDENTIFIER) + sizeof(InstanceName) + sizeof(Padding)
+ ULONG CounterId; // CounterId, or PERF_WILDCARD_COUNTER for all counters.
+ ULONG InstanceId; // InstanceId, or 0xFFFFFFFF to not filter on instance ID.
+ ULONG Index; // Set by PerfQueryCounterInfo to the position in which the corresponding counter data is returned.
+ ULONG Reserved; // Reserved.
+ // Followed by:
+ // WCHAR InstanceName[];
+ // WCHAR Padding[];
+} PERF_COUNTER_IDENTIFIER, *PPERF_COUNTER_IDENTIFIER;
+
+typedef struct _PERF_DATA_HEADER {
+ ULONG dwTotalSize; // = sizeof(PERF_DATA_HEADER) + sizeof(PERF_COUNTER_HEADER blocks...)
+ ULONG dwNumCounters; // The number of PERF_COUNTER_HEADER blocks.
+ LONGLONG PerfTimeStamp; // Timestamp from a high-resolution clock.
+ LONGLONG PerfTime100NSec; // The number of 100 nanosecond intervals since January 1, 1601, in Coordinated Universal Time (UTC).
+ LONGLONG PerfFreq; // The frequency of a high-resolution clock.
+ SYSTEMTIME SystemTime; // The time at which data is collected on the provider side.
+ // Followed by:
+ // PERF_COUNTER_HEADER blocks...;
+} PERF_DATA_HEADER, *PPERF_DATA_HEADER;
+
+typedef enum _PerfCounterDataType {
+ PERF_ERROR_RETURN = 0, /* An error occurred when the performance counter value was queried. */
+ PERF_SINGLE_COUNTER = 1, /* Query returned a single counter from a single-instance. */
+ PERF_MULTIPLE_COUNTERS = 2, /* Query returned multiple counters from a single instance. */
+ PERF_MULTIPLE_INSTANCES = 4, /* Query returned a single counter from each of multiple instances. */
+ PERF_COUNTERSET = 6 /* Query returned multiple counters from each of multiple instances. */
+} PerfCounterDataType;
+
+typedef struct _PERF_COUNTER_HEADER {
+ ULONG dwStatus; // Win32 error code indicating success/failure of the query operation.
+ PerfCounterDataType dwType; // Result type - error, single/single, multi/single, single/multi, multi/multi.
+ ULONG dwSize; // = sizeof(PERF_COUNTER_HEADER) + sizeof(Additional data)
+ ULONG Reserved; // Reserved.
+ // Followed by additional data:
+ // If dwType == PERF_ERROR_RETURN: nothing.
+ // If dwType == PERF_SINGLE_COUNTER: PERF_COUNTER_DATA block.
+ // If dwType == PERF_MULTIPLE_COUNTERS: PERF_MULTI_COUNTERS block + PERF_COUNTER_DATA blocks.
+ // If dwType == PERF_MULTIPLE_INSTANCES: PERF_MULTI_INSTANCES block.
+ // If dwType == PERF_COUNTERSET: PERF_MULTI_COUNTERS block + PERF_MULTI_INSTANCES block.
+} PERF_COUNTER_HEADER, *PPERF_COUNTER_HEADER;
+
+typedef struct _PERF_MULTI_INSTANCES {
+ ULONG dwTotalSize; // = sizeof(PERF_MULTI_INSTANCES) + sizeof(instance data blocks...)
+ ULONG dwInstances; // Number of instance data blocks.
+ // Followed by:
+ // Instance data blocks...;
+} PERF_MULTI_INSTANCES, *PPERF_MULTI_INSTANCES;
+
+typedef struct _PERF_MULTI_COUNTERS {
+ ULONG dwSize; // sizeof(PERF_MULTI_COUNTERS) + sizeof(CounterIds)
+ ULONG dwCounters; // Number of counter ids.
+ // Followed by:
+ // DWORD CounterIds[dwCounters];
+} PERF_MULTI_COUNTERS, *PPERF_MULTI_COUNTERS;
+
+typedef struct _PERF_COUNTER_DATA {
+ ULONG dwDataSize; // Size of the counter data, in bytes.
+ ULONG dwSize; // = sizeof(PERF_COUNTER_DATA) + sizeof(Data) + sizeof(Padding)
+ // Followed by:
+ // BYTE Data[dwDataSize];
+ // BYTE Padding[];
+} PERF_COUNTER_DATA, *PPERF_COUNTER_DATA;
+
+_Success_(return == ERROR_SUCCESS)
+ ULONG
+ WINAPI
+ PerfEnumerateCounterSetInstances(
+ _In_opt_z_ LPCWSTR szMachine,
+ _In_ LPCGUID pCounterSetId,
+ _Out_opt_bytecap_post_bytecount_(cbInstances, *pcbInstancesActual) PPERF_INSTANCE_HEADER pInstances,
+ DWORD cbInstances,
+ _Out_ LPDWORD pcbInstancesActual);
+
+_Success_(return == ERROR_SUCCESS)
+ ULONG
+ WINAPI
+ PerfOpenQueryHandle(
+ _In_opt_z_ LPCWSTR szMachine,
+ _Out_ HANDLE* phQuery);
+
+_Success_(return == ERROR_SUCCESS)
+ ULONG
+ WINAPI
+ PerfCloseQueryHandle(
+ _In_ HANDLE hQuery);
+
+_Success_(return == ERROR_SUCCESS)
+ ULONG
+ WINAPI
+ PerfAddCounters(
+ _In_ HANDLE hQuery,
+ _Inout_bytecount_(cbCounters) PPERF_COUNTER_IDENTIFIER pCounters,
+ DWORD cbCounters);
+
+_Success_(return == ERROR_SUCCESS)
+ ULONG
+ WINAPI
+ PerfDeleteCounters(
+ _In_ HANDLE hQuery,
+ _Inout_bytecount_(cbCounters) PPERF_COUNTER_IDENTIFIER pCounters,
+ DWORD cbCounters);
+
+_Success_(return == ERROR_SUCCESS)
+ ULONG
+ WINAPI
+ PerfQueryCounterData(
+ _In_ HANDLE hQuery,
+ _Out_opt_bytecap_post_bytecount_(cbCounterBlock, *pcbCounterBlockActual) PPERF_DATA_HEADER pCounterBlock,
+ DWORD cbCounterBlock,
+ _Out_ LPDWORD pcbCounterBlockActual);
diff --git a/src/common/windows/registry.c b/src/common/windows/registry.c
new file mode 100644
index 0000000..3618760
--- /dev/null
+++ b/src/common/windows/registry.c
@@ -0,0 +1,388 @@
+#include "registry.h"
+#include "unicode.h"
+#include "common/mallocHelper.h"
+#include "common/debug.h"
+#include "common/windows/nt.h"
+
+#include <stdalign.h>
+#include <ntstatus.h>
+
+static HANDLE hRootKeys[8 /*(uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS - (uintptr_t) HKEY_CLASSES_ROOT + 1*/];
+
+static const char* hKey2Str(HANDLE hRootKey) {
+#define HKEY_CASE(compareKey) \
+ if (hRootKey == hRootKeys[(uintptr_t) compareKey - (uintptr_t) HKEY_CLASSES_ROOT]) return #compareKey;
+ HKEY_CASE(HKEY_CLASSES_ROOT)
+ HKEY_CASE(HKEY_CURRENT_USER)
+ HKEY_CASE(HKEY_LOCAL_MACHINE)
+ HKEY_CASE(HKEY_USERS)
+ HKEY_CASE(HKEY_PERFORMANCE_DATA)
+ HKEY_CASE(HKEY_CURRENT_CONFIG)
+ HKEY_CASE(HKEY_DYN_DATA)
+ HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS)
+#undef HKEY_CASE
+
+ return "UNKNOWN";
+}
+
+HANDLE ffRegGetRootKeyHandle(HKEY hKey) {
+ assert(hKey);
+ assert((uintptr_t) hKey >= (uintptr_t) HKEY_CLASSES_ROOT && (uintptr_t) hKey <= (uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS);
+
+ FF_DEBUG("Getting root key handle for HKEY %08llx", (uint64_t) (uintptr_t) hKey);
+
+ HANDLE result = hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT];
+ if (result) {
+ FF_DEBUG("Found cached root key handle for %s -> %p", hKey2Str(result), result);
+ return result;
+ }
+
+ switch ((uintptr_t) hKey) {
+ case (uintptr_t) HKEY_CURRENT_USER: {
+ NTSTATUS status = RtlOpenCurrentUser(KEY_READ, &result);
+ if (!NT_SUCCESS(status)) {
+ FF_DEBUG("RtlOpenCurrentUser() failed: %s", ffDebugNtStatus(status));
+ return NULL;
+ }
+ break;
+ }
+
+ case (uintptr_t) HKEY_LOCAL_MACHINE: {
+ UNICODE_STRING path = RTL_CONSTANT_STRING(L"\\Registry\\Machine");
+ NTSTATUS status = NtOpenKey(&result, KEY_READ, &(OBJECT_ATTRIBUTES) {
+ .Length = sizeof(OBJECT_ATTRIBUTES),
+ .RootDirectory = NULL,
+ .ObjectName = &path,
+ .Attributes = OBJ_CASE_INSENSITIVE,
+ });
+ if (!NT_SUCCESS(status)) {
+ FF_DEBUG("NtOpenKey(%ls) failed: %s (0x%08lx)", path.Buffer, ffDebugNtStatus(status), status);
+ return NULL;
+ }
+ break;
+ }
+ default:
+ // Unsupported
+ FF_DEBUG("Unsupported root key: %p", hKey);
+ assert(false);
+ return NULL;
+ }
+ hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT] = result;
+ FF_DEBUG("Opened root key %s -> %p", hKey2Str(result), result);
+ return result;
+}
+
+bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error) {
+ assert(hKey);
+ assert(subKeyW);
+ assert(result);
+
+ FF_DEBUG("Opening subkey %s\\%ls for read", hKey2Str(hKey), subKeyW);
+
+ USHORT subKeyLen = (USHORT) (wcslen(subKeyW) * sizeof(wchar_t));
+ if (!NT_SUCCESS(NtOpenKey(result, KEY_READ, &(OBJECT_ATTRIBUTES) {
+ .Length = sizeof(OBJECT_ATTRIBUTES),
+ .RootDirectory = hKey,
+ .ObjectName = &(UNICODE_STRING) {
+ .Length = subKeyLen,
+ .MaximumLength = subKeyLen + (USHORT) sizeof(wchar_t),
+ .Buffer = (wchar_t*) subKeyW,
+ },
+ }))) {
+ FF_DEBUG("NtOpenKey(%s\\<subkey>) failed", hKey2Str(hKey));
+ if (error) {
+ FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW);
+ ffStrbufAppendF(error, "NtOpenKey(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
+ }
+ return false;
+ }
+ FF_DEBUG("Opened subkey under %s -> %p", hKey2Str(hKey), *result);
+ return true;
+}
+
+static bool processRegValue(const FFRegValueArg* arg, const ULONG regType, const void* regData, ULONG regDataLen, FFstrbuf* error) {
+ switch (arg->type) {
+ case FF_ARG_TYPE_STRBUF: {
+ if (regType != REG_SZ && regType != REG_EXPAND_SZ) {
+ goto type_mismatch;
+ }
+
+ FFstrbuf* strbuf = (FFstrbuf*) arg->value;
+ uint32_t strLen = regDataLen / sizeof(wchar_t);
+ if (strLen == 0) {
+ ffStrbufClear(strbuf);
+ } else {
+ const wchar_t* ws = (const wchar_t*) regData;
+ if (ws[strLen - 1] == L'\0') {
+ --strLen;
+ }
+ ffStrbufSetNWS(strbuf, strLen, ws);
+ }
+ break;
+ }
+
+ case FF_ARG_TYPE_UINT:
+ case FF_ARG_TYPE_UINT64:
+ case FF_ARG_TYPE_UINT16:
+ case FF_ARG_TYPE_UINT8:
+ case FF_ARG_TYPE_BOOL: {
+ uint64_t value = 0;
+
+ if (regType == REG_DWORD) {
+ if (regDataLen < sizeof(uint32_t)) {
+ goto type_mismatch;
+ }
+ value = *(uint32_t*) regData;
+ } else if (regType == REG_QWORD) {
+ if (regDataLen < sizeof(uint64_t)) {
+ goto type_mismatch;
+ }
+ value = *(uint64_t*) regData;
+ } else {
+ goto type_mismatch;
+ }
+
+ if (arg->type == FF_ARG_TYPE_UINT) {
+ *(uint32_t*) arg->value = (uint32_t) value;
+ } else if (arg->type == FF_ARG_TYPE_UINT64) {
+ *(uint64_t*) arg->value = (uint64_t) value;
+ } else if (arg->type == FF_ARG_TYPE_UINT16) {
+ *(uint16_t*) arg->value = (uint16_t) value;
+ } else if (arg->type == FF_ARG_TYPE_UINT8) {
+ *(uint8_t*) arg->value = (uint8_t) value;
+ } else if (arg->type == FF_ARG_TYPE_BOOL) {
+ *(bool*) arg->value = value != 0;
+ }
+ break;
+ }
+
+ case FF_ARG_TYPE_FLOAT: {
+ if (regDataLen < sizeof(float)) {
+ goto type_mismatch;
+ }
+ *(float*) arg->value = *(float*) regData;
+ break;
+ }
+
+ case FF_ARG_TYPE_DOUBLE: {
+ if (regDataLen < sizeof(double)) {
+ goto type_mismatch;
+ }
+ *(double*) arg->value = *(double*) regData;
+ break;
+ }
+
+ case FF_ARG_TYPE_LIST: {
+ if (regType != REG_MULTI_SZ) {
+ goto type_mismatch;
+ }
+
+ FFlist* list = (FFlist*) arg->value;
+ ffListClear(list);
+
+ for (
+ const wchar_t* ptr = (const wchar_t*) regData;
+ (const uint8_t*) ptr < (const uint8_t*) regData + regDataLen && *ptr;
+ ptr++) {
+ uint32_t strLen = (uint32_t) wcsnlen(ptr, regDataLen / sizeof(wchar_t) - (size_t) (ptr - (const wchar_t*) regData));
+ ffStrbufInitNWS(FF_LIST_ADD(FFstrbuf, *list), strLen, ptr);
+ ptr += strLen;
+ }
+ break;
+ }
+
+ case FF_ARG_TYPE_BUFFER: {
+ FFArgBuffer* buffer = (FFArgBuffer*) arg->value;
+ if (buffer->length == 0) {
+ buffer->data = malloc(regDataLen);
+ } else if (buffer->length < regDataLen) {
+ if (error) {
+ FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
+ ffStrbufAppendF(error, "ffRegReadValues(%s) buffer too small (%u): expected %u", nameA.chars, (unsigned) buffer->length, (unsigned) regDataLen);
+ }
+ return false;
+ }
+ buffer->length = regDataLen;
+ memcpy(buffer->data, regData, regDataLen);
+ break;
+ }
+
+ case FF_ARG_TYPE_INT: // Use UINT instead
+ case FF_ARG_TYPE_STRING:
+ case FF_ARG_TYPE_NULL:
+ default:
+ if (error) {
+ FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
+ ffStrbufAppendF(error, "processRegValue(%s) unsupported FFArgType %u", nameA.chars, (unsigned) arg->type);
+ }
+ return false;
+ }
+
+ return true;
+
+type_mismatch:
+ FF_DEBUG("ffRegReadValues(%ls) type mismatch: regType=%u, argType=%u, dataLen=%u",
+ arg->name ?: L"(default)",
+ (unsigned) regType,
+ (unsigned) arg->type,
+ (unsigned) regDataLen);
+ if (error) {
+ FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
+ ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: regType=%u, argType=%u, dataLen=%u", nameA.chars, (unsigned) regType, (unsigned) arg->type, (unsigned) regDataLen);
+ }
+ return false;
+}
+
+bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error) {
+ UNICODE_STRING* valueNameU = &(UNICODE_STRING) {
+ .Length = arg->name ? (USHORT) (wcslen(arg->name) * sizeof(wchar_t)) : 0 /*(default)*/,
+ .MaximumLength = 0,
+ .Buffer = (wchar_t*) arg->name,
+ };
+
+ alignas(KEY_VALUE_PARTIAL_INFORMATION) uint8_t staticBuffer[128 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
+ FF_AUTO_FREE uint8_t* dynamicBuffer = NULL;
+
+ KEY_VALUE_PARTIAL_INFORMATION* buffer = (KEY_VALUE_PARTIAL_INFORMATION*) &staticBuffer;
+ DWORD bufSize = sizeof(staticBuffer);
+ if (NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize))) {
+ goto process_value;
+ }
+
+ if (bufSize == 0) {
+ FF_DEBUG("NtQueryValueKey(%p, %ls) failed (bufSize=0)", hKey, arg->name ?: L"(default)");
+ if (error) {
+ FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
+ ffStrbufAppendF(error, "NtQueryValueKey(%p, %s) failed", hKey, valueNameA.chars);
+ }
+ return false;
+ }
+
+ dynamicBuffer = (uint8_t*) malloc(bufSize);
+ buffer = (KEY_VALUE_PARTIAL_INFORMATION*) dynamicBuffer;
+
+ if (!NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize))) {
+ FF_DEBUG("NtQueryValueKey(%p, %ls, buffer=%u) failed", hKey, arg->name ?: L"(default)", (unsigned) bufSize);
+ if (error) {
+ FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
+ ffStrbufAppendF(error, "NtQueryValueKey(%p, %s, buffer) failed", hKey, valueNameA.chars);
+ }
+ return false;
+ }
+
+process_value:
+ FF_DEBUG("Read value from %p (%ls), type=%u, len=%u", hKey, arg->name ?: L"(default)", (unsigned) buffer->Type, (unsigned) buffer->DataLength);
+ return processRegValue(arg, buffer->Type, buffer->Data, buffer->DataLength, error);
+}
+
+bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error) {
+ if (__builtin_expect(argc == 0, false)) {
+ return true;
+ }
+
+ assert(argv);
+
+ FF_AUTO_FREE UNICODE_STRING* names = (UNICODE_STRING*) calloc(argc, sizeof(*names));
+ FF_AUTO_FREE KEY_VALUE_ENTRY* entries = (KEY_VALUE_ENTRY*) calloc(argc, sizeof(*entries));
+
+ for (uint32_t i = 0; i < argc; ++i) {
+ if (__builtin_expect(!argv[i].value, false)) {
+ FF_DEBUG("ffRegReadValues(argv[%u].value) is NULL", (unsigned) i);
+ if (error) {
+ ffStrbufAppendF(error, "ffRegReadValues(argv[%u].pVar) is NULL", (unsigned) i);
+ }
+ return false;
+ }
+
+ names[i] = (UNICODE_STRING) {
+ .Length = argv[i].name ? (USHORT) (wcslen(argv[i].name) * sizeof(wchar_t)) : 0 /*(default)*/,
+ .MaximumLength = 0,
+ .Buffer = (wchar_t*) argv[i].name,
+ };
+ entries[i].ValueName = &names[i];
+ }
+
+ ULONG bufferSize = argc * 128;
+ if (bufferSize < 512) {
+ bufferSize = 512;
+ }
+
+ FF_AUTO_FREE uint8_t* buffer = NULL;
+
+ while (true) {
+ buffer = (uint8_t*) realloc(buffer, bufferSize);
+
+ ULONG writtenSize = bufferSize;
+ ULONG requiredSize = 0;
+ NTSTATUS status = NtQueryMultipleValueKey(hKey, entries, argc, buffer, &writtenSize, &requiredSize);
+
+ if (!NT_SUCCESS(status)) {
+ // Buffer too small: docs guarantee requiredSize is returned when provided.
+ if (requiredSize > bufferSize) {
+ FF_DEBUG("NtQueryMultipleValueKey(%p) resize buffer: %u -> %u", hKey, (unsigned) bufferSize, (unsigned) requiredSize);
+ bufferSize = requiredSize;
+ continue;
+ }
+
+ FF_DEBUG("NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X", hKey, (unsigned) argc, (unsigned) status);
+ if (error) {
+ ffStrbufAppendF(error, "NtQueryMultipleValueKey(%p, argc=%u) failed, status=0x%08X", hKey, (unsigned) argc, (unsigned) status);
+ }
+ return false;
+ }
+
+ break;
+ }
+
+ for (uint32_t i = 0; i < argc; ++i) {
+ const FFRegValueArg* arg = &argv[i];
+ const KEY_VALUE_ENTRY* entry = &entries[i];
+
+ FF_DEBUG("Read value[%u] from %p: type=%u, len=%u", (unsigned) i, hKey, (unsigned) entry->Type, (unsigned) entry->DataLength);
+ if (!processRegValue(arg, entry->Type, buffer + entry->DataOffset, entry->DataLength, error)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error) {
+ assert(hKey);
+ assert(result);
+
+ alignas(KEY_BASIC_INFORMATION) uint8_t buffer[sizeof(KEY_BASIC_INFORMATION) + MAX_PATH * sizeof(wchar_t)];
+ ULONG bufSize = (ULONG) sizeof(buffer);
+ KEY_BASIC_INFORMATION* keyInfo = (KEY_BASIC_INFORMATION*) buffer;
+
+ if (!NT_SUCCESS(NtEnumerateKey(hKey, index, KeyBasicInformation, keyInfo, bufSize, &bufSize))) {
+ FF_DEBUG("NtEnumerateKey(hKey=%p, index=%u) failed", hKey, (unsigned) index);
+ if (error) {
+ ffStrbufAppendF(error, "NtEnumerateKey(hKey=%p, %u, keyInfo) failed", hKey, (unsigned) index);
+ }
+ return false;
+ }
+
+ ffStrbufSetNWS(result, keyInfo->NameLength / sizeof(wchar_t), keyInfo->Name);
+ return true;
+}
+
+bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error) {
+ assert(hKey);
+ assert(result);
+
+ alignas(KEY_FULL_INFORMATION) uint8_t buffer[sizeof(KEY_FULL_INFORMATION) + MAX_PATH * sizeof(wchar_t)];
+ ULONG bufSize = sizeof(buffer);
+ KEY_FULL_INFORMATION* keyInfo = (KEY_FULL_INFORMATION*) buffer;
+
+ if (!NT_SUCCESS(NtQueryKey(hKey, KeyFullInformation, keyInfo, bufSize, &bufSize))) {
+ FF_DEBUG("NtQueryKey(hKey=%p, KeyFullInformation) failed", hKey);
+ if (error) {
+ ffStrbufAppendF(error, "NtQueryKey(hKey=%p, KeyFullInformation, keyInfo) failed", hKey);
+ }
+ return false;
+ }
+
+ *result = (uint32_t) keyInfo->SubKeys;
+ return true;
+}
diff --git a/src/common/windows/registry.h b/src/common/windows/registry.h
new file mode 100644
index 0000000..9745372
--- /dev/null
+++ b/src/common/windows/registry.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include "fastfetch.h"
+#include "common/argType.h"
+#include "common/io.h"
+
+#ifndef HKEY_CURRENT_USER
+ #define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR) ((LONG) 0x80000000))
+ #define HKEY_CURRENT_USER ((HKEY) (ULONG_PTR) ((LONG) 0x80000001))
+ #define HKEY_LOCAL_MACHINE ((HKEY) (ULONG_PTR) ((LONG) 0x80000002))
+ #define HKEY_USERS ((HKEY) (ULONG_PTR) ((LONG) 0x80000003))
+ #define HKEY_PERFORMANCE_DATA ((HKEY) (ULONG_PTR) ((LONG) 0x80000004))
+ #define HKEY_CURRENT_CONFIG ((HKEY) (ULONG_PTR) ((LONG) 0x80000005))
+ #define HKEY_DYN_DATA ((HKEY) (ULONG_PTR) ((LONG) 0x80000006))
+ #define HKEY_CURRENT_USER_LOCAL_SETTINGS ((HKEY) (ULONG_PTR) ((LONG) 0x80000007))
+#endif
+
+typedef struct FFRegValueArg {
+ FFArgType type;
+ const void* value;
+ const wchar_t* name;
+} FFRegValueArg;
+
+HANDLE ffRegGetRootKeyHandle(HKEY hKey);
+bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error);
+bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error);
+bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error);
+bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error);
+bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error);
+
+static inline bool ffRegOpenKeyForRead(HKEY hRootKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error) {
+ return ffRegOpenSubkeyForRead(ffRegGetRootKeyHandle(hRootKey), subKeyW, result, error);
+}
+
+static inline bool ffRegReadStrbuf(HANDLE hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error) {
+ return ffRegReadValue(hKey, &(FFRegValueArg) {
+ .type = FF_ARG_TYPE_STRBUF,
+ .value = result,
+ .name = valueNameW,
+ },
+ error);
+}
+static inline bool ffRegReadUint(HANDLE hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error) {
+ return ffRegReadValue(hKey, &(FFRegValueArg) {
+ .type = FF_ARG_TYPE_UINT,
+ .value = result,
+ .name = valueNameW,
+ },
+ error);
+}
+static inline bool ffRegReadUint64(HANDLE hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error) {
+ return ffRegReadValue(hKey, &(FFRegValueArg) {
+ .type = FF_ARG_TYPE_UINT64,
+ .value = result,
+ .name = valueNameW,
+ },
+ error);
+}
+static inline bool ffRegReadData(HANDLE hKey, const wchar_t* valueNameW, FFArgBuffer* buffer, FFstrbuf* error) {
+ return ffRegReadValue(hKey, &(FFRegValueArg) {
+ .type = FF_ARG_TYPE_BUFFER,
+ .value = buffer,
+ .name = valueNameW,
+ },
+ error);
+}
diff --git a/src/common/windows/unicode.c b/src/common/windows/unicode.c
new file mode 100644
index 0000000..06008ba
--- /dev/null
+++ b/src/common/windows/unicode.c
@@ -0,0 +1,44 @@
+#include "unicode.h"
+
+#include "common/windows/nt.h"
+
+void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) {
+ if (!length) {
+ ffStrbufClear(result);
+ return;
+ }
+
+ ULONG size_needed = 0;
+ NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t));
+
+ if (size_needed == 0) {
+ ffStrbufSetF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status);
+ return;
+ }
+
+ ffStrbufEnsureFixedLengthFree(result, size_needed);
+ RtlUnicodeToUTF8N(result->chars, size_needed, &size_needed, source, length * sizeof(wchar_t));
+
+ result->length = size_needed;
+ result->chars[size_needed] = '\0';
+}
+
+void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) {
+ if (!length) {
+ return;
+ }
+
+ ULONG size_needed = 0;
+ NTSTATUS status = RtlUnicodeToUTF8N(NULL, 0, &size_needed, source, length * sizeof(wchar_t));
+
+ if (size_needed == 0) {
+ ffStrbufAppendF(result, "RtlUnicodeToUTF8N failed: %X", (unsigned) status);
+ return;
+ }
+
+ ffStrbufEnsureFree(result, size_needed);
+ RtlUnicodeToUTF8N(result->chars + result->length, size_needed, &size_needed, source, length * sizeof(wchar_t));
+
+ result->length += size_needed;
+ result->chars[result->length] = '\0';
+}
diff --git a/src/common/windows/unicode.h b/src/common/windows/unicode.h
new file mode 100644
index 0000000..f58b425
--- /dev/null
+++ b/src/common/windows/unicode.h
@@ -0,0 +1,46 @@
+#pragma once
+
+#include "common/FFstrbuf.h"
+#include <wchar.h>
+
+void ffStrbufSetNWS(FFstrbuf* result, uint32_t length, const wchar_t* source);
+void ffStrbufAppendNWS(FFstrbuf* result, uint32_t length, const wchar_t* source);
+
+static inline void ffStrbufSetWS(FFstrbuf* result, const wchar_t* source) {
+ if (!source) {
+ return ffStrbufClear(result);
+ }
+ return ffStrbufSetNWS(result, (uint32_t) wcslen(source), source);
+}
+
+static inline void ffStrbufAppendWS(FFstrbuf* result, const wchar_t* source) {
+ if (!source) {
+ return;
+ }
+ return ffStrbufAppendNWS(result, (uint32_t) wcslen(source), source);
+}
+
+static inline void ffStrbufInitNWS(FFstrbuf* result, uint32_t length, const wchar_t* source) {
+ ffStrbufInit(result);
+ return ffStrbufSetNWS(result, length, source);
+}
+
+static inline void ffStrbufInitWS(FFstrbuf* result, const wchar_t* source) {
+ if (!source) {
+ return ffStrbufInit(result);
+ }
+ return ffStrbufInitNWS(result, (uint32_t) wcslen(source), source);
+}
+
+static inline FFstrbuf ffStrbufCreateNWS(uint32_t length, const wchar_t* source) {
+ FFstrbuf result;
+ ffStrbufInitNWS(&result, length, source);
+ return result;
+}
+
+static inline FFstrbuf ffStrbufCreateWS(const wchar_t* source) {
+ if (!source) {
+ return ffStrbufCreate();
+ }
+ return ffStrbufCreateNWS((uint32_t) wcslen(source), source);
+}
diff --git a/src/common/windows/unicode.hpp b/src/common/windows/unicode.hpp
new file mode 100644
index 0000000..f9c355a
--- /dev/null
+++ b/src/common/windows/unicode.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#ifdef __cplusplus
+
+extern "C" {
+ #include "unicode.h"
+}
+
+ #include <string_view>
+
+static inline void ffStrbufInitWSV(FFstrbuf* result, const std::wstring_view source) {
+ return ffStrbufInitNWS(result, (uint32_t) source.size(), source.data());
+}
+
+static inline FFstrbuf ffStrbufCreateWSV(const std::wstring_view source) {
+ return ffStrbufCreateNWS((uint32_t) source.size(), source.data());
+}
+
+static inline void ffStrbufSetWSV(FFstrbuf* result, const std::wstring_view source) {
+ return ffStrbufSetNWS(result, (uint32_t) source.size(), source.data());
+}
+
+#else
+
+ #error Must be included in C++ source file
+
+#endif
diff --git a/src/common/windows/util.hpp b/src/common/windows/util.hpp
new file mode 100644
index 0000000..939f6bb
--- /dev/null
+++ b/src/common/windows/util.hpp
@@ -0,0 +1,21 @@
+#pragma once
+
+#include <utility>
+#include <type_traits>
+
+template <typename Fn>
+struct on_scope_exit {
+ static_assert(std::is_nothrow_move_constructible<Fn>::value,
+ "Fn must be nothrow move constructible");
+
+ explicit on_scope_exit(Fn&& fn) noexcept
+ : _fn(std::move(fn)) {};
+ on_scope_exit(const on_scope_exit&) = delete;
+ on_scope_exit& operator=(const on_scope_exit&) = delete;
+ ~on_scope_exit() noexcept {
+ this->_fn();
+ }
+
+ private:
+ Fn _fn;
+};
diff --git a/src/common/windows/variant.cpp b/src/common/windows/variant.cpp
new file mode 100644
index 0000000..d5610d3
--- /dev/null
+++ b/src/common/windows/variant.cpp
@@ -0,0 +1,20 @@
+#include "variant.hpp"
+
+#include <oleauto.h>
+
+FFWmiVariant::FFWmiVariant(std::initializer_list<PCWSTR> strings) : FFWmiVariant() {
+ SAFEARRAYBOUND bound = {
+ .cElements = (ULONG) strings.size(),
+ .lLbound = 0,
+ };
+ SAFEARRAY* psa = SafeArrayCreate(VT_BSTR, 1, &bound);
+
+ LONG i = 0;
+ for (PCWSTR str : strings) {
+ SafeArrayPutElement(psa, &i, bstr_t(str));
+ ++i;
+ }
+
+ this->vt = VT_ARRAY | VT_BSTR;
+ this->parray = psa;
+}
diff --git a/src/common/windows/variant.hpp b/src/common/windows/variant.hpp
new file mode 100644
index 0000000..58e46b7
--- /dev/null
+++ b/src/common/windows/variant.hpp
@@ -0,0 +1,164 @@
+#include <oaidl.h>
+#include <propidl.h>
+#include <type_traits>
+#include <utility>
+#include <string_view>
+#include <cassert>
+#include <cstdint>
+
+template <typename TVariant>
+struct FFBaseVariant : TVariant {
+ bool hasValue() {
+ return this->vt != VT_EMPTY;
+ }
+
+ explicit operator bool() {
+ return this->hasValue();
+ }
+
+ template <typename T>
+ T get() {
+ // boolean
+ if constexpr (std::is_same_v<T, bool>) {
+ assert(this->vt == VT_BOOL);
+ return this->boolVal != VARIANT_FALSE;
+ }
+
+ // signed
+ else if constexpr (std::is_same_v<T, int8_t>) {
+ assert(this->vt == VT_I1);
+ return this->cVal;
+ } else if constexpr (std::is_same_v<T, int16_t>) {
+ assert(this->vt == VT_I2);
+ return this->iVal;
+ } else if constexpr (std::is_same_v<T, int32_t>) {
+ assert(this->vt == VT_I4 || this->vt == VT_INT);
+ return this->intVal;
+ } else if constexpr (std::is_same_v<T, int64_t>) {
+ assert(this->vt == VT_I8);
+ return this->llVal;
+ }
+
+ // unsigned
+ else if constexpr (std::is_same_v<T, uint8_t>) {
+ assert(this->vt == VT_UI1);
+ return this->bVal;
+ } else if constexpr (std::is_same_v<T, uint16_t>) {
+ assert(this->vt == VT_UI2);
+ return this->uiVal;
+ } else if constexpr (std::is_same_v<T, uint32_t>) {
+ assert(this->vt == VT_UI4 || this->vt == VT_UINT);
+ return this->uintVal;
+ } else if constexpr (std::is_same_v<T, uint64_t>) {
+ assert(this->vt == VT_UI8);
+ return this->ullVal;
+ }
+
+ // decimal
+ else if constexpr (std::is_same_v<T, float>) {
+ assert(this->vt == VT_R4);
+ return this->fltVal;
+ } else if constexpr (std::is_same_v<T, double>) {
+ assert(this->vt == VT_R8);
+ return this->dblVal;
+ }
+
+ // string
+ else if constexpr (std::is_same_v<T, std::string_view>) {
+ assert(this->vt == VT_LPSTR);
+ return this->pcVal;
+ } else if constexpr (std::is_same_v<T, std::wstring_view>) {
+ assert(this->vt == VT_BSTR || this->vt == VT_LPWSTR);
+ if (this->vt == VT_LPWSTR) {
+ return this->bstrVal;
+ } else {
+ return { this->bstrVal, SysStringLen(this->bstrVal) };
+ }
+ }
+
+ // array signed
+ else if constexpr (std::is_same_v<T, std::pair<const int8_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_I1);
+ return std::make_pair((int8_t*) this->parray->pvData, this->parray->cDims);
+ } else if constexpr (std::is_same_v<T, std::pair<const int16_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_I2);
+ return std::make_pair((int16_t*) this->parray->pvData, this->parray->cDims);
+ } else if constexpr (std::is_same_v<T, std::pair<const int32_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_I4);
+ return std::make_pair((int32_t*) this->parray->pvData, this->parray->cDims);
+ } else if constexpr (std::is_same_v<T, std::pair<const int64_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_I8);
+ return std::make_pair((int64_t*) this->parray->pvData, this->parray->cDims);
+ }
+
+ // array unsigned
+ else if constexpr (std::is_same_v<T, std::pair<const uint8_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_UI1);
+ return std::make_pair((uint8_t*) this->parray->pvData, this->parray->cDims);
+ } else if constexpr (std::is_same_v<T, std::pair<const uint16_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_UI2);
+ return std::make_pair((uint16_t*) this->parray->pvData, this->parray->cDims);
+ } else if constexpr (std::is_same_v<T, std::pair<const uint32_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_UI4);
+ return std::make_pair((uint32_t*) this->parray->pvData, this->parray->cDims);
+ } else if constexpr (std::is_same_v<T, std::pair<const uint64_t*, uint32_t>>) {
+ assert(this->vt & VT_ARRAY);
+ assert((this->vt & ~VT_ARRAY) == VT_UI8);
+ return std::make_pair((uint64_t*) this->parray->pvData, this->parray->cDims);
+ } else {
+ assert(false && "unsupported type");
+ __builtin_unreachable();
+ }
+ }
+};
+
+struct FFWmiVariant : FFBaseVariant<VARIANT> {
+ FFWmiVariant(const FFWmiVariant&) = delete;
+ FFWmiVariant(FFWmiVariant&&); // don't define it to enforce NRVO optimization
+ explicit FFWmiVariant() {
+ VariantInit(this);
+ }
+ explicit FFWmiVariant(std::initializer_list<PCWSTR> strings);
+ ~FFWmiVariant() {
+ VariantClear(this);
+ }
+};
+static_assert(sizeof(FFWmiVariant) == sizeof(VARIANT), "");
+
+struct FFPropVariant : FFBaseVariant<PROPVARIANT> {
+ FFPropVariant(const FFPropVariant&) = delete;
+ FFPropVariant(FFPropVariant&&); // don't define it to enforce NRVO optimization
+ explicit FFPropVariant() {
+ PropVariantInit(this);
+ }
+ ~FFPropVariant() {
+ PropVariantClear(this);
+ }
+};
+static_assert(sizeof(FFPropVariant) == sizeof(PROPVARIANT), "");
+
+namespace {
+// Provide our bstr_t to avoid libstdc++ dependency
+struct bstr_t {
+ explicit bstr_t(const wchar_t* str) noexcept : _bstr(SysAllocString(str)) {}
+ ~bstr_t(void) noexcept {
+ SysFreeString(_bstr);
+ }
+ explicit operator const wchar_t*(void) const noexcept {
+ return _bstr;
+ }
+ operator BSTR(void) const noexcept {
+ return _bstr;
+ }
+
+ private:
+ BSTR _bstr;
+};
+} // namespace
diff --git a/src/common/windows/version.c b/src/common/windows/version.c
new file mode 100644
index 0000000..4d61c78
--- /dev/null
+++ b/src/common/windows/version.c
@@ -0,0 +1,77 @@
+#include "common/debug.h"
+#include "common/mallocHelper.h"
+#include "common/windows/version.h"
+#include "common/windows/unicode.h"
+
+#include <windows.h>
+
+bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version) {
+ FF_DEBUG("ffGetFileVersion: enter filePath=%ls stringName=%ls", filePath, stringName);
+
+ DWORD handle;
+ DWORD size = GetFileVersionInfoSizeW(filePath, &handle);
+ if (size == 0) {
+ FF_DEBUG("GetFileVersionInfoSizeW failed: %s", ffDebugWin32Error(GetLastError()));
+ return false;
+ }
+
+ FF_DEBUG("GetFileVersionInfoSizeW ok: size=%lu handle=%lu",
+ (unsigned long) size,
+ (unsigned long) handle);
+
+ FF_AUTO_FREE void* versionData = malloc(size);
+ if (!versionData) {
+ FF_DEBUG("malloc failed: size=%lu", (unsigned long) size);
+ return false;
+ }
+
+ if (!GetFileVersionInfoW(filePath, handle, size, versionData)) {
+ FF_DEBUG("GetFileVersionInfoW failed: %s", ffDebugWin32Error(GetLastError()));
+ return false;
+ }
+
+ FF_DEBUG("GetFileVersionInfoW ok");
+
+ if (!stringName) {
+ VS_FIXEDFILEINFO* verInfo;
+ UINT len;
+ if (VerQueryValueW(versionData, L"\\", (void**) &verInfo, &len) &&
+ len &&
+ verInfo->dwSignature == 0xFEEF04BD) {
+ ffStrbufSetF(version, "%u.%u.%u.%u", (unsigned) ((verInfo->dwProductVersionMS >> 16) & 0xffff), (unsigned) ((verInfo->dwProductVersionMS >> 0) & 0xffff), (unsigned) ((verInfo->dwProductVersionLS >> 16) & 0xffff), (unsigned) ((verInfo->dwProductVersionLS >> 0) & 0xffff));
+ FF_DEBUG("fixed version resolved: %s", version->chars);
+ return true;
+ }
+
+ FF_DEBUG("fixed version query failed or invalid signature");
+ return false;
+ }
+
+ struct {
+ WORD language;
+ WORD codePage;
+ }* translations;
+ UINT translationsLen;
+
+ if (VerQueryValueW(versionData, L"\\VarFileInfo\\Translation", (void**) &translations, &translationsLen) &&
+ translationsLen >= sizeof(*translations)) {
+ wchar_t subBlock[128];
+ snwprintf(subBlock, ARRAY_SIZE(subBlock), L"\\StringFileInfo\\%04x%04x\\%ls", translations[0].language, translations[0].codePage, stringName);
+ FF_DEBUG("query version string with translation: %ls", subBlock);
+
+ wchar_t* value;
+ UINT valueLen; // Number of characters, including null terminator
+ if (VerQueryValueW(versionData, subBlock, (void**) &value, &valueLen) && valueLen > 0) {
+ ffStrbufSetNWS(version, valueLen - 1, value);
+ FF_DEBUG("version string resolved (translation fallback): %s", version->chars);
+ return true;
+ }
+
+ FF_DEBUG("translation fallback query failed");
+ } else {
+ FF_DEBUG("no translation table found in version resource");
+ }
+
+ FF_DEBUG("ffGetFileVersion failed");
+ return false;
+}
diff --git a/src/common/windows/version.h b/src/common/windows/version.h
new file mode 100644
index 0000000..457c053
--- /dev/null
+++ b/src/common/windows/version.h
@@ -0,0 +1,14 @@
+#include "fastfetch.h"
+
+/**
+ * @brief Retrieves a specific version string for a Windows file.
+ *
+ * This function gets a version string from a Windows file's version information.
+ *
+ * @param filePath The path to the file for which version information is requested.
+ * @param stringName The name of the specific version string to retrieve (e.g., "FileVersion", "ProductVersion").
+ * @param version Pointer to an FFstrbuf where the version string will be stored.
+ *
+ * @return true if the version string was successfully retrieved, false otherwise.
+ */
+bool ffGetFileVersion(const wchar_t* filePath, const wchar_t* stringName, FFstrbuf* version);
diff --git a/src/common/windows/version.rc b/src/common/windows/version.rc
new file mode 100644
index 0000000..f01ca7f
--- /dev/null
+++ b/src/common/windows/version.rc
@@ -0,0 +1,37 @@
+#ifdef RC_INVOKED
+
+#include <winuser.h>
+#include <winver.h>
+#include <ntdef.h>
+#include "fastfetch_config.h"
+
+#define FF_TO_STR1(str) #str
+#define FF_TO_STR(str) FF_TO_STR1(str)
+
+CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "manifest.xml"
+id ICON "logo.ico"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM
+ PRODUCTVERSION FASTFETCH_PROJECT_VERSION_MAJOR,FASTFETCH_PROJECT_VERSION_MINOR,FASTFETCH_PROJECT_VERSION_PATCH,FASTFETCH_PROJECT_VERSION_TWEAK_NUM
+ FILEOS VOS_NT
+ FILETYPE VFT_APP
+{
+ BLOCK "StringFileInfo" {
+ BLOCK "040904b0" {
+ VALUE "Comments", FASTFETCH_PROJECT_DESCRIPTION
+ VALUE "FileDescription", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME)
+ VALUE "FileVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK
+ VALUE "InternalName", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME)
+ VALUE "LegalCopyright", FASTFETCH_PROJECT_LICENSE
+ VALUE "OriginalFilename", FF_TO_STR(FASTFETCH_TARGET_BINARY_NAME) ".exe"
+ VALUE "ProductName", FASTFETCH_PROJECT_NAME " - " FASTFETCH_PROJECT_DESCRIPTION
+ VALUE "ProductVersion", FASTFETCH_PROJECT_VERSION FASTFETCH_PROJECT_VERSION_TWEAK
+ VALUE "CompanyName", FASTFETCH_PROJECT_HOMEPAGE_URL
+ }
+ }
+ BLOCK "VarFileInfo" {
+ VALUE "Translation", 0x0409, 1200
+ }
+}
+#endif
diff --git a/src/common/windows/wmi.h b/src/common/windows/wmi.h
new file mode 100644
index 0000000..aa50c00
--- /dev/null
+++ b/src/common/windows/wmi.h
@@ -0,0 +1,54 @@
+#pragma once
+
+#include <windef.h>
+#include <wmistr.h>
+#include <assert.h>
+
+/**
+ * The WmiOpenBlock function opens the WMI data block object for the specified WMI class.
+ *
+ * \param Guid Specifies the GUID for WMI class.
+ * \param DesiredAccess Specifies the desired access rights to the data block object.
+ * \param DataBlockHandle Pointer to a memory location where the routine returns a handle to the data block object.
+ * \return ULONG Successful or errant status.
+ */
+NTSYSAPI ULONG NTAPI
+WmiOpenBlock(
+ _In_ LPCGUID Guid,
+ _In_ ACCESS_MASK DesiredAccess,
+ _Out_ PHANDLE DataBlockHandle);
+
+/**
+ * The WmiQueryAllDataW function returns all WMI data blocks that implement a given WMI class (Unicode).
+ *
+ * \param DataBlockHandle Handle to a WMI data block object.
+ * \param BufferLength Pointer to a memory location that specifies the size of the buffer.
+ * \param Buffer Pointer to the buffer where the routine returns the WMI data.
+ * \return ULONG Successful or errant status.
+ */
+NTSYSAPI ULONG NTAPI
+WmiQueryAllDataW(
+ _In_ HANDLE DataBlockHandle,
+ _Inout_ PULONG BufferLength,
+ _Out_writes_bytes_opt_(*BufferLength) PVOID Buffer);
+
+/**
+ * The WmiCloseBlock function closes a WMI data block object.
+ *
+ * \param DataBlockHandle Handle to the data block object to be closed.
+ * \return ULONG Successful or errant status.
+ */
+NTSYSAPI ULONG NTAPI
+WmiCloseBlock(
+ _In_ HANDLE DataBlockHandle);
+
+static inline void ffCloseWmiBlock(HANDLE* hBlock) {
+ assert(hBlock);
+ if (*hBlock) {
+ WmiCloseBlock(*hBlock);
+ }
+}
+
+#define FF_AUTO_CLOSE_WMI_BLOCK __attribute__((cleanup(ffCloseWmiBlock)))
+
+// MOF: https://github.com/tpn/winsdk-10/blob/master/Include/10.0.16299.0/km/wmicore.mof