From 41aaf1e6e8afc72b0541860577631a440083d4c2 Mon Sep 17 00:00:00 2001 From: Ryan Liptak Date: Fri, 28 Jul 2023 23:36:04 -0700 Subject: [PATCH] windows_sdk: Get the latest installed version when using COM Before, iteration would stop whenever an installation with vcruntime.lib was found, but that may not be the most recent installed version. Instead, we now iterate all installed instances and choose the one with the newest version. --- src/windows_sdk.zig | 59 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 2 deletions(-) diff --git a/src/windows_sdk.zig b/src/windows_sdk.zig index ecd0350483..85c10226bd 100644 --- a/src/windows_sdk.zig +++ b/src/windows_sdk.zig @@ -602,6 +602,16 @@ const MsvcLibDir = struct { } defer _ = setup_config.vtable.unknown.Release(setup_config); + var setup_helper: *ISetupHelper = undefined; + switch (setup_config.vtable.unknown.QueryInterface( + setup_config, + ISetupHelper.IID, + @ptrCast(&setup_helper), + )) { + windows.S_OK => {}, + else => return error.PathNotFound, + } + var all_instances: *IEnumSetupInstances = undefined; switch (setup_config.vtable.setup_configuration.EnumInstances(setup_config, &all_instances)) { windows.S_OK => {}, @@ -610,6 +620,8 @@ const MsvcLibDir = struct { } defer _ = all_instances.vtable.unknown.Release(all_instances); + var latest_version: windows.ULONGLONG = 0; + var latest_version_lib_dir: ?[]const u8 = null; while (true) { var cur: *ISetupInstance = undefined; switch (all_instances.vtable.enum_setup_instances.Next(all_instances, 1, &cur, null)) { @@ -620,6 +632,23 @@ const MsvcLibDir = struct { } defer _ = cur.vtable.unknown.Release(cur); + var installation_version_bstr: windows.BSTR = undefined; + switch (cur.vtable.setup_instance.GetInstallationVersion(cur, &installation_version_bstr)) { + windows.S_OK => {}, + windows.E_OUTOFMEMORY => return error.OutOfMemory, + else => continue, + } + defer SysFreeString(installation_version_bstr); + + var parsed_version: windows.ULONGLONG = undefined; + switch (setup_helper.vtable.setup_helper.ParseVersion(setup_helper, installation_version_bstr, &parsed_version)) { + windows.S_OK => {}, + else => continue, + } + + // We want to end up with the most recent version installed + if (parsed_version <= latest_version) continue; + var installation_path_bstr: windows.BSTR = undefined; switch (cur.vtable.setup_instance.GetInstallationPath(cur, &installation_path_bstr)) { windows.S_OK => {}, @@ -635,9 +664,14 @@ const MsvcLibDir = struct { }; errdefer allocator.free(lib_dir_path); - return lib_dir_path; + if (latest_version_lib_dir) |prev_lib_dir| { + allocator.free(prev_lib_dir); + } + latest_version_lib_dir = lib_dir_path; + latest_version = parsed_version; } - return error.PathNotFound; + + return latest_version_lib_dir orelse error.PathNotFound; } fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path_w: []const u16) error{ OutOfMemory, PathNotFound }![]const u8 { @@ -999,6 +1033,27 @@ const ISetupInstance = extern struct { } }; +const ISetupHelper = extern struct { + vtable: *extern struct { + unknown: IUnknown.VTable(ISetupHelper), + setup_helper: VTable(ISetupHelper), + }, + + const IID_Value = windows.GUID.parse("{42b21b78-6192-463e-87bf-d577838f1d5c}"); + pub const IID = &IID_Value; + + pub fn VTable(comptime T: type) type { + return extern struct { + ParseVersion: *const fn ( + self: *T, + pwszVersion: windows.BSTR, // [in] + pullVersion: *windows.ULONGLONG, // [out] + ) callconv(windows.WINAPI) windows.HRESULT, + ParseVersionRange: *anyopaque, + }; + } +}; + const SetupConfiguration = extern struct { const CLSID_Value = windows.GUID.parse("{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"); pub const CLSID = &CLSID_Value;