@@ -23,7 +23,7 @@ pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooL
23
23
if (builtin .os .tag != .windows ) return error .NotFound ;
24
24
25
25
//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
27
error .KeyNotFound = > return error .NotFound ,
28
28
};
29
29
defer roots_key .closeKey ();
@@ -137,11 +137,17 @@ fn iterateAndFilterByVersion(
137
137
return dirs .toOwnedSlice ();
138
138
}
139
139
140
+ const 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
+
140
146
const RegistryWtf8 = struct {
141
147
key : windows.HKEY ,
142
148
143
149
/// 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
151
const key_wtf16le : [:0 ]const u16 = key_wtf16le : {
146
152
var key_wtf16le_buf : [RegistryWtf16Le .key_name_max_len ]u16 = undefined ;
147
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
157
break :key_wtf16le key_wtf16le_buf [0.. key_wtf16le_len :0 ];
152
158
};
153
159
154
- const registry_wtf16le = try RegistryWtf16Le .openKey (hkey , key_wtf16le );
160
+ const registry_wtf16le = try RegistryWtf16Le .openKey (hkey , key_wtf16le , options );
155
161
return .{ .key = registry_wtf16le .key };
156
162
}
157
163
@@ -239,15 +245,17 @@ const RegistryWtf16Le = struct {
239
245
pub const value_name_max_len = 16_383 ;
240
246
241
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
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
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
254
const return_code_int : windows.HRESULT = windows .advapi32 .RegOpenKeyExW (
247
255
hkey ,
248
256
key_wtf16le ,
249
257
0 ,
250
- windows . KEY_QUERY_VALUE | windows . KEY_WOW64_32KEY | windows . KEY_ENUMERATE_SUB_KEYS ,
258
+ access ,
251
259
& key ,
252
260
);
253
261
const return_code : windows.Win32Error = @enumFromInt (return_code_int );
@@ -484,13 +492,14 @@ pub const Installation = struct {
484
492
version_key_name : []const u8 ,
485
493
) error { OutOfMemory , InstallationNotFound , PathTooLong , VersionTooLong }! Installation {
486
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
501
for ([_ ]windows.HKEY { windows .HKEY_LOCAL_MACHINE , windows .HKEY_CURRENT_USER }) | hkey | {
489
- break :key RegistryWtf8 .openKey (hkey , std .fmt .bufPrint (
490
- & key_name_buf ,
491
- "SOFTWARE{s}\\ Microsoft\\ Microsoft SDKs\\ Windows\\ {s}" ,
492
- .{ wow6432node , version_key_name },
493
- ) catch unreachable ) catch | err | switch (err ) {
502
+ break :key RegistryWtf8 .openKey (hkey , key_name , .{ .wow64_32 = wow6432node }) catch | err | switch (err ) {
494
503
error .KeyNotFound = > return error .InstallationNotFound ,
495
504
};
496
505
}
@@ -563,6 +572,7 @@ pub const Installation = struct {
563
572
const options_key = RegistryWtf8 .openKey (
564
573
windows .HKEY_LOCAL_MACHINE ,
565
574
reg_query_as_wtf8 ,
575
+ .{ .wow64_32 = true },
566
576
) catch | err | switch (err ) {
567
577
error .KeyNotFound = > return false ,
568
578
};
@@ -587,9 +597,34 @@ pub const Installation = struct {
587
597
};
588
598
589
599
const 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
625
fn findInstancesDirViaCLSID (allocator : std.mem.Allocator ) error { OutOfMemory , PathNotFound }! std.fs.Dir {
591
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
628
error .KeyNotFound = > return error .PathNotFound ,
594
629
};
595
630
defer setup_config_key .closeKey ();
@@ -604,6 +639,8 @@ const MsvcLibDir = struct {
604
639
};
605
640
defer allocator .free (dll_path );
606
641
642
+ if (! std .fs .path .isAbsolute (dll_path )) return error .PathNotFound ;
643
+
607
644
var path_it = std .fs .path .componentIterator (dll_path ) catch return error .PathNotFound ;
608
645
// the .dll filename
609
646
_ = path_it .last ();
@@ -622,22 +659,40 @@ const MsvcLibDir = struct {
622
659
}
623
660
624
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 been
662
+ // 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
671
// loaded via COM for SetupConfiguration.
627
- return findInstancesDirViaCLSID (allocator ) catch | orig_err | {
628
- // If that can't be found, fall back to manually appending
629
- // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA%
672
+ method2 : {
673
+ return findInstancesDirViaCLSID (allocator ) catch | err | switch (err ) {
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
681
const program_data = std .process .getEnvVarOwned (allocator , "PROGRAMDATA" ) catch | err | switch (err ) {
631
682
error .OutOfMemory = > | e | return e ,
632
- else = > return orig_err ,
683
+ error .InvalidWtf8 = > unreachable ,
684
+ error .EnvironmentVariableNotFound = > break :method3 ,
633
685
};
634
686
defer allocator .free (program_data );
635
687
688
+ if (! std .fs .path .isAbsolute (program_data )) break :method3 ;
689
+
636
690
const instances_path = try std .fs .path .join (allocator , &.{ program_data , "Microsoft" , "VisualStudio" , "Packages" , "_Instances" });
637
691
defer allocator .free (instances_path );
638
692
639
- return std .fs .openDirAbsolute (instances_path , .{ .iterate = true }) catch return orig_err ;
640
- };
693
+ return std .fs .openDirAbsolute (instances_path , .{ .iterate = true }) catch break :method3 ;
694
+ }
695
+ return error .PathNotFound ;
641
696
}
642
697
643
698
/// Intended to be equivalent to `ISetupHelper.ParseVersion`
@@ -896,7 +951,7 @@ const MsvcLibDir = struct {
896
951
}
897
952
}
898
953
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
955
defer vs7_key .closeKey ();
901
956
try_vs7_key : {
902
957
const path_maybe_with_trailing_slash = vs7_key .getString (allocator , "" , "14.0" ) catch | err | switch (err ) {
0 commit comments