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: {
// Microsoft doesn't include the .0 in the ProductVersion key
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 };
}