authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-05-09 22:22:41-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2024-05-09 22:22:41-07:00
logcf8f0cdae9f6ca844c60adad6ac45d203d8baf38
treefe673fbb989e3948e0e8501210d6ddc1bedde6b2
parente69caaa39f06b7ddf00c1e439c3df91f4efbdef3

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).

1 files changed, 52 insertions(+), 7 deletions(-)

lib/std/zig/WindowsSdk.zig+52-7
...@@ -587,6 +587,31 @@ pub const Installation = struct {...@@ -587,6 +587,31 @@ pub const Installation = struct {
587};587};
588588
589const MsvcLibDir = struct {589const MsvcLibDir = struct {
590 fn findInstancesDirViaSetup(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
591 const vs_setup_key_path = "SOFTWARE\\Microsoft\\VisualStudio\\Setup";
592 const vs_setup_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, vs_setup_key_path) catch |err| switch (err) {
593 error.KeyNotFound => return error.PathNotFound,
594 };
595 defer vs_setup_key.closeKey();
596
597 const packages_path = vs_setup_key.getString(allocator, "", "CachePath") catch |err| switch (err) {
598 error.NotAString,
599 error.ValueNameNotFound,
600 error.StringNotFound,
601 => return error.PathNotFound,
602
603 error.OutOfMemory => return error.OutOfMemory,
604 };
605 defer allocator.free(packages_path);
606
607 if (!std.fs.path.isAbsolute(packages_path)) return error.PathNotFound;
608
609 const instances_path = try std.fs.path.join(allocator, &.{ packages_path, "_Instances" });
610 defer allocator.free(instances_path);
611
612 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound;
613 }
614
590 fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {615 fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
591 const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";616 const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";
592 const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) {617 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 {...@@ -604,6 +629,8 @@ const MsvcLibDir = struct {
604 };629 };
605 defer allocator.free(dll_path);630 defer allocator.free(dll_path);
606631
632 if (!std.fs.path.isAbsolute(dll_path)) return error.PathNotFound;
633
607 var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;634 var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;
608 // the .dll filename635 // the .dll filename
609 _ = path_it.last();636 _ = path_it.last();
...@@ -622,22 +649,40 @@ const MsvcLibDir = struct {...@@ -622,22 +649,40 @@ const MsvcLibDir = struct {
622 }649 }
623650
624 fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {651 fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
625 // First try to get the path from the .dll that would have been652 // First, try getting the packages cache path from the registry.
653 // This only seems to exist when the path is different from the default.
654 method1: {
655 return findInstancesDirViaSetup(allocator) catch |err| switch (err) {
656 error.OutOfMemory => |e| return e,
657 error.PathNotFound => break :method1,
658 };
659 }
660 // Otherwise, try to get the path from the .dll that would have been
626 // loaded via COM for SetupConfiguration.661 // loaded via COM for SetupConfiguration.
627 return findInstancesDirViaCLSID(allocator) catch |orig_err| {662 method2: {
628 // If that can't be found, fall back to manually appending663 return findInstancesDirViaCLSID(allocator) catch |err| switch (err) {
629 // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%664 error.OutOfMemory => |e| return e,
665 error.PathNotFound => break :method2,
666 };
667 }
668 // If that can't be found, fall back to manually appending
669 // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
670 method3: {
630 const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {671 const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {
631 error.OutOfMemory => |e| return e,672 error.OutOfMemory => |e| return e,
632 else => return orig_err,673 error.InvalidWtf8 => unreachable,
674 error.EnvironmentVariableNotFound => break :method3,
633 };675 };
634 defer allocator.free(program_data);676 defer allocator.free(program_data);
635677
678 if (!std.fs.path.isAbsolute(program_data)) break :method3;
679
636 const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });680 const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
637 defer allocator.free(instances_path);681 defer allocator.free(instances_path);
638682
639 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err;683 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch break :method3;
640 };684 }
685 return error.PathNotFound;
641 }686 }
642687
643 /// Intended to be equivalent to `ISetupHelper.ParseVersion`688 /// Intended to be equivalent to `ISetupHelper.ParseVersion`