const Key = struct {
handle: windows.HANDLE,
fn close(self: Key) void {
windows.CloseHandle(self.handle);
}
fn open(self: Key, sub_path: []const u16) error{KeyNotFound}!Key {
var key: Key = undefined;
const attr: windows.OBJECT.ATTRIBUTES = .{
.RootDirectory = self.handle,
.Attributes = .{},
.ObjectName = @constCast(&windows.UNICODE_STRING.init(sub_path)),
.SecurityDescriptor = null,
.SecurityQualityOfService = null,
};
const status = windows.ntdll.NtOpenKeyEx(
&key.handle,
.{ .SPECIFIC = .{
.KEY = .{
.QUERY_VALUE = true,
.ENUMERATE_SUB_KEYS = true,
},
} },
&attr,
.{},
);
switch (status) {
.SUCCESS => return key,
else => return error.KeyNotFound,
}
}
const ValueEntry = union(enum) {
default: void,
name: []const u16,
};
fn getString(
key: Key,
gpa: Allocator,
entry: ValueEntry,
comptime result_encoding: enum { wtf16, wtf8 },
) error{ OutOfMemory, ValueNameNotFound, NotAString, StringNotFound }!(switch (result_encoding) {
.wtf8 => []u8,
.wtf16 => []u16,
}) {
const num_data_bytes = windows.MAX_PATH * 2;
const stack_buf_len = @sizeOf(windows.KEY.VALUE.PARTIAL_INFORMATION) + num_data_bytes;
var stack_info_buf: [stack_buf_len]u8 align(@alignOf(windows.KEY.VALUE.PARTIAL_INFORMATION)) = undefined;
var result_len: windows.ULONG = undefined;
const rc = windows.ntdll.NtQueryValueKey(
key.handle,
switch (entry) {
.name => |name| @constCast(&windows.UNICODE_STRING.init(name)),
.default => @constCast(&windows.UNICODE_STRING.empty),
},
.Partial,
&stack_info_buf,
stack_buf_len,
&result_len,
);
var heap_info_buf: ?[]align(@alignOf(windows.KEY.VALUE.PARTIAL_INFORMATION)) u8 = null;
defer if (heap_info_buf) |buf| gpa.free(buf);
const info: *const windows.KEY.VALUE.PARTIAL_INFORMATION = switch (rc) {
.SUCCESS => @ptrCast(&stack_info_buf),
.BUFFER_OVERFLOW, .BUFFER_TOO_SMALL => heap_info: {
heap_info_buf = try gpa.alignedAlloc(u8, .of(windows.KEY.VALUE.PARTIAL_INFORMATION), result_len);
const heap_rc = windows.ntdll.NtQueryValueKey(
key.handle,
switch (entry) {
.name => |name| @constCast(&windows.UNICODE_STRING.init(name)),
.default => @constCast(&windows.UNICODE_STRING.empty),
},
.Partial,
heap_info_buf.?.ptr,
@intCast(heap_info_buf.?.len),
&result_len,
);
switch (heap_rc) {
.SUCCESS => break :heap_info @ptrCast(heap_info_buf.?.ptr),
.OBJECT_NAME_NOT_FOUND => return error.ValueNameNotFound,
else => return error.StringNotFound,
}
},
.OBJECT_NAME_NOT_FOUND => return error.ValueNameNotFound,
else => return error.StringNotFound,
};
switch (info.Type) {
.SZ => {},
else => return error.NotAString,
}
const data_wtf16_with_nul = @as([*]const u16, @ptrCast(@alignCast(info.data())))[0..@divExact(info.DataLength, 2)];
const data_wtf16 = std.mem.trimEnd(u16, data_wtf16_with_nul, L("\x00"));
switch (result_encoding) {
.wtf16 => return gpa.dupe(u16, data_wtf16),
.wtf8 => return std.unicode.wtf16LeToWtf8Alloc(gpa, data_wtf16),
}
}
fn getDword(key: Key, entry: ValueEntry) error{ ValueNameNotFound, NotADword, DwordNotFound }!windows.DWORD {
const num_data_bytes = @sizeOf(windows.DWORD);
const buf_len = @sizeOf(windows.KEY.VALUE.PARTIAL_INFORMATION) + num_data_bytes;
var info_buf: [buf_len]u8 align(@alignOf(windows.KEY.VALUE.PARTIAL_INFORMATION)) = undefined;
var result_len: windows.ULONG = undefined;
const rc = windows.ntdll.NtQueryValueKey(
key.handle,
switch (entry) {
.name => |name| @constCast(&windows.UNICODE_STRING.init(name)),
.default => @constCast(&windows.UNICODE_STRING.empty),
},
.Partial,
&info_buf,
buf_len,
&result_len,
);
switch (rc) {
.SUCCESS => {},
.OBJECT_NAME_NOT_FOUND => return error.ValueNameNotFound,
else => return error.DwordNotFound,
}
const info: *const windows.KEY.VALUE.PARTIAL_INFORMATION = @ptrCast(&info_buf);
switch (info.Type) {
.DWORD => {},
else => return error.NotADword,
}
return std.mem.bytesToValue(windows.DWORD, info.data());
}
}