Packages (Windows): caches winget packages count; enables winget by default

This commit is contained in:
李通洲
2026-09-20 13:05:09 +08:00
parent 67da836a27
commit f96a290f3b
10 changed files with 150 additions and 47 deletions
+17 -13
View File
@@ -1307,19 +1307,23 @@ typedef struct _KEY_BASIC_INFORMATION {
_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;
typedef struct _KEY_CACHED_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 SubKeys; // The number of subkeys for a key.
ULONG MaxNameLen; // The maximum length, in bytes, of a subkey name.
ULONG Values; // The number of value entries.
ULONG MaxValueNameLen; // The maximum length, in bytes, of a value entry name.
ULONG MaxValueDataLen; // The maximum length, in bytes, of a value entry data field.
ULONG NameLength; // Size, in bytes, of the key's own (leaf) name, without the terminator. The name itself is not stored.
} KEY_CACHED_INFORMATION, *PKEY_CACHED_INFORMATION;
// 36 bytes of members, but LARGE_INTEGER makes the struct 8-byte aligned, and that is exactly what
// the kernel demands: a 36-byte buffer is rejected with STATUS_BUFFER_TOO_SMALL (measured).
// The kernel writes all 40 bytes -- the 4 padding bytes included, zeroed -- and reports
// ResultLength = 40 whatever size you pass, so a larger buffer buys nothing (measured 40..4096).
// This is the only information class that never appends a string: NameLength is the length of the
// key's own (leaf) name in bytes without the terminator, and the name itself is not returned.
static_assert(sizeof(KEY_CACHED_INFORMATION) == 40, "KEY_CACHED_INFORMATION should be 40 bytes");
NTSYSAPI NTSTATUS NTAPI NtQueryKey(
_In_ HANDLE KeyHandle,
+16 -8
View File
@@ -355,6 +355,10 @@ bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* err
ULONG bufSize = (ULONG) sizeof(buffer);
KEY_BASIC_INFORMATION* keyInfo = (KEY_BASIC_INFORMATION*) buffer;
// Every failure mode fails NT_SUCCESS, and the read below depends on that: on a too-small buffer
// the kernel returns STATUS_BUFFER_OVERFLOW, writes the *full* NameLength but copies only part of
// the name (measured), so treating that status as success would read past what was written.
// The longest name the CM accepts, 255 characters, still fits: 255 * 2 + 16 <= sizeof(buffer).
if (!NT_SUCCESS(NtEnumerateKey(hKey, index, KeyBasicInformation, keyInfo, bufSize, &bufSize))) {
FF_DEBUG("NtEnumerateKey(hKey=%p, index=%u) failed", hKey, (unsigned) index);
if (error) {
@@ -367,22 +371,26 @@ bool ffRegGetSubKey(HANDLE hKey, uint32_t index, FFstrbuf* result, FFstrbuf* err
return true;
}
bool ffRegGetNSubKeys(HANDLE hKey, uint32_t* result, FFstrbuf* error) {
// Reads the counters the key object already caches. KeyFullInformation would return the same numbers
// but also marshals the key's class string out; measured per query on the ARP and MSIX keys:
// 837 ns vs 895 ns (interleaved, 200k queries each). The open dominates the total -- 22.10 us vs
// 22.40 us for open + query -- so this is a small win, not a big one.
//
// The kernel writes exactly sizeof(KEY_CACHED_INFORMATION) bytes and rejects a smaller buffer with
// STATUS_BUFFER_TOO_SMALL without writing anything, so pass a whole KEY_CACHED_INFORMATION.
bool ffRegQueryKey(HANDLE hKey, KEY_CACHED_INFORMATION* 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;
ULONG bufSize = (ULONG) sizeof(*result);
if (!NT_SUCCESS(NtQueryKey(hKey, KeyFullInformation, keyInfo, bufSize, &bufSize))) {
FF_DEBUG("NtQueryKey(hKey=%p, KeyFullInformation) failed", hKey);
if (!NT_SUCCESS(NtQueryKey(hKey, KeyCachedInformation, result, bufSize, &bufSize))) {
FF_DEBUG("NtQueryKey(hKey=%p, KeyCachedInformation) failed", hKey);
if (error) {
ffStrbufAppendF(error, "NtQueryKey(hKey=%p, KeyFullInformation, keyInfo) failed", hKey);
ffStrbufAppendF(error, "NtQueryKey(hKey=%p, KeyCachedInformation, keyInfo) failed", hKey);
}
return false;
}
*result = (uint32_t) keyInfo->SubKeys;
return true;
}
+4 -1
View File
@@ -3,6 +3,7 @@
#include "fastfetch.h"
#include "common/argType.h"
#include "common/io.h"
#include "common/windows/nt.h"
#ifndef HKEY_CURRENT_USER
#define HKEY_CLASSES_ROOT ((HKEY) (ULONG_PTR) ((LONG) 0x80000000))
@@ -26,7 +27,9 @@ bool ffRegOpenSubkeyForRead(HANDLE hKey, const wchar_t* subKeyW, HANDLE* result,
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);
// Fills `result` with the counters the key object caches, including LastWriteTime: the number of
// 100-nanosecond intervals since this key or any of its values was last changed (FILETIME epoch).
bool ffRegQueryKey(HANDLE hKey, KEY_CACHED_INFORMATION* result, FFstrbuf* error);
static inline bool ffRegOpenKeyForRead(HKEY hRootKey, const wchar_t* subKeyW, HANDLE* result, FFstrbuf* error) {
return ffRegOpenSubkeyForRead(ffRegGetRootKeyHandle(hRootKey), subKeyW, result, error);