Crate wmi

source ·
Expand description

§WMI-rs

WMI is a management API for Windows-based operating systems. This crate provides a high level Rust API focused around data retrieval (vs. making changes to the system and watching for event which are also supported by WMI).

This crate also uses serde to transform pointers to WMI class objects into plain Rust structs.

All data is copied to Owning data structures, so the final structs are not tied in any way to the original WMI object (refer to MSDN’s Creating a WMI Application Using C++ to learn more about how data is handled by WMI).

Before using WMI, a connection must be created.

use wmi::{COMLibrary, WMIConnection};
let com_con = COMLibrary::new()?;
let wmi_con = WMIConnection::new(com_con)?;

There are multiple ways to get data from the OS using this crate.

§Operating on untyped Variants

WMI data model is based on COM’s VARIANT Type, which is a struct capable of holding many types of data.

This crate provides the analogous Variant enum.

Using this enum, we can execute a simple WMI query and inspect the results.

use wmi::*;
let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
use std::collections::HashMap;
use wmi::Variant;
let results: Vec<HashMap<String, Variant>> = wmi_con.raw_query("SELECT * FROM Win32_OperatingSystem").unwrap();

for os in results {
    println!("{:#?}", os);
}

§Using strongly typed data structures

Using serde, it is possible to return a struct representing the the data.

use serde::Deserialize;
use wmi::WMIDateTime;

#[derive(Deserialize, Debug)]
#[serde(rename = "Win32_OperatingSystem")]
#[serde(rename_all = "PascalCase")]
struct OperatingSystem {
    caption: String,
    debug: bool,
    last_boot_up_time: WMIDateTime,
}

let results: Vec<OperatingSystem> = wmi_con.query()?;

for os in results {
    println!("{:#?}", os);
}

Because the name of the struct given to serde matches the WMI class name, the SQL query can be inferred.

§Subscribing to event notifications

Using this crate you can subscribe to events notifications generated upon changes in WMI data and services.

When querying for events, it is important to remember there are two types of event notifications.
The first one is notifications about changes to the standard WMI data models. They are called intrinsic events.
Events like __InstanceCreationEvent or __NamespaceDeletionEvent are examples of such events.

The second type is notifications about changes made by providers. They are called extrinsic events.
Any WMI class deriving from the __ExtrinsicEvent class is an extrinsic event.
An example of such events are Win32_ProcessStartTrace and Win32_VolumeChangeEvent classes.

For more information about event queries, see here.
You can use WMI Code Creator to see available events and create queries for them.

The notification method returns an iterator that waits for any incoming events resulting from the provided query. Loops reading from this iterator will not end until they are broken.

An example of subscribing to an intrinsic event notification for every new Win32_Process

#[derive(Deserialize, Debug)]
#[serde(rename = "__InstanceCreationEvent")]
#[serde(rename_all = "PascalCase")]
struct NewProcessEvent {
    target_instance: Process
}

#[derive(Deserialize, Debug)]
#[serde(rename = "Win32_Process")]
#[serde(rename_all = "PascalCase")]
struct Process {
    process_id: u32,
    name: String,
    executable_path: Option<String>,
}

let mut filters = HashMap::<String, FilterValue>::new();

filters.insert("TargetInstance".to_owned(), FilterValue::is_a::<Process>()?);

let iterator = wmi_con.filtered_notification::<NewProcessEvent>(&filters, Some(Duration::from_secs(1)))?;

for result in iterator {
    let process = result?.target_instance;
    println!("New process!");
    println!("PID:        {}", process.process_id);
    println!("Name:       {}", process.name);
    println!("Executable: {:?}", process.executable_path);
} // Loop will end only on error

An example of subscribing to an extrinsic event notification Win32_ProcessStartTrace

#[derive(Deserialize, Debug)]
#[serde(rename = "Win32_ProcessStartTrace")]
#[serde(rename_all = "PascalCase")]
struct ProcessStartTrace {
    process_id: u32,
    process_name: String,
}

let iterator = wmi_con.notification::<ProcessStartTrace>()?;

for result in iterator {
    let trace = result?;
    println!("Process started!");
    println!("PID:  {}", trace.process_id);
    println!("Name: {}", trace.process_name);
} // Loop will end only on error

§Internals

WMIConnection is used to create and execute a WMI query, returning IWbemClassWrapper which is a wrapper for a WMI object pointer.

Then, from_wbem_class_obj is used to create a Rust struct with the equivalent data.

Deserializing data from WMI and into Rust is done via serde and is implemented in the de module. More info can be found in serde’s documentation about writing a data format. The deserializer will either use the field names defined on the output struct, or retrieve all field names from WMI if the output is a HashMap.

There are two main data structures (other than pointers to object) which convert native data to Rust data structures: Variant and SafeArrayAccessor.

Most native objects has an equivalent wrapper struct which implements Drop for that data.

§Async Query

Async queries use WMI’s native async support (but a runtime like tokio, async-std or futures::executor::block_on is still required).

use wmi::*;
use futures::StreamExt;
let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
let results = wmi_con
    .exec_query_async_native_wrapper("SELECT OSArchitecture FROM Win32_OperatingSystem")?
    .collect::<Vec<_>>().await;

It it also possible to return a struct representing the the data.

use wmi::*;
let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
use serde::Deserialize;

#[derive(Deserialize, Debug)]
#[serde(rename = "Win32_OperatingSystem")]
#[serde(rename_all = "PascalCase")]
struct OperatingSystem {
    caption: String,
    debug: bool,
}

let results: Vec<OperatingSystem> = wmi_con.async_query().await?;

for os in results {
    println!("{:#?}", os);
}

Re-exports§

Modules§