authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-07-29 09:47:39-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-07-29 09:47:39-07:00
logb8dda2dbe1d6685e8d190cf0608eedf507819e6c
tree1cf9ba30841e937d6691d8bcbe91b705c08c46f7
parent796927b900ad4b774c30d1cb545d606f23040d2f
parent41aaf1e6e8afc72b0541860577631a440083d4c2
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16594 from squeek502/windows-sdk-com

windows_sdk.zig: Reinstate COM ISetupEnumInstances logic

3 files changed, 345 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+336-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,146 @@ pub const ZigWindowsSDK = struct {...@@ -576,6 +575,146 @@ 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 setup_helper: *ISetupHelper = undefined;
606 switch (setup_config.vtable.unknown.QueryInterface(
607 setup_config,
608 ISetupHelper.IID,
609 @ptrCast(&setup_helper),
610 )) {
611 windows.S_OK => {},
612 else => return error.PathNotFound,
613 }
614
615 var all_instances: *IEnumSetupInstances = undefined;
616 switch (setup_config.vtable.setup_configuration.EnumInstances(setup_config, &all_instances)) {
617 windows.S_OK => {},
618 windows.E_OUTOFMEMORY => return error.OutOfMemory,
619 else => return error.PathNotFound,
620 }
621 defer _ = all_instances.vtable.unknown.Release(all_instances);
622
623 var latest_version: windows.ULONGLONG = 0;
624 var latest_version_lib_dir: ?[]const u8 = null;
625 while (true) {
626 var cur: *ISetupInstance = undefined;
627 switch (all_instances.vtable.enum_setup_instances.Next(all_instances, 1, &cur, null)) {
628 windows.S_OK => {},
629 windows.S_FALSE => break,
630 windows.E_OUTOFMEMORY => return error.OutOfMemory,
631 else => return error.PathNotFound,
632 }
633 defer _ = cur.vtable.unknown.Release(cur);
634
635 var installation_version_bstr: windows.BSTR = undefined;
636 switch (cur.vtable.setup_instance.GetInstallationVersion(cur, &installation_version_bstr)) {
637 windows.S_OK => {},
638 windows.E_OUTOFMEMORY => return error.OutOfMemory,
639 else => continue,
640 }
641 defer SysFreeString(installation_version_bstr);
642
643 var parsed_version: windows.ULONGLONG = undefined;
644 switch (setup_helper.vtable.setup_helper.ParseVersion(setup_helper, installation_version_bstr, &parsed_version)) {
645 windows.S_OK => {},
646 else => continue,
647 }
648
649 // We want to end up with the most recent version installed
650 if (parsed_version <= latest_version) continue;
651
652 var installation_path_bstr: windows.BSTR = undefined;
653 switch (cur.vtable.setup_instance.GetInstallationPath(cur, &installation_path_bstr)) {
654 windows.S_OK => {},
655 windows.E_OUTOFMEMORY => return error.OutOfMemory,
656 else => continue,
657 }
658 defer SysFreeString(installation_path_bstr);
659
660 const installation_path_w = std.mem.span(installation_path_bstr);
661 const lib_dir_path = libDirFromInstallationPath(allocator, installation_path_w) catch |err| switch (err) {
662 error.OutOfMemory => |e| return e,
663 error.PathNotFound => continue,
664 };
665 errdefer allocator.free(lib_dir_path);
666
667 if (latest_version_lib_dir) |prev_lib_dir| {
668 allocator.free(prev_lib_dir);
669 }
670 latest_version_lib_dir = lib_dir_path;
671 latest_version = parsed_version;
672 }
673
674 return latest_version_lib_dir orelse error.PathNotFound;
675 }
676
677 fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path_w: []const u16) error{ OutOfMemory, PathNotFound }![]const u8 {
678 // Each UTF-16LE code unit may be expanded to 3 UTF-8 bytes.
679 var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path_w.len * 3);
680 errdefer lib_dir_buf.deinit();
681
682 lib_dir_buf.items.len = std.unicode.utf16leToUtf8(lib_dir_buf.unusedCapacitySlice(), installation_path_w) catch {
683 return error.PathNotFound;
684 };
685
686 if (!std.fs.path.isSep(lib_dir_buf.getLast())) {
687 try lib_dir_buf.append('\\');
688 }
689 const installation_path_with_trailing_sep_len = lib_dir_buf.items.len;
690
691 try lib_dir_buf.appendSlice("VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
692 var default_tools_version_buf: [512]u8 = undefined;
693 const default_tools_version_contents = std.fs.cwd().readFile(lib_dir_buf.items, &default_tools_version_buf) catch {
694 return error.PathNotFound;
695 };
696 var tokenizer = std.mem.tokenizeAny(u8, default_tools_version_contents, " \r\n");
697 const default_tools_version = tokenizer.next() orelse return error.PathNotFound;
698
699 lib_dir_buf.shrinkRetainingCapacity(installation_path_with_trailing_sep_len);
700 try lib_dir_buf.appendSlice("VC\\Tools\\MSVC\\");
701 try lib_dir_buf.appendSlice(default_tools_version);
702 const folder_with_arch = "\\Lib\\" ++ comptime switch (builtin.target.cpu.arch) {
703 .x86 => "x86",
704 .x86_64 => "x64",
705 .arm, .armeb => "arm",
706 .aarch64 => "arm64",
707 else => |tag| @compileError("MSVC lib dir cannot be detected on architecture " ++ tag),
708 };
709 try lib_dir_buf.appendSlice(folder_with_arch);
710
711 if (!verifyLibDir(lib_dir_buf.items)) {
712 return error.PathNotFound;
713 }
714
715 return lib_dir_buf.toOwnedSlice();
716 }
717
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-instance718 // 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 {719 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
581720
...@@ -661,6 +800,10 @@ const MsvcLibDir = struct {...@@ -661,6 +800,10 @@ const MsvcLibDir = struct {
661 };800 };
662 errdefer allocator.free(msvc_dir);801 errdefer allocator.free(msvc_dir);
663802
803 if (!verifyLibDir(msvc_dir)) {
804 return error.PathNotFound;
805 }
806
664 return msvc_dir;807 return msvc_dir;
665 }808 }
666809
...@@ -720,36 +863,213 @@ const MsvcLibDir = struct {...@@ -720,36 +863,213 @@ const MsvcLibDir = struct {
720 };863 };
721 try base_path.appendSlice(folder_with_arch);864 try base_path.appendSlice(folder_with_arch);
722865
866 if (!verifyLibDir(base_path.items)) {
867 return error.PathNotFound;
868 }
869
723 const full_path = try base_path.toOwnedSlice();870 const full_path = try base_path.toOwnedSlice();
724 return full_path;871 return full_path;
725 }872 }
726873
874 fn verifyLibDir(lib_dir_path: []const u8) bool {
875 std.debug.assert(std.fs.path.isAbsolute(lib_dir_path)); // should be already handled in `findVia*`
876
877 var dir = std.fs.openDirAbsolute(lib_dir_path, .{}) catch return false;
878 defer dir.close();
879
880 const stat = dir.statFile("vcruntime.lib") catch return false;
881 if (stat.kind != .file)
882 return false;
883
884 return true;
885 }
886
727 /// Find path to MSVC's `lib/` directory.887 /// Find path to MSVC's `lib/` directory.
728 /// Caller owns the result.888 /// Caller owns the result.
729 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound, PathTooLong }![]const u8 {889 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
730 const full_path = MsvcLibDir.findViaRegistry(allocator) catch |err1| switch (err1) {890 const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
731 error.OutOfMemory => return error.OutOfMemory,891 error.OutOfMemory => return error.OutOfMemory,
732 error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err2| switch (err2) {892 error.PathNotFound => MsvcLibDir.findViaRegistry(allocator) catch |err2| switch (err2) {
733 error.OutOfMemory => return error.OutOfMemory,893 error.OutOfMemory => return error.OutOfMemory,
734 error.PathNotFound => return error.MsvcLibDirNotFound,894 error.PathNotFound => MsvcLibDir.findViaVs7Key(allocator) catch |err3| switch (err3) {
895 error.OutOfMemory => return error.OutOfMemory,
896 error.PathNotFound => return error.MsvcLibDirNotFound,
897 },
735 },898 },
736 };899 };
737 errdefer allocator.free(full_path);900 errdefer allocator.free(full_path);
738 std.debug.assert(std.fs.path.isAbsolute(full_path)); // should be already handled in `findVia*`
739901
740 var dir = std.fs.openDirAbsolute(full_path, .{}) catch |err| switch (err) {902 return full_path;
741 error.NameTooLong => return error.PathTooLong,903 }
742 else => return error.MsvcLibDirNotFound,904};
905
906const IUnknown = extern struct {
907 vtable: *VTable(IUnknown),
908
909 const IID_Value = windows.GUID.parse("{00000000-0000-0000-c000-000000000046}");
910 pub const IID = &IID_Value;
911
912 pub fn VTable(comptime T: type) type {
913 return extern struct {
914 QueryInterface: *const fn (
915 self: *T,
916 riid: ?*const windows.GUID,
917 ppvObject: ?*?*anyopaque,
918 ) callconv(windows.WINAPI) windows.HRESULT,
919 AddRef: *const fn (
920 self: *T,
921 ) callconv(windows.WINAPI) u32,
922 Release: *const fn (
923 self: *T,
924 ) callconv(windows.WINAPI) u32,
743 };925 };
744 defer dir.close();926 }
927};
745928
746 const stat = dir.statFile("vcruntime.lib") catch |err| switch (err) {929const ISetupConfiguration = extern struct {
747 error.NameTooLong => return error.PathTooLong,930 vtable: *extern struct {
748 else => return error.MsvcLibDirNotFound,931 unknown: IUnknown.VTable(ISetupConfiguration),
932 setup_configuration: VTable(ISetupConfiguration),
933 },
934
935 const IID_Value = windows.GUID.parse("{42843719-db4c-46c2-8e7c-64f1816efd5b}");
936 pub const IID = &IID_Value;
937
938 pub fn VTable(comptime T: type) type {
939 return extern struct {
940 EnumInstances: *const fn (
941 self: *T,
942 ppEnumInstances: **IEnumSetupInstances, // [out]
943 ) callconv(windows.WINAPI) windows.HRESULT,
944 GetInstanceForCurrentProcess: *const fn (
945 self: *T,
946 ppInstance: **ISetupInstance, // [out]
947 ) callconv(windows.WINAPI) windows.HRESULT,
948 GetInstanceForPath: *const fn (
949 self: *T,
950 wzPath: windows.LPCWSTR, // [in]
951 ppInstance: **ISetupInstance, // [out]
952 ) callconv(windows.WINAPI) windows.HRESULT,
749 };953 };
750 if (stat.kind != .file)954 }
751 return error.MsvcLibDirNotFound;955};
752956
753 return full_path;957const IEnumSetupInstances = extern struct {
958 vtable: *extern struct {
959 unknown: IUnknown.VTable(IEnumSetupInstances),
960 enum_setup_instances: VTable(IEnumSetupInstances),
961 },
962
963 const IID_Value = windows.GUID.parse("{6380bcff-41d3-4b2e-8b2e-bf8a6810c848}");
964 pub const IID = &IID_Value;
965
966 pub fn VTable(comptime T: type) type {
967 return extern struct {
968 /// Returns S_OK if the number of elements were fetched,
969 /// S_FALSE if nothing was fetched (at end of enumeration),
970 /// E_INVALIDARG if `celt` is greater than 1 and pceltFetched is NULL,
971 /// or E_OUTOFMEMORY if an ISetupInstance could not be allocated.
972 Next: *const fn (
973 self: *T,
974 /// The number of product instances to retrieve
975 celt: windows.ULONG, // [in]
976 /// A pointer to an array of ISetupInstance
977 rgelt: **ISetupInstance, // [out]
978 /// A pointer to the number of product instances retrieved.
979 /// If `celt` is 1 this paramter may be NULL
980 pceltFetched: ?*windows.ULONG,
981 ) callconv(windows.WINAPI) windows.HRESULT,
982 Skip: *const fn (
983 self: *T,
984 /// The number of product instances to skip
985 celt: windows.ULONG, // [in]
986 ) callconv(windows.WINAPI) windows.HRESULT,
987 Reset: *const fn (
988 self: *T,
989 ) callconv(windows.WINAPI) void,
990 Clone: *const fn (
991 self: *T,
992 ppenum: **IEnumSetupInstances, // [out]
993 ) callconv(windows.WINAPI) windows.HRESULT,
994 };
995 }
996};
997
998const ISetupInstance = extern struct {
999 vtable: *extern struct {
1000 unknown: IUnknown.VTable(ISetupInstance),
1001 setup_instance: VTable(ISetupInstance),
1002 },
1003
1004 const IID_Value = windows.GUID.parse("{b41463c3-8866-43b5-bc33-2b0676f7f42e}");
1005 pub const IID = &IID_Value;
1006
1007 pub fn VTable(comptime T: type) type {
1008 return extern struct {
1009 GetInstanceId: *const fn (
1010 self: *T,
1011 pbstrInstanceId: *windows.BSTR, // [out]
1012 ) callconv(windows.WINAPI) windows.HRESULT,
1013 GetInstallDate: *const fn (
1014 self: *T,
1015 pInstallDate: *windows.FILETIME, // [out]
1016 ) callconv(windows.WINAPI) windows.HRESULT,
1017 GetInstallationName: *const fn (
1018 self: *T,
1019 pbstrInstallationName: *windows.BSTR, // [out]
1020 ) callconv(windows.WINAPI) windows.HRESULT,
1021 GetInstallationPath: *const fn (
1022 self: *T,
1023 pbstrInstallationPath: *windows.BSTR, // [out]
1024 ) callconv(windows.WINAPI) windows.HRESULT,
1025 GetInstallationVersion: *const fn (
1026 self: *T,
1027 pbstrInstallationVersion: *windows.BSTR, // [out]
1028 ) callconv(windows.WINAPI) windows.HRESULT,
1029 GetDisplayName: *anyopaque,
1030 GetDescription: *anyopaque,
1031 ResolvePath: *anyopaque,
1032 };
754 }1033 }
755};1034};
1035
1036const ISetupHelper = extern struct {
1037 vtable: *extern struct {
1038 unknown: IUnknown.VTable(ISetupHelper),
1039 setup_helper: VTable(ISetupHelper),
1040 },
1041
1042 const IID_Value = windows.GUID.parse("{42b21b78-6192-463e-87bf-d577838f1d5c}");
1043 pub const IID = &IID_Value;
1044
1045 pub fn VTable(comptime T: type) type {
1046 return extern struct {
1047 ParseVersion: *const fn (
1048 self: *T,
1049 pwszVersion: windows.BSTR, // [in]
1050 pullVersion: *windows.ULONGLONG, // [out]
1051 ) callconv(windows.WINAPI) windows.HRESULT,
1052 ParseVersionRange: *anyopaque,
1053 };
1054 }
1055};
1056
1057const SetupConfiguration = extern struct {
1058 const CLSID_Value = windows.GUID.parse("{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}");
1059 pub const CLSID = &CLSID_Value;
1060};
1061
1062extern "ole32" fn CoCreateInstance(
1063 rclsid: ?*const windows.GUID, // [in]
1064 pUnkOuter: ?*IUnknown, // [in]
1065 dwClsContext: windows.DWORD, // [in]
1066 riid: ?*const windows.GUID, // [in]
1067 ppv: **anyopaque, // [out]
1068) callconv(windows.WINAPI) windows.HRESULT;
1069
1070extern "oleaut32" fn SysFreeString(bstrString: ?windows.BSTR) callconv(windows.WINAPI) void;
1071
1072const CLSCTX = struct {
1073 const INPROC_SERVER = 0x1;
1074 const INPROC_HANDLER = 0x2;
1075};