| ... | ... | @@ -88,7 +88,7 @@ const RegistryUtf8 = struct { |
| 88 | 88 | key: windows.HKEY, |
| 89 | 89 | |
| 90 | 90 | /// Assert that `key` is valid UTF-8 string |
| 91 | | pub fn openKey(key: []const u8) error{KeyNotFound}!RegistryUtf8 { |
| 91 | pub fn openKey(hkey: windows.HKEY, key: []const u8) error{KeyNotFound}!RegistryUtf8 { |
| 92 | 92 | const key_utf16le: [:0]const u16 = key_utf16le: { |
| 93 | 93 | var key_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined; |
| 94 | 94 | const key_utf16le_len: usize = std.unicode.utf8ToUtf16Le(key_utf16le_buf[0..], key) catch |err| switch (err) { |
| ... | ... | @@ -98,7 +98,7 @@ const RegistryUtf8 = struct { |
| 98 | 98 | break :key_utf16le key_utf16le_buf[0..key_utf16le_len :0]; |
| 99 | 99 | }; |
| 100 | 100 | |
| 101 | | const registry_utf16le = try RegistryUtf16Le.openKey(key_utf16le); |
| 101 | const registry_utf16le = try RegistryUtf16Le.openKey(hkey, key_utf16le); |
| 102 | 102 | return RegistryUtf8{ .key = registry_utf16le.key }; |
| 103 | 103 | } |
| 104 | 104 | |
| ... | ... | @@ -191,10 +191,10 @@ const RegistryUtf16Le = struct { |
| 191 | 191 | /// Under HKEY_LOCAL_MACHINE with flags: |
| 192 | 192 | /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS. |
| 193 | 193 | /// After finishing work, call `closeKey`. |
| 194 | | fn openKey(key_utf16le: [:0]const u16) error{KeyNotFound}!RegistryUtf16Le { |
| 194 | fn openKey(hkey: windows.HKEY, key_utf16le: [:0]const u16) error{KeyNotFound}!RegistryUtf16Le { |
| 195 | 195 | var key: windows.HKEY = undefined; |
| 196 | 196 | const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW( |
| 197 | | windows.HKEY_LOCAL_MACHINE, |
| 197 | hkey, |
| 198 | 198 | key_utf16le, |
| 199 | 199 | 0, |
| 200 | 200 | windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS, |
| ... | ... | @@ -352,7 +352,7 @@ pub const Windows10Sdk = struct { |
| 352 | 352 | /// Caller owns the result's fields. |
| 353 | 353 | /// After finishing work, call `free(allocator)`. |
| 354 | 354 | fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk { |
| 355 | | const v10_key = RegistryUtf8.openKey("SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) { |
| 355 | const v10_key = RegistryUtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows\\v10.0") catch |err| switch (err) { |
| 356 | 356 | error.KeyNotFound => return error.Windows10SdkNotFound, |
| 357 | 357 | }; |
| 358 | 358 | defer v10_key.closeKey(); |
| ... | ... | @@ -417,7 +417,7 @@ pub const Windows10Sdk = struct { |
| 417 | 417 | error.NoSpaceLeft => return false, |
| 418 | 418 | }; |
| 419 | 419 | |
| 420 | | const options_key = RegistryUtf8.openKey(reg_query_as_utf8) catch |err| switch (err) { |
| 420 | const options_key = RegistryUtf8.openKey(windows.HKEY_LOCAL_MACHINE, reg_query_as_utf8) catch |err| switch (err) { |
| 421 | 421 | error.KeyNotFound => return false, |
| 422 | 422 | }; |
| 423 | 423 | defer options_key.closeKey(); |
| ... | ... | @@ -523,7 +523,7 @@ pub const ZigWindowsSDK = struct { |
| 523 | 523 | if (builtin.os.tag != .windows) return error.NotFound; |
| 524 | 524 | |
| 525 | 525 | //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed |
| 526 | | const roots_key = RegistryUtf8.openKey(WINDOWS_KIT_REG_KEY) catch |err| switch (err) { |
| 526 | const roots_key = RegistryUtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) { |
| 527 | 527 | error.KeyNotFound => return error.NotFound, |
| 528 | 528 | }; |
| 529 | 529 | defer roots_key.closeKey(); |
| ... | ... | @@ -581,113 +581,169 @@ pub const ZigWindowsSDK = struct { |
| 581 | 581 | }; |
| 582 | 582 | |
| 583 | 583 | const MsvcLibDir = struct { |
| 584 | | // https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration |
| 585 | | fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 { |
| 586 | | switch (windows.ole32.CoInitializeEx(null, windows.COINIT.MULTITHREADED)) { |
| 587 | | windows.S_OK, windows.S_FALSE => {}, |
| 588 | | windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 589 | | else => return error.PathNotFound, |
| 590 | | } |
| 591 | | // > To close the COM library gracefully on a thread, each successful |
| 592 | | // > call to CoInitialize or CoInitializeEx, including any call that |
| 593 | | // > returns S_FALSE, must be balanced by a corresponding call to CoUninitialize. |
| 594 | | // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-coinitializeex |
| 595 | | defer windows.ole32.CoUninitialize(); |
| 596 | | |
| 597 | | var setup_config: *ISetupConfiguration = undefined; |
| 598 | | switch (CoCreateInstance( |
| 599 | | SetupConfiguration.CLSID, |
| 600 | | null, |
| 601 | | CLSCTX.INPROC_SERVER | CLSCTX.INPROC_HANDLER, |
| 602 | | ISetupConfiguration.IID, |
| 603 | | @ptrCast(&setup_config), |
| 604 | | )) { |
| 605 | | windows.S_OK => {}, |
| 606 | | windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 607 | | else => return error.PathNotFound, |
| 608 | | } |
| 609 | | defer _ = setup_config.vtable.unknown.Release(setup_config); |
| 610 | | |
| 611 | | var setup_helper: *ISetupHelper = undefined; |
| 612 | | switch (setup_config.vtable.unknown.QueryInterface( |
| 613 | | setup_config, |
| 614 | | ISetupHelper.IID, |
| 615 | | @ptrCast(&setup_helper), |
| 616 | | )) { |
| 617 | | windows.S_OK => {}, |
| 618 | | else => return error.PathNotFound, |
| 619 | | } |
| 584 | fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { |
| 585 | const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"; |
| 586 | const setup_config_key = RegistryUtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) { |
| 587 | error.KeyNotFound => return error.PathNotFound, |
| 588 | }; |
| 589 | defer setup_config_key.closeKey(); |
| 620 | 590 | |
| 621 | | var all_instances: *IEnumSetupInstances = undefined; |
| 622 | | switch (setup_config.vtable.setup_configuration.EnumInstances(setup_config, &all_instances)) { |
| 623 | | windows.S_OK => {}, |
| 624 | | windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 625 | | else => return error.PathNotFound, |
| 626 | | } |
| 627 | | defer _ = all_instances.vtable.unknown.Release(all_instances); |
| 628 | | |
| 629 | | var latest_version: windows.ULONGLONG = 0; |
| 630 | | var latest_version_lib_dir: ?[]const u8 = null; |
| 631 | | while (true) { |
| 632 | | var cur: *ISetupInstance = undefined; |
| 633 | | switch (all_instances.vtable.enum_setup_instances.Next(all_instances, 1, &cur, null)) { |
| 634 | | windows.S_OK => {}, |
| 635 | | windows.S_FALSE => break, |
| 636 | | windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 637 | | else => return error.PathNotFound, |
| 638 | | } |
| 639 | | defer _ = cur.vtable.unknown.Release(cur); |
| 591 | const dll_path = setup_config_key.getString(allocator, "InprocServer32", "") catch |err| switch (err) { |
| 592 | error.NotAString, |
| 593 | error.ValueNameNotFound, |
| 594 | error.StringNotFound, |
| 595 | => return error.PathNotFound, |
| 640 | 596 | |
| 641 | | var installation_version_bstr: windows.BSTR = undefined; |
| 642 | | switch (cur.vtable.setup_instance.GetInstallationVersion(cur, &installation_version_bstr)) { |
| 643 | | windows.S_OK => {}, |
| 644 | | windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 645 | | else => continue, |
| 646 | | } |
| 647 | | defer SysFreeString(installation_version_bstr); |
| 597 | error.OutOfMemory => return error.OutOfMemory, |
| 598 | }; |
| 599 | defer allocator.free(dll_path); |
| 648 | 600 | |
| 649 | | var parsed_version: windows.ULONGLONG = undefined; |
| 650 | | switch (setup_helper.vtable.setup_helper.ParseVersion(setup_helper, installation_version_bstr, &parsed_version)) { |
| 651 | | windows.S_OK => {}, |
| 652 | | else => continue, |
| 601 | var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound; |
| 602 | // the .dll filename |
| 603 | _ = path_it.last(); |
| 604 | const root_path = while (path_it.previous()) |dir_component| { |
| 605 | if (std.ascii.eqlIgnoreCase(dir_component.name, "VisualStudio")) { |
| 606 | break dir_component.path; |
| 653 | 607 | } |
| 608 | } else { |
| 609 | return error.PathNotFound; |
| 610 | }; |
| 611 | |
| 612 | const instances_path = try std.fs.path.join(allocator, &.{ root_path, "Packages", "_Instances" }); |
| 613 | defer allocator.free(instances_path); |
| 614 | |
| 615 | return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return error.PathNotFound; |
| 616 | } |
| 617 | |
| 618 | fn findInstancesDir(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { |
| 619 | // First try to get the path from the .dll that would have been |
| 620 | // loaded via COM for SetupConfiguration. |
| 621 | return findInstancesDirViaCLSID(allocator) catch |orig_err| { |
| 622 | // If that can't be found, fall back to manually appending |
| 623 | // `Microsoft\VisualStudio\Packages\_Instances` to %PROGRAMDATA% |
| 624 | const program_data = std.process.getEnvVarOwned(allocator, "PROGRAMDATA") catch |err| switch (err) { |
| 625 | error.OutOfMemory => |e| return e, |
| 626 | else => return orig_err, |
| 627 | }; |
| 628 | defer allocator.free(program_data); |
| 629 | |
| 630 | const instances_path = try std.fs.path.join(allocator, &.{ program_data, "Microsoft", "VisualStudio", "Packages", "_Instances" }); |
| 631 | defer allocator.free(instances_path); |
| 632 | |
| 633 | return std.fs.openDirAbsolute(instances_path, .{ .iterate = true }) catch return orig_err; |
| 634 | }; |
| 635 | } |
| 636 | |
| 637 | /// Intended to be equivalent to `ISetupHelper.ParseVersion` |
| 638 | /// Example: 17.4.33205.214 -> 0x0011000481b500d6 |
| 639 | fn parseVersionQuad(version: []const u8) error{InvalidVersion}!u64 { |
| 640 | var it = std.mem.splitScalar(u8, version, '.'); |
| 641 | const a = it.next() orelse return error.InvalidVersion; |
| 642 | const b = it.next() orelse return error.InvalidVersion; |
| 643 | const c = it.next() orelse return error.InvalidVersion; |
| 644 | const d = it.next() orelse return error.InvalidVersion; |
| 645 | if (it.next()) |_| return error.InvalidVersion; |
| 646 | var result: u64 = undefined; |
| 647 | var result_bytes = std.mem.asBytes(&result); |
| 648 | |
| 649 | std.mem.writeInt( |
| 650 | u16, |
| 651 | result_bytes[0..2], |
| 652 | std.fmt.parseUnsigned(u16, d, 10) catch return error.InvalidVersion, |
| 653 | .little, |
| 654 | ); |
| 655 | std.mem.writeInt( |
| 656 | u16, |
| 657 | result_bytes[2..4], |
| 658 | std.fmt.parseUnsigned(u16, c, 10) catch return error.InvalidVersion, |
| 659 | .little, |
| 660 | ); |
| 661 | std.mem.writeInt( |
| 662 | u16, |
| 663 | result_bytes[4..6], |
| 664 | std.fmt.parseUnsigned(u16, b, 10) catch return error.InvalidVersion, |
| 665 | .little, |
| 666 | ); |
| 667 | std.mem.writeInt( |
| 668 | u16, |
| 669 | result_bytes[6..8], |
| 670 | std.fmt.parseUnsigned(u16, a, 10) catch return error.InvalidVersion, |
| 671 | .little, |
| 672 | ); |
| 673 | |
| 674 | return result; |
| 675 | } |
| 676 | |
| 677 | /// Intended to be equivalent to ISetupConfiguration.EnumInstances: |
| 678 | /// https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration |
| 679 | /// but without the use of COM in order to avoid a dependency on ole32.dll |
| 680 | /// |
| 681 | /// The logic in this function is intended to match what ISetupConfiguration does |
| 682 | /// under-the-hood, as verified using Procmon. |
| 683 | fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 { |
| 684 | // Typically `%PROGRAMDATA%\Microsoft\VisualStudio\Packages\_Instances` |
| 685 | // This will contain directories with names of instance IDs like 80a758ca, |
| 686 | // which will contain `state.json` files that have the version and |
| 687 | // installation directory. |
| 688 | var instances_dir = try findInstancesDir(allocator); |
| 689 | defer instances_dir.close(); |
| 690 | |
| 691 | var state_subpath_buf: [std.fs.MAX_NAME_BYTES + 32]u8 = undefined; |
| 692 | var latest_version_lib_dir = std.ArrayListUnmanaged(u8){}; |
| 693 | errdefer latest_version_lib_dir.deinit(allocator); |
| 694 | |
| 695 | var latest_version: u64 = 0; |
| 696 | var instances_dir_it = instances_dir.iterateAssumeFirstIteration(); |
| 697 | while (instances_dir_it.next() catch return error.PathNotFound) |entry| { |
| 698 | if (entry.kind != .directory) continue; |
| 699 | |
| 700 | var fbs = std.io.fixedBufferStream(&state_subpath_buf); |
| 701 | const writer = fbs.writer(); |
| 702 | |
| 703 | writer.writeAll(entry.name) catch unreachable; |
| 704 | writer.writeByte(std.fs.path.sep) catch unreachable; |
| 705 | writer.writeAll("state.json") catch unreachable; |
| 706 | |
| 707 | const json_contents = instances_dir.readFileAlloc(allocator, fbs.getWritten(), std.math.maxInt(usize)) catch continue; |
| 708 | defer allocator.free(json_contents); |
| 709 | |
| 710 | var parsed = std.json.parseFromSlice(std.json.Value, allocator, json_contents, .{}) catch continue; |
| 711 | defer parsed.deinit(); |
| 712 | |
| 713 | if (parsed.value != .object) continue; |
| 714 | const catalog_info = parsed.value.object.get("catalogInfo") orelse continue; |
| 715 | if (catalog_info != .object) continue; |
| 716 | const product_version_value = catalog_info.object.get("buildVersion") orelse continue; |
| 717 | if (product_version_value != .string) continue; |
| 718 | const product_version_text = product_version_value.string; |
| 719 | const parsed_version = parseVersionQuad(product_version_text) catch continue; |
| 654 | 720 | |
| 655 | 721 | // We want to end up with the most recent version installed |
| 656 | 722 | if (parsed_version <= latest_version) continue; |
| 657 | 723 | |
| 658 | | var installation_path_bstr: windows.BSTR = undefined; |
| 659 | | switch (cur.vtable.setup_instance.GetInstallationPath(cur, &installation_path_bstr)) { |
| 660 | | windows.S_OK => {}, |
| 661 | | windows.E_OUTOFMEMORY => return error.OutOfMemory, |
| 662 | | else => continue, |
| 663 | | } |
| 664 | | defer SysFreeString(installation_path_bstr); |
| 724 | const installation_path = parsed.value.object.get("installationPath") orelse continue; |
| 725 | if (installation_path != .string) continue; |
| 665 | 726 | |
| 666 | | const installation_path_w = std.mem.span(installation_path_bstr); |
| 667 | | const lib_dir_path = libDirFromInstallationPath(allocator, installation_path_w) catch |err| switch (err) { |
| 727 | const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string) catch |err| switch (err) { |
| 668 | 728 | error.OutOfMemory => |e| return e, |
| 669 | 729 | error.PathNotFound => continue, |
| 670 | 730 | }; |
| 671 | | errdefer allocator.free(lib_dir_path); |
| 731 | defer allocator.free(lib_dir_path); |
| 672 | 732 | |
| 673 | | if (latest_version_lib_dir) |prev_lib_dir| { |
| 674 | | allocator.free(prev_lib_dir); |
| 675 | | } |
| 676 | | latest_version_lib_dir = lib_dir_path; |
| 733 | latest_version_lib_dir.clearRetainingCapacity(); |
| 734 | try latest_version_lib_dir.appendSlice(allocator, lib_dir_path); |
| 677 | 735 | latest_version = parsed_version; |
| 678 | 736 | } |
| 679 | 737 | |
| 680 | | return latest_version_lib_dir orelse error.PathNotFound; |
| 738 | if (latest_version_lib_dir.items.len == 0) return error.PathNotFound; |
| 739 | return latest_version_lib_dir.toOwnedSlice(allocator); |
| 681 | 740 | } |
| 682 | 741 | |
| 683 | | fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path_w: []const u16) error{ OutOfMemory, PathNotFound }![]const u8 { |
| 684 | | // Each UTF-16LE code unit may be expanded to 3 UTF-8 bytes. |
| 685 | | var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path_w.len * 3); |
| 742 | fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8) error{ OutOfMemory, PathNotFound }![]const u8 { |
| 743 | var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path.len + 64); |
| 686 | 744 | errdefer lib_dir_buf.deinit(); |
| 687 | 745 | |
| 688 | | lib_dir_buf.items.len = std.unicode.utf16leToUtf8(lib_dir_buf.unusedCapacitySlice(), installation_path_w) catch { |
| 689 | | return error.PathNotFound; |
| 690 | | }; |
| 746 | lib_dir_buf.appendSliceAssumeCapacity(installation_path); |
| 691 | 747 | |
| 692 | 748 | if (!std.fs.path.isSep(lib_dir_buf.getLast())) { |
| 693 | 749 | try lib_dir_buf.append('\\'); |
| ... | ... | @@ -838,7 +894,7 @@ const MsvcLibDir = struct { |
| 838 | 894 | } |
| 839 | 895 | } |
| 840 | 896 | |
| 841 | | const vs7_key = RegistryUtf8.openKey("SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound; |
| 897 | const vs7_key = RegistryUtf8.openKey(windows.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7") catch return error.PathNotFound; |
| 842 | 898 | defer vs7_key.closeKey(); |
| 843 | 899 | try_vs7_key: { |
| 844 | 900 | const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) { |
| ... | ... | @@ -910,174 +966,3 @@ const MsvcLibDir = struct { |
| 910 | 966 | return full_path; |
| 911 | 967 | } |
| 912 | 968 | }; |
| 913 | | |
| 914 | | const IUnknown = extern struct { |
| 915 | | vtable: *VTable(IUnknown), |
| 916 | | |
| 917 | | const IID_Value = windows.GUID.parse("{00000000-0000-0000-c000-000000000046}"); |
| 918 | | pub const IID = &IID_Value; |
| 919 | | |
| 920 | | pub fn VTable(comptime T: type) type { |
| 921 | | return extern struct { |
| 922 | | QueryInterface: *const fn ( |
| 923 | | self: *T, |
| 924 | | riid: ?*const windows.GUID, |
| 925 | | ppvObject: ?*?*anyopaque, |
| 926 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 927 | | AddRef: *const fn ( |
| 928 | | self: *T, |
| 929 | | ) callconv(windows.WINAPI) u32, |
| 930 | | Release: *const fn ( |
| 931 | | self: *T, |
| 932 | | ) callconv(windows.WINAPI) u32, |
| 933 | | }; |
| 934 | | } |
| 935 | | }; |
| 936 | | |
| 937 | | const ISetupConfiguration = extern struct { |
| 938 | | vtable: *extern struct { |
| 939 | | unknown: IUnknown.VTable(ISetupConfiguration), |
| 940 | | setup_configuration: VTable(ISetupConfiguration), |
| 941 | | }, |
| 942 | | |
| 943 | | const IID_Value = windows.GUID.parse("{42843719-db4c-46c2-8e7c-64f1816efd5b}"); |
| 944 | | pub const IID = &IID_Value; |
| 945 | | |
| 946 | | pub fn VTable(comptime T: type) type { |
| 947 | | return extern struct { |
| 948 | | EnumInstances: *const fn ( |
| 949 | | self: *T, |
| 950 | | ppEnumInstances: **IEnumSetupInstances, // [out] |
| 951 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 952 | | GetInstanceForCurrentProcess: *const fn ( |
| 953 | | self: *T, |
| 954 | | ppInstance: **ISetupInstance, // [out] |
| 955 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 956 | | GetInstanceForPath: *const fn ( |
| 957 | | self: *T, |
| 958 | | wzPath: windows.LPCWSTR, // [in] |
| 959 | | ppInstance: **ISetupInstance, // [out] |
| 960 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 961 | | }; |
| 962 | | } |
| 963 | | }; |
| 964 | | |
| 965 | | const IEnumSetupInstances = extern struct { |
| 966 | | vtable: *extern struct { |
| 967 | | unknown: IUnknown.VTable(IEnumSetupInstances), |
| 968 | | enum_setup_instances: VTable(IEnumSetupInstances), |
| 969 | | }, |
| 970 | | |
| 971 | | const IID_Value = windows.GUID.parse("{6380bcff-41d3-4b2e-8b2e-bf8a6810c848}"); |
| 972 | | pub const IID = &IID_Value; |
| 973 | | |
| 974 | | pub fn VTable(comptime T: type) type { |
| 975 | | return extern struct { |
| 976 | | /// Returns S_OK if the number of elements were fetched, |
| 977 | | /// S_FALSE if nothing was fetched (at end of enumeration), |
| 978 | | /// E_INVALIDARG if `celt` is greater than 1 and pceltFetched is NULL, |
| 979 | | /// or E_OUTOFMEMORY if an ISetupInstance could not be allocated. |
| 980 | | Next: *const fn ( |
| 981 | | self: *T, |
| 982 | | /// The number of product instances to retrieve |
| 983 | | celt: windows.ULONG, // [in] |
| 984 | | /// A pointer to an array of ISetupInstance |
| 985 | | rgelt: **ISetupInstance, // [out] |
| 986 | | /// A pointer to the number of product instances retrieved. |
| 987 | | /// If `celt` is 1 this paramter may be NULL |
| 988 | | pceltFetched: ?*windows.ULONG, |
| 989 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 990 | | Skip: *const fn ( |
| 991 | | self: *T, |
| 992 | | /// The number of product instances to skip |
| 993 | | celt: windows.ULONG, // [in] |
| 994 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 995 | | Reset: *const fn ( |
| 996 | | self: *T, |
| 997 | | ) callconv(windows.WINAPI) void, |
| 998 | | Clone: *const fn ( |
| 999 | | self: *T, |
| 1000 | | ppenum: **IEnumSetupInstances, // [out] |
| 1001 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1002 | | }; |
| 1003 | | } |
| 1004 | | }; |
| 1005 | | |
| 1006 | | const ISetupInstance = extern struct { |
| 1007 | | vtable: *extern struct { |
| 1008 | | unknown: IUnknown.VTable(ISetupInstance), |
| 1009 | | setup_instance: VTable(ISetupInstance), |
| 1010 | | }, |
| 1011 | | |
| 1012 | | const IID_Value = windows.GUID.parse("{b41463c3-8866-43b5-bc33-2b0676f7f42e}"); |
| 1013 | | pub const IID = &IID_Value; |
| 1014 | | |
| 1015 | | pub fn VTable(comptime T: type) type { |
| 1016 | | return extern struct { |
| 1017 | | GetInstanceId: *const fn ( |
| 1018 | | self: *T, |
| 1019 | | pbstrInstanceId: *windows.BSTR, // [out] |
| 1020 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1021 | | GetInstallDate: *const fn ( |
| 1022 | | self: *T, |
| 1023 | | pInstallDate: *windows.FILETIME, // [out] |
| 1024 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1025 | | GetInstallationName: *const fn ( |
| 1026 | | self: *T, |
| 1027 | | pbstrInstallationName: *windows.BSTR, // [out] |
| 1028 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1029 | | GetInstallationPath: *const fn ( |
| 1030 | | self: *T, |
| 1031 | | pbstrInstallationPath: *windows.BSTR, // [out] |
| 1032 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1033 | | GetInstallationVersion: *const fn ( |
| 1034 | | self: *T, |
| 1035 | | pbstrInstallationVersion: *windows.BSTR, // [out] |
| 1036 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1037 | | GetDisplayName: *anyopaque, |
| 1038 | | GetDescription: *anyopaque, |
| 1039 | | ResolvePath: *anyopaque, |
| 1040 | | }; |
| 1041 | | } |
| 1042 | | }; |
| 1043 | | |
| 1044 | | const ISetupHelper = extern struct { |
| 1045 | | vtable: *extern struct { |
| 1046 | | unknown: IUnknown.VTable(ISetupHelper), |
| 1047 | | setup_helper: VTable(ISetupHelper), |
| 1048 | | }, |
| 1049 | | |
| 1050 | | const IID_Value = windows.GUID.parse("{42b21b78-6192-463e-87bf-d577838f1d5c}"); |
| 1051 | | pub const IID = &IID_Value; |
| 1052 | | |
| 1053 | | pub fn VTable(comptime T: type) type { |
| 1054 | | return extern struct { |
| 1055 | | ParseVersion: *const fn ( |
| 1056 | | self: *T, |
| 1057 | | pwszVersion: windows.BSTR, // [in] |
| 1058 | | pullVersion: *windows.ULONGLONG, // [out] |
| 1059 | | ) callconv(windows.WINAPI) windows.HRESULT, |
| 1060 | | ParseVersionRange: *anyopaque, |
| 1061 | | }; |
| 1062 | | } |
| 1063 | | }; |
| 1064 | | |
| 1065 | | const SetupConfiguration = extern struct { |
| 1066 | | const CLSID_Value = windows.GUID.parse("{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"); |
| 1067 | | pub const CLSID = &CLSID_Value; |
| 1068 | | }; |
| 1069 | | |
| 1070 | | extern "ole32" fn CoCreateInstance( |
| 1071 | | rclsid: ?*const windows.GUID, // [in] |
| 1072 | | pUnkOuter: ?*IUnknown, // [in] |
| 1073 | | dwClsContext: windows.DWORD, // [in] |
| 1074 | | riid: ?*const windows.GUID, // [in] |
| 1075 | | ppv: **anyopaque, // [out] |
| 1076 | | ) callconv(windows.WINAPI) windows.HRESULT; |
| 1077 | | |
| 1078 | | extern "oleaut32" fn SysFreeString(bstrString: ?windows.BSTR) callconv(windows.WINAPI) void; |
| 1079 | | |
| 1080 | | const CLSCTX = struct { |
| 1081 | | const INPROC_SERVER = 0x1; |
| 1082 | | const INPROC_HANDLER = 0x2; |
| 1083 | | }; |