From cf8f0cdae9f6ca844c60adad6ac45d203d8baf38 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Thu, 9 May 2024 22:22:41 -0700 Subject: [PATCH] WindowsSdk: Fix finding the _Instances directory when it's not in the default location Information about installed MSVC instances are stored in `state.json` files within a `Packages/_Instances` directory. The default location for this is `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances`. However, it is possible for the Packages directory to be put somewhere else. In that case, the registry value `HKLM\SOFTWARE\Microsoft\VisualStudio\Setup\CachePath` is set and contains the path to the Packages directory. Previously, WindowsSdk did not check that registry value. After this commit, the registry value `HKLM\SOFTWARE\Microsoft\VisualStudio\Setup\CachePath` is checked first, which matches what ISetupEnumInstances does (according to a Procmon log). --- lib/std/zig/WindowsSdk.zig | 59 +++++++++++++++++++++++++++++++++----- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/lib/std/zig/WindowsSdk.zig b/lib/std/zig/WindowsSdk.zig index bebb0fc5a1..f7f1eda0f3 100644 --- a/lib/std/zig/WindowsSdk.zig +++ b/lib/std/zig/WindowsSdk.zig @@ -587,6 +587,31 @@ 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) { @@ -604,6 +629,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 +649,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`