Global (Windows): prefers NTAPI for querying registry values

This commit is contained in:
李通洲
2026-03-13 10:50:31 +08:00
parent 6ed8aac6bb
commit ea6ad4ac86
15 changed files with 575 additions and 201 deletions
+6 -5
View File
@@ -205,22 +205,23 @@ static const char* detectWine(void)
static void getSystemReleaseAndVersion(FFPlatformSysinfo* info)
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
return;
uint32_t ubr = 0;
ffRegReadUint(hKey, L"UBR", &ubr, NULL);
ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
FF_ARG(ubr, L"UBR"),
FF_ARG(info->version, L"BuildLabEx"),
}, NULL);
ffStrbufAppendF(&info->release,
ffStrbufSetF(&info->release,
"%u.%u.%u.%u",
(unsigned) SharedUserData->NtMajorVersion,
(unsigned) SharedUserData->NtMinorVersion,
(unsigned) SharedUserData->NtBuildNumber,
(unsigned) ubr);
ffRegReadStrbuf(hKey, L"BuildLabEx", &info->version, NULL);
const char* wineVersion = detectWine();
if (wineVersion)
ffStrbufSetF(&info->name, "Wine_%s", wineVersion);
+93
View File
@@ -1005,3 +1005,96 @@ NTSYSAPI NTSTATUS NTAPI RtlExpandEnvironmentStrings(
_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 RtlFormatCurrentUserKeyPath(
_Out_ PUNICODE_STRING CurrentUserKeyPath
);
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
);
+333 -111
View File
@@ -1,17 +1,21 @@
#include "registry.h"
#include "unicode.h"
#include "common/mallocHelper.h"
#include "common/windows/nt.h"
static const char* hKey2Str(HKEY hKey)
#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(hKey == compareKey) return #compareKey;
#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_PERFORMANCE_TEXT)
HKEY_CASE(HKEY_PERFORMANCE_NLSTEXT)
HKEY_CASE(HKEY_CURRENT_CONFIG)
HKEY_CASE(HKEY_DYN_DATA)
HKEY_CASE(HKEY_CURRENT_USER_LOCAL_SETTINGS)
@@ -20,152 +24,370 @@ static const char* hKey2Str(HKEY hKey)
return "UNKNOWN";
}
bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error)
HANDLE ffRegGetRootKeyHandle(HKEY hKey)
{
if(RegOpenKeyExW(hKey, subKeyW, 0, KEY_READ, result) != ERROR_SUCCESS)
assert(hKey);
assert((uintptr_t) hKey >= (uintptr_t) HKEY_CLASSES_ROOT && (uintptr_t) hKey <= (uintptr_t) HKEY_CURRENT_USER_LOCAL_SETTINGS);
if (hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT])
return hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT];
HANDLE result = NULL;
switch ((uintptr_t) hKey)
{
if(error)
case (uintptr_t) HKEY_CURRENT_USER: {
UNICODE_STRING path = {};
if (!NT_SUCCESS(RtlFormatCurrentUserKeyPath(&path))) return NULL;
if (!NT_SUCCESS(NtOpenKey(&result, KEY_READ, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = NULL,
.ObjectName = &path,
})))
{
RtlFreeUnicodeString(&path);
return NULL;
}
RtlFreeUnicodeString(&path);
break;
}
case (uintptr_t) HKEY_LOCAL_MACHINE: {
if (!NT_SUCCESS(NtOpenKey(&result, KEY_READ, &(OBJECT_ATTRIBUTES) {
.Length = sizeof(OBJECT_ATTRIBUTES),
.RootDirectory = NULL,
.ObjectName = &(UNICODE_STRING)RTL_CONSTANT_STRING(L"\\Registry\\Machine"),
})))
return NULL;
break;
}
default:
// Unsupported
assert(false);
return NULL;
}
hRootKeys[(uintptr_t) hKey - (uintptr_t) HKEY_CLASSES_ROOT] = result;
return result;
}
bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error)
{
assert(hKey);
assert(subKeyW);
assert(result);
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,
},
})))
{
if (error)
{
FF_STRBUF_AUTO_DESTROY subKeyA = ffStrbufCreateWS(subKeyW);
ffStrbufAppendF(error, "RegOpenKeyExW(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
ffStrbufAppendF(error, "NtOpenKey(%s\\%s) failed", hKey2Str(hKey), subKeyA.chars);
}
return false;
}
return true;
}
bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error)
static bool processRegValue(const FFRegValueArg* arg, const ULONG regType, const void* regData, ULONG regDataLen, FFstrbuf* error)
{
DWORD bufSize; //with tailing '\0'
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, NULL, &bufSize) != ERROR_SUCCESS)
switch (arg->type)
{
if(error)
case FF_ARG_TYPE_STRBUF:
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_SZ) failed", valueNameA.chars);
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;
}
return false;
}
assert(bufSize >= sizeof(wchar_t));
wchar_t* FF_AUTO_FREE resultW = (wchar_t*)malloc(bufSize);
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_SZ, NULL, resultW, &bufSize) != ERROR_SUCCESS)
{
if(error)
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:
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_REG_SZ) failed", valueNameA.chars);
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;
}
return false;
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 && regType != REG_BINARY)
goto type_mismatch;
FFlist* list = (FFlist*) arg->value;
ffListClear(list);
if (regType == REG_MULTI_SZ)
{
if (list->elementSize != sizeof(FFstrbuf))
{
if (error)
{
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: expected list of strbuf for REG_MULTI_SZ", nameA.chars);
}
return false;
}
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;
}
}
else
{
if (list->elementSize != sizeof(uint8_t))
{
if (error)
{
FF_STRBUF_AUTO_DESTROY nameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "ffRegReadValues(%s) type mismatch: expected list of uint8_t for REG_BINARY", nameA.chars);
}
return false;
}
ffListReserve(list, regDataLen);
memcpy(list->data, regData, regDataLen);
list->length = 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;
}
ffStrbufSetNWS(result, (uint32_t) (bufSize / sizeof(*resultW) - 1), resultW);
return true;
type_mismatch:
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 ffRegReadData(HKEY hKey, const wchar_t* valueNameW, uint8_t** result, uint32_t* length, FFstrbuf* error)
bool ffRegReadValue(HANDLE hKey, const FFRegValueArg* arg, FFstrbuf* error)
{
assert(result && length);
DWORD bufSize = 0;
LONG err = RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_BINARY, NULL, NULL, &bufSize);
if(err != ERROR_SUCCESS || bufSize == 0)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_BINARY, NULL, NULL, &bufSize) failed", valueNameA.chars);
}
return false;
}
UNICODE_STRING* valueNameU = &(UNICODE_STRING) {
.Length = arg->name ? (USHORT) (wcslen(arg->name) * sizeof(wchar_t)) : 0 /*(default)*/,
.MaximumLength = 0,
.Buffer = (wchar_t*) arg->name,
};
uint8_t* buf = (uint8_t*) malloc(bufSize);
err = RegGetValueW(hKey, NULL, valueNameW, RRF_RT_REG_BINARY, NULL, buf, &bufSize);
if(err != ERROR_SUCCESS)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, NULL, RRF_RT_REG_BINARY, NULL, length) failed", valueNameA.chars);
}
free(buf);
return false;
}
*result = buf;
*length = bufSize;
return true;
}
alignas(KEY_VALUE_PARTIAL_INFORMATION) uint8_t staticBuffer[128 + sizeof(KEY_VALUE_PARTIAL_INFORMATION)];
FF_AUTO_FREE uint8_t* dynamicBuffer = NULL;
bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error)
{
DWORD bufSize = sizeof(*result);
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_DWORD, NULL, result, &bufSize) != ERROR_SUCCESS)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_DWORD) failed", valueNameA.chars);
}
return false;
}
return true;
}
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;
bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error)
{
DWORD bufSize = sizeof(*result);
if(RegGetValueW(hKey, NULL, valueNameW, RRF_RT_QWORD, NULL, result, &bufSize) != ERROR_SUCCESS)
{
if(error)
{
if(!valueNameW)
valueNameW = L"(default)";
FF_STRBUF_AUTO_DESTROY valueNameA = ffStrbufCreateWS(valueNameW);
ffStrbufAppendF(error, "RegGetValueW(%s, result, RRF_RT_QWORD) failed", valueNameA.chars);
}
return false;
}
return true;
}
bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error)
{
DWORD bufSize = 0;
if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, NULL, &bufSize, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
if (bufSize == 0)
{
if (error)
ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed");
{
FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "NtQueryValueKey(%s, %s) failed", hKey2Str(hKey), valueNameA.chars);
}
return false;
}
++bufSize;
wchar_t* FF_AUTO_FREE resultW = (wchar_t*) malloc(bufSize * sizeof(*resultW));
if(RegEnumKeyExW(hKey, index, resultW, &bufSize, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
dynamicBuffer = (uint8_t*) malloc(bufSize);
buffer = (KEY_VALUE_PARTIAL_INFORMATION*) dynamicBuffer;
if (!NT_SUCCESS(NtQueryValueKey(hKey, valueNameU, KeyValuePartialInformation, buffer, bufSize, &bufSize)))
{
if (error)
ffStrbufAppendF(error, "RegEnumKeyExW(hKey, %u) failed", (unsigned) index);
{
FF_STRBUF_AUTO_DESTROY valueNameA = arg->name ? ffStrbufCreateWS(arg->name) : ffStrbufCreateStatic("(default)");
ffStrbufAppendF(error, "NtQueryValueKey(%s, %s, buffer) failed", hKey2Str(hKey), valueNameA.chars);
}
return false;
}
ffStrbufSetWS(result, resultW);
return true;
process_value:
return processRegValue(arg, buffer->Type, buffer->Data, buffer->DataLength, error);
}
bool ffRegGetNSubKeys(HKEY hKey, uint32_t* result, FFstrbuf* error)
bool ffRegReadValues(HANDLE hKey, uint32_t argc, const FFRegValueArg argv[], FFstrbuf* error)
{
DWORD buffer;
if(RegQueryInfoKeyW(hKey, NULL, NULL, NULL, &buffer, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
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))
{
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)
{
bufferSize = requiredSize;
continue;
}
if (error)
ffStrbufAppendF(error, "NtQueryMultipleValueKey(%s, argc=%u) failed, status=0x%08X",
hKey2Str(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];
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)))
{
if (error)
ffStrbufAppendS(error, "RegQueryInfoKeyW(hKey) failed");
ffStrbufAppendF(error, "NtEnumerateKey(hKey, %u, keyInfo) failed", (unsigned) index);
return false;
}
*result = buffer;
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)))
{
if (error)
ffStrbufAppendS(error, "NtQueryKey(hKey, KeyFullInformation, keyInfo) failed");
return false;
}
*result = (uint32_t) keyInfo->SubKeys;
return true;
}
+60 -13
View File
@@ -1,21 +1,68 @@
#pragma once
#include "fastfetch.h"
#include "common/argType.h"
#include "common/io.h"
#include <windows.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
static inline void wrapRegCloseKey(HKEY* phKey)
typedef struct FFRegValueArg
{
if(*phKey)
RegCloseKey(*phKey);
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);
}
#define FF_HKEY_AUTO_DESTROY HKEY __attribute__((__cleanup__(wrapRegCloseKey)))
bool ffRegOpenKeyForRead(HKEY hKey, const wchar_t* subKeyW, HKEY* result, FFstrbuf* error);
bool ffRegReadStrbuf(HKEY hKey, const wchar_t* valueNameW, FFstrbuf* result, FFstrbuf* error);
bool ffRegReadData(HKEY hKey, const wchar_t* valueNameW, uint8_t** result, uint32_t* length, FFstrbuf* error);
bool ffRegReadUint(HKEY hKey, const wchar_t* valueNameW, uint32_t* result, FFstrbuf* error);
bool ffRegReadUint64(HKEY hKey, const wchar_t* valueNameW, uint64_t* result, FFstrbuf* error);
bool ffRegGetSubKey(HKEY hKey, uint32_t index, FFstrbuf* result, FFstrbuf* error);
bool ffRegGetNSubKeys(HKEY hKey, uint32_t* result, FFstrbuf* 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, FFlist* result /*list of uint8_t*/, FFstrbuf* error)
{
return ffRegReadValue(hKey, &(FFRegValueArg) {
.type = FF_ARG_TYPE_LIST,
.value = result,
.name = valueNameW,
}, error);
}
+8 -18
View File
@@ -250,28 +250,18 @@ static const char* detectNCores(FFCPUResult* cpu)
static const char* detectByRegistry(FFCPUResult* cpu)
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", &hKey, NULL))
return "ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L\"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0\", &hKey, NULL) failed";
ffRegReadStrbuf(hKey, L"ProcessorNameString", &cpu->name, NULL);
if (ffRegReadStrbuf(hKey, L"VendorIdentifier", &cpu->vendor, NULL))
if (ffRegReadValues(hKey, 3, (FFRegValueArg[]) {
FF_ARG(cpu->name, L"ProcessorNameString"),
FF_ARG(cpu->vendor, L"VendorIdentifier"),
FF_ARG(cpu->frequencyBase, L"~MHz"),
}, NULL))
ffStrbufTrimRightSpace(&cpu->vendor);
if (cpu->coresLogical == 0)
{
FF_HKEY_AUTO_DESTROY hProcsKey = NULL;
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor", &hProcsKey, NULL))
{
uint32_t cores;
if (ffRegGetNSubKeys(hProcsKey, &cores, NULL))
cpu->coresOnline = cpu->coresPhysical = cpu->coresLogical = (uint16_t) cores;
}
}
uint32_t mhz;
if(ffRegReadUint(hKey, L"~MHz", &mhz, NULL))
cpu->frequencyBase = mhz;
else
return "ffRegReadValues() failed for CPU registry key";
return NULL;
}
+7 -6
View File
@@ -1,17 +1,18 @@
#include "cursor.h"
#include "common/io.h"
#include "common/windows/registry.h"
void ffDetectCursor(FFCursorResult* result)
{
FF_HKEY_AUTO_DESTROY hKey;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Cursors", &hKey, &result->error))
{
if (!ffRegReadStrbuf(hKey, NULL, &result->theme, &result->error))
return;
uint32_t cursorBaseSize;
if (ffRegReadUint(hKey, L"CursorBaseSize", &cursorBaseSize, NULL))
ffStrbufAppendF(&result->size, "%u", (unsigned) cursorBaseSize);
if (ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
FF_ARG(result->theme, NULL),
FF_ARG(cursorBaseSize, L"CursorBaseSize"),
}, &result->error))
ffStrbufAppendUInt(&result->size, cursorBaseSize);
}
}
@@ -1,6 +1,7 @@
#include "displayserver.h"
#include "common/windows/unicode.h"
#include "common/edidHelper.h"
#include "common/windows/registry.h"
#include "common/windows/unicode.h"
#include <windows.h>
#include <shellscalingapi.h>
@@ -49,8 +50,7 @@ static void detectDisplays(FFDisplayServerResult* ds)
.id = path->targetInfo.id,
},
};
uint8_t edidData[1024];
DWORD edidLength = 0;
FF_LIST_AUTO_DESTROY edid = ffListCreate(sizeof(uint8_t));
if(DisplayConfigGetDeviceInfo(&targetName.header) == ERROR_SUCCESS)
{
@@ -69,16 +69,17 @@ static void detectDisplays(FFDisplayServerResult* ds)
}
wcscpy(pRegPath, L"Device Parameters");
edidLength = ARRAY_SIZE(edidData);
if (RegGetValueW(HKEY_LOCAL_MACHINE, regPath, L"EDID", RRF_RT_REG_BINARY, NULL, edidData, &edidLength) == ERROR_SUCCESS &&
edidLength > 0 && edidLength % 128 == 0)
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regPath, &hKey, NULL) &&
ffRegReadData(hKey, L"EDID", &edid, NULL) &&
ffEdidIsValid(edid.data, edid.length))
{
ffEdidGetName(edidData, &name);
ffEdidGetPhysicalSize(edidData, &physicalWidth, &physicalHeight);
ffEdidGetName(edid.data, &name);
ffEdidGetPhysicalSize(edid.data, &physicalWidth, &physicalHeight);
}
else
{
edidLength = 0;
ffListClear(&edid);
if (targetName.flags.friendlyNameFromEdid)
ffStrbufSetWS(&name, targetName.monitorFriendlyDeviceName);
else
@@ -209,8 +210,8 @@ static void detectDisplays(FFDisplayServerResult* ds)
else
display->hdrStatus = FF_DISPLAY_HDR_STATUS_UNKNOWN;
}
if (edidLength > 0)
ffEdidGetSerialAndManufactureDate(edidData, &display->serial, &display->manufactureYear, &display->manufactureWeek);
if (edid.length > 0)
ffEdidGetSerialAndManufactureDate(edid.data, &display->serial, &display->manufactureYear, &display->manufactureWeek);
display->drrStatus = path->flags & DISPLAYCONFIG_PATH_BOOST_REFRESH_RATE ? FF_DISPLAY_DRR_STATUS_ENABLED : FF_DISPLAY_DRR_STATUS_DISABLED;
}
}
+11 -4
View File
@@ -1,12 +1,12 @@
#include "gpu.h"
#include "detection/gpu/gpu_driver_specific.h"
#include "common/library.h"
#include "common/windows/unicode.h"
#include "common/windows/registry.h"
#include "common/mallocHelper.h"
#include "common/debug.h"
#include "common/windows/nt.h"
#include <windows.h>
#include <cfgmgr32.h>
#define FF_EMPTY_GUID_STR L"{00000000-0000-0000-0000-000000000000}"
@@ -19,6 +19,13 @@ const uint32_t regDriverKeyPrefixLength = (uint32_t) __builtin_strlen("SYSTEM\\C
#define GUID_DEVCLASS_DISPLAY_STRING L"{4d36e968-e325-11ce-bfc1-08002be10318}" // Found in <devguid.h>
static inline void wrapRegCloseKey(HKEY* phKey)
{
if(*phKey)
RegCloseKey(*phKey);
}
#define FF_HKEY_AUTO_DESTROY __attribute__((__cleanup__(wrapRegCloseKey)))
const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist* gpus)
{
FF_DEBUG("Starting GPU detection");
@@ -115,7 +122,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
uint64_t adapterLuid = 0;
FF_HKEY_AUTO_DESTROY hVideoIdKey = NULL;
FF_HKEY_AUTO_DESTROY HKEY hVideoIdKey = NULL;
wchar_t buffer[256];
ULONG bufferLen = 0;
@@ -147,7 +154,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
{
FF_DEBUG("Found VideoID: %ls", buffer);
wmemcpy(regDirectxKey + regDirectxKeyPrefixLength, buffer, FF_GUID_STRLEN);
FF_HKEY_AUTO_DESTROY hDirectxKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hDirectxKey = NULL;
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDirectxKey, &hDirectxKey, NULL))
{
FF_DEBUG("Opened DirectX registry key");
@@ -237,7 +244,7 @@ const char* ffDetectGPUImpl(FF_MAYBE_UNUSED const FFGPUOptions* options, FFlist*
{
FF_DEBUG("Found driver GUID: %ls", buffer);
wmemcpy(regDriverKey + regDriverKeyPrefixLength, buffer, FF_GUID_STRLEN + strlen("\\0000"));
FF_HKEY_AUTO_DESTROY hRegDriverKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hRegDriverKey = NULL;
if (ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regDriverKey, &hRegDriverKey, NULL))
{
FF_DEBUG("Opened driver registry key");
+1 -1
View File
@@ -3,7 +3,7 @@
const char* ffDetectIcons(FFIconsResult* result)
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\NewStartPanel", &hKey, NULL) &&
!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\ClassicStartMenu", &hKey, NULL))
return "ffRegOpenKeyForRead(Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\HideDesktopIcons\\{NewStartPanel|ClassicStartMenu}) failed";
+4 -8
View File
@@ -10,7 +10,7 @@ PWSTR WINAPI BrandingFormatString(PCWSTR format);
static bool getCodeName(FFOSResult* os)
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
return false;
@@ -39,13 +39,9 @@ void ffDetectOSImpl(FFOSResult* os)
if(os->variant.length == 0) // Windows PE?
{
wchar_t buf[128];
DWORD bufSize = (DWORD) sizeof(buf); // with trailing '\0'
if(RegGetValueW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", L"ProductName", RRF_RT_REG_SZ, NULL, buf, &bufSize) == ERROR_SUCCESS)
{
assert(bufSize >= sizeof(wchar_t));
ffStrbufSetNWS(&os->variant, bufSize / sizeof(wchar_t) - 1, buf);
}
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", &hKey, NULL))
ffRegReadStrbuf(hKey, L"ProductName", &os->variant, NULL);
}
ffStrbufSet(&os->prettyName, &os->variant);
@@ -268,16 +268,25 @@ static void detectConEmu(FFTerminalFontResult* terminalFont)
static void detectWarp(FFTerminalFontResult* terminalFont)
{
FF_HKEY_AUTO_DESTROY key = NULL;
FF_AUTO_CLOSE_FD HANDLE key = NULL;
if (!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Software\\Warp.dev\\Warp", &key, &terminalFont->error))
return;
FF_STRBUF_AUTO_DESTROY fontName = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY fontSize = ffStrbufCreate();
if (!ffRegReadStrbuf(key, L"FontName", &fontName, NULL))
if (ffRegReadValues(key, 2, (FFRegValueArg[]) {
FF_ARG(fontName, L"FontName"),
FF_ARG(fontSize, L"FontSize")
}, &terminalFont->error))
{
ffStrbufTrim(&fontName, '"');
ffStrbufAppendS(&fontSize, "px");
}
else
{
ffStrbufSetS(&fontName, "Hack");
if (!ffRegReadStrbuf(key, L"FontSize", &fontSize, &terminalFont->error))
ffStrbufSetS(&fontSize, "13");
ffStrbufSetS(&fontSize, "13.0px");
}
ffFontInitValues(&terminalFont->font, fontName.chars, fontSize.chars);
}
@@ -191,7 +191,7 @@ static bool detectDefaultTerminal(FFTerminalResult* result)
goto conhost;
}
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(ffRegOpenKeyForRead(HKEY_LOCAL_MACHINE, regPath, &hKey, NULL))
{
FF_STRBUF_AUTO_DESTROY path = ffStrbufCreate();
+1 -1
View File
@@ -3,7 +3,7 @@
const char* ffDetectWallpaper(FFstrbuf* result)
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if(!ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Control Panel\\Desktop", &hKey, NULL))
return "ffRegOpenKeyForRead(Control Panel\\Desktop) failed";
+24 -17
View File
@@ -63,18 +63,18 @@ const char* colorHexToString(DWORD hex)
bool ffDetectWmTheme(FFstrbuf* themeOrError)
{
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes", &hKey, NULL))
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if (ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes", &hKey, NULL))
{
FF_STRBUF_AUTO_DESTROY theme = ffStrbufCreate();
if(ffRegReadStrbuf(hKey, L"CurrentTheme", &theme, NULL))
if (ffRegReadStrbuf(hKey, L"CurrentTheme", &theme, NULL))
{
ffStrbufSubstrBeforeLastC(&theme, '.');
ffStrbufSubstrAfterLastC(&theme, '\\');
if(isalpha(theme.chars[0]))
theme.chars[0] = (char)toupper(theme.chars[0]);
ffStrbufAppendF(themeOrError, "%s", theme.chars);
ffStrbufAppend(themeOrError, &theme);
}
}
}
@@ -82,28 +82,35 @@ bool ffDetectWmTheme(FFstrbuf* themeOrError)
do {
uint32_t rgbColor;
uint32_t bgrColor;
DWORD bufSize = sizeof(bgrColor);
if(RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"AccentColor", RRF_RT_REG_DWORD, NULL, &bgrColor, &bufSize) == ERROR_SUCCESS)
rgbColor = ((bgrColor & 0xFF) << 16) | (bgrColor & 0xFF00) | ((bgrColor >> 16) & 0xFF);
else if(RegGetValueW(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", L"ColorizationColor", RRF_RT_REG_DWORD, NULL, &rgbColor, &bufSize) == ERROR_SUCCESS)
rgbColor &= 0xFFFFFF;
else
break;
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if (ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"Software\\Microsoft\\Windows\\DWM", &hKey, NULL))
{
if (ffRegReadUint(hKey, L"AccentColor", &bgrColor, NULL))
rgbColor = ((bgrColor & 0xFF) << 16) | (bgrColor & 0xFF00) | ((bgrColor >> 16) & 0xFF);
else if (ffRegReadUint(hKey, L"ColorizationColor", &rgbColor, NULL))
rgbColor &= 0xFFFFFF;
else
break;
}
else break;
if(themeOrError->length > 0) ffStrbufAppendS(themeOrError, " - ");
if (themeOrError->length > 0) ffStrbufAppendS(themeOrError, " - ");
const char* text = colorHexToString(rgbColor);
if(text)
if (text)
ffStrbufAppendS(themeOrError, text);
else
ffStrbufAppendF(themeOrError, "#%06lX", (long)rgbColor);
} while (false);
{
FF_HKEY_AUTO_DESTROY hKey = NULL;
if(ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", &hKey, NULL))
FF_AUTO_CLOSE_FD HANDLE hKey = NULL;
if (ffRegOpenKeyForRead(HKEY_CURRENT_USER, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize", &hKey, NULL))
{
uint32_t system = 1, apps = 1;
if (ffRegReadUint(hKey, L"SystemUsesLightTheme", &system, NULL) && ffRegReadUint(hKey, L"AppsUseLightTheme", &apps, NULL))
if (ffRegReadValues(hKey, 2, (FFRegValueArg[]) {
FF_ARG(system, L"SystemUsesLightTheme"),
FF_ARG(apps, L"AppsUseLightTheme"),
}, NULL))
{
bool paren = themeOrError->length > 0;
if (paren)
@@ -117,7 +124,7 @@ bool ffDetectWmTheme(FFstrbuf* themeOrError)
if(themeOrError->length == 0)
{
ffStrbufAppendS(themeOrError, "Failed to find current theme");
ffStrbufSetStatic(themeOrError, "Failed to find current theme");
return false;
}
return true;
+1 -1
View File
@@ -9,7 +9,7 @@ static void verify(const char* format, const char* arg, const char* expected, in
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
FF_STRBUF_AUTO_DESTROY formatter = ffStrbufCreateStatic(format);
const FFformatarg arguments[] = {
{ .type = FF_FORMAT_ARG_TYPE_STRING, arg }
{ .type = FF_ARG_TYPE_STRING, arg }
};
ffParseFormatString(&result, &formatter, 1, arguments);
if (!ffStrbufEqualS(&result, expected))