Expand description
§WMI-rs
WMI is a management API for Windows-based operating systems. This crate provides both a high-level Rust API (focused on data retrieval, event queries and method execution), as well as a mid-level API for easy access to native WMI methods.
This crate also uses serde
for transforming between native WMI class objects and plain Rust structs.
§Quickstart
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 Variant
s
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 crate::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.
§High-level API functions
Queries and data retrieval - WMIConnection::query
, WMIConnection::filtered_query
, WMIConnection::get
, WMIConnection::get_by_path
and WMIConnection::associators
.
Event listening - WMIConnection::notification
and WMIConnection::filtered_notification
.
Method calling - WMIConnection::exec_class_method
and WMIConnection::exec_instance_method
.
Most of these have async
versions as well.
§Mid-level API functions
Queries and data retrieval - WMIConnection::get_object
, WMIConnection::exec_query
and WMIConnection::exec_query_async
.
Event listening - WMIConnection::exec_notification_query
and WMIConnection::exec_notification_query_async
.
Method calling - WMIConnection::exec_method
and IWbemClassWrapper
.
These try to keep the names of the underlying WMI machinery.
§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
§Executing Methods
The crate also offers support for executing WMI methods on classes and instances.
See WMIConnection::exec_class_method
, WMIConnection::exec_instance_method
and WMIConnection::exec_method
for detailed examples.
§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
.
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:
crate::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::*;
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);
}
Modules§
Structs§
- COMLibrary
- A marker to indicate that the current thread was
CoInitialize
d. - IWbem
Class Wrapper - A wrapper around a IWbemClassObject.
- WMIConnection
- A connection to the local WMI provider.
- WMIContext
- Provides access to WMI’s context, available via
WMIConnection::ctx
. - WMIDate
Time - A wrapper type around
chrono
’sDateTime
(if thechrono
feature is active. ), which supports parsing from WMI-format strings. - WMIDuration
- A wrapper type around Duration, which supports parsing from WMI-format strings.
Enums§
Functions§
- build_
notification_ query - Build an SQL query for an event notification subscription with the given filters and within polling time, over the given type (using its fields). For example, for:
- build_
query - Build an SQL query for the given filters, over the given type (using its name and fields). For example, for:
- quote_
and_ escape_ wql_ str - Quote/escape a string for WQL.
Type Aliases§
- WMIResult
- Alias type for
Result<T, WMIError>