feature. See also
. The project being documented here (as the example) is the Zig library itself.
WindowsSdk.Installation
pub const Installation = struct
File
Code
pub const Installation = struct {
path: []const u8,
version: []const u8,
fn find(
gpa: Allocator,
io: Io,
registry: *Registry,
roots_key: Registry.Key,
roots_subkey: []const u16,
prefix: []const u8,
version_key_name: []const u16,
) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
roots: {
const installation = findFromRoot(gpa, io, roots_key, roots_subkey, prefix) catch
break :roots;
if (installation.isValidVersion(roots_key)) return installation;
installation.free(gpa);
}
{
const installation = try findFromInstallationFolder(gpa, registry, version_key_name);
if (installation.isValidVersion(roots_key)) return installation;
installation.free(gpa);
}
return error.InstallationNotFound;
}
fn findFromRoot(
gpa: Allocator,
io: Io,
roots_key: Registry.Key,
roots_subkey: []const u16,
prefix: []const u8,
) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
const path = path: {
const path_w_maybe_with_trailing_slash = roots_key.getString(gpa, .{ .name = roots_subkey }, .wtf16) catch |err| switch (err) {
error.NotAString,
error.ValueNameNotFound,
error.StringNotFound,
=> return error.InstallationNotFound,
error.OutOfMemory => |e| return e,
};
defer gpa.free(path_w_maybe_with_trailing_slash);
if (!std.fs.path.isAbsoluteWindowsWtf16(path_w_maybe_with_trailing_slash)) {
return error.InstallationNotFound;
}
const path_w = std.mem.trimEnd(u16, path_w_maybe_with_trailing_slash, L("\\/"));
break :path try std.unicode.wtf16LeToWtf8Alloc(gpa, path_w);
};
errdefer gpa.free(path);
const version = version: {
var buf: [Dir.max_path_bytes]u8 = undefined;
const sdk_lib_dir_path = std.fmt.bufPrint(buf[0..], "{s}\\Lib\\", .{path}) catch |err| switch (err) {
error.NoSpaceLeft => return error.PathTooLong,
};
if (!Dir.path.isAbsolute(sdk_lib_dir_path)) return error.InstallationNotFound;
var sdk_lib_dir = Dir.openDirAbsolute(io, sdk_lib_dir_path, .{
.iterate = true,
}) catch |err| switch (err) {
error.NameTooLong => return error.PathTooLong,
else => return error.InstallationNotFound,
};
defer sdk_lib_dir.close(io);
var iterator = sdk_lib_dir.iterate();
const versions = try iterateAndFilterByVersion(&iterator, gpa, io, prefix);
if (versions.len == 0) return error.InstallationNotFound;
defer {
for (versions[1..]) |version| gpa.free(version);
gpa.free(versions);
}
break :version versions[0];
};
errdefer gpa.free(version);
return .{ .path = path, .version = version };
}
fn findFromInstallationFolder(
gpa: Allocator,
registry: *Registry,
version_key_name: []const u16,
) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
const key_name = try std.mem.concat(gpa, u16, &.{ L("Microsoft\\Microsoft SDKs\\Windows\\"), version_key_name });
defer gpa.free(key_name);
const key = registry.tryOpenSoftwareKeyWithPrecedence(switch (is_32_bit) {
true => &.{
.{ .root = .local_machine },
.{ .root = .current_user },
},
false => &.{
.{ .root = .local_machine, .wow64 = .wow64_32 },
.{ .root = .current_user, .wow64 = .wow64_32 },
.{ .root = .local_machine, .wow64 = .native },
.{ .root = .current_user, .wow64 = .native },
},
}, key_name) catch {
return error.InstallationNotFound;
};
defer key.close();
const path: []const u8 = path: {
const path_w_maybe_with_trailing_slash = key.getString(gpa, .{ .name = L("InstallationFolder") }, .wtf16) catch |err| switch (err) {
error.NotAString,
error.ValueNameNotFound,
error.StringNotFound,
=> return error.InstallationNotFound,
error.OutOfMemory => |e| return e,
};
defer gpa.free(path_w_maybe_with_trailing_slash);
if (!std.fs.path.isAbsoluteWindowsWtf16(path_w_maybe_with_trailing_slash)) {
return error.InstallationNotFound;
}
const path_w = std.mem.trimEnd(u16, path_w_maybe_with_trailing_slash, L("\\/"));
break :path try std.unicode.wtf16LeToWtf8Alloc(gpa, path_w);
};
errdefer gpa.free(path);
const version: []const u8 = version: {
const version_without_0 = key.getString(gpa, .{ .name = L("ProductVersion") }, .wtf16) catch |err| switch (err) {
error.NotAString,
error.ValueNameNotFound,
error.StringNotFound,
=> return error.InstallationNotFound,
error.OutOfMemory => |e| return e,
};
defer gpa.free(version_without_0);
if (version_without_0.len + ".0".len > product_version_max_length) {
return error.VersionTooLong;
}
var version: std.array_list.Managed(u8) = try .initCapacity(gpa, version_without_0.len + 2);
errdefer version.deinit();
try std.unicode.wtf16LeToWtf8ArrayList(&version, version_without_0);
try version.appendSlice(".0");
break :version try version.toOwnedSlice();
};
errdefer gpa.free(version);
return .{ .path = path, .version = version };
}
fn isValidVersion(installation: Installation, roots_key: Registry.Key) bool {
var version_buf: [product_version_max_length]u16 = undefined;
const version_len = std.unicode.wtf8ToWtf16Le(&version_buf, installation.version) catch return false;
const version = version_buf[0..version_len];
const options_key_name = "Installed Options";
const buf_len = product_version_max_length + options_key_name.len + 2;
var buf: [buf_len]u16 = undefined;
var query: std.ArrayList(u16) = .initBuffer(&buf);
query.appendSliceAssumeCapacity(version);
query.appendAssumeCapacity('\\');
query.appendSliceAssumeCapacity(L(options_key_name));
const options_key = roots_key.open(query.items) catch |err| switch (err) {
error.KeyNotFound => return false,
};
defer options_key.close();
const option_name = comptime switch (builtin.target.cpu.arch) {
.thumb => "OptionId.DesktopCPParm",
.aarch64 => "OptionId.DesktopCPParm64",
.x86 => "OptionId.DesktopCPPx86",
.x86_64 => "OptionId.DesktopCPPx64",
else => |tag| @compileError("Windows SDK cannot be detected on architecture " ++ tag),
};
const reg_value = options_key.getDword(.{ .name = L(option_name) }) catch return false;
return (reg_value == 1);
}
fn free(install: Installation, gpa: Allocator) void {
gpa.free(install.path);
gpa.free(install.version);
}
}