From cb8a782e905a18e0d508fc2aed82253f7579d72a Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Mon, 7 Oct 2024 02:52:53 +0200 Subject: [PATCH 1/3] Extra cli program to print COM interface information about the current Windows version --- note-reverse-engineer-com-interfaces.md | 65 + print-com-interfaces/Cargo.lock | 1722 +++++++++++++++++++++++ print-com-interfaces/Cargo.toml | 31 + print-com-interfaces/src/main.rs | 939 ++++++++++++ 4 files changed, 2757 insertions(+) create mode 100644 note-reverse-engineer-com-interfaces.md create mode 100644 print-com-interfaces/Cargo.lock create mode 100644 print-com-interfaces/Cargo.toml create mode 100644 print-com-interfaces/src/main.rs diff --git a/note-reverse-engineer-com-interfaces.md b/note-reverse-engineer-com-interfaces.md new file mode 100644 index 0000000..e4f8468 --- /dev/null +++ b/note-reverse-engineer-com-interfaces.md @@ -0,0 +1,65 @@ +# Reverse engineering COM interfaces required for Virtual Desktop manipulation + +To manipulate virtual desktops in Windows it is required to use unstable COM interface that might change between Windows versions. This document therefore describes the process used to support new Windows versions. + +---- + +[Issue #14 ("Reverse engineering process") on the C# library Slion/VirtualDesktop](https://github.com/Slion/VirtualDesktop/issues/14) describes how they go about finding the definition for the relevant COM interfaces: + +> We need to document the reverse engineering process to make it easier to support future versions of Windows. +> +> My understanding is that you need to run a Python script from a fork of [GetVirtualDesktopAPI_DIA] that dumps the GUIDs and interfaces definitions from `twinui.pcshell.dll` using [Debug Interface Access] and [Microsoft Symbol Server]. +> +> It would be nice to port that Python script to C# and integrate it to this repository. That would make it even easier to perform reverse engineering. +> +> [GetVirtualDesktopAPI_DIA]: https://github.com/mzomparelli/GetVirtualDesktopAPI_DIA +> [Debug Interface Access]: https://learn.microsoft.com/en-us/visualstudio/debugger/debug-interface-access/debug-interface-access-sdk +> [Microsoft Symbol Server]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/microsoft-public-symbols + +The python script mentioned above has been mostly reimplemented in Rust, see the [`print-com-interfaces`](./print-com-interfaces) subdirectory. + +---- + +The [Readme for the Slion/VirtualDesktop C# library](https://github.com/Slion/VirtualDesktop/blob/7e37b9848aef681713224dae558d2e51960cf41e/README.md#windows-version-support) also has some useful information for easily finding the current interface ids: + +> ### Windows version support +> +> The class IDs of some of the undocumented interfaces we use tend to change a lot between different versions of Windows. +> If the demo application crashes on start-up chances are all you need to do is provide the proper IDs for the version of Windows you are running on. +> +> Open `regedit` and export this path into a file: `\HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface`. +> Open the resulting reg file and search it for matches against the whole word of each interface name we need: +> +> - `IApplicationView` +> - `IApplicationViewCollection` +> - `IObjectArray` +> - `IServiceProvider` +> - `IVirtualDesktop` +> - `IVirtualDesktopManager` +> - `IVirtualDesktopManagerInternal` +> - `IVirtualDesktopNotification` +> - `IVirtualDesktopNotificationService` +> - `IVirtualDesktopPinnedApps` +> +> Once you have the IDs add them in a new `setting` element in [app.config]. +> Make sure to specify the correct 5 digits Windows build version. +> You can get it using one of those methods: +> +> - From the UI run: `winver` +> - From shell run: `ver` +> - From powershell run: `cmd /c ver` +> +> Make sure to contribute back your changes. +> +> [app.config]: https://github.com/Slion/VirtualDesktop/blob/7e37b9848aef681713224dae558d2e51960cf41e/src/VirtualDesktop/app.config + +---- + +It can be worth looking at other similar virtual desktop libraries to see if they already support the Windows version in question: + +- [Slion/VirtualDesktop](https://github.com/Slion/VirtualDesktop): C# wrapper for the Virtual Desktop API on Windows 11. + - Interface ids in [src/VirtualDesktop/app.config](https://github.com/Slion/VirtualDesktop/blob/main/src/VirtualDesktop/app.config) + - Method signatures in [src/VirtualDesktop/Interop](https://github.com/Slion/VirtualDesktop/tree/main/src/VirtualDesktop/Interop) + - These are actually compiled when the app is executed by the `ComInterfaceAssemblyBuilder.CreateAssembly` method at: [src/VirtualDesktop/Interop/ComInterfaceAssemblyBuilder.cs](https://github.com/Slion/VirtualDesktop/blob/main/src/VirtualDesktop/Interop/ComInterfaceAssemblyBuilder.cs) + +- [MScholtes/VirtualDesktop](https://github.com/MScholtes/VirtualDesktop): C# command line tool to manage virtual desktops in Windows 10 diff --git a/print-com-interfaces/Cargo.lock b/print-com-interfaces/Cargo.lock new file mode 100644 index 0000000..8fa2840 --- /dev/null +++ b/print-com-interfaces/Cargo.lock @@ -0,0 +1,1722 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "anstream" +version = "0.6.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" + +[[package]] +name = "anstyle-parse" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "async-compression" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" +dependencies = [ + "flate2", + "futures-core", + "futures-io", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets 0.52.6", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" + +[[package]] +name = "cab" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171228650e6721d5acc0868a462cd864f49ac5f64e4a42cde270406e64e404d2" +dependencies = [ + "byteorder", + "flate2", + "lzxd", + "time", +] + +[[package]] +name = "cc" +version = "1.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7be5744db7978a28d9df86a214130d106a89ce49644cbc4e3f0c22c3fba30615" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5fbc17d3ef8278f55b282b2a2e75ae6f6c7d4bb70ed3d0382375104bfafdb4b" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" + +[[package]] +name = "cpp_demangle" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96e58d342ad113c2b878f16d5d034c03be492ae460cdbc02b7f0f2284d310c7d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.48.0", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "eyre" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" +dependencies = [ + "indenter", + "once_cell", +] + +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + +[[package]] +name = "flate2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs4" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c6b3bd49c37d2aa3f3f2220233b29a7cd23f79d1fe70e5337d25fb390793de" +dependencies = [ + "rustix", + "windows-sys 0.52.0", +] + +[[package]] +name = "futures-channel" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" + +[[package]] +name = "futures-io" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" + +[[package]] +name = "futures-macro" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" + +[[package]] +name = "futures-task" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" + +[[package]] +name = "futures-util" +version = "0.3.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "hyper" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "ipnet" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "js-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.159" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lzxd" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de7336a183103429ad66d11d56d8bdc9c4a2916f6b85a8f11e5b127bde12001" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +dependencies = [ + "libc", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +dependencies = [ + "hermit-abi", + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "msvc-demangler" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4c25a3bb7d880e8eceab4822f3141ad0700d20f025991c1f03bd3d00219a5fc" +dependencies = [ + "bitflags", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" +dependencies = [ + "flate2", + "memchr", + "ruzstd", +] + +[[package]] +name = "once_cell" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pdb" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82040a392923abe6279c00ab4aff62d5250d1c8555dc780e4b02783a7aa74863" +dependencies = [ + "fallible-iterator", + "scroll", + "uuid", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "portable-atomic" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "print-com-interfaces" +version = "0.1.0" +dependencies = [ + "clap", + "debugid", + "eyre", + "object", + "pdb", + "symbolic-common", + "symbolic-demangle", + "symsrv", + "tokio", + "uuid", + "windows", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" +dependencies = [ + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", +] + +[[package]] +name = "quinn-proto" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" +dependencies = [ + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", +] + +[[package]] +name = "quinn-udp" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" +dependencies = [ + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + +[[package]] +name = "reqwest" +version = "0.12.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" +dependencies = [ + "base64", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "once_cell", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "webpki-roots", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" + +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustls" +version = "0.23.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "415d9944693cb90382053259f89fbb077ea730ad7273047ec63b19bc9b160ba8" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "ruzstd" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c3938e133aac070997ddc684d4b393777d293ba170f2988c8fd5ea2ad4ce21" +dependencies = [ + "twox-hash", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04c565b551bafbef4157586fa379538366e4385d42082f255bfd96e4fe8519da" + +[[package]] +name = "serde" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.210" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.128" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "symbolic-common" +version = "12.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "366f1b4c6baf6cfefc234bbd4899535fca0b06c74443039a73f6dfb2fad88d77" +dependencies = [ + "debugid", + "memmap2", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aba05ba5b9962ea5617baf556293720a8b2d0a282aa14ee4bf10e22efc7da8c8" +dependencies = [ + "cc", + "cpp_demangle", + "msvc-demangler", + "rustc-demangle", + "symbolic-common", +] + +[[package]] +name = "symsrv" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc84816f92e67d3613ceccab0694df7b4a10514770ce5ab9c75b32de512ab53" +dependencies = [ + "async-compression", + "cab", + "dirs", + "fs4", + "futures-util", + "http", + "reqwest", + "scopeguard", + "thiserror", + "tokio", +] + +[[package]] +name = "syn" +version = "2.0.79" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "futures-core", +] + +[[package]] +name = "thiserror" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde", + "time-core", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "tinyvec" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "twox-hash" +version = "1.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +dependencies = [ + "cfg-if", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" + +[[package]] +name = "unicode-ident" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" + +[[package]] +name = "unicode-normalization" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" + +[[package]] +name = "wasm-streams" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e072d4e72f700fb3443d8fe94a39315df013eef1104903cdb0a2abd322bbecd" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/print-com-interfaces/Cargo.toml b/print-com-interfaces/Cargo.toml new file mode 100644 index 0000000..258c512 --- /dev/null +++ b/print-com-interfaces/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "print-com-interfaces" +version = "0.1.0" +edition = "2021" +publish = false +description = "Find information about Virtual Desktop COM interfaces from debug info provided by Microsoft Symbol Server" + +[dependencies] +eyre = "0.6.12" # Nicer Rust errors +tokio = { version = "1.40.0", features = ["rt-multi-thread", "macros"] } # Async runtime +clap = { version = "4.5.19", features = ["derive"] } # CLI argument parsing + +object = "0.36.4" # Parse DLL to get unique breakpad debug id associated with PDB file +debugid = "0.8.0" # Format breakpad id as string +symsrv = "0.5.3" # Download PDB file from Microsoft Symbol Server +pdb = { package = "pdb", version = "0.8" } # Parse PDB file +symbolic-demangle = "12.12.0" # Interpret symbol names +symbolic-common = "12.12.0" # Exposes types used by symbolic-demangle +uuid = "1.10.0" # Parse and print GUID interface ids +windows = { version = "0.58.0", features = [ + "Win32_System_SystemInformation", + "Wdk_System_SystemServices", + "Win32_System_Registry" +] } # Get Windows version + read registry to find IID + +# symbolic-debuginfo = "12.12.0" # Alternative to object and pdb crates (too simple API, only exposes functions) +# pdb-addr2line = "0.10.4" # Requires type information that has been removed from the PDB files we are reading. + + +# Don't make this a part of the root workspace: +[workspace] diff --git a/print-com-interfaces/src/main.rs b/print-com-interfaces/src/main.rs new file mode 100644 index 0000000..4cff7b2 --- /dev/null +++ b/print-com-interfaces/src/main.rs @@ -0,0 +1,939 @@ +use clap::Parser; +use debugid::DebugId; +use eyre::{Context, OptionExt}; +use object::{ + read::pe::{ImageNtHeaders, ImageOptionalHeader, PeFile32, PeFile64}, + Object as _, +}; +use pdb::{FallibleIterator, Rva, PDB}; +use std::{collections::HashMap, fmt, fs::File, path::PathBuf, rc::Rc, str::FromStr}; +use symbolic_demangle::Demangle as _; +use symsrv::SymsrvDownloader; +use windows::{core::Owned, Win32::System::Registry::HKEY}; + +/// This program uses [Microsoft Symbol Server] to get debug symbols for +/// `twinui.pcshell.dll` and then searches those symbols for information related +/// to the Virtual Desktop COM interfaces. +/// +/// Code was inspired by the python script at [GetVirtualDesktopAPI_DIA] +/// +/// [GetVirtualDesktopAPI_DIA]: https://github.com/mzomparelli/GetVirtualDesktopAPI_DIA +/// +/// [Microsoft Symbol Server]: https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/microsoft-public-symbols +#[derive(Debug, clap::Parser)] +struct Args { + /// Show all interface ids and show info about all virtual function tables. + /// If this is not specified then only info about COM interfaces that seem + /// relevant will be shown. + #[clap(long = "all", visible_alias = "unfiltered")] + unfiltered: bool, + + /// Specify a PeCodeId for the `twinui.pcshell.dll` file in order to + /// download a specific version of the dll file from a Microsoft Symbol + /// Server. + /// + /// Note: if the specified version already exists then nothing will be + /// downloaded. + #[clap(long)] + twinui_dll_id: Option, + + /// Specify a PeCodeId for the `actxprxy.dll` file in order to download a + /// specific version of the dll file from a Microsoft Symbol Server. + /// + /// Note: if the specified version already exists then nothing will be + /// downloaded. + #[clap(long)] + actxprxy_dll_id: Option, + + /// Don't use any information from `twinui.pcshell.dll`. + #[clap(long, conflicts_with = "twinui_dll_id")] + skip_twinui: bool, + + /// Don't use any information from `actxprxy.dll`. + #[clap(long, conflicts_with = "actxprxy_dll_id")] + skip_actxprxy: bool, + + /// Don't print the current Windows version. Implied if a DLL PeCodeId is specified. + #[clap(long)] + skip_windows_version: bool, + + /// Don't search for interface ids in the Windows registry. Implied if a DLL PeCodeId is specified. + #[clap(long)] + skip_windows_registry: bool, +} + +fn system32() -> eyre::Result { + // https://learn.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables + if let Some(found) = std::env::var_os("CSIDL_SYSTEM") { + Ok(found.into()) + } else if let Some(windows) = + std::env::var_os("WINDIR").or_else(|| std::env::var_os("SYSTEMROOT")) + { + Ok(PathBuf::from(windows).join("System32")) + } else { + // Assume it on the C drive: + Ok(PathBuf::from(r"C:\Windows\System32")) + } +} + +/// Contains virtual function tables (vftables). +fn twinui_pcshell_path() -> eyre::Result { + Ok(system32()?.join("twinui.pcshell.dll")) +} + +/// Contains IID values for private virtual desktop interfaces. +fn actxprxy_path() -> eyre::Result { + Ok(system32()?.join("actxprxy.dll")) +} + +/// Parts of known mangled names for vtables +const VIRTUAL_DESKTOP_V_TABLE_NAMES: &[&str] = &[ + "??_7CVirtualDesktop@@6BIVirtualDesktop@@@", + "??_7CVirtualDesktop@@6B?$Chain", + "??_7CVirtualDesktopManager@@6BIVirtualDesktopManagerInternal2@@@", + "??_7CVirtualDesktopNotificationsDerived@@6BIVirtualDesktopNotification@@@", + "??_7CVirtualDesktopNotificationsDerived@@6B@", + "??_7CVirtualDesktopHotkeyHandler@@6B@", + "??_7VirtualDesktopsApi@@6B@", + "??_7VirtualPinnedAppsHandler@@6B?$Chain", + "??_7ApplicationViewCollectionBase@@6B@", +]; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct WindowsVersion { + pub major_version: u32, + pub minor_version: u32, + pub build_number: u32, + pub patch_version: Option, +} +impl WindowsVersion { + /// Get the Windows patch version (the last number in the full version). + /// + /// # References + /// + /// - This is how the C# VirtualDesktop library does it: [VirtualDesktop/src/VirtualDesktop/Utils/OS.cs at 7e37b9848aef681713224dae558d2e51960cf41e ยท mzomparelli/VirtualDesktop](https://github.com/mzomparelli/VirtualDesktop/blob/7e37b9848aef681713224dae558d2e51960cf41e/src/VirtualDesktop/Utils/OS.cs#L21) + /// - We use this function: [RegGetValueW in windows::Win32::System::Registry - Rust](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Registry/fn.RegGetValueW.html) + /// - Function docs: [RegGetValueW function (winreg.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-reggetvaluew) + /// - StackOverflow usage example: [windows - RegGetValueW(), how to do it right - Stack Overflow](https://stackoverflow.com/questions/78224404/reggetvaluew-how-to-do-it-right) + /// - Info about the registry key: [.net - C# - How to show the full Windows 10 build number? - Stack Overflow](https://stackoverflow.com/questions/52041735/c-sharp-how-to-show-the-full-windows-10-build-number) + fn read_patch_version_from_registry() -> Option { + use windows::{ + core::w, + Win32::System::Registry::{RegGetValueW, HKEY_LOCAL_MACHINE, RRF_RT_REG_DWORD}, + }; + + let mut buffer: [u8; 4] = [0; 4]; + let mut cb_data = buffer.len() as u32; + let res = unsafe { + RegGetValueW( + HKEY_LOCAL_MACHINE, + w!(r#"SOFTWARE\Microsoft\Windows NT\CurrentVersion"#), + w!("UBR"), + RRF_RT_REG_DWORD, + Some(std::ptr::null_mut()), + Some(buffer.as_mut_ptr() as _), + Some(&mut cb_data as *mut u32), + ) + }; + if res.is_err() { + eprintln!( + "Failed to read Windows patch version from the registry: {:?}", + windows::core::Error::from(res.to_hresult()) + ); + return None; + } + + // REG_DWORD is signed 32-bit, using little endian + let patch_version = i32::from_le_bytes(buffer); + if patch_version < 0 { + eprintln!( + "Windows patch version read from the registry was negative \ + ({patch_version}), ignoring read value" + ); + } + u32::try_from(patch_version).ok() + } + /// Get info about the current Windows version. + /// + /// # Determining Windows Version + /// + /// We could use the [`GetVersionExW` function + /// (sysinfoapi.h)](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getversionexw), + /// but it is deprecated after Windows 8.1. It also changes behavior depending + /// on what manifest is embedded in the executable. + /// + /// That pages links to [Version Helper functions - Win32 + /// apps](https://learn.microsoft.com/en-us/windows/win32/sysinfo/version-helper-apis) + /// where we are linked to the [`IsWindowsVersionOrGreater` function + /// (versionhelpers.h)](https://learn.microsoft.com/en-us/windows/win32/api/VersionHelpers/nf-versionhelpers-iswindowsversionorgreater) + /// and the [`VerifyVersionInfoA` function + /// (winbase.h)](https://learn.microsoft.com/en-us/windows/win32/api/Winbase/nf-winbase-verifyversioninfoa) + /// that it uses internally (though the later function is deprecated in Windows + /// 10). + /// + /// We can use `RtlGetVersion` [RtlGetVersion function (wdm.h) - Windows + /// drivers](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion?redirectedfrom=MSDN) + /// as mentioned at [c++ - Detecting Windows 10 version - Stack + /// Overflow](https://stackoverflow.com/questions/36543301/detecting-windows-10-version/36545162#36545162). + /// + /// # `windows` API References + /// + /// - [GetVersionExW in windows::Win32::System::SystemInformation - + /// Rust](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/SystemInformation/fn.GetVersionExW.html) + /// - Affected by manifest. + /// - [RtlGetVersion in windows::Wdk::System::SystemServices - + /// Rust](https://microsoft.github.io/windows-docs-rs/doc/windows/Wdk/System/SystemServices/fn.RtlGetVersion.html) + /// - Always returns the correct version. + pub fn get() -> eyre::Result { + let mut version: windows::Win32::System::SystemInformation::OSVERSIONINFOW = + Default::default(); + version.dwOSVersionInfoSize = core::mem::size_of_val(&version) as u32; + unsafe { windows::Wdk::System::SystemServices::RtlGetVersion(&mut version) } + .ok() + .context("Failed to get Windows version from RtlGetVersion")?; + + let patch_version = Self::read_patch_version_from_registry(); + Ok(Self { + major_version: version.dwMajorVersion, + minor_version: version.dwMinorVersion, + build_number: version.dwBuildNumber, + patch_version, + }) + } +} +impl fmt::Display for WindowsVersion { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}.{}.{}.{}", + self.major_version, + self.minor_version, + self.build_number, + match self.patch_version { + Some(v) => v.to_string(), + None => "N/A".to_owned(), + } + ) + } +} + +#[derive(Debug, Clone)] +pub struct RegistryIIDs { + interface_key: Rc>, + index: u32, +} +impl RegistryIIDs { + /// Opens the registry directory + /// `HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface`. + /// + /// # References + /// + /// - [RegOpenKeyExW in windows::Win32::System::Registry - Rust](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Registry/fn.RegOpenKeyExW.html) + /// - [RegOpenKeyExW function (winreg.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regopenkeyexw) + /// - [Registry Key Security and Access Rights - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-key-security-and-access-rights) + pub fn open() -> eyre::Result { + use windows::{ + core::w, + Win32::System::Registry::{ + RegOpenKeyExW, HKEY_LOCAL_MACHINE, KEY_ENUMERATE_SUB_KEYS, KEY_QUERY_VALUE, + }, + }; + + let mut result = HKEY::default(); + unsafe { + RegOpenKeyExW( + HKEY_LOCAL_MACHINE, + w!(r#"SOFTWARE\Classes\Interface"#), + 0, + KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE, + &mut result, + ) + } + .ok()?; + + Ok(RegistryIIDs { + interface_key: Rc::new(unsafe { Owned::new(result) }), + index: 0, + }) + } +} +impl Iterator for RegistryIIDs { + type Item = eyre::Result<(uuid::Uuid, String)>; + + /// Get a subkey of the interface key and interpret its name as an interface + /// id and its default value as the interface name. + /// + /// # References + /// + /// - [RegEnumKeyW in windows::Win32::System::Registry - Rust](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Registry/fn.RegEnumKeyW.html) + /// - [RegEnumKeyW function (winreg.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-regenumkeyw) + /// - [Registry element size limits - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/sysinfo/registry-element-size-limits) + /// - [RegGetValueW in windows::Win32::System::Registry - Rust](https://microsoft.github.io/windows-docs-rs/doc/windows/Win32/System/Registry/fn.RegGetValueW.html) + /// - [RegGetValueW function (winreg.h) - Win32 apps | Microsoft Learn](https://learn.microsoft.com/en-us/windows/win32/api/winreg/nf-winreg-reggetvaluew) + fn next(&mut self) -> Option { + use windows::{ + core::PCWSTR, + Win32::{ + Foundation::{ERROR_FILE_NOT_FOUND, ERROR_MORE_DATA, ERROR_NO_MORE_ITEMS}, + System::Registry::{RegEnumKeyW, RegGetValueW, RRF_RT_REG_SZ}, + }, + }; + + loop { + // Key name can't be more than 255 characters long. + // Buffer that receives the name of the subkey, including the terminating null character. + // Only the name of the subkey, not the full key hierarchy. + let mut key_name = [0; 1024]; + let result = + unsafe { RegEnumKeyW(**self.interface_key, self.index, Some(&mut key_name)) }; + if result == ERROR_NO_MORE_ITEMS { + return None; + } + self.index += 1; + if let Err(e) = result.ok() { + return Some(Err(eyre::eyre!(e)).context( + "Failed to open subkey of registry key \ + HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface", + )); + } + + let key_str = match String::from_utf16( + &key_name[..key_name.iter().position(|&c| c == 0).unwrap_or_default()], + ) { + Ok(v) => v, + Err(e) => { + return Some(Err(e).context( + "Failed to convert name to utf16 for registry key in \ + HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface", + )) + } + }; + let iid = match uuid::Uuid::from_str(key_str.trim_matches(['{', '}'])) { + Ok(v) => v, + Err(e) => { + return Some(Err(e).context(format!( + "Failed to parse key name as guid for registry key \ + HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface\\{key_str}" + ))) + } + }; + + let mut heap_buffer; + let mut stack_buffer = [0_u16; 512]; + let mut value_buf = stack_buffer.as_mut_slice(); + let mut value_len: u32 = std::mem::size_of_val(value_buf) as u32; + let mut result = unsafe { + RegGetValueW( + **self.interface_key, + PCWSTR::from_raw(key_name.as_ptr()), + PCWSTR::null(), + RRF_RT_REG_SZ, + None, + Some(value_buf.as_mut_ptr() as *mut core::ffi::c_void), + Some(&mut value_len), + ) + }; + if result == ERROR_MORE_DATA { + heap_buffer = vec![ + 0_u16; + value_len as usize / core::mem::size_of::() + + (value_len % 2) as usize + ]; + value_buf = heap_buffer.as_mut_slice(); + result = unsafe { + RegGetValueW( + **self.interface_key, + PCWSTR::from_raw(key_name.as_ptr()), + PCWSTR::null(), + RRF_RT_REG_SZ, + None, + Some(value_buf.as_mut_ptr() as *mut core::ffi::c_void), + Some(&mut value_len), + ) + }; + } + if result == ERROR_FILE_NOT_FOUND { + // No value (interface name) for this key, so ignore it. + continue; + } + if let Err(e) = result.ok() { + return Some(Err(eyre::eyre!(e)).context(format!( + "Failed to read value for subkey of registry key \ + HKEY_LOCAL_MACHINE\\SOFTWARE\\Classes\\Interface\\{key_str}", + ))); + } + let value_len = value_len as usize / core::mem::size_of::() - 1; + + let value = String::from_utf16_lossy(&value_buf[..value_len]); + + return Some(Ok((iid, value))); + } + } +} + +/// The code ID for a Windows PE file. +/// +/// When combined with the binary name, the `PeCodeId` lets you obtain binaries from +/// symbol servers. It is not useful on its own, it has to be paired with the binary name. +/// +/// A Windows binary's `PeCodeId` is distinct from its debug ID (= pdb GUID + age). +/// If you have a binary file, you can get both the `PeCodeId` and the debug ID +/// from it. If you only have a PDB file, you usually *cannot* get the `PeCodeId` of +/// the corresponding binary from it. +/// +/// Note: copied from the [`wholesym`] crate. +/// +/// [`wholesym`]: https://docs.rs/samply-symbols/0.23.0/src/samply_symbols/shared.rs.html#227 +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct PeCodeId { + pub timestamp: u32, + pub image_size: u32, +} +impl PeCodeId { + /// Code from `make_library_info` function in "samply-symbols-0.23.0\src\binary_image.rs". + pub fn for_file_data(data: &[u8]) -> eyre::Result { + if let Ok(file) = PeFile64::parse(data) { + Self::from_pe_file(file) + } else { + Self::from_pe_file(PeFile32::parse(data)?) + } + } + /// Code from pe_info function in "samply-symbols-0.23.0\src\binary_image.rs" + fn from_pe_file<'buf, Pe: ImageNtHeaders>( + pe: object::read::pe::PeFile<'buf, Pe, &'buf [u8]>, + ) -> eyre::Result { + // The code identifier consists of the `time_date_stamp` field id the COFF header, followed by + // the `size_of_image` field in the optional header. If the optional PE header is not present, + // this identifier is `None`. + let header = pe.nt_headers(); + let timestamp = header + .file_header() + .time_date_stamp + .get(object::LittleEndian); + let image_size = header.optional_header().size_of_image(); + Ok(PeCodeId { + timestamp, + image_size, + }) + } +} +impl FromStr for PeCodeId { + type Err = &'static str; + + fn from_str(s: &str) -> Result { + if s.len() < 9 || s.len() > 16 { + return Err("invalid length"); + } + let timestamp = u32::from_str_radix(&s[..8], 16).map_err(|_| "invalid timestamp")?; + let image_size = u32::from_str_radix(&s[8..], 16).map_err(|_| "invalid image size")?; + Ok(Self { + timestamp, + image_size, + }) + } +} +impl std::fmt::Display for PeCodeId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_fmt(format_args!("{:08X}{:x}", self.timestamp, self.image_size)) + } +} + +/// The Portable Executable (PE) format is a file format for executables, object +/// code, DLLs. +pub struct PeFile { + /// Path where the dll file can be found. + dll_path: PathBuf, + /// Path where debug info can be found. + pdb_path: Option, +} +impl PeFile { + pub fn new(dll_path: impl Into) -> Self { + Self { + dll_path: dll_path.into(), + pdb_path: None, + } + } + /// Dll file name without file extension. + pub fn file_stem(&self) -> eyre::Result<&str> { + self.dll_path + .file_stem() + .ok_or_eyre("dll paths have file names")? + .to_str() + .ok_or_eyre("dll files have UTF8 file names") + } + /// Get a debug id that can be used to download a `.pdb` file. Use the + /// [`DebugId::breakpad`] method and then [`ToString::to_string`] that. + pub fn debug_id(&self) -> eyre::Result { + let data = std::fs::read(&self.dll_path) + .with_context(|| format!("Failed to read {}", self.dll_path.display()))?; + + let object = object::File::parse(data.as_slice())?; + + if let Ok(Some(pdb_info)) = object.pdb_info() { + // Copied code from "samply-symbols-0.23.0\src\debugid_util.rs" + Ok(DebugId::from_guid_age(&pdb_info.guid(), pdb_info.age())?) + } else { + Err(eyre::eyre!("No debug info available for object")) + } + } + /// This id can be used to download the `.dll` file. + pub fn pe_code_id(&self) -> eyre::Result { + PeCodeId::for_file_data(self.read_dll()?.as_slice()) + } + /// Ensures `dll_path` points to a DLL with the specified code id. If the + /// current DLL doesn't match the id then a new DLL will be downloaded. + /// + /// Returns `true` if a new DLL had to be downloaded. + pub async fn maybe_download_dll( + &mut self, + downloader: &SymsrvDownloader, + wanted_pe_code_id: PeCodeId, + ) -> eyre::Result { + if self.pe_code_id()? == wanted_pe_code_id { + return Ok(false); + } + let dll_name = self + .dll_path + .file_name() + .ok_or_eyre("dll paths have file names")? + .to_str() + .ok_or_eyre("dll files have UTF8 file names")?; + assert!(dll_name.to_ascii_lowercase().ends_with(".dll")); + + // Get hash: + let hash = self.pe_code_id()?.to_string(); + + // Download and cache a DLL file. + let local_path = downloader.get_file(dll_name, &hash).await?; + + // At this point we don't want to use the DLL inside + // C:/Windows/System32, instead we want to use the newly downloaded DLL + // next to the executable: + self.dll_path = local_path; + + Ok(true) + } + /// Download and cache `.pdb` debug symbol file. + pub async fn download_pdb(&mut self, downloader: &SymsrvDownloader) -> eyre::Result<()> { + let pdb_name = self.dll_path.with_extension("pdb"); + let pdb_name = pdb_name + .file_name() + .ok_or_eyre("dll paths have file names")? + .to_str() + .ok_or_eyre("dll files have UTF8 file names")?; + assert!(pdb_name.to_ascii_lowercase().ends_with(".pdb")); + + // Get hash: + let hash = self.debug_id()?.breakpad().to_string(); + + // Download and cache a PDB file. + let local_path = downloader.get_file(pdb_name, &hash).await?; + self.pdb_path = Some(local_path); + Ok(()) + } + pub fn open_pdb(&self) -> eyre::Result> { + let file = std::fs::File::open( + self.pdb_path + .as_deref() + .ok_or_eyre("Haven't downloaded pdb file yet")?, + )?; + Ok(pdb::PDB::open(file)?) + } + pub fn read_dll(&self) -> eyre::Result> { + std::fs::read(&self.dll_path) + .with_context(|| format!("Failed to read DLL file at: {}", self.dll_path.display())) + } +} + +fn setup_download_next_to_exe() -> SymsrvDownloader { + // Parse the _NT_SYMBOL_PATH environment variable. + let symbol_path_env = symsrv::get_symbol_path_from_environment(); + let symbol_path = symbol_path_env + .as_deref() + .unwrap_or("srv**https://msdl.microsoft.com/download/symbols"); + let parsed_symbol_path = symsrv::parse_nt_symbol_path(symbol_path); + + // Create a downloader which follows the _NT_SYMBOL_PATH recipe. + let mut downloader = SymsrvDownloader::new(parsed_symbol_path); + downloader.set_default_downstream_store( + // Download files next to the executable: + std::env::current_exe() + .ok() + .and_then(|exe| exe.parent().map(|exe_dir| exe_dir.join("Symbols"))) + .or_else(symsrv::get_home_sym_dir), + ); + downloader +} + +#[derive(Debug, Default, Clone, Copy)] +struct AddressInfo { + size: u32, + rva: Rva, +} + +type SymbolWithSize<'sym> = (Option, pdb::Symbol<'sym>); + +/// Inspired by [`symbolic_debuginfo::SymbolMap::from_iter`], assumes that a +/// symbol occupies all space until the next symbol. +fn calculate_size_for_symbols( + symbols: &mut [SymbolWithSize<'_>], + address_map: &pdb::AddressMap<'_>, +) { + let mut symbols = symbols + .iter_mut() + .filter_map(|(size, sym)| match sym.parse().ok()? { + pdb::SymbolData::Public(public_symbol) => public_symbol + .offset + .to_rva(address_map) + .map(|rva| (size, rva)), + pdb::SymbolData::ProcedureReference(_proc_ref) => { + // Ignore for now + None + } + unexpected => todo!("didn't expect this kod of symbol: {unexpected:?}"), + }) + .collect::>(); + symbols.sort_by_key(|(_, start)| start.0); + + // symbols.dedup_by_key(|(_, start)| start.0); // We could do this but then some symbols won't get a size... + + for ix in 1..symbols.len() { + let start = symbols[ix - 1].1; + // There might be multiple "symbols" at the same offset, find the next one: + let Some((_, end)) = symbols[ix..].iter().find(|(_, end)| *end != start) else { + break; + }; + let size = end + .checked_sub(start) + .expect("Since symbols are sorted the later once should have larger offsets"); + *symbols[ix - 1].0 = Some(AddressInfo { size, rva: start }); + } +} + +struct DllRelated { + symbols: pdb::SymbolTable<'static>, + address_map: pdb::AddressMap<'static>, + /// All data from the DLL file. + dll_data: Vec, +} +impl DllRelated { + fn collect(dll_info: &PeFile) -> eyre::Result { + let mut pdb = dll_info.open_pdb()?; + + if !pdb.type_information()?.is_empty() { + eprintln!( + "Info: Type info isn't empty for {} as was expected, perhaps it could be useful", + dll_info.file_stem()? + ); + } + if !pdb.frame_table()?.is_empty() { + eprintln!( + "Info: Frame table isn't empty for {} as was expected, perhaps it could be useful", + dll_info.file_stem()? + ); + } + if !pdb.id_information()?.is_empty() { + eprintln!( + "Info: Id information isn't empty for {} as was expected, perhaps it could be useful", + dll_info.file_stem()? + ); + } + + let symbols = pdb.global_symbols()?; + let address_map = pdb.address_map()?; + + let dll_data = dll_info.read_dll()?; + + Ok(Self { + symbols, + address_map, + dll_data, + }) + } + /// Symbol together with its estimated size (from the + /// [`calculate_size_for_symbols`]). + fn estimate_symbol_sizes(&self) -> eyre::Result>> { + let mut all_symbols = self + .symbols + .iter() + .map(|sym| Ok((None, sym))) + .collect::>()?; + calculate_size_for_symbols(all_symbols.as_mut_slice(), &self.address_map); + Ok(all_symbols) + } +} + +#[tokio::main] +async fn main() -> eyre::Result<()> { + let Args { + unfiltered, + twinui_dll_id, + actxprxy_dll_id, + skip_twinui, + skip_actxprxy, + skip_windows_version, + skip_windows_registry, + } = Args::parse(); + + if twinui_dll_id.is_none() && actxprxy_dll_id.is_none() { + println!("\nAnalyzing COM interfaces for local Windows installation.\n"); + if !skip_windows_version { + println!("Windows Version: {}\n\n", WindowsVersion::get()?); + } + + if !skip_windows_registry { + println!( + r"Interface ids (IID) from Registry key HKEY_LOCAL_MACHINE\SOFTWARE\Classes\Interface" + ); + println!(); + let reg_iids = RegistryIIDs::open()?; + for res in reg_iids { + let (iid, name) = res?; + if unfiltered + || name.contains("VirtualDesktop") + || (name.contains("ApplicationView") && !name.contains("CViewManagement")) + { + println!("{iid} for {name}"); + } + } + println!("\n"); + } + } else { + println!("\nAnalyzing COM interfaces for specific DLL files using PE code ids.\n") + } + + let downloader = setup_download_next_to_exe(); + + let mut twinui = PeFile::new(twinui_pcshell_path()?); + let mut actxprxy = PeFile::new(actxprxy_path()?); + let mut pe_files = [ + (&mut twinui, twinui_dll_id, skip_twinui), + (&mut actxprxy, actxprxy_dll_id, skip_actxprxy), + ]; + + println!("DLL and PDB information:"); + eprintln!( + "\nFetching PDB (and DLLs if PE code ids were specified) from Microsoft Symbol Server" + ); + for (pe_file, dll_id, skip) in &mut pe_files { + eprintln!(); + if *skip { + eprintln!("Ignoring information from: {}", pe_file.file_stem()?); + continue; + } + + if let Some(dll_id) = dll_id { + let did_download = pe_file + .maybe_download_dll(&downloader, *dll_id) + .await + .with_context(|| { + format!( + "Failed to download DLL for {}", + pe_file.file_stem().unwrap() + ) + })?; + if did_download { + eprintln!( + "PeCodeId for {}.dll differed from the dll in local Windows installation, \ + so it was downloaded from a Microsoft Symbol Server", + pe_file.file_stem()? + ); + } else { + eprintln!( + "PeCodeId for {}.dll matched with the dll in local Windows installation, \ + so nothing was downloaded", + pe_file.file_stem()? + ); + } + } + eprintln!("Using dll file at: {}", pe_file.dll_path.display()); + + pe_file.download_pdb(&downloader).await?; + eprintln!( + "Using pdb debug file at: {}", + pe_file.pdb_path.as_ref().unwrap().display() + ); + + println!( + "\n{}.dll with PeCodeId: {}", + pe_file.file_stem()?, + pe_file.pe_code_id()? + ); + println!( + "{}.pdb with breakpad id: {}", + pe_file.file_stem()?, + pe_file.debug_id()?.breakpad() + ); + } + + println!("\n\nInterface ids (IID) read from the DLL files using PDB debug info:\n"); + + // actxprxy related: + let actxprxy_info = (!skip_actxprxy) + .then(|| DllRelated::collect(&actxprxy)) + .transpose()?; + let actxprxy_symbols = actxprxy_info + .as_ref() + .map(|info| info.estimate_symbol_sizes()) + .transpose()?; + + // twinui related: + let twinui_info = (!skip_twinui) + .then(|| DllRelated::collect(&twinui)) + .transpose()?; + let twinui_symbols = twinui_info + .as_ref() + .map(|info| info.estimate_symbol_sizes()) + .transpose()?; + + // Search both dll files even though we are likely only interested in IID from actxprxy.dll: + let pdb_related = [ + (&actxprxy_info, &actxprxy_symbols), + (&twinui_info, &twinui_symbols), + ]; + for related in pdb_related { + let (Some(info), Some(all_symbols)) = related else { + continue; + }; + + for (size, symbol) in all_symbols { + let Ok(pdb::SymbolData::Public(data)) = symbol.parse() else { + continue; + }; + if !data.name.as_bytes().starts_with(b"IID_") { + // Note an interface id. + continue; + } + if !unfiltered + && !data.name.to_string().contains("VirtualDesktop") + // Note: IApplicationView iid is not in any of the dlls we are currently searching + && !data.name.to_string().contains("IApplicationView") + { + // Likely not an interface id we are interested in. + continue; + } + if size.unwrap_or_default().size < 16 { + eyre::bail!( + "Expected IID size to be 16 or larger but it was {}", + size.unwrap_or_default().size + ); + } + let rva = data.offset.to_rva(&info.address_map).unwrap_or_default(); + let iid = &info.dll_data[rva.0 as usize..][..16]; + let iid = uuid::Uuid::from_slice_le(iid).context("Failed to parse IID as GUID")?; + + println!("{iid:X} for {}", data.name); + } + } + println!(); + + let (Some(twinui_info), Some(twinui_all_symbols)) = (&twinui_info, twinui_symbols) else { + eprintln!("\nSkipping virtual function tables because of --skip-twinui flag\n"); + return Ok(()); + }; + + println!("\n\n\nVirtual function tables (vftable) for COM interfaces read from the DLL files using PDB debug info:"); + + let mut symbol_lookup = HashMap::new(); + for (info, sym) in &twinui_all_symbols { + let Some(info) = info else { continue }; + symbol_lookup.insert(info.rva, (info, sym)); + } + + let twinui_image_base = + object::File::parse(twinui_info.dll_data.as_slice())?.relative_address_base(); + + for (size, symbol) in &twinui_all_symbols { + // Will be either SymbolData::ProcedureReference or + // SymbolData::Public + + let Ok(pdb::SymbolData::Public(data)) = symbol.parse() else { + continue; + }; + let rva = data + .offset + .to_rva(&twinui_info.address_map) + .unwrap_or_default(); + let name = data.name.to_string(); + + // These filtering rules were ported from the Python script: + if !(unfiltered + || (VIRTUAL_DESKTOP_V_TABLE_NAMES + .iter() + .any(|part| name.contains(part)) + || (name.contains("_7CWin32ApplicationView") + && name.contains("IApplicationView") + && !name.contains("Microsoft") + && !name.contains("IApplicationViewBase")))) + { + // This symbol likely isn't relevant. + continue; + } + + let name_info = symbolic_common::Name::new( + data.name.to_string(), + symbolic_common::NameMangling::Unknown, + symbolic_common::Language::Unknown, + ); + let _lang = name_info.detect_language(); + let demangled = name_info.demangle(symbolic_demangle::DemangleOptions::complete()); + + if !matches!(&demangled, Some(demangled) if demangled.contains("vftable")) { + // Not a vtable definition! + continue; + } + if let Some(demangled) = &demangled { + println!("\n\nDumping vftable: {} ({})", demangled, data.name); + } else { + println!("\n\nDumping vftable: ({})", data.name); + } + if let Some(size) = size { + println!("\tVftable estimated size: {} bytes", size.size); + } + + let vft_data = + &twinui_info.dll_data[rva.0 as usize..][..size.unwrap_or_default().size as usize]; + let vft_ptrs = vft_data + .chunks_exact(8) + .map(|bytes| { + u64::from_le_bytes(bytes.try_into().expect("slices should be 8 bytes long")) + }) + .map(|ptr| ptr.saturating_sub(twinui_image_base)); + for (method_index, method_ptr) in vft_ptrs.enumerate() { + let Ok(method_ptr) = u32::try_from(method_ptr) else { + eprintln!( + "Warning: a method address in the DLL didn't fit in 32bit and was ignored" + ); + println!("\tMethod {method_index:02}: Unknown ({:x})", method_ptr); + continue; + }; + let method_ptr = Rva(method_ptr); + + let Some((_info, sym)) = symbol_lookup.get(&method_ptr) else { + println!("\tMethod {method_index:02}: Unknown ({:x})", method_ptr.0); + continue; + }; + + let Ok(pdb::SymbolData::Public(sym)) = sym.parse() else { + unreachable!("previously parsed symbol when gathering address info"); + }; + + let name_info = symbolic_common::Name::new( + sym.name.to_string(), + symbolic_common::NameMangling::Unknown, + symbolic_common::Language::Unknown, + ); + let _lang = name_info.detect_language(); + let demangled = name_info.demangle(symbolic_demangle::DemangleOptions::complete()); + + println!( + "\tMethod {method_index:02}: {} ({})", + demangled.unwrap_or_default(), + sym.name + ) + } + } + eprintln!(); + + Ok(()) +} From bf0904e4318542e12253d5259c96b4bd65edd3de Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Mon, 7 Oct 2024 02:54:26 +0200 Subject: [PATCH 2/3] Inform rust-analyzer in VS Code to load the helper cli program by default --- .vscode/extensions.json | 5 +++++ .vscode/settings.json | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 .vscode/extensions.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..61ed5d4 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "rust-lang.rust-analyzer" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index ebb1e8a..4f31933 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,14 @@ "editor.formatOnSave": true, "triggerTaskOnSave.restart": true, "rust-analyzer.cargo.features": "all", + "rust-analyzer.diagnostics.disabled": [ + // This incorrectly warns for some macro generated code in the "interface.rs" file: + "non_camel_case_types" + ], + "rust-analyzer.linkedProjects": [ + "Cargo.toml", + "print-com-interfaces/Cargo.toml" + ], "triggerTaskOnSave.tasks": { // "rust: cargo build": ["**/*.rs"] }, From 4d28c0bc57e282403704892c34fca8c480f353a5 Mon Sep 17 00:00:00 2001 From: Lej77 <31554212+Lej77@users.noreply.github.com> Date: Mon, 7 Oct 2024 03:43:52 +0200 Subject: [PATCH 3/3] More links for reverse-engineer-com-interfaces --- note-reverse-engineer-com-interfaces.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/note-reverse-engineer-com-interfaces.md b/note-reverse-engineer-com-interfaces.md index e4f8468..19de487 100644 --- a/note-reverse-engineer-com-interfaces.md +++ b/note-reverse-engineer-com-interfaces.md @@ -55,6 +55,12 @@ The [Readme for the Slion/VirtualDesktop C# library](https://github.com/Slion/Vi ---- +[Issue #76 (Not working with Windows 11 22H2) on the C# library Grabacr07/VirtualDesktop](https://github.com/Grabacr07/VirtualDesktop/issues/76) has many comments about how to reverse engineer the COM interfaces. They also mention that the interface ids in the Windows registry can no longer be found by name (they might still be there without any name and could perhaps be found by searching for parts of their GUID even if some digits have changed). + +That issue thread also links to [Pull Request #1 (Add support for 22449 & correct naming by NyaMisty) on the C# library mntone/VirtualDesktop](https://github.com/mntone/VirtualDesktop/pull/1#issuecomment-922269079) which has more direct instructions for finding the interface ids. + +---- + It can be worth looking at other similar virtual desktop libraries to see if they already support the Windows version in question: - [Slion/VirtualDesktop](https://github.com/Slion/VirtualDesktop): C# wrapper for the Virtual Desktop API on Windows 11.