authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-12 09:39:13-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-05-22 07:04:47-07:00
log6ce3c2423ece2a63bf52d2a39f55fba321925d0b
tree905432a45675820a754ce17073c60fee2ac77fd0
parent52150b701cc5dae8864c908a6003d8dbe6b4d7b9

Merge pull request #19926 from squeek502/windows-sdk-cachepath-registry

WindowsSdk: Fix finding the _Instances directory when it's not in the default location

1 files changed, 76 insertions(+), 21 deletions(-)

lib/std/zig/WindowsSdk.zig+76-21
...@@ -23,7 +23,7 @@ pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooL...@@ -23,7 +23,7 @@ pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooL
23 if (builtin.os.tag != .windows) return error.NotFound;23 if (builtin.os.tag != .windows) return error.NotFound;
2424
25 //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed25 //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
26 const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, windows_kits_reg_key) catch |err| switch (err) {26 const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, windows_kits_reg_key, .{ .wow64_32 = true }) catch |err| switch (err) {
27 error.KeyNotFound => return error.NotFound,27 error.KeyNotFound => return error.NotFound,
28 };28 };
29 defer roots_key.closeKey();29 defer roots_key.closeKey();
...@@ -137,11 +137,17 @@ fn iterateAndFilterByVersion(...@@ -137,11 +137,17 @@ fn iterateAndFilterByVersion(
137 return dirs.toOwnedSlice();137 return dirs.toOwnedSlice();
138}138}
139139
140const OpenOptions = struct {
141 /// Sets the KEY_WOW64_32KEY access flag.
142 /// https://learn.microsoft.com/en-us/windows/win32/winprog64/accessing-an-alternate-registry-view
143 wow64_32: bool = false,
144};
145
140const RegistryWtf8 = struct {146const RegistryWtf8 = struct {
141 key: windows.HKEY,147 key: windows.HKEY,
142148
143 /// Assert that `key` is valid WTF-8 string149 /// Assert that `key` is valid WTF-8 string
144 pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryWtf8 {150 pub fn openKey(hkey: windows.HKEY, key: []const u8, options: OpenOptions) error{KeyNotFound}!RegistryWtf8 {
145 const key_wtf16le: [:0]const u16 = key_wtf16le: {151 const key_wtf16le: [:0]const u16 = key_wtf16le: {
146 var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;152 var key_wtf16le_buf: [RegistryWtf16Le.key_name_max_len]u16 = undefined;
147 const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) {153 const key_wtf16le_len: usize = std.unicode.wtf8ToWtf16Le(key_wtf16le_buf[0..], key) catch |err| switch (err) {
...@@ -151,7 +157,7 @@ const RegistryWtf8 = struct {...@@ -151,7 +157,7 @@ const RegistryWtf8 = struct {
151 break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0];157 break :key_wtf16le key_wtf16le_buf[0..key_wtf16le_len :0];
152 };158 };
153159
154 const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le);160 const registry_wtf16le = try RegistryWtf16Le.openKey(hkey, key_wtf16le, options);
155 return .{ .key = registry_wtf16le.key };161 return .{ .key = registry_wtf16le.key };
156 }162 }
157163
...@@ -239,15 +245,17 @@ const RegistryWtf16Le = struct {...@@ -239,15 +245,17 @@ const RegistryWtf16Le = struct {
239 pub const value_name_max_len = 16_383;245 pub const value_name_max_len = 16_383;
240246
241 /// Under HKEY_LOCAL_MACHINE with flags:247 /// Under HKEY_LOCAL_MACHINE with flags:
242 /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS.248 /// KEY_QUERY_VALUE, KEY_ENUMERATE_SUB_KEYS, optionally KEY_WOW64_32KEY.
243 /// After finishing work, call `closeKey`.249 /// After finishing work, call `closeKey`.
244 fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16) error{KeyNotFound}!RegistryWtf16Le {250 fn openKey(hkey: windows.HKEY, key_wtf16le: [:0]const u16, options: OpenOptions) error{KeyNotFound}!RegistryWtf16Le {
245 var key: windows.HKEY = undefined;251 var key: windows.HKEY = undefined;
252 var access: windows.REGSAM = windows.KEY_QUERY_VALUE | windows.KEY_ENUMERATE_SUB_KEYS;
253 if (options.wow64_32) access |= windows.KEY_WOW64_32KEY;
246 const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(254 const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW(
247 hkey,255 hkey,
248 key_wtf16le,256 key_wtf16le,
249 0,257 0,
250 windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS,258 access,
251 &key,259 &key,
252 );260 );
253 const return_code: windows.Win32Error = @enumFromInt(return_code_int);261 const return_code: windows.Win32Error = @enumFromInt(return_code_int);
...@@ -484,13 +492,14 @@ pub const Installation = struct {...@@ -484,13 +492,14 @@ pub const Installation = struct {
484 version_key_name: []const u8,492 version_key_name: []const u8,
485 ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {493 ) error{ OutOfMemory, InstallationNotFound, PathTooLong, VersionTooLong }!Installation {
486 var key_name_buf: [RegistryWtf16Le.key_name_max_len]u8 = undefined;494 var key_name_buf: [RegistryWtf16Le.key_name_max_len]u8 = undefined;
487 const key = key: for ([_][]const u8{ "\\Wow6432Node", "" }) |wow6432node| {495 const key_name = std.fmt.bufPrint(
496 &key_name_buf,
497 "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\{s}",
498 .{version_key_name},
499 ) catch unreachable;
500 const key = key: for ([_]bool{ true, false }) |wow6432node| {
488 for ([_]windows.HKEY{ windows.HKEY_LOCAL_MACHINE, windows.HKEY_CURRENT_USER }) |hkey| {501 for ([_]windows.HKEY{ windows.HKEY_LOCAL_MACHINE, windows.HKEY_CURRENT_USER }) |hkey| {
489 break :key RegistryWtf8.openKey(hkey, std.fmt.bufPrint(502 break :key RegistryWtf8.openKey(hkey, key_name, .{ .wow64_32 = wow6432node }) catch |err| switch (err) {
490 &key_name_buf,
491 "SOFTWARE{s}\\Microsoft\\Microsoft SDKs\\Windows\\{s}",
492 .{ wow6432node, version_key_name },
493 ) catch unreachable) catch |err| switch (err) {
494 error.KeyNotFound => return error.InstallationNotFound,503 error.KeyNotFound => return error.InstallationNotFound,
495 };504 };
496 }505 }
...@@ -563,6 +572,7 @@ pub const Installation = struct {...@@ -563,6 +572,7 @@ pub const Installation = struct {
563 const options_key = RegistryWtf8.openKey(572 const options_key = RegistryWtf8.openKey(
564 windows.HKEY_LOCAL_MACHINE,573 windows.HKEY_LOCAL_MACHINE,
565 reg_query_as_wtf8,574 reg_query_as_wtf8,
575 .{ .wow64_32 = true },
566 ) catch |err| switch (err) {576 ) catch |err| switch (err) {
567 error.KeyNotFound => return false,577 error.KeyNotFound => return false,
568 };578 };
...@@ -587,9 +597,34 @@ pub const Installation = struct {...@@ -587,9 +597,34 @@ pub const Installation = struct {
587};597};
588598
589const MsvcLibDir = struct {599const MsvcLibDir = struct {
600 fn findInstancesDirViaSetup(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
601 const vs_setup_key_path = "SOFTWARE\\Microsoft\\VisualStudio\\Setup";
602 const vs_setup_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, vs_setup_key_path, .{}) catch |err| switch (err) {
603 error.KeyNotFound => return error.PathNotFound,
604 };
605 defer vs_setup_key.closeKey();
606
607 const packages_path = vs_setup_key.getString(allocator, "", "CachePath") catch |err| switch (err) {
608 error.NotAString,
609 error.ValueNameNotFound,
610 error.StringNotFound,
611 => return error.PathNotFound,
612
613 error.OutOfMemory => return error.OutOfMemory,
614 };
615 defer allocator.free(packages_path);
616
617 if (!std.fs.path.isAbsolute(packages_path)) return error.PathNotFound;
618
619 const instances_path = try std.fs.path.join(allocator, &.{ packages_path, "_Instances" });
620 defer allocator.free(instances_path);
621
622 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound;
623 }
624
590 fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {625 fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
591 const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";626 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) {627 const setup_config_key = RegistryWtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid, .{}) catch |err| switch (err) {
593 error.KeyNotFound => return error.PathNotFound,628 error.KeyNotFound => return error.PathNotFound,
594 };629 };
595 defer setup_config_key.closeKey();630 defer setup_config_key.closeKey();
...@@ -604,6 +639,8 @@ const MsvcLibDir = struct {...@@ -604,6 +639,8 @@ const MsvcLibDir = struct {
604 };639 };
605 defer allocator.free(dll_path);640 defer allocator.free(dll_path);
606641
642 if (!std.fs.path.isAbsolute(dll_path)) return error.PathNotFound;
643
607 var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;644 var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound;
608 // the .dll filename645 // the .dll filename
609 _ = path_it.last();646 _ = path_it.last();
...@@ -622,22 +659,40 @@ const MsvcLibDir = struct {...@@ -622,22 +659,40 @@ const MsvcLibDir = struct {
622 }659 }
623660
624 fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {661 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 been662 // First, try getting the packages cache path from the registry.
663 // This only seems to exist when the path is different from the default.
664 method1: {
665 return findInstancesDirViaSetup(allocator) catch |err| switch (err) {
666 error.OutOfMemory => |e| return e,
667 error.PathNotFound => break :method1,
668 };
669 }
670 // Otherwise, try to get the path from the .dll that would have been
626 // loaded via COM for SetupConfiguration.671 // loaded via COM for SetupConfiguration.
627 return findInstancesDirViaCLSID(allocator) catch |orig_err| {672 method2: {
628 // If that can't be found, fall back to manually appending673 return findInstancesDirViaCLSID(allocator) catch |err| switch (err) {
629 // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%674 error.OutOfMemory => |e| return e,
675 error.PathNotFound => break :method2,
676 };
677 }
678 // If that can't be found, fall back to manually appending
679 // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
680 method3: {
630 const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {681 const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) {
631 error.OutOfMemory => |e| return e,682 error.OutOfMemory => |e| return e,
632 else => return orig_err,683 error.InvalidWtf8 => unreachable,
684 error.EnvironmentVariableNotFound => break :method3,
633 };685 };
634 defer allocator.free(program_data);686 defer allocator.free(program_data);
635687
688 if (!std.fs.path.isAbsolute(program_data)) break :method3;
689
636 const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });690 const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" });
637 defer allocator.free(instances_path);691 defer allocator.free(instances_path);
638692
639 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err;693 return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch break :method3;
640 };694 }
695 return error.PathNotFound;
641 }696 }
642697
643 /// Intended to be equivalent to `ISetupHelper.ParseVersion`698 /// Intended to be equivalent to `ISetupHelper.ParseVersion`
...@@ -896,7 +951,7 @@ const MsvcLibDir = struct {...@@ -896,7 +951,7 @@ const MsvcLibDir = struct {
896 }951 }
897 }952 }
898953
899 const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound;954 const vs7_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", .{ .wow64_32 = true }) catch return error.PathNotFound;
900 defer vs7_key.closeKey();955 defer vs7_key.closeKey();
901 try_vs7_key: {956 try_vs7_key: {
902 const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {957 const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) {