| ... | @@ -88,7 +88,7 @@ const RegistryUtf8 = struct { | ... | @@ -88,7 +88,7 @@ const RegistryUtf8 = struct { |
| 88 | key: windows.HKEY, | 88 | key: windows.HKEY, |
| 89 | | 89 | |
| 90 | /// Assert that `key` is valid UTF-8 string | 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 | const key_utf16le: [:0]const u16 = key_utf16le: { | 92 | const key_utf16le: [:0]const u16 = key_utf16le: { |
| 93 | var key_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined; | 93 | var key_utf16le_buf: [RegistryUtf16Le.key_name_max_len]u16 = undefined; |
| 94 | const key_utf16le_len: usize = std.unicode.utf8ToUtf16Le(key_utf16le_buf[0..], key) catch |err| switch (err) { | 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,7 +98,7 @@ const RegistryUtf8 = struct { |
| 98 | break :key_utf16le key_utf16le_buf[0..key_utf16le_len :0]; | 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 | return RegistryUtf8{ .key = registry_utf16le.key }; | 102 | return RegistryUtf8{ .key = registry_utf16le.key }; |
| 103 | } | 103 | } |
| 104 | | 104 | |
| ... | @@ -191,10 +191,10 @@ const RegistryUtf16Le = struct { | ... | @@ -191,10 +191,10 @@ const RegistryUtf16Le = struct { |
| 191 | /// Under HKEY_LOCAL_MACHINE with flags: | 191 | /// Under HKEY_LOCAL_MACHINE with flags: |
| 192 | /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS. | 192 | /// KEY_QUERY_VALUE, KEY_WOW64_32KEY, and KEY_ENUMERATE_SUB_KEYS. |
| 193 | /// After finishing work, call `closeKey`. | 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 | var key: windows.HKEY = undefined; | 195 | var key: windows.HKEY = undefined; |
| 196 | const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW( | 196 | const return_code_int: windows.HRESULT = windows.advapi32.RegOpenKeyExW( |
| 197 | windows.HKEY_LOCAL_MACHINE, | 197 | hkey, |
| 198 | key_utf16le, | 198 | key_utf16le, |
| 199 | 0, | 199 | 0, |
| 200 | windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS, | 200 | windows.KEY_QUERY_VALUE | windows.KEY_WOW64_32KEY | windows.KEY_ENUMERATE_SUB_KEYS, |
| ... | @@ -352,7 +352,7 @@ pub const Windows10Sdk = struct { | ... | @@ -352,7 +352,7 @@ pub const Windows10Sdk = struct { |
| 352 | /// Caller owns the result's fields. | 352 | /// Caller owns the result's fields. |
| 353 | /// After finishing work, call `free(allocator)`. | 353 | /// After finishing work, call `free(allocator)`. |
| 354 | fn find(allocator: std.mem.Allocator) error{ OutOfMemory, Windows10SdkNotFound, PathTooLong, VersionTooLong }!Windows10Sdk { | 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 | error.KeyNotFound => return error.Windows10SdkNotFound, | 356 | error.KeyNotFound => return error.Windows10SdkNotFound, |
| 357 | }; | 357 | }; |
| 358 | defer v10_key.closeKey(); | 358 | defer v10_key.closeKey(); |
| ... | @@ -417,7 +417,7 @@ pub const Windows10Sdk = struct { | ... | @@ -417,7 +417,7 @@ pub const Windows10Sdk = struct { |
| 417 | error.NoSpaceLeft => return false, | 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 | error.KeyNotFound => return false, | 421 | error.KeyNotFound => return false, |
| 422 | }; | 422 | }; |
| 423 | defer options_key.closeKey(); | 423 | defer options_key.closeKey(); |
| ... | @@ -523,7 +523,7 @@ pub const ZigWindowsSDK = struct { | ... | @@ -523,7 +523,7 @@ pub const ZigWindowsSDK = struct { |
| 523 | if (builtin.os.tag != .windows) return error.NotFound; | 523 | if (builtin.os.tag != .windows) return error.NotFound; |
| 524 | | 524 | |
| 525 | //note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed | 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 | error.KeyNotFound => return error.NotFound, | 527 | error.KeyNotFound => return error.NotFound, |
| 528 | }; | 528 | }; |
| 529 | defer roots_key.closeKey(); | 529 | defer roots_key.closeKey(); |
| ... | @@ -581,113 +581,169 @@ pub const ZigWindowsSDK = struct { | ... | @@ -581,113 +581,169 @@ pub const ZigWindowsSDK = struct { |
| 581 | }; | 581 | }; |
| 582 | | 582 | |
| 583 | const MsvcLibDir = struct { | 583 | const MsvcLibDir = struct { |
| 584 | // https://learn.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.setup.configuration | 584 | fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir { |
| 585 | fn findViaCOM(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }![]const u8 { | 585 | const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}"; |
| 586 | switch (windows.ole32.CoInitializeEx(null, windows.COINIT.MULTITHREADED)) { | 586 | const setup_config_key = RegistryUtf8.openKey(windows.HKEY_CLASSES_ROOT, "CLSID\\" ++ setup_configuration_clsid) catch |err| switch (err) { |
| 587 | windows.S_OK, windows.S_FALSE => {}, | 587 | error.KeyNotFound => return error.PathNotFound, |
| 588 | windows.E_OUTOFMEMORY => return error.OutOfMemory, | 588 | }; |
| 589 | else => return error.PathNotFound, | 589 | defer setup_config_key.closeKey(); |
| 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 | } | | |
| 620 | | 590 | |
| 621 | var all_instances: *IEnumSetupInstances = undefined; | 591 | const dll_path = setup_config_key.getString(allocator, "InprocServer32", "") catch |err| switch (err) { |
| 622 | switch (setup_config.vtable.setup_configuration.EnumInstances(setup_config, &all_instances)) { | 592 | error.NotAString, |
| 623 | windows.S_OK => {}, | 593 | error.ValueNameNotFound, |
| 624 | windows.E_OUTOFMEMORY => return error.OutOfMemory, | 594 | error.StringNotFound, |
| 625 | else => return error.PathNotFound, | 595 | => 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); | | |
| 640 | | 596 | |
| 641 | var installation_version_bstr: windows.BSTR = undefined; | 597 | error.OutOfMemory => return error.OutOfMemory, |
| 642 | switch (cur.vtable.setup_instance.GetInstallationVersion(cur, &installation_version_bstr)) { | 598 | }; |
| 643 | windows.S_OK => {}, | 599 | defer allocator.free(dll_path); |
| 644 | windows.E_OUTOFMEMORY => return error.OutOfMemory, | | |
| 645 | else => continue, | | |
| 646 | } | | |
| 647 | defer SysFreeString(installation_version_bstr); | | |
| 648 | | 600 | |
| 649 | var parsed_version: windows.ULONGLONG = undefined; | 601 | var path_it = std.fs.path.componentIterator(dll_path) catch return error.PathNotFound; |
| 650 | switch (setup_helper.vtable.setup_helper.ParseVersion(setup_helper, installation_version_bstr, &parsed_version)) { | 602 | // the .dll filename |
| 651 | windows.S_OK => {}, | 603 | _ = path_it.last(); |
| 652 | else => continue, | 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 | // We want to end up with the most recent version installed | 721 | // We want to end up with the most recent version installed |
| 656 | if (parsed_version <= latest_version) continue; | 722 | if (parsed_version <= latest_version) continue; |
| 657 | | 723 | |
| 658 | var installation_path_bstr: windows.BSTR = undefined; | 724 | const installation_path = parsed.value.object.get("installationPath") orelse continue; |
| 659 | switch (cur.vtable.setup_instance.GetInstallationPath(cur, &installation_path_bstr)) { | 725 | if (installation_path != .string) continue; |
| 660 | windows.S_OK => {}, | | |
| 661 | windows.E_OUTOFMEMORY => return error.OutOfMemory, | | |
| 662 | else => continue, | | |
| 663 | } | | |
| 664 | defer SysFreeString(installation_path_bstr); | | |
| 665 | | 726 | |
| 666 | const installation_path_w = std.mem.span(installation_path_bstr); | 727 | const lib_dir_path = libDirFromInstallationPath(allocator, installation_path.string) catch |err| switch (err) { |
| 667 | const lib_dir_path = libDirFromInstallationPath(allocator, installation_path_w) catch |err| switch (err) { | | |
| 668 | error.OutOfMemory => |e| return e, | 728 | error.OutOfMemory => |e| return e, |
| 669 | error.PathNotFound => continue, | 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| { | 733 | latest_version_lib_dir.clearRetainingCapacity(); |
| 674 | allocator.free(prev_lib_dir); | 734 | try latest_version_lib_dir.appendSlice(allocator, lib_dir_path); |
| 675 | } | | |
| 676 | latest_version_lib_dir = lib_dir_path; | | |
| 677 | latest_version = parsed_version; | 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 { | 742 | fn libDirFromInstallationPath(allocator: std.mem.Allocator, installation_path: []const u8) error{ OutOfMemory, PathNotFound }![]const u8 { |
| 684 | // Each UTF-16LE code unit may be expanded to 3 UTF-8 bytes. | 743 | var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path.len + 64); |
| 685 | var lib_dir_buf = try std.ArrayList(u8).initCapacity(allocator, installation_path_w.len * 3); | | |
| 686 | errdefer lib_dir_buf.deinit(); | 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 { | 746 | lib_dir_buf.appendSliceAssumeCapacity(installation_path); |
| 689 | return error.PathNotFound; | | |
| 690 | }; | | |
| 691 | | 747 | |
| 692 | if (!std.fs.path.isSep(lib_dir_buf.getLast())) { | 748 | if (!std.fs.path.isSep(lib_dir_buf.getLast())) { |
| 693 | try lib_dir_buf.append('\\'); | 749 | try lib_dir_buf.append('\\'); |
| ... | @@ -838,7 +894,7 @@ const MsvcLibDir = struct { | ... | @@ -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 | defer vs7_key.closeKey(); | 898 | defer vs7_key.closeKey(); |
| 843 | try_vs7_key: { | 899 | try_vs7_key: { |
| 844 | const path_maybe_with_trailing_slash = vs7_key.getString(allocator, "", "14.0") catch |err| switch (err) { | 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,174 +966,3 @@ const MsvcLibDir = struct { |
| 910 | return full_path; | 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 | }; | | |