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); } }