In Getting started with Rust/WinRT we used the import macro to generate Rust bindings for Windows APIs directly into the Rust module where the import macro is used. This can be a nested module if you wish. Here’s an example using the Windows.System.Diagnostics namespace, which is documented here.
mod bindings { winrt::import!( dependencies os types windows::system::diagnostics::* ); }
Notice how in the following main function, I now use bindings as part of the Rust path for the windows::system::diagnostics module:
fn main() -> winrt::Result<()> { use bindings::windows::system::diagnostics::*; for process in ProcessDiagnosticInfo::get_for_processes()? { println!( "id: {:5} packaged: {:5} name: {}", process.process_id()?, process.is_packaged()?, process.executable_file_name()? ); } Ok(()) }
This will give you a quick dump of the processes currently running on your machine:
C:\sample>cargo run Finished dev [unoptimized + debuginfo] target(s) in 10.54s Running `target\debug\sample.exe` id: 4 packaged: false name: System id: 176 packaged: false name: Secure System id: 284 packaged: false name: Registry id: 8616 packaged: true name: RuntimeBroker.exe id: 10732 packaged: false name: svchost.exe . . .
As I pointed out last time, the time it takes can quickly become prohibitive. One option is to implement the bindings module in a separate bindings.rs file. While this will give a marginal improvement, Cargo is far better at caching the results if you stick the code in its own crate. Back in the console, let’s add a sub crate to house the generated bindings.
C:\sample>cargo new --lib bindings Created library `bindings` package
We then need to update the outer project to tell Cargo that it now depends on this new bindings library. To do that, we need to add bindings as a dependency in the Cargo.toml file for the sample project:
[dependencies] winrt = "0.7.0" bindings = { path = "bindings" }
While the first dependency is resolved via crates.io the bindings dependency uses a relative path to find the sub crate. This is all it takes to ensure that cargo will automatically build and cache the new dependency. Now let’s get the bindings library configured to import the WinRT types. Inside the bindings project, open the Cargo.toml file where we can add the winrt dependency:
[dependencies] winrt = "0.7.0"
We can then simply remove the import macro from the original project’s main.rs source file and add it to the bindings project’s lib.rs source file:
winrt::import!( dependencies os types windows::system::diagnostics::* );
The first time I run the example, it completes in about 10 seconds:
C:\sample>cargo run Compiling bindings v0.1.0 (C:\sample\bindings) Compiling sample v0.1.0 (C:\sample) Finished dev [unoptimized + debuginfo] target(s) in 10.61s Running `target\debug\sample.exe` id: 4 packaged: false name: System id: 176 packaged: false name: Secure System . . .
Now let’s make a small change to see whether incremental build time improves. Back in the sample project’s main function, we can turn the vector returned by get_for_processes into an iterator and limit the results to the first 5 processes:
fn main() -> winrt::Result<()> { use bindings::windows::system::diagnostics::*; for process in ProcessDiagnosticInfo::get_for_processes()? .into_iter() .take(5) { println!( "id: {:5} packaged: {:5} name: {}", process.process_id()?, process.is_packaged()?, process.executable_file_name()? ); } Ok(()) }
Cargo does quick work of recompiling and gets us running in under a second:
C:\sample>cargo run Compiling sample v0.1.0 (C:\sample) Finished dev [unoptimized + debuginfo] target(s) in 0.69s Running `target\debug\sample.exe` id: 4 packaged: false name: System id: 176 packaged: false name: Secure System id: 284 packaged: false name: Registry id: 1064 packaged: false name: smss.exe id: 1484 packaged: false name: csrss.exe
Now that’s much better. But there’s more! Using the import macro has a few drawbacks, but I’ll talk about creating your own build script next time. So stay tuned!