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;
24962496pub const PVOID = *anyopaque;
24972497pub const PWSTR = [*:0]WCHAR;
24982498pub const PCWSTR = [*:0]const WCHAR;
2499/// Allocated by SysAllocString, freed by SysFreeString
2500pub const BSTR = [*:0]WCHAR;
24992501pub const SIZE_T = usize;
25002502pub const UINT = c_uint;
25012503pub const ULONG_PTR = usize;
......@@ -3253,6 +3255,7 @@ pub const KF_FLAG_SIMPLE_IDLIST = 256;
32533255pub const KF_FLAG_ALIAS_ONLY = -2147483648;
32543256
32553257pub const S_OK = 0;
3258pub const S_FALSE = 0x00000001;
32563259pub const E_NOTIMPL = @as(c_long, @bitCast(@as(c_ulong, 0x80004001)));
32573260pub const E_NOINTERFACE = @as(c_long, @bitCast(@as(c_ulong, 0x80004002)));
32583261pub const E_POINTER = @as(c_long, @bitCast(@as(c_ulong, 0x80004003)));
......@@ -3563,15 +3566,11 @@ pub const RTL_RUN_ONCE = extern struct {
35633566
35643567pub const RTL_RUN_ONCE_INIT = RTL_RUN_ONCE{ .Ptr = null };
35653568
3566pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
3567pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
3568pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
3569pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
3570pub const COINIT = enum(c_int) {
3571 COINIT_APARTMENTTHREADED = 2,
3572 COINIT_MULTITHREADED = 0,
3573 COINIT_DISABLE_OLE1DDE = 4,
3574 COINIT_SPEED_OVER_MEMORY = 8,
3569pub const COINIT = struct {
3570 pub const APARTMENTTHREADED = 2;
3571 pub const MULTITHREADED = 0;
3572 pub const DISABLE_OLE1DDE = 4;
3573 pub const SPEED_OVER_MEMORY = 8;
35753574};
35763575
35773576pub const MEMORY_BASIC_INFORMATION = extern struct {
lib/std/os/windows/ole32.zig+1
......@@ -8,4 +8,5 @@ const HRESULT = windows.HRESULT;
88pub extern "ole32" fn CoTaskMemFree(pv: LPVOID) callconv(WINAPI) void;
99pub extern "ole32" fn CoUninitialize() callconv(WINAPI) void;
1010pub extern "ole32" fn CoGetCurrentProcess() callconv(WINAPI) DWORD;
11pub extern "ole32" fn CoInitialize(pvReserved: ?LPVOID) callconv(WINAPI) HRESULT;
1112pub 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 {
550550
551551 const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
552552 error.MsvcLibDirNotFound => null,
553 error.PathTooLong => null,
554553 error.OutOfMemory => return error.OutOfMemory,
555554 };
556555 errdefer allocator.free(msvc_lib_dir);
......@@ -576,6 +575,146 @@ pub const ZigWindowsSDK = struct {
576575};
577576
578577const 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
579718 // 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
580719 fn findViaRegistry(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 {
581720
......@@ -661,6 +800,10 @@ const MsvcLibDir = struct {
661800 };
662801 errdefer allocator.free(msvc_dir);
663802
803 if (!verifyLibDir(msvc_dir)) {
804 return error.PathNotFound;
805 }
806
664807 return msvc_dir;
665808 }
666809
......@@ -720,36 +863,213 @@ const MsvcLibDir = struct {
720863 };
721864 try base_path.appendSlice(folder_with_arch);
722865
866 if (!verifyLibDir(base_path.items)) {
867 return error.PathNotFound;
868 }
869
723870 const full_path = try base_path.toOwnedSlice();
724871 return full_path;
725872 }
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
727887 /// Find path to MSVC's `lib/` directory.
728888 /// 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) {
889 pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, MsvcLibDirNotFound }![]const u8 {
890 const full_path = MsvcLibDir.findViaCOM(allocator) catch |err1| switch (err1) {
731891 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) {
733893 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 },
735898 },
736899 };
737900 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) {
741 error.NameTooLong => return error.PathTooLong,
742 else => return error.MsvcLibDirNotFound,
902 return full_path;
903 }
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,
743925 };
744 defer dir.close();
926 }
927};
745928
746 const stat = dir.statFile("vcruntime.lib") catch |err| switch (err) {
747 error.NameTooLong => return error.PathTooLong,
748 else => return error.MsvcLibDirNotFound,
929const ISetupConfiguration = extern struct {
930 vtable: *extern struct {
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,
749953 };
750 if (stat.kind != .file)
751 return error.MsvcLibDirNotFound;
954 }
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 };
7541033 }
7551034};
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};