authorgravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2023-07-28 13:00:04-07:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2023-07-28 19:19:32-07:00
log8579f720b045222dfb42d451552528f164740cd2
tree4309c7d3adf9002cb8441bd511adea5c55d3f434
parent282cb5ee5d75b4de2f215479b0c5531750908608

windows_sdk.zig: Reinstate COM ISetupEnumInstances logic

The C++ version of this code used this logic, and it turns out it is able to find some setups that the current registry/Vs7 methods cannot. For example, if only the "Build Tools for Visual Studio" are installed but not Visual Studio itself, then only the ISetupEnumInstances method seems to find it. Follow up to #15657, fixes a regression caused by moving from the C++ version to the Zig version

3 files changed, 290 insertions(+), 25 deletions(-)

lib/std/os/windows.zig+8-9
...@@ -2496,6 +2496,8 @@ pub const LPCWSTR = [*:0]const WCHAR;...@@ -2496,6 +2496,8 @@ pub const LPCWSTR = [*:0]const WCHAR;
2496pub const PVOID = *anyopaque;2496pub const PVOID = *anyopaque;
2497pub const PWSTR = [*:0]WCHAR;2497pub const PWSTR = [*:0]WCHAR;
2498pub const PCWSTR = [*:0]const WCHAR;2498pub const PCWSTR = [*:0]const WCHAR;
2499/// Allocated by SysAllocString, freed by SysFreeString
2500pub const BSTR = [*:0]WCHAR;
2499pub const SIZE_T = usize;2501pub const SIZE_T = usize;
2500pub const UINT = c_uint;2502pub const UINT = c_uint;
2501pub const ULONG_PTR = usize;2503pub const ULONG_PTR = usize;
...@@ -3253,6 +3255,7 @@ pub const KF_FLAG_SIMPLE_IDLIST = 256;...@@ -3253,6 +3255,7 @@ pub const KF_FLAG_SIMPLE_IDLIST = 256;
3253pub const KF_FLAG_ALIAS_ONLY = -2147483648;3255pub const KF_FLAG_ALIAS_ONLY = -2147483648;
32543256
3255pub const S_OK = 0;3257pub const S_OK = 0;
3258pub const S_FALSE = 0x00000001;
3256pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));3259pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
3257pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));3260pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
3258pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));3261pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
...@@ -3563,15 +3566,11 @@ pub const RTL_RUN_ONCE = extern struct {...@@ -3563,15 +3566,11 @@ pub const RTL_RUN_ONCE = extern struct {
35633566
3564pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };3567pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
35653568
3566pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;3569pub const COINIT = struct {
3567pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;3570 pub const APARTMENTTHREADED = 2;
3568pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;3571 pub const MULTITHREADED = 0;
3569pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;3572 pub const DISABLE_OLE1DDE = 4;
3570pub const COINIT = enum(c_int) {3573 pub const SPEED_OVER_MEMORY = 8;
3571 COINIT_APARTMENTTHREADED = 2,
3572 COINIT_MULTITHREADED = 0,
3573 COINIT_DISABLE_OLE1DDE = 4,
3574 COINIT_SPEED_OVER_MEMORY = 8,
3575};3574};
35763575
3577pub const MEMORY_BASIC_INFORMATION = extern struct {3576pub const MEMORY_BASIC_INFORMATION = extern struct {
lib/std/os/windows/ole32.zig+1
...@@ -8,4 +8,5 @@ const HRESULT = windows.HRESULT;...@@ -8,4 +8,5 @@ const HRESULT = windows.HRESULT;
8pub extern "ole32" fn CoTaskMemFree(pv: LPVOID) callconv(WINAPI) void;8pub extern "ole32" fn CoTaskMemFree(pv: LPVOID) callconv(WINAPI) void;
9pub extern "ole32" fn CoUninitialize() callconv(WINAPI) void;9pub extern "ole32" fn CoUninitialize() callconv(WINAPI) void;
10pub extern "ole32" fn CoGetCurrentProcess() callconv(WINAPI) DWORD;10pub extern "ole32" fn CoGetCurrentProcess() callconv(WINAPI) DWORD;
11pub extern "ole32" fn CoInitialize(pvReserved: ?LPVOID) callconv(WINAPI) HRESULT;
11pub extern "ole32" fn CoInitializeEx(pvReserved: ?LPVOID, dwCoInit: DWORD) callconv(WINAPI) HRESULT;12pub extern "ole32" fn CoInitializeEx(pvReserved: ?LPVOID, dwCoInit: DWORD) callconv(WINAPI) HRESULT;
src/windows_sdk.zig+281-16
...@@ -550,7 +550,6 @@ pub const ZigWindowsSDK = struct {...@@ -550,7 +550,6 @@ pub const ZigWindowsSDK = struct {
550550
551 const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {551 const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
552 error.MsvcLibDirNotFound => null,552 error.MsvcLibDirNotFound => null,
553 error.PathTooLong => null,
554 error.OutOfMemory => return error.OutOfMemory,553 error.OutOfMemory => return error.OutOfMemory,
555 };554 };
556 errdefer allocator.free(msvc_lib_dir);555 errdefer allocator.free(msvc_lib_dir);
...@@ -576,6 +575,112 @@ pub const ZigWindowsSDK = struct {...@@ -576,6 +575,112 @@ pub const ZigWindowsSDK = struct {
576};575};
577576
578const MsvcLibDir = struct {577const 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 // https://learn.microsoft.com/en-us/visualstudio/install/tools-for-managing-visual-studio-instances?view=vs-2022#editing-the-registry-for-a-visual-studio-instance684 // 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 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {685 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
581686
...@@ -661,6 +766,10 @@ const MsvcLibDir = struct {...@@ -661,6 +766,10 @@ const MsvcLibDir = struct {
661 };766 };
662 errdefer allocator.free(msvc_dir);767 errdefer allocator.free(msvc_dir);
663768
769 if (!verifyLibDir(msvc_dir)) {
770 return error.PathNotFound;
771 }
772
664 return msvc_dir;773 return msvc_dir;
665 }774 }
666775
...@@ -720,36 +829,192 @@ const MsvcLibDir = struct {...@@ -720,36 +829,192 @@ const MsvcLibDir = struct {
720 };829 };
721 try base_path.appendSlice(folder_with_arch);830 try base_path.appendSlice(folder_with_arch);
722831
832 if (!verifyLibDir(base_path.items)) {
833 return error.PathNotFound;
834 }
835
723 const full_path = try base_path.toOwnedSlice();836 const full_path = try base_path.toOwnedSlice();
724 return full_path;837 return full_path;
725 }838 }
726839
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 /// Find path to MSVC's `lib/` directory.853 /// Find path to MSVC's `lib/` directory.
728 /// Caller owns the result.854 /// Caller owns the result.
729 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound, PathTooLong }![]const u8 {855 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
730 const full_path = MsvcLibDir.findViaRegistry(allocator) catch |err1| switch (err1) {856 const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
731 error.OutOfMemory => return error.OutOfMemory,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 error.OutOfMemory => return error.OutOfMemory,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 errdefer allocator.free(full_path);866 errdefer allocator.free(full_path);
738 std.debug.assert(std.fs.path.isAbsolute(full_path)); // should be already handled in `findVia*`
739867
740 var dir = std.fs.openDirAbsolute(full_path, .{}) catch |err| switch (err) {868 return full_path;
741 error.NameTooLong => return error.PathTooLong,869 }
742 else => return error.MsvcLibDirNotFound,870};
871
872const 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};
745894
746 const stat = dir.statFile("vcruntime.lib") catch |err| switch (err) {895const ISetupConfiguration = extern struct {
747 error.NameTooLong => return error.PathTooLong,896 vtable: *extern struct {
748 else => return error.MsvcLibDirNotFound,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)920 }
751 return error.MsvcLibDirNotFound;921};
752922
753 return full_path;923const 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
964const 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
1002const SetupConfiguration = extern struct {
1003 const CLSID_Value = windows.GUID.parse("{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}");
1004 pub const CLSID = &CLSID_Value;
1005};
1006
1007extern "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
1015extern "oleaut32" fn SysFreeString(bstrString: ?windows.BSTR) callconv(windows.WINAPI) void;
1016
1017const CLSCTX = struct {
1018 const INPROC_SERVER = 0x1;
1019 const INPROC_HANDLER = 0x2;
1020};