struct Installation [src]
Fields
path: []const u8
version: []const u8
Source
pub const Installation = struct {
path: []const u8,
version: []const u8,
/// Find path and version of Windows SDK.
/// Caller owns the result's fields.
/// After finishing work, call `free(allocator)`.
fn find(
allocator: std.mem.Allocator,
roots_key: RegistryWtf8,
roots_subkey: []const u8,
prefix: []const u8,
version_key_name: []const u8,
) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
roots: {
const installation = findFromRoot(allocator, roots_key, roots_subkey, prefix) catch
break :roots;
if (installation.isValidVersion()) return installation;
installation.free(allocator);
}
{
const installation = try findFromInstallationFolder(allocator, version_key_name);
if (installation.isValidVersion()) return installation;
installation.free(allocator);
}
return error.InstallationNotFound;
}
fn findFromRoot(
allocator: std.mem.Allocator,
roots_key: RegistryWtf8,
roots_subkey: []const u8,
prefix: []const u8,
) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
const path = path: {
const path_maybe_with_trailing_slash = roots_key.getString(allocator, "", roots_subkey) catch |err| switch (err) {
error.NotAString => return error.InstallationNotFound,
error.ValueNameNotFound => return error.InstallationNotFound,
error.StringNotFound => return error.InstallationNotFound,
error.OutOfMemory => return error.OutOfMemory,
};
if (path_maybe_with_trailing_slash.len > std.fs.max_path_bytes or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
allocator.free(path_maybe_with_trailing_slash);
return error.PathTooLong;
}
var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
errdefer path.deinit();
// String might contain trailing slash, so trim it here
if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
break :path try path.toOwnedSlice();
};
errdefer allocator.free(path);
const version = version: {
var buf: [std.fs.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 (!std.fs.path.isAbsolute(sdk_lib_dir_path)) return error.InstallationNotFound;
// enumerate files in sdk path looking for latest version
var sdk_lib_dir = std.fs.openDirAbsolute(sdk_lib_dir_path, .{
.iterate = true,
}) catch |err| switch (err) {
error.NameTooLong => return error.PathTooLong,
else => return error.InstallationNotFound,
};
defer sdk_lib_dir.close();
var iterator = sdk_lib_dir.iterate();
const versions = try iterateAndFilterByVersion(&iterator, allocator, prefix);
if (versions.len == 0) return error.InstallationNotFound;
defer {
for (versions[1..]) |version| allocator.free(version);
allocator.free(versions);
}
break :version versions[0];
};
errdefer allocator.free(version);
return .{ .path = path, .version = version };
}
fn findFromInstallationFolder(
allocator: std.mem.Allocator,
version_key_name: []const u8,
) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
var key_name_buf: [RegistryWtf16Le.key_name_max_len]u8 = undefined;
const key_name = std.fmt.bufPrint(
&key_name_buf,
"SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\{s}",
.{version_key_name},
) catch unreachable;
const key = key: for ([_]bool{ true, false }) |wow6432node| {
for ([_]windows.HKEY{ windows.HKEY_LOCAL_MACHINE, windows.HKEY_CURRENT_USER }) |hkey| {
break :key RegistryWtf8.openKey(hkey, key_name, .{ .wow64_32 = wow6432node }) catch |err| switch (err) {
error.KeyNotFound => return error.InstallationNotFound,
};
}
} else return error.InstallationNotFound;
defer key.closeKey();
const path: []const u8 = path: {
const path_maybe_with_trailing_slash = key.getString(allocator, "", "InstallationFolder") catch |err| switch (err) {
error.NotAString => return error.InstallationNotFound,
error.ValueNameNotFound => return error.InstallationNotFound,
error.StringNotFound => return error.InstallationNotFound,
error.OutOfMemory => return error.OutOfMemory,
};
if (path_maybe_with_trailing_slash.len > std.fs.max_path_bytes or !std.fs.path.isAbsolute(path_maybe_with_trailing_slash)) {
allocator.free(path_maybe_with_trailing_slash);
return error.PathTooLong;
}
var path = std.ArrayList(u8).fromOwnedSlice(allocator, path_maybe_with_trailing_slash);
errdefer path.deinit();
// String might contain trailing slash, so trim it here
if (path.items.len > "C:\\".len and path.getLast() == '\\') _ = path.pop();
const path_without_trailing_slash = try path.toOwnedSlice();
break :path path_without_trailing_slash;
};
errdefer allocator.free(path);
const version: []const u8 = version: {
// note(dimenus): Microsoft doesn't include the .0 in the ProductVersion key....
const version_without_0 = key.getString(allocator, "", "ProductVersion") catch |err| switch (err) {
error.NotAString => return error.InstallationNotFound,
error.ValueNameNotFound => return error.InstallationNotFound,
error.StringNotFound => return error.InstallationNotFound,
error.OutOfMemory => return error.OutOfMemory,
};
if (version_without_0.len + ".0".len > product_version_max_length) {
allocator.free(version_without_0);
return error.VersionTooLong;
}
var version = std.ArrayList(u8).fromOwnedSlice(allocator, version_without_0);
errdefer version.deinit();
try version.appendSlice(".0");
const version_with_0 = try version.toOwnedSlice();
break :version version_with_0;
};
errdefer allocator.free(version);
return .{ .path = path, .version = version };
}
/// Check whether this version is enumerated in registry.
fn isValidVersion(installation: Installation) bool {
var buf: [std.fs.max_path_bytes]u8 = undefined;
const reg_query_as_wtf8 = std.fmt.bufPrint(buf[0..], "{s}\\{s}\\Installed Options", .{
windows_kits_reg_key,
installation.version,
}) catch |err| switch (err) {
error.NoSpaceLeft => return false,
};
const options_key = RegistryWtf8.openKey(
windows.HKEY_LOCAL_MACHINE,
reg_query_as_wtf8,
.{ .wow64_32 = true },
) catch |err| switch (err) {
error.KeyNotFound => return false,
};
defer options_key.closeKey();
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("", option_name) catch return false;
return (reg_value == 1);
}
fn free(install: Installation, allocator: std.mem.Allocator) void {
allocator.free(install.path);
allocator.free(install.version);
}
}