diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig index bebb0fc5a113..01a2e7d26347 100644 --- a/lib/std/zig/WindowsSdk.zig +++ b/lib/std/zig/WindowsSdk.zig @@ -23,7 +23,7 @@ pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooL if (builtin.os.tag != .windows) return error.NotFound; //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed - const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, windows_kits_reg_key) catch |err| switch (err) { + const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, windows_kits_reg_key, .{ .wow64_32 = true }) catch |err| switch (err) { error.KeyNotFound => return error.NotFound, }; defer roots_key.closeKey(); @@ -137,11 +137,17 @@ fn iterateAndFilterByVersion( return dirs.toOwnedSlice(); } +const OpenOptions = struct { + /// Sets the KEY_WOW64_32KEY access flag. + /// https://learn.microsoft.com/en-us/windows/win32/winprog64/accessing-an-alternate-registry-view + wow64_32: bool = false, +}; + const RegistryWtf8 = struct { key: windows.HKEY, /// Assert that `key` is valid WTF-8 string - pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryWtf8 { + pub fn openKey(hkey: windows.HKEY, key: []const u8, options: OpenOptions) error{KeyNotFound}!RegistryWtf8 { const key_wtf16le: [:0]const u16 = key_wtf16le: { var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined; const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) { @@ -151,7 +157,7 @@ const RegistryWtf8 = struct { break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0]; }; - const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le); + const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le, options); return .{ .key = registry_wtf16le.key }; } @@ -239,15 +245,17 @@ const RegistryWtf16Le = struct { pub const value_name_max_len = 16_383; /// Under HKEY_LOCAL_MACHINE with flags: - /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS. + /// KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, optionally KEY_WOW64_32KEY. /// After finishing work, call `closeKey`. - fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le { + fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16, options: OpenOptions) error{KeyNotFound}!RegistryWtf16Le { var key: windows.HKEY = undefined; + var access: windows.REGSAM = windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS; + if (options.wow64_32) access |= windows.KEY_WOW64_32KEY; const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW( hkey, key_wtf16le, 0, - windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS, + access, &key, ); const return_code: windows.Win32Error = @enumFromInt(return_code_int); @@ -484,13 +492,14 @@ pub const Installation = struct { version_key_name: []const u8, ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation { var key_name_buf: [RegistryWtf16Le.key_name_max_len]u8 = undefined; - const key = key: for ([_][]const u8{ "\\Wow6432Node", "" }) |wow6432node| { + 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, std.fmt.bufPrint( - &key_name_buf, - "SOFTWARE{s}\\Microsoft\\Microsoft SDKs\\Windows\\{s}", - .{ wow6432node, version_key_name }, - ) catch unreachable) catch |err| switch (err) { + break :key RegistryWtf8.openKey(hkey, key_name, .{ .wow64_32 = wow6432node }) catch |err| switch (err) { error.KeyNotFound => return error.InstallationNotFound, }; } @@ -563,6 +572,7 @@ pub const Installation = struct { const options_key = RegistryWtf8.openKey( windows.HKEY_LOCAL_MACHINE, reg_query_as_wtf8, + .{ .wow64_32 = true }, ) catch |err| switch (err) { error.KeyNotFound => return false, }; @@ -587,9 +597,34 @@ pub const Installation = struct { }; const MsvcLibDir = struct { + fn findInstancesDirViaSetup(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { + const vs_setup_key_path = "SOFTWARE\\Microsoft\\VisualStudio\\Setup"; + const vs_setup_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, vs_setup_key_path, .{}) catch |err| switch (err) { + error.KeyNotFound => return error.PathNotFound, + }; + defer vs_setup_key.closeKey(); + + const packages_path = vs_setup_key.getString(allocator, "", "CachePath") catch |err| switch (err) { + error.NotAString, + error.ValueNameNotFound, + error.StringNotFound, + => return error.PathNotFound, + + error.OutOfMemory => return error.OutOfMemory, + }; + defer allocator.free(packages_path); + + if (!std.fs.path.isAbsolute(packages_path)) return error.PathNotFound; + + const instances_path = try std.fs.path.join(allocator, &.{ packages_path, "_Instances" }); + defer allocator.free(instances_path); + + return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound; + } + fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"; - const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) { + const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid, .{}) catch |err| switch (err) { error.KeyNotFound => return error.PathNotFound, }; defer setup_config_key.closeKey(); @@ -604,6 +639,8 @@ const MsvcLibDir = struct { }; defer allocator.free(dll_path); + if (!std.fs.path.isAbsolute(dll_path)) return error.PathNotFound; + var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound; // the .dll filename _ = path_it.last(); @@ -622,22 +659,40 @@ const MsvcLibDir = struct { } fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { - // First try to get the path from the .dll that would have been + // First, try getting the packages cache path from the registry. + // This only seems to exist when the path is different from the default. + method1: { + return findInstancesDirViaSetup(allocator) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.PathNotFound => break :method1, + }; + } + // Otherwise, try to get the path from the .dll that would have been // loaded via COM for SetupConfiguration. - return findInstancesDirViaCLSID(allocator) catch |orig_err| { - // If that can't be found, fall back to manually appending - // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA% + method2: { + return findInstancesDirViaCLSID(allocator) catch |err| switch (err) { + error.OutOfMemory => |e| return e, + error.PathNotFound => break :method2, + }; + } + // If that can't be found, fall back to manually appending + // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA% + method3: { const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) { error.OutOfMemory => |e| return e, - else => return orig_err, + error.InvalidWtf8 => unreachable, + error.EnvironmentVariableNotFound => break :method3, }; defer allocator.free(program_data); + if (!std.fs.path.isAbsolute(program_data)) break :method3; + const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" }); defer allocator.free(instances_path); - return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err; - }; + return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch break :method3; + } + return error.PathNotFound; } /// Intended to be equivalent to `ISetupHelper.ParseVersion` @@ -896,7 +951,7 @@ const MsvcLibDir = struct { } } - const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound; + const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", .{ .wow64_32 = true }) catch return error.PathNotFound; defer vs7_key.closeKey(); try_vs7_key: { const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {