wmi/lib.rs
1//! # WMI-rs
2//!
3//! [WMI] is a management API for Windows-based operating systems.
4//! This crate provides both a high-level Rust API (focused on data retrieval, event queries and method execution),
5//! as well as a mid-level API for easy access to native WMI methods.
6//!
7//! This crate also uses `serde` for transforming between native WMI class objects and plain Rust structs.
8//!
9//! # Quickstart
10//!
11//! Before using WMI, a connection must be created.
12//!
13//! ```rust
14//! # fn main() -> wmi::WMIResult<()> {
15//! use wmi::{COMLibrary, WMIConnection};
16//! let com_con = COMLibrary::new()?;
17//! let wmi_con = WMIConnection::new(com_con)?;
18//! # Ok(())
19//! # }
20//! ```
21//!
22//! There are multiple ways to get data from the OS using this crate.
23//!
24//! ## Operating on untyped `Variant`s
25//!
26//! WMI data model is based on COM's [`VARIANT`] Type, which is a struct capable of holding
27//! many types of data.
28//!
29//! This crate provides the analogous [`crate::Variant`] enum.
30//!
31//! Using this enum, we can execute a simple WMI query and inspect the results.
32//!
33//! ```edition2018
34//! # fn main() -> wmi::WMIResult<()> {
35//! use wmi::*;
36//! let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
37//! use std::collections::HashMap;
38//! use wmi::Variant;
39//! let results: Vec<HashMap<String, Variant>> = wmi_con.raw_query("SELECT * FROM Win32_OperatingSystem").unwrap();
40//!
41//! for os in results {
42//! println!("{:#?}", os);
43//! }
44//! # Ok(())
45//! # }
46//! ```
47//!
48//! ## Using strongly typed data structures
49//!
50//! Using `serde`, it is possible to return a struct representing the the data.
51//!
52//! ```edition2018
53//! # fn main() -> wmi::WMIResult<()> {
54//! # use wmi::*;
55//! # let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
56//! use serde::Deserialize;
57//! # #[cfg(feature = "chrono")]
58//! use wmi::WMIDateTime;
59//! # #[cfg(all(feature = "time", not(feature = "chrono")))]
60//! # use wmi::WMIOffsetDateTime as WMIDateTime;
61//!
62//! #[derive(Deserialize, Debug)]
63//! #[serde(rename = "Win32_OperatingSystem")]
64//! #[serde(rename_all = "PascalCase")]
65//! struct OperatingSystem {
66//! caption: String,
67//! debug: bool,
68//! last_boot_up_time: WMIDateTime,
69//! }
70//!
71//! let results: Vec<OperatingSystem> = wmi_con.query()?;
72//!
73//! for os in results {
74//! println!("{:#?}", os);
75//! }
76//! # Ok(())
77//! # }
78//! ```
79//!
80//! Because the name of the struct given to `serde` matches the [WMI class] name, the SQL query
81//! can be inferred.
82//!
83//! [WMI]: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/about-wmi
84//! [Creating a WMI Application Using C++]: https://docs.microsoft.com/en-us/windows/desktop/wmisdk/creating-a-wmi-application-using-c-
85//! [`VARIANT`]: https://docs.microsoft.com/en-us/windows/desktop/api/oaidl/ns-oaidl-tagvariant
86//! [WMI class]: https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-operatingsystem
87//!
88//! # High-level API functions
89//!
90//! Queries and data retrieval - [`WMIConnection::query`], [`WMIConnection::filtered_query`], [`WMIConnection::get`], [`WMIConnection::get_by_path`] and [`WMIConnection::associators`].
91//!
92//! Event listening - [`WMIConnection::notification`] and [`WMIConnection::filtered_notification`].
93//!
94//! Method calling - [`WMIConnection::exec_class_method`] and [`WMIConnection::exec_instance_method`].
95//!
96//! Most of these have `async` versions as well.
97//!
98//! # Mid-level API functions
99//!
100//! Queries and data retrieval - [`WMIConnection::get_object`], [`WMIConnection::exec_query`] and [`WMIConnection::exec_query_async`].
101//!
102//! Event listening - [`WMIConnection::exec_notification_query`] and [`WMIConnection::exec_notification_query_async`].
103//!
104//! Method calling - [`WMIConnection::exec_method`] and [`IWbemClassWrapper`].
105//!
106//! These try to keep the names of the underlying WMI machinery.
107//!
108//! # Subscribing to event notifications
109//!
110//! Using this crate you can subscribe to events notifications generated upon changes in WMI data and services.
111//!
112//! When querying for events, it is important to remember there are two types of event notifications. \
113//! The first one is notifications about changes to the standard WMI data models. They are called **intrinsic events**. \
114//! Events like [`__InstanceCreationEvent`] or [`__NamespaceDeletionEvent`] are examples of such events.
115//!
116//! The second type is notifications about changes made by providers. They are called **extrinsic events**. \
117//! Any WMI class deriving from the [`__ExtrinsicEvent`] class is an extrinsic event. \
118//! An example of such events are [`Win32_ProcessStartTrace`] and [`Win32_VolumeChangeEvent`] classes.
119//!
120//! For more information about event queries, [see here](https://docs.microsoft.com/en-us/windows/win32/wmisdk/receiving-a-wmi-event#event-queries).\
121//! You can use [WMI Code Creator] to see available events and create queries for them.
122//!
123//! The [`notification`] method returns an iterator that waits for any incoming events
124//! resulting from the provided query. Loops reading from this iterator will not end until they are broken.
125//!
126//! An example of subscribing to an intrinsic event notification for every new [`Win32_Process`]
127//! ```edition2018
128//! # use wmi::*;
129//! # #[cfg(not(feature = "test"))]
130//! # fn main() {}
131//! # #[cfg(feature = "test")]
132//! # fn main() -> WMIResult<()>{
133//! # use serde::Deserialize;
134//! # use std::{collections::HashMap, time::Duration};
135//! # let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
136//! #[derive(Deserialize, Debug)]
137//! #[serde(rename = "__InstanceCreationEvent")]
138//! #[serde(rename_all = "PascalCase")]
139//! struct NewProcessEvent {
140//! target_instance: Process
141//! }
142//!
143//! #[derive(Deserialize, Debug)]
144//! #[serde(rename = "Win32_Process")]
145//! #[serde(rename_all = "PascalCase")]
146//! struct Process {
147//! process_id: u32,
148//! name: String,
149//! executable_path: Option<String>,
150//! }
151//!
152//! let mut filters = HashMap::<String, FilterValue>::new();
153//!
154//! filters.insert("TargetInstance".to_owned(), FilterValue::is_a::<Process>()?);
155//!
156//! let iterator = wmi_con.filtered_notification::<NewProcessEvent>(&filters, Some(Duration::from_secs(1)))?;
157//! # tests::start_test_program();
158//!
159//! for result in iterator {
160//! let process = result?.target_instance;
161//! println!("New process!");
162//! println!("PID: {}", process.process_id);
163//! println!("Name: {}", process.name);
164//! println!("Executable: {:?}", process.executable_path);
165//! # break;
166//! } // Loop will end only on error
167//! # Ok(())
168//! # }
169//! ```
170//!
171//! An example of subscribing to an extrinsic event notification [`Win32_ProcessStartTrace`]
172//! ```edition2018
173//! # use wmi::*;
174//! # #[cfg(not(feature = "test"))]
175//! # fn main() {}
176//! # #[cfg(feature = "test")]
177//! # fn main() -> WMIResult<()> {
178//! # tests::ignore_access_denied(run())
179//! # }
180//! # #[cfg(feature = "test")]
181//! # fn run() -> WMIResult<()>{
182//! # use serde::Deserialize;
183//! # use std::{collections::HashMap, time::Duration};
184//! # let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
185//! #[derive(Deserialize, Debug)]
186//! #[serde(rename = "Win32_ProcessStartTrace")]
187//! #[serde(rename_all = "PascalCase")]
188//! struct ProcessStartTrace {
189//! process_id: u32,
190//! process_name: String,
191//! }
192//!
193//! let iterator = wmi_con.notification::<ProcessStartTrace>()?;
194//! # tests::start_test_program();
195//!
196//! for result in iterator {
197//! let trace = result?;
198//! println!("Process started!");
199//! println!("PID: {}", trace.process_id);
200//! println!("Name: {}", trace.process_name);
201//! # break;
202//! } // Loop will end only on error
203//! # Ok(())
204//! # }
205//! ```
206//!
207//! [`Win32_Process`]: https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-process
208//! [`__InstanceCreationEvent`]: https://docs.microsoft.com/en-us/windows/win32/wmisdk/--instancecreationevent
209//! [`__NamespaceDeletionEvent`]: https://docs.microsoft.com/en-us/windows/win32/wmisdk/--namespacedeletionevent
210//! [`__ExtrinsicEvent`]: https://docs.microsoft.com/en-us/windows/win32/wmisdk/--extrinsicevent
211//! [`Win32_ProcessStartTrace`]: https://docs.microsoft.com/en-us/previous-versions/windows/desktop/krnlprov/win32-processstarttrace
212//! [`Win32_VolumeChangeEvent`]: https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-volumechangeevent
213//! [WMI Code Creator]: https://www.microsoft.com/en-us/download/details.aspx?id=8572
214//! [`notification`]: connection/struct.WMIConnection.html#method.notification
215//!
216//! # Executing Methods
217//!
218//! The crate also offers support for executing WMI methods on classes and instances.
219//!
220//! See [`WMIConnection::exec_class_method`], [`WMIConnection::exec_instance_method`] and [`WMIConnection::exec_method`]
221//! for detailed examples.
222//!
223//! # Internals
224//!
225//! [`WMIConnection`] is used to create and execute a WMI query, returning
226//! [`IWbemClassWrapper`] which is a wrapper for a WMI object pointer.
227//!
228//! Then, `from_wbem_class_obj` is used to create a Rust struct with the equivalent data.
229//!
230//! Deserializing data from WMI and into Rust is done via `serde`.
231//! More info can be found in `serde`'s documentation about [writing a data format].
232//! The deserializer will either use the field names defined on the output struct,
233//! or retrieve all field names from WMI if the output is a `HashMap`.
234//!
235//! [writing a data format]: https://serde.rs/data-format.html
236//!
237//! There are two main data structures (other than pointers to object) which convert native data to Rust data structures:
238//! [`crate::Variant`] and [`SafeArrayAccessor`](safearray::SafeArrayAccessor).
239//!
240//! Most native objects has an equivalent wrapper struct which implements `Drop` for that data.
241//!
242//! # Async Query
243//!
244//! Async queries use WMI's native async support (but a runtime like `tokio`, `async-std` or `futures::executor::block_on` is still required).
245//!
246//! ```edition2018
247//! # use futures::executor::block_on;
248//! # block_on(exec_async_query()).unwrap();
249//! # async fn exec_async_query() -> wmi::WMIResult<()> {
250//! use wmi::*;
251//! let wmi_con = WMIConnection::new(COMLibrary::new()?)?;
252//! use serde::Deserialize;
253//!
254//! #[derive(Deserialize, Debug)]
255//! #[serde(rename = "Win32_OperatingSystem")]
256//! #[serde(rename_all = "PascalCase")]
257//! struct OperatingSystem {
258//! caption: String,
259//! debug: bool,
260//! }
261//!
262//! let results: Vec<OperatingSystem> = wmi_con.async_query().await?;
263//!
264//! for os in results {
265//! println!("{:#?}", os);
266//! }
267//! # Ok(())
268//! # }
269//! ```
270//!
271#![allow(non_camel_case_types)]
272#![allow(non_snake_case)]
273#![allow(unused_unsafe)]
274#![allow(clippy::arc_with_non_send_sync)]
275#![allow(clippy::needless_lifetimes)]
276#![cfg(windows)]
277
278mod connection;
279
280#[cfg(feature = "chrono")]
281mod datetime;
282
283#[cfg(feature = "time")]
284mod datetime_time;
285
286mod context;
287mod de;
288mod duration;
289mod method;
290mod query;
291mod result_enumerator;
292pub mod safearray;
293mod ser;
294mod utils;
295mod variant;
296
297mod async_query;
298mod query_sink;
299
300mod notification;
301
302#[cfg(any(test, feature = "test"))]
303pub mod tests;
304
305pub use connection::{COMLibrary, WMIConnection};
306
307#[cfg(feature = "chrono")]
308pub use datetime::WMIDateTime;
309
310#[cfg(feature = "time")]
311pub use datetime_time::WMIOffsetDateTime;
312
313pub use context::{ContextValueType, WMIContext};
314pub use duration::WMIDuration;
315pub use query::{build_notification_query, build_query, quote_and_escape_wql_str, FilterValue};
316pub use result_enumerator::IWbemClassWrapper;
317pub use utils::{WMIError, WMIResult};
318pub use variant::Variant;
319
320#[doc = include_str!("../README.md")]
321#[cfg(all(doctest, feature = "chrono"))]
322pub struct ReadmeDoctests;