@@ -550,7 +550,6 @@ pub const ZigWindowsSDK = struct {
550
550
551
551
const msvc_lib_dir : ? []const u8 = MsvcLibDir .find (allocator ) catch | err | switch (err ) {
552
552
error .MsvcLibDirNotFound = > null ,
553
- error .PathTooLong = > null ,
554
553
error .OutOfMemory = > return error .OutOfMemory ,
555
554
};
556
555
errdefer allocator .free (msvc_lib_dir );
@@ -576,6 +575,112 @@ pub const ZigWindowsSDK = struct {
576
575
};
577
576
578
577
const MsvcLibDir = struct {
578
+ // https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration
579
+ fn findViaCOM (allocator : std.mem.Allocator ) error { OutOfMemory , PathNotFound }! []const u8 {
580
+ switch (windows .ole32 .CoInitializeEx (null , windows .COINIT .MULTITHREADED )) {
581
+ windows .S_OK , windows .S_FALSE = > {},
582
+ windows .E_OUTOFMEMORY = > return error .OutOfMemory ,
583
+ else = > return error .PathNotFound ,
584
+ }
585
+ // > To close the COM library gracefully on a thread, each successful
586
+ // > call to CoInitialize or CoInitializeEx, including any call that
587
+ // > returns S_FALSE, must be balanced by a corresponding call to CoUninitialize.
588
+ // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex
589
+ defer windows .ole32 .CoUninitialize ();
590
+
591
+ var setup_config : * ISetupConfiguration = undefined ;
592
+ switch (CoCreateInstance (
593
+ SetupConfiguration .CLSID ,
594
+ null ,
595
+ CLSCTX .INPROC_SERVER | CLSCTX .INPROC_HANDLER ,
596
+ ISetupConfiguration .IID ,
597
+ @ptrCast (& setup_config ),
598
+ )) {
599
+ windows .S_OK = > {},
600
+ windows .E_OUTOFMEMORY = > return error .OutOfMemory ,
601
+ else = > return error .PathNotFound ,
602
+ }
603
+ defer _ = setup_config .vtable .unknown .Release (setup_config );
604
+
605
+ var all_instances : * IEnumSetupInstances = undefined ;
606
+ switch (setup_config .vtable .setup_configuration .EnumInstances (setup_config , & all_instances )) {
607
+ windows .S_OK = > {},
608
+ windows .E_OUTOFMEMORY = > return error .OutOfMemory ,
609
+ else = > return error .PathNotFound ,
610
+ }
611
+ defer _ = all_instances .vtable .unknown .Release (all_instances );
612
+
613
+ while (true ) {
614
+ var cur : * ISetupInstance = undefined ;
615
+ switch (all_instances .vtable .enum_setup_instances .Next (all_instances , 1 , & cur , null )) {
616
+ windows .S_OK = > {},
617
+ windows .S_FALSE = > break ,
618
+ windows .E_OUTOFMEMORY = > return error .OutOfMemory ,
619
+ else = > return error .PathNotFound ,
620
+ }
621
+ defer _ = cur .vtable .unknown .Release (cur );
622
+
623
+ var installation_path_bstr : windows.BSTR = undefined ;
624
+ switch (cur .vtable .setup_instance .GetInstallationPath (cur , & installation_path_bstr )) {
625
+ windows .S_OK = > {},
626
+ windows .E_OUTOFMEMORY = > return error .OutOfMemory ,
627
+ else = > continue ,
628
+ }
629
+ defer SysFreeString (installation_path_bstr );
630
+
631
+ const installation_path_w = std .mem .span (installation_path_bstr );
632
+ const lib_dir_path = libDirFromInstallationPath (allocator , installation_path_w ) catch | err | switch (err ) {
633
+ error .OutOfMemory = > | e | return e ,
634
+ error .PathNotFound = > continue ,
635
+ };
636
+ errdefer allocator .free (lib_dir_path );
637
+
638
+ return lib_dir_path ;
639
+ }
640
+ return error .PathNotFound ;
641
+ }
642
+
643
+ fn libDirFromInstallationPath (allocator : std.mem.Allocator , installation_path_w : []const u16 ) error { OutOfMemory , PathNotFound }! []const u8 {
644
+ // Each UTF-16LE code unit may be expanded to 3 UTF-8 bytes.
645
+ var lib_dir_buf = try std .ArrayList (u8 ).initCapacity (allocator , installation_path_w .len * 3 );
646
+ errdefer lib_dir_buf .deinit ();
647
+
648
+ lib_dir_buf .items .len = std .unicode .utf16leToUtf8 (lib_dir_buf .unusedCapacitySlice (), installation_path_w ) catch {
649
+ return error .PathNotFound ;
650
+ };
651
+
652
+ if (! std .fs .path .isSep (lib_dir_buf .getLast ())) {
653
+ try lib_dir_buf .append ('\\ ' );
654
+ }
655
+ const installation_path_with_trailing_sep_len = lib_dir_buf .items .len ;
656
+
657
+ try lib_dir_buf .appendSlice ("VC\\ Auxiliary\\ Build\\ Microsoft.VCToolsVersion.default.txt" );
658
+ var default_tools_version_buf : [512 ]u8 = undefined ;
659
+ const default_tools_version_contents = std .fs .cwd ().readFile (lib_dir_buf .items , & default_tools_version_buf ) catch {
660
+ return error .PathNotFound ;
661
+ };
662
+ var tokenizer = std .mem .tokenizeAny (u8 , default_tools_version_contents , " \r \n " );
663
+ const default_tools_version = tokenizer .next () orelse return error .PathNotFound ;
664
+
665
+ lib_dir_buf .shrinkRetainingCapacity (installation_path_with_trailing_sep_len );
666
+ try lib_dir_buf .appendSlice ("VC\\ Tools\\ MSVC\\ " );
667
+ try lib_dir_buf .appendSlice (default_tools_version );
668
+ const folder_with_arch = "\\ Lib\\ " ++ comptime switch (builtin .target .cpu .arch ) {
669
+ .x86 = > "x86" ,
670
+ .x86_64 = > "x64" ,
671
+ .arm , .armeb = > "arm" ,
672
+ .aarch64 = > "arm64" ,
673
+ else = > | tag | @compileError ("MSVC lib dir cannot be detected on architecture " ++ tag ),
674
+ };
675
+ try lib_dir_buf .appendSlice (folder_with_arch );
676
+
677
+ if (! verifyLibDir (lib_dir_buf .items )) {
678
+ return error .PathNotFound ;
679
+ }
680
+
681
+ return lib_dir_buf .toOwnedSlice ();
682
+ }
683
+
579
684
// https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance
580
685
fn findViaRegistry (allocator : std.mem.Allocator ) error { OutOfMemory , PathNotFound }! []const u8 {
581
686
@@ -661,6 +766,10 @@ const MsvcLibDir = struct {
661
766
};
662
767
errdefer allocator .free (msvc_dir );
663
768
769
+ if (! verifyLibDir (msvc_dir )) {
770
+ return error .PathNotFound ;
771
+ }
772
+
664
773
return msvc_dir ;
665
774
}
666
775
@@ -720,36 +829,192 @@ const MsvcLibDir = struct {
720
829
};
721
830
try base_path .appendSlice (folder_with_arch );
722
831
832
+ if (! verifyLibDir (base_path .items )) {
833
+ return error .PathNotFound ;
834
+ }
835
+
723
836
const full_path = try base_path .toOwnedSlice ();
724
837
return full_path ;
725
838
}
726
839
840
+ fn verifyLibDir (lib_dir_path : []const u8 ) bool {
841
+ std .debug .assert (std .fs .path .isAbsolute (lib_dir_path )); // should be already handled in `findVia*`
842
+
843
+ var dir = std .fs .openDirAbsolute (lib_dir_path , .{}) catch return false ;
844
+ defer dir .close ();
845
+
846
+ const stat = dir .statFile ("vcruntime.lib" ) catch return false ;
847
+ if (stat .kind != .file )
848
+ return false ;
849
+
850
+ return true ;
851
+ }
852
+
727
853
/// Find path to MSVC's `lib/` directory.
728
854
/// Caller owns the result.
729
- pub fn find (allocator : std.mem.Allocator ) error { OutOfMemory , MsvcLibDirNotFound , PathTooLong }! []const u8 {
730
- const full_path = MsvcLibDir .findViaRegistry (allocator ) catch | err1 | switch (err1 ) {
855
+ pub fn find (allocator : std.mem.Allocator ) error { OutOfMemory , MsvcLibDirNotFound }! []const u8 {
856
+ const full_path = MsvcLibDir .findViaCOM (allocator ) catch | err1 | switch (err1 ) {
731
857
error .OutOfMemory = > return error .OutOfMemory ,
732
- error .PathNotFound = > MsvcLibDir .findViaVs7Key (allocator ) catch | err2 | switch (err2 ) {
858
+ error .PathNotFound = > MsvcLibDir .findViaRegistry (allocator ) catch | err2 | switch (err2 ) {
733
859
error .OutOfMemory = > return error .OutOfMemory ,
734
- error .PathNotFound = > return error .MsvcLibDirNotFound ,
860
+ error .PathNotFound = > MsvcLibDir .findViaVs7Key (allocator ) catch | err3 | switch (err3 ) {
861
+ error .OutOfMemory = > return error .OutOfMemory ,
862
+ error .PathNotFound = > return error .MsvcLibDirNotFound ,
863
+ },
735
864
},
736
865
};
737
866
errdefer allocator .free (full_path );
738
- std .debug .assert (std .fs .path .isAbsolute (full_path )); // should be already handled in `findVia*`
739
867
740
- var dir = std .fs .openDirAbsolute (full_path , .{}) catch | err | switch (err ) {
741
- error .NameTooLong = > return error .PathTooLong ,
742
- else = > return error .MsvcLibDirNotFound ,
868
+ return full_path ;
869
+ }
870
+ };
871
+
872
+ const IUnknown = extern struct {
873
+ vtable : * VTable (IUnknown ),
874
+
875
+ const IID_Value = windows .GUID .parse ("{00000000-0000-0000-c000-000000000046}" );
876
+ pub const IID = & IID_Value ;
877
+
878
+ pub fn VTable (comptime T : type ) type {
879
+ return extern struct {
880
+ QueryInterface : * const fn (
881
+ self : * T ,
882
+ riid : ? * const windows.GUID ,
883
+ ppvObject : ? * ? * anyopaque ,
884
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
885
+ AddRef : * const fn (
886
+ self : * T ,
887
+ ) callconv (windows .WINAPI ) u32 ,
888
+ Release : * const fn (
889
+ self : * T ,
890
+ ) callconv (windows .WINAPI ) u32 ,
743
891
};
744
- defer dir .close ();
892
+ }
893
+ };
745
894
746
- const stat = dir .statFile ("vcruntime.lib" ) catch | err | switch (err ) {
747
- error .NameTooLong = > return error .PathTooLong ,
748
- else = > return error .MsvcLibDirNotFound ,
895
+ const ISetupConfiguration = extern struct {
896
+ vtable : * extern struct {
897
+ unknown : IUnknown .VTable (ISetupConfiguration ),
898
+ setup_configuration : VTable (ISetupConfiguration ),
899
+ },
900
+
901
+ const IID_Value = windows .GUID .parse ("{42843719-db4c-46c2-8e7c-64f1816efd5b}" );
902
+ pub const IID = & IID_Value ;
903
+
904
+ pub fn VTable (comptime T : type ) type {
905
+ return extern struct {
906
+ EnumInstances : * const fn (
907
+ self : * T ,
908
+ ppEnumInstances : ** IEnumSetupInstances , // [out]
909
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
910
+ GetInstanceForCurrentProcess : * const fn (
911
+ self : * T ,
912
+ ppInstance : ** ISetupInstance , // [out]
913
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
914
+ GetInstanceForPath : * const fn (
915
+ self : * T ,
916
+ wzPath : windows.LPCWSTR , // [in]
917
+ ppInstance : ** ISetupInstance , // [out]
918
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
749
919
};
750
- if ( stat . kind != .file )
751
- return error . MsvcLibDirNotFound ;
920
+ }
921
+ } ;
752
922
753
- return full_path ;
923
+ const IEnumSetupInstances = extern struct {
924
+ vtable : * extern struct {
925
+ unknown : IUnknown .VTable (IEnumSetupInstances ),
926
+ enum_setup_instances : VTable (IEnumSetupInstances ),
927
+ },
928
+
929
+ const IID_Value = windows .GUID .parse ("{6380bcff-41d3-4b2e-8b2e-bf8a6810c848}" );
930
+ pub const IID = & IID_Value ;
931
+
932
+ pub fn VTable (comptime T : type ) type {
933
+ return extern struct {
934
+ /// Returns S_OK if the number of elements were fetched,
935
+ /// S_FALSE if nothing was fetched (at end of enumeration),
936
+ /// E_INVALIDARG if `celt` is greater than 1 and pceltFetched is NULL,
937
+ /// or E_OUTOFMEMORY if an ISetupInstance could not be allocated.
938
+ Next : * const fn (
939
+ self : * T ,
940
+ /// The number of product instances to retrieve
941
+ celt : windows.ULONG , // [in]
942
+ /// A pointer to an array of ISetupInstance
943
+ rgelt : ** ISetupInstance , // [out]
944
+ /// A pointer to the number of product instances retrieved.
945
+ /// If `celt` is 1 this paramter may be NULL
946
+ pceltFetched : ? * windows.ULONG ,
947
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
948
+ Skip : * const fn (
949
+ self : * T ,
950
+ /// The number of product instances to skip
951
+ celt : windows.ULONG , // [in]
952
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
953
+ Reset : * const fn (
954
+ self : * T ,
955
+ ) callconv (windows .WINAPI ) void ,
956
+ Clone : * const fn (
957
+ self : * T ,
958
+ ppenum : ** IEnumSetupInstances , // [out]
959
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
960
+ };
754
961
}
755
962
};
963
+
964
+ const ISetupInstance = extern struct {
965
+ vtable : * extern struct {
966
+ unknown : IUnknown .VTable (ISetupInstance ),
967
+ setup_instance : VTable (ISetupInstance ),
968
+ },
969
+
970
+ const IID_Value = windows .GUID .parse ("{b41463c3-8866-43b5-bc33-2b0676f7f42e}" );
971
+ pub const IID = & IID_Value ;
972
+
973
+ pub fn VTable (comptime T : type ) type {
974
+ return extern struct {
975
+ GetInstanceId : * const fn (
976
+ self : * T ,
977
+ pbstrInstanceId : * windows.BSTR , // [out]
978
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
979
+ GetInstallDate : * const fn (
980
+ self : * T ,
981
+ pInstallDate : * windows.FILETIME , // [out]
982
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
983
+ GetInstallationName : * const fn (
984
+ self : * T ,
985
+ pbstrInstallationName : * windows.BSTR , // [out]
986
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
987
+ GetInstallationPath : * const fn (
988
+ self : * T ,
989
+ pbstrInstallationPath : * windows.BSTR , // [out]
990
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
991
+ GetInstallationVersion : * const fn (
992
+ self : * T ,
993
+ pbstrInstallationVersion : * windows.BSTR , // [out]
994
+ ) callconv (windows .WINAPI ) windows.HRESULT ,
995
+ GetDisplayName : * anyopaque ,
996
+ GetDescription : * anyopaque ,
997
+ ResolvePath : * anyopaque ,
998
+ };
999
+ }
1000
+ };
1001
+
1002
+ const SetupConfiguration = extern struct {
1003
+ const CLSID_Value = windows .GUID .parse ("{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}" );
1004
+ pub const CLSID = & CLSID_Value ;
1005
+ };
1006
+
1007
+ extern "ole32" fn CoCreateInstance (
1008
+ rclsid : ? * const windows.GUID , // [in]
1009
+ pUnkOuter : ? * IUnknown , // [in]
1010
+ dwClsContext : windows.DWORD , // [in]
1011
+ riid : ? * const windows.GUID , // [in]
1012
+ ppv : ** anyopaque , // [out]
1013
+ ) callconv (windows .WINAPI ) windows .HRESULT ;
1014
+
1015
+ extern "oleaut32" fn SysFreeString (bstrString : ? windows.BSTR ) callconv (windows .WINAPI ) void ;
1016
+
1017
+ const CLSCTX = struct {
1018
+ const INPROC_SERVER = 0x1 ;
1019
+ const INPROC_HANDLER = 0x2 ;
1020
+ };
0 commit comments