Code Monkey home page Code Monkey logo

wmi-rs's Introduction

wmi

checkcrates.io docs.rs

WMI (Windows Management Instrumentation) crate for rust.

# Cargo.toml
[dependencies]
wmi = "0.13"

Examples

Queries can be deserialized into a free-form HashMap or a struct:

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use serde::Deserialize;
use wmi::{COMLibrary, Variant, WMIConnection, WMIDateTime};
use std::collections::HashMap;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let com_con = COMLibrary::new()?;
    let wmi_con = WMIConnection::new(com_con.into())?;

    let results: Vec<HashMap<String, Variant>> = wmi_con.raw_query("SELECT * FROM Win32_OperatingSystem")?;

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

    #[derive(Deserialize, Debug)]
    struct Win32_OperatingSystem {
        Caption: String,
        Name: String,
        CurrentTimeZone: i16,
        Debug: bool,
        EncryptionLevel: u32,
        ForegroundApplicationBoost: u8,
        LastBootUpTime: WMIDateTime,
    }

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

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

    Ok(())
}

chrono vs time

If you prefer to use the time crate instead of the default chrono, include wmi as

[dependencies]
wmi-rs = { version = "*", default-features = false, features = ["time"] }

and use the WMIOffsetDateTime wrapper instead of the WMIDateTime wrapper.

Async Queries

WMI supports async queries, with methods like ExecAsyncQuery.

#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

use serde::Deserialize;
use wmi::{COMLibrary, Variant, WMIConnection, WMIDateTime};
use std::collections::HashMap;
use futures::executor::block_on;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let com_con = COMLibrary::new()?;
    let wmi_con = WMIConnection::new(com_con.into())?;

    block_on(exec_async_query(&wmi_con))?;

    Ok(())
}

async fn exec_async_query(wmi_con: &WMIConnection) -> Result<(), Box<dyn std::error::Error>> {
    let results: Vec<HashMap<String, Variant>> =
        wmi_con.async_raw_query("SELECT * FROM Win32_OperatingSystem").await?;

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

    #[derive(Deserialize, Debug)]
    struct Win32_OperatingSystem {
        Caption: String,
        Name: String,
        CurrentTimeZone: i16,
        Debug: bool,
        EncryptionLevel: u32,
        ForegroundApplicationBoost: u8,
        LastBootUpTime: WMIDateTime,
    }

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

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

    Ok(())
}

License

The wmi crate is licensed under either of

Apache License, Version 2.0, (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
MIT license (LICENSE-MIT or https://opensource.org/licenses/MIT)

at your option.

wmi-rs's People

Contributors

apennamen avatar cameron-rowe avatar darkskygit avatar dependabot-preview[bot] avatar dependabot[bot] avatar eutampieri avatar fantasyteddy avatar maulingmonkey avatar minebarteksa avatar nidhihemanth avatar ohadravid avatar robot-rover avatar sn99 avatar vnagarnaik avatar vthib avatar yusufpapurcu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

wmi-rs's Issues

Helper to resolve registry links

You already have the helper wmi::query::quote_and_escape_wql_str(). This is about adding another helper, specifically to transform a registry path like HKEY_CURRENT_USER\SOFTWARE\Foo\Bar into HKEY_USERS\<SID>\SOFTWARE\Foo\Bar. This is necessary to make the WMI query SELECT * FROM RegistryValueChangeEvent WHERE ... work. I'm sure it's also necessary for other registry-related queries.

I wrote an implementation for this - not as a helper function, but as an iterator, so that the SID has to be found out only once (in my case, when listening for changes to multiple registry values):

pub trait MakeResolveRegKeysIter<'a, I>
where
    I: IntoIterator<Item = (&'a str, &'a str)>,
{
    /// Creates an iterator that yields resolved hive-subkey pairs.
    ///
    /// This means changing `("HKEY_CURRENT_USER", r"foo\bar")` to `("HKEY_USERS", r"<SID>\foo\bar")` and passing through any other pairs unchanged. [`HKEY_CLASSES_ROOT`](https://learn.microsoft.com/en-us/windows/win32/sysinfo/hkey-classes-root-key) links to `HKEY_LOCAL_MACHINE\SOFTWARE\Classes` as well as `HKEY_CURRENT_USER\SOFTWARE\Classes` in a merging way, which is why it can't be resolved.
    ///
    /// The subkey shouldn't be preceded by a backslash.
    fn resolve_reg_keys(self) -> ResolveRegKeys<'a, I::IntoIter>;
}

impl<'a, I> MakeResolveRegKeysIter<'a, I::IntoIter> for I
where
    I: IntoIterator<Item = (&'a str, &'a str)>,
{
    fn resolve_reg_keys(self) -> ResolveRegKeys<'a, I::IntoIter> {
        ResolveRegKeys {
            iter: self.into_iter(),
            current_user_sid: None,
        }
    }
}

/// The iterator returned by [`MakeResolveRegKeysIter::resolve_reg_keys()`].
pub struct ResolveRegKeys<'a, I>
where
    I: Iterator<Item = (&'a str, &'a str)>,
{
    iter: I,
    current_user_sid: Option<String>,
}

impl<'a, I> Iterator for ResolveRegKeys<'a, I>
where
    I: Iterator<Item = (&'a str, &'a str)>,
{
    type Item = WMIResult<(&'a str, Cow<'a, str>)>;

    fn next(&mut self) -> Option<Self::Item> {
        Some(Ok(match self.iter.next()? {
            ("HKEY_CURRENT_USER", subkey) => {
                let current_user_sid = if let Some(sid) = self.current_user_sid.as_ref() {
                    sid
                } else {
                    let sid = match current_user_sid() {
                        Ok(sid) => sid,
                        Err(error) => {
                            return Some(Err(WMIError::HResultError {
                                hres: error.code().0,
                            }))
                        }
                    };
                    self.current_user_sid.insert(sid)
                };

                (
                    "HKEY_USERS",
                    Cow::Owned(current_user_sid.to_owned() + r"\" + subkey),
                )
            }
            (hkey, subkey) => (hkey, Cow::Borrowed(subkey)),
        }))
    }
}

fn current_user_sid() -> windows::core::Result<String> {
    let process_token_handle = ResGuard::with_mut_acq_and_close_handle(|handle| unsafe {
        OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, handle).ok()
    })?;

    let mut sid_and_attrs_buffer = Vec::<u8>::new();
    let mut sid_and_attrs_buffer_size = 0;

    dual_call(
        FirstCallExpectation::Win32Error(ERROR_INSUFFICIENT_BUFFER),
        |getting_buffer_size| unsafe {
            GetTokenInformation(
                *process_token_handle,
                TokenUser,
                (!getting_buffer_size).then(|| {
                    sid_and_attrs_buffer.resize(sid_and_attrs_buffer_size as _, 0);
                    sid_and_attrs_buffer.as_mut_ptr().cast()
                }),
                sid_and_attrs_buffer_size,
                &mut sid_and_attrs_buffer_size,
            )
            .ok()
        },
    )?;

    let string_sid = unsafe {
        ResGuard::with_mut_pwstr_acq_and_local_free(|pwstr| {
            ConvertSidToStringSidW(
                (&*sid_and_attrs_buffer.as_ptr().cast::<SID_AND_ATTRIBUTES>()).Sid,
                pwstr,
            )
            .ok()
        })?
        .to_string()?
    };

    Ok(string_sid)
}

(Note that, in this version, I made use of my crate windows-helpers.)

Are you willing to take such an iterator into your repo? If you want, I can provide it as a runnable, cloneable repo for testing or intermediate development purposes.

WMIConnection::create_services spawns 40+ threads

Hi...

I had something strange happen...
the Connection.rs test (and my app) leaves behind 40+ threads after the call
to Connection new:

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let com_con = COMLibrary::new().unwrap();
        let wmi_con = WMIConnection::new(com_con.into()).unwrap();

        let p_svc = wmi_con.svc();

        assert_eq!(p_svc.is_null(), false);
    }
}

It happens in ConnectServer:

    fn create_services(&mut self, path: &str) -> Result<(), WMIError> {
        debug!("Calling ConnectServer");
        let mut p_svc = ptr::null_mut::<IWbemServices>();
        let object_path_bstr = WideCString::from_str(path)?;
        unsafe {
>           check_hres((*self.loc()).ConnectServer(
                object_path_bstr.as_ptr() as *mut _,
                ptr::null_mut(),
                ptr::null_mut(),
                ptr::null_mut(),
                0,
                ptr::null_mut(),
                ptr::null_mut(),
                &mut p_svc,
            ))?;
        }
        self.p_svc = NonNull::new(p_svc as *mut IWbemServices);
        debug!("Got service {:?}", self.p_svc);
        Ok(())
    }```

All with same call-stack

NtWaitForAlertByThreadId (@NtWaitForAlertByThreadId:8)
RtlSleepConditionVariableSRW (@RtlSleepConditionVariableSRW:80)
SleepConditionVariableSRW (@SleepConditionVariableSRW:14)
DllCanUnloadNow (@DllCanUnloadNow:10814)
DllCanUnloadNow (@DllCanUnloadNow:10814)
DllCanUnloadNow (@DllCanUnloadNow:10814)
DllCanUnloadNow (@DllCanUnloadNow:5416)
DllCanUnloadNow (@DllCanUnloadNow:10814)
DllCanUnloadNow (@DllCanUnloadNow:5044)
DllCanUnloadNow (@DllCanUnloadNow:10814)
BaseThreadInitThunk (@BaseThreadInitThunk:9)
RtlUserThreadStart (@RtlUserThreadStart:12)

```text
stable-x86_64-pc-windows-msvc - Up to date : 1.47.0 (18bf6b4f0 2020-10-07)
stable-x86_64-unknown-linux-gnu - Up to date : 1.47.0 (18bf6b4f0 2020-10-07)
wmi v0.6.0 (C:\Development\Rust\rust_cargo_sources\wmi-rs)
โ”œโ”€โ”€ chrono v0.4.10
โ”‚   โ”œโ”€โ”€ num-integer v0.1.41
โ”‚   โ”‚   โ””โ”€โ”€ num-traits v0.2.10
โ”‚   โ”‚       [build-dependencies]
โ”‚   โ”‚       โ””โ”€โ”€ autocfg v0.1.7
โ”‚   โ”‚   [build-dependencies]
โ”‚   โ”‚   โ””โ”€โ”€ autocfg v0.1.7
โ”‚   โ”œโ”€โ”€ num-traits v0.2.10 (*)
โ”‚   โ”œโ”€โ”€ serde v1.0.103
โ”‚   โ”‚   โ””โ”€โ”€ serde_derive v1.0.103
โ”‚   โ”‚       โ”œโ”€โ”€ proc-macro2 v1.0.6
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ unicode-xid v0.2.0
โ”‚   โ”‚       โ”œโ”€โ”€ quote v1.0.2
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ proc-macro2 v1.0.6 (*)
โ”‚   โ”‚       โ””โ”€โ”€ syn v1.0.11
โ”‚   โ”‚           โ”œโ”€โ”€ proc-macro2 v1.0.6 (*)
โ”‚   โ”‚           โ”œโ”€โ”€ quote v1.0.2 (*)
โ”‚   โ”‚           โ””โ”€โ”€ unicode-xid v0.2.0
โ”‚   โ””โ”€โ”€ time v0.1.42
โ”‚       โ”œโ”€โ”€ libc v0.2.66
โ”‚       โ””โ”€โ”€ winapi v0.3.9
โ”œโ”€โ”€ log v0.4.8
โ”‚   โ””โ”€โ”€ cfg-if v0.1.10
โ”œโ”€โ”€ serde v1.0.103 (*)
โ”œโ”€โ”€ thiserror v1.0.20
โ”‚   โ””โ”€โ”€ thiserror-impl v1.0.20
โ”‚       โ”œโ”€โ”€ proc-macro2 v1.0.6 (*)
โ”‚       โ”œโ”€โ”€ quote v1.0.2 (*)
โ”‚       โ””โ”€โ”€ syn v1.0.11 (*)
โ”œโ”€โ”€ widestring v0.4.0
โ””โ”€โ”€ winapi v0.3.9
[dev-dependencies]
โ”œโ”€โ”€ criterion v0.3.0
โ”‚   โ”œโ”€โ”€ atty v0.2.13
โ”‚   โ”‚   โ””โ”€โ”€ winapi v0.3.9
โ”‚   โ”œโ”€โ”€ cast v0.2.3
โ”‚   โ”‚   [build-dependencies]
โ”‚   โ”‚   โ””โ”€โ”€ rustc_version v0.2.3
โ”‚   โ”‚       โ””โ”€โ”€ semver v0.9.0
โ”‚   โ”‚           โ””โ”€โ”€ semver-parser v0.7.0
โ”‚   โ”œโ”€โ”€ clap v2.33.0
โ”‚   โ”‚   โ”œโ”€โ”€ bitflags v1.2.1
โ”‚   โ”‚   โ”œโ”€โ”€ textwrap v0.11.0
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ unicode-width v0.1.6
โ”‚   โ”‚   โ””โ”€โ”€ unicode-width v0.1.6
โ”‚   โ”œโ”€โ”€ criterion-plot v0.4.0
โ”‚   โ”‚   โ”œโ”€โ”€ cast v0.2.3 (*)
โ”‚   โ”‚   โ””โ”€โ”€ itertools v0.8.2
โ”‚   โ”‚       โ””โ”€โ”€ either v1.5.3
โ”‚   โ”œโ”€โ”€ csv v1.1.1
โ”‚   โ”‚   โ”œโ”€โ”€ bstr v0.2.8
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ lazy_static v1.4.0
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ memchr v2.2.1
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ libc v0.2.66
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ regex-automata v0.1.8
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ byteorder v1.3.2
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ serde v1.0.103 (*)
โ”‚   โ”‚   โ”œโ”€โ”€ csv-core v0.1.6
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ memchr v2.2.1 (*)
โ”‚   โ”‚   โ”œโ”€โ”€ itoa v0.4.4
โ”‚   โ”‚   โ”œโ”€โ”€ ryu v1.0.2
โ”‚   โ”‚   โ””โ”€โ”€ serde v1.0.103 (*)
โ”‚   โ”œโ”€โ”€ itertools v0.8.2 (*)
โ”‚   โ”œโ”€โ”€ lazy_static v1.4.0
โ”‚   โ”œโ”€โ”€ num-traits v0.2.10 (*)
โ”‚   โ”œโ”€โ”€ rand_core v0.5.1
โ”‚   โ”‚   โ””โ”€โ”€ getrandom v0.1.13
โ”‚   โ”‚       โ””โ”€โ”€ cfg-if v0.1.10
โ”‚   โ”œโ”€โ”€ rand_os v0.2.2
โ”‚   โ”‚   โ”œโ”€โ”€ getrandom v0.1.13 (*)
โ”‚   โ”‚   โ””โ”€โ”€ rand_core v0.5.1 (*)
โ”‚   โ”œโ”€โ”€ rand_xoshiro v0.3.1
โ”‚   โ”‚   โ””โ”€โ”€ rand_core v0.5.1 (*)
โ”‚   โ”œโ”€โ”€ rayon v1.2.1
โ”‚   โ”‚   โ”œโ”€โ”€ crossbeam-deque v0.7.2
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ crossbeam-epoch v0.8.0
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ cfg-if v0.1.10
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ crossbeam-utils v0.7.0
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ cfg-if v0.1.10
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ lazy_static v1.4.0
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   [build-dependencies]
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ autocfg v0.1.7
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ lazy_static v1.4.0
โ”‚   โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ memoffset v0.5.3
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   [build-dependencies]
โ”‚   โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ rustc_version v0.2.3 (*)
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ scopeguard v1.0.0
โ”‚   โ”‚   โ”‚   โ”‚   [build-dependencies]
โ”‚   โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ autocfg v0.1.7
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ crossbeam-utils v0.7.0 (*)
โ”‚   โ”‚   โ”œโ”€โ”€ either v1.5.3
โ”‚   โ”‚   โ””โ”€โ”€ rayon-core v1.6.1
โ”‚   โ”‚       โ”œโ”€โ”€ crossbeam-deque v0.7.2 (*)
โ”‚   โ”‚       โ”œโ”€โ”€ crossbeam-queue v0.2.0
โ”‚   โ”‚       โ”‚   โ””โ”€โ”€ crossbeam-utils v0.7.0 (*)
โ”‚   โ”‚       โ”œโ”€โ”€ crossbeam-utils v0.7.0 (*)
โ”‚   โ”‚       โ”œโ”€โ”€ lazy_static v1.4.0
โ”‚   โ”‚       โ””โ”€โ”€ num_cpus v1.11.1
โ”‚   โ”‚           โ””โ”€โ”€ libc v0.2.66
โ”‚   โ”œโ”€โ”€ serde v1.0.103 (*)
โ”‚   โ”œโ”€โ”€ serde_derive v1.0.103 (*)
โ”‚   โ”œโ”€โ”€ serde_json v1.0.42
โ”‚   โ”‚   โ”œโ”€โ”€ itoa v0.4.4
โ”‚   โ”‚   โ”œโ”€โ”€ ryu v1.0.2
โ”‚   โ”‚   โ””โ”€โ”€ serde v1.0.103 (*)
โ”‚   โ”œโ”€โ”€ tinytemplate v1.0.2
โ”‚   โ”‚   โ”œโ”€โ”€ serde v1.0.103 (*)
โ”‚   โ”‚   โ””โ”€โ”€ serde_json v1.0.42 (*)
โ”‚   โ””โ”€โ”€ walkdir v2.2.9
โ”‚       โ”œโ”€โ”€ same-file v1.0.5
โ”‚       โ”‚   โ””โ”€โ”€ winapi-util v0.1.2
โ”‚       โ”‚       โ””โ”€โ”€ winapi v0.3.9
โ”‚       โ”œโ”€โ”€ winapi v0.3.9
โ”‚       โ””โ”€โ”€ winapi-util v0.1.2 (*)
โ”œโ”€โ”€ lazy_static v1.4.0
โ””โ”€โ”€ serde_json v1.0.42 (*)

Any ideas ?

Thanks
JR

WMIConnection::create_services hangs indefinitely calling IWbemLocator::ConnectServer

Hi,

I have identified an issue within WMIConnection::create_services where it is passing 0 for lSecurityFlags when calling IWbemLocator::ConnectServer. According to Microsoft docs, this causes the API to attempt to connect indefinitely, leading to moments where a simple query will halt the entire program.

I suggest the default for lSecurityFlags be changed to WBEM_FLAG_CONNECT_USE_MAX_WAIT and an unsafe option be given to wait indefinitely to prevent future cases such as this.

UB when mixing windows-rs and wmi-rs crates

Hello,

I have a bit of a problem with safe code using both windows-rs and wmi-rs causing an ACCESS_VIOLATION. Here's a reproducer: https://github.com/roblabla/reproducer-wmi-windows-rs, you may need to run it multiple before it triggers the access violation as it depends on a race condition between threads. Here's what happens:

  • In one thread, I use windows-rs to do some calls to WinRT.
  • In another thread, I use the WMI crate to interact with the WMI. This enforces a call to CoInitialize (through the COMLibrary::new constructor), and will call CoUninitialize when the WMIConnection is dropped.

Here's the problem: When the WMIConnection gets dropped, it will also drop the COMLibrary, which will call CoUninitialize. Because no CoInitialize is currently active in the WinRT thread, all currently loaded COM DLLs will be unloaded, including whatever DLL was loaded by WinRT. So the thread running WinRT code will segfault because the code it's running gets unloaded.

Now, windows-rs does try to initialize its COM environment. Specifically, if RoGetActivationFactory returns a CO_E_NOTINITIALIZED error, it calls CoIncrementMTAUsage, and never decrements the counter after, ensuring that the COM subsystem stays initialized forever.

The problem is that this leads to a race condition with the thread doing WMI calls, e.g. in case of this sequence of event:

  • WMI calls CoInitialize
  • WinRT factory runs. COM is initialized, so it does not call CoIncrementMTAUsage
  • WMI finishes its work, calls CoUninitialized. This will lead to WinRT dlls being unloaded
  • WinRT is now running code that got unmapped -> ACCESS_VIOLATION, UB.

Now, this all looks like an error in windows-rs, but while raising the issue there, I was met with basically a WONTFIX: microsoft/windows-rs#1169. So I'm now opening this issue because it looks like calling CoUninitialize in a rust environment is basically expected to be unsafe, thus breaking the COMLibrary abstraction.

Support ExecNotificationQueryAsync

Hi, I have a need to query with ExecNotificationQueryAsync or ExecNotificationQuery (see here), but this function is not implemented. Would it be too much trouble to add this?

(I'd write it myself, but the code is too complex for me to figure out the right way. For now I've ended up having to make my own crate implementing this feature)

Unable to query Win32_Tpm class

Greetings, I am trying to query Win32_Tpm class but I get the following error Error: HResultError { hres: -2147217385 }

My rust code:

use serde::Deserialize;
use std::collections::HashMap;
use std::time::Duration;
use wmi::{COMLibrary, WMIConnection};
use wmi::{Variant, WMIDateTime};

#[derive(Deserialize, Debug)]
#[serde(rename = "Win32_Tpm")]
#[serde(rename_all = "PascalCase")]
struct Win32Tpm {
    is_activated_initial_value: Option<bool>,
    is_enabled_initial_value: Option<bool>,
    is_owned_initial_value: Option<bool>,
    spec_version: Option<String>,
    manufacturer_version: Option<String>,
    manufacturer_version_info: Option<String>,
    manufacturer_id: Option<u32>,
    physical_presence_version_info: Option<String>,
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let com_con = COMLibrary::new()?;
    let wmi_con =
        WMIConnection::with_namespace_path("Root\\CIMV2\\Security\\MicrosoftTpm", com_con)?;

    let results: Vec<Win32Tpm> = wmi_con.query()?; // Error: HResultError { hres: -2147217385 } 

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

    Ok(())
}

I have trouble understanding where I am going wrong. I am running it in administrative mode. Using something like Get-WmiObject -Namespace "root\CIMV2\Security\MicrosoftTpm" -Class "Win32_Tpm" in powershell works fine.

Possibly migrate from failure to thiserror

Since failure is deprecated and recommends thiserror & anyhow, it'd be nice to migrate away from it in a future release.

Failure use in libraries we've found a bit tricky to integrate with our own non-failure error handling at least.

Builds failing on docs.rs

Hi! I'm a maintainer for docs.rs, and i noticed that this crate was failing to build on our system. it looks like all your dependencies are gated on Windows builds. While this makes sense for what the crate is doing, it creates a situation where the crate totally fails to build on non-Windows systems, like docs.rs. There are a couple methods to get your docs onto docs.rs:

1. Tell docs.rs to build your crate for Windows first (recommended)

There's an options section you can add to your Cargo.toml that will configure how docs.rs builds your crate. One of the available options is the "default target" which is the target that docs.rs will build first, as well as the one that will appear when people try to load your crate on docs.rs. Adding the following section to your Cargo.toml will work:

[package.metadata.docs.rs]
default-target = "x86_64-pc-windows-msvc"

2. Gate your crate contents on cfg(windows)

A trick winapi pulls to speed up builds on non-Windows systems is to add #![cfg(windows)] to the root of the crate to completely wipe out the crate contents unless you're building for Windows. This will help if any user of your crate doesn't gate your crate in their Cargo.toml, since your crate will start "building" on non-Windows platforms, even if it ultimately can't be used. This can be combined with the previous strategy, too.

If it would be easier, i can open a PR to add these to your crate. Thanks!

Support for methods in IWbemClassWrapper

It would be really helpful to get support for methods on IWbemClassWrapper like we have for properties.

I'm thinking of just two methods, a list_methods and exec_method. This would allow to use WMI for than just retrieve information.

IWbemServices::ExecMethod support for WMIConnection

Hi,

As this is intended to be an "all things WMI" sort of crate by the look of it, I would like to introduce the idea of implementing a high-level ExecMethod. It appears rather straightforward, requiring only a couple helper methods for obtaining the parameters a method requires and setting the properties for said parameters if they are classes.

I propose a single exposed method, execute_method, which would take in an object path (strObjectPath: BStr), method name (strMethodName: BStr), potentially a HashMap<String, T> or serialized class for parameters (pInParams: *mut IWbemClassObject) and another potential HashMap<String, Variant> or serialized class for results (ppOutParams: &*mut IWbemClassObject).

fn execute_method(&self, strObjectPath: &str, strMethodName: &str, inParams: HashMap<String, T>) -> Result<U, WMIError> {
    let object_path = BStr::from_str(strObjectPath)?;
    let method_name = BStr::from_str(strMethodName)?;

    let mut pObject = ptr::null_mut::<IWbemClassObject>();
    unsafe {
        check_hres((*self.svc).GetObject(
            object_path.as_bstr(),
            0,
            ptr::null_mut(),
            &mut pObject,
            ptr::null_mut()
        ))?;
    }

    let mut pInParams = ptr::null_mut::<IWbemClassObject>();
    let mut pOutParams = ptr::null_mut::<IWbemClassObject>();
    unsafe {
        check_hres((*pObject).GetMethod(
            method_name.as_lpcwstr(),
            0,
            &mut pInParams,
            &mut pOutParams
        ))?;
    }

    // Work with Variants/serializing etc. (I am still fairly new to Rust and the Win32 API so I am unsure of this yet)
    unsafe {
        check_hres((*self.svc).ExecMethod(
            object_path.as_bstr(),
            method_name.as_bstr(),
            0,
            ptr::null_mut(),
            pInParams,
            &mut pOutParams,
            ptr::null_mut()
        ))?;
    }

    // Handle serializing of pOutParams
}

Note: I am still by all means a beginner with Rust best practices. This example is most likely not the best way to go about this, I just wanted to bring this to attention and state that I am currently attempting to implement it as a side project.

COMLibrary always fails on second call

The current API is error prone when using COMLibrary::new() multiple times.

The following naive code while always fail due to the duplicate security initialization.
The right way of doing it would be to somehow keep track on the security initialization state and use COMLibrary::without_security() which is weird and not documented (this is what the internal tests do).

use wmi::{COMLibrary, WMIResult, WMIConnection};

fn do_something() -> WMIResult<()> {
  let com_con = COMLibrary::new()?;
  let wmi_con = WMIConnection::new(com_con.into())?;
  // ... something
  Ok(())
}

fn main() -> Result<(), Box<dyn std::error::Error>>  {
  // use COM once
  do_something()?;
  // use COM a second time, maybe later, maybe on another thread
  do_something()?;
  Ok(())
}

COMLibrary::new() could easily treat RPC_E_TOO_LATE as not an error.

Async notifications do not handle cancelling the notification

When using the async_notification_* helpers, a stream impl is returned. However, there is nothing available to cancel the notification. If the stream is dropped, this will simply decrement the refcounting on the Arc of the stream queue, but the sink will still be notified and fill the stream queue, which will grow unbounded.

To cancel the notification, CancelAsyncCall must be called on the connection, supplying the sink handle.

I'm working on a fix for this, I'll write a MR for this when it is ready.

wmi::result_enumerator::QueryResultEnumerator::new should be marked `unsafe`

wmi::result_enumerator::QueryResultEnumerator::new is public and takes a raw, possibly invalid pointer, which will later be dereferenced in safe functions like Drop::drop:

pub fn new(wmi_con: &'a WMIConnection, p_enumerator: *mut IEnumWbemClassObject) -> Self {

EDIT: So should some other new fns - Option<NonNull> can still be constructed from invalid pointers:

pub fn new(ptr: Option<NonNull<IWbemClassObject>>) -> Self {

Can be invoked with "safe" code like:

IWbemClassWrapper::new(NonNull::new(4 as _));

Migrate from winapi to windows-rs

windows and windows-sys are new crates that are officially developped and supported by Microsoft (and quite actively maintained), while winapi looks unmaintained and is currently being phased out and replaced by those windows crates in many projects.

I have done so locally to check how feasible it would be and it brings a lot of simplification to the code:

  • existing BSTR wrapper, no need for a custom one
  • automatic handling of Clone & Drop for all WBEM types, so no need to use AddRef/Release by hand
  • existing helpers for COM interfaces, with casting between interfaces properly handled and the QuerySink impl being available
  • and more

Would you be open to a winapi => windows migration? I can push a PR doing this migration and we can discuss it more in details in it

unimplemented!() at safearray.rs:125

Hello I'm using this library for accessing remote device's wmi. But project panic's in this point. The reason is HttpCustomHeaders behind IIsWebServerSetting. This element returning data like this:

"HttpCustomHeaders":  [ "System.Management.ManagementBaseObject"],

In this point safearray:125 unimplemented!() panicking. I think we can remove this statements from code. Because unimplemented!() can be a problem in production. Also you can assign me I can handle this.

async support?

For a heim crate I'm looking for a way to fetch the temperature sensors data for Windows and it seems that WMI and MSAcpi_ThermalZoneTemperature class is the only reasonable solution to do that.

Since heim is an async lib, I would prefer the async interface to WMI, but it seems that wmi crate does not has it at the moment. Do you planning to add it, maybe?

Question: Handling Variants & Deserialization

Hello!

I am struggling to work with some of the types.

struct Win32_Process {
    CommandLine: Variant, // This line here only works as type Variant
    Name: String
}

When querying for processes, the CommandLine type according to the docs located here say that it should be of type String, however, when running it with string, Serde throws a JSON error that says it cannot invalid type: Option value, expected a string.

This makes sense because this can sometimes be null (for svchost, etc). However, I cannot seem to get the underlying value to be a string?

edit: nevermind, just as I posted this, I figured it out.

let path : String = process.CommandLine.try_into().unwrap_or("".to_string());

Calling methods on WMI objects

I am trying to get the Rust equivalent to this python code (intended to run in a Xen guest with the Xen PV drivers installed):

import wmi
wmi_connection = wmi.WMI(namespace="root\\wmi")
xs_base = wmi_connection.CitrixXenStoreBase()[0]
sid = xs_base.AddSession("MyNewSession")[0]
session = wmi_connection.query(f"select * from CitrixXenStoreSession where SessionId={sid}")[0]
session.GetValue("domid")
session.EndSession()

I am able to retrieve xs_base through a query() call, but that's filling a user-specified struct, and does not appear to allow keeping a handle on the object for later use, and I did not find an API for such calls in the crate.

How could I make those calls?

Edit: I finally found out exec_query_native_wrapper, which IIUC returns the objects handles. But that seems to be a long shot to actually call any methods on this. Looks like this should get through IWbemServices::ExecMethod(), which is not actually called in this crate.

Can't use WMIConnection in background async closures

I could be wrong, but the tokio::task::spawn_local nor tokio::spawn didn't work for me. tokio::spawn didn't compile as expected, but tokio::task::spawn_local panicked: thread 'main' panicked at 'spawn_local called from outside of a task::LocalSet'. I tried to spawn a background task that uses wmi.

Edit: Found out previous versions used std::rc::Rc for COMLibrary, so #53 is not direct issue, but not being able to spawn background tasks using wmi is something to consider

Failed to compile on Win7-64 with Rust 1.38

I would really love to use your wmi module. Unfortunately though, it won't build for me:

R:\rs\wmitest>cargo run --release --
   Compiling wmi v0.4.5
error[E0554]: `#![feature]` may not be used on the stable release channel
   --> C:\Users\mark\.cargo\registry\src\github.com-1ecc6299db9ec823\wmi-0.4.5\src\lib.rs:101:1
    |
101 | #![feature(ptr_internals, custom_attribute)]
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0554`.
error: Could not compile `wmi`.

To learn more, run the command again with --verbose.
R:\rs\wmitest>rustc -V
rustc 1.38.0 (625451e37 2019-09-23)

Remove COMLibrary struct

The COMLibrary struct initializes COM when constructed and de-initializes it when dropped. This is fine in a simple application where wmi-rs is the only user of COM. However it causes problems when wmi-rs is used together with other crates that make COM calls. One such issue is that repeated initialization of COM fails. Generally speaking COM initialization is the responsibility of the main application and not something that a library should do.

Data type error when reading Win32_VideoController.AdapterRAM

When reading Win32_VideoController, I am seeing a negative value for AdapterRAM. The Win32_VideoController doc shows AdapterRAM as uint32. However, the wmi crate sees the data type as I8. With 8 bytes, this storage size should be fine for a 4GB card. But the value comes out to a negative number.

I logged the value that is received using a simple query:

    let wmi_con = WMIConnection::new(COMLibrary::new().unwrap().into()).unwrap();
    let class_obj: Vec<HashMap<String, wmi::Variant>> = wmi_con.raw_query(
        "SELECT Name, AdapterRAM, DriverVersion FROM Win32_VideoController",
    ).unwrap();
    for item in class_obj.iter() {
        info!("Found gpu object:");
        for (key, value) in item.iter() {
            if key == "AdapterRAM" {
                info!("\"{}\": \"{:?}\"", key, value);
            }
        }
    }

Output:

"AdapterRAM": "I8(-1048576)"

This is the same query, but from powershell:

PS C:\> Get-WmiObject Win32_VideoController | Select AdapterRAM

AdapterRAM
----------
4293918720

I also found a discussion topic on this exact field, however, there is no resolution.

Is there anything that can be done to fix this issue within the crate?

Cannot async within traits

I'm able to use async_query() in simple tests, but not within an async trait. A simple example:

#[async_trait]
trait TestWmiTrait {
    async fn do_query() -> Result<()>;
}

struct TestWmi;
#[async_trait]
impl TestWmiTrait for TestWmi {
    async fn do_query() -> Result<()> {
        let com_con = COMLibrary::new()?;
        let wmi_con = WMIConnection::new(com_con.into())?;
        let _: Vec<UserProfile> = wmi_con.async_query().await?;
        Ok(())
    }
}

#[tokio::test]
async fn can_query_wmi_async() -> Result<()> {
    TestWmi::do_query().await?;
    Ok(())
}

Results in the following:

error: future cannot be sent between threads safely
   --> crates\bds_host\src\account\windows.rs:412:43
    |
412 |           async fn do_query() -> Result<()> {
    |  ___________________________________________^
413 | |             let com_con = COMLibrary::new()?;
414 | |             let wmi_con = WMIConnection::new(com_con.into())?;
415 | |             let _: Vec<UserProfile> = wmi_con.async_query().await?;
416 | |             Ok(())
417 | |         }
    | |_________^ future created by async block is not `Send`
    |
    = help: within `[async block@crates\bds_host\src\account\windows.rs:412:43: 417:10]`, the trait `Send` is not implemented for `*mut ()`
note: future is not `Send` as this value is used across an await
   --> crates\bds_host\src\account\windows.rs:415:61
    |
413 |             let com_con = COMLibrary::new()?;
    |                 ------- has type `wmi::COMLibrary` which is not `Send`
414 |             let wmi_con = WMIConnection::new(com_con.into())?;
415 |             let _: Vec<UserProfile> = wmi_con.async_query().await?;
    |                                                             ^^^^^ await occurs here, with `com_con` maybe used later
416 |             Ok(())
417 |         }
    |         - `com_con` is later dropped here
    = note: required for the cast from `Pin<Box<[async block@crates\bds_host\src\account\windows.rs:412:43: 417:10]>>` to `Pin<Box<(dyn Future<Output = std::result::Result<(), anyhow::Error>> + Send + 'async_trait)>>

Any ideas? Note that the same code works outside of a trait method.

Add "LIKE" support to FilterValue

I'm trying to detect FTDI devices connected to my computer and tell the user if their drivers aren't installed properly. I can use the following raw query, but I'd like to use proper filters as I think they look nicer:

wmi_con.raw_query("SELECT * FROM Win32_PnPEntity WHERE DeviceID LIKE '%VID_0403%'")

wmi-rs only supports equality for now. I'd make a PR myself but there's a few design thoughts that I don't really know how to answer. Should it be something like FilterValue::StringLike? Should FilterValue also be able to support "Not equals" in the future?

Is there a way to suscribe to multiple events in the same thread?

I am tying to subscribe to process and thread event at the same time, ideally the code would look something like this (with commented parts in):

use futures::StreamExt;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::time::Duration;

use wmi::FilterValue;
use wmi::{COMLibrary, WMIConnection};

#[derive(Deserialize, Debug)]
struct __InstanceCreationEvent {
    TargetInstance: Win32_Process,
}

#[derive(Deserialize, Debug)]
struct Win32_Process {
    ProcessID: u32,
}

#[derive(Deserialize, Serialize, Debug)]
struct __InstanceCreationThreadEvent {
    TargetInstance: Win32_Thread,
}

#[derive(Deserialize, Serialize, Debug)]
struct Win32_Thread {
    ProcessHandle: String,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let com_con = COMLibrary::new()?;
    let wmi = WMIConnection::new(com_con)?;

    let mut filters_process = HashMap::new();
    let mut filters_thread = HashMap::new();

    filters_process.insert(
        "TargetInstance".to_owned(),
        FilterValue::is_a::<Win32_Process>()?,
    );
    filters_thread.insert(
        "TargetInstance".to_owned(),
        FilterValue::is_a::<Win32_Thread>()?,
    );

    let mut stream_process = wmi.async_filtered_notification::<__InstanceCreationEvent>(
        &filters_process,
        Some(Duration::from_secs(1)),
    )?;

    /*
    let wmi_thread = WMIConnection::new(com_con)?;

    let mut stream_thread = wmi.async_filtered_notification::<__InstanceCreationThreadEvent>(
        &filters_thread,
        Some(Duration::from_secs(1)),
    )?;
    */

    while let Some(k) = stream_process.next().await {
        println!("{:?}", k);
    }

    Ok(())
}

Query SQLi

These are problematic if s contains double quotes:

wmi-rs/src/query.rs

Lines 102 to 103 in b8101bd

FilterValue::Str(s) => format!("\"{}\"", s),
FilterValue::String(s) => format!("\"{}\"", s),

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wmi/6c8a38f4-4ee1-47cb-99f1-b42718a575ce links https://www.dmtf.org/sites/default/files/standards/documents/DSP0004V2.3_final.pdf and seems to imply \" should do the trick... or perhaps this conversion should occur when creating the FilterValue in the first place?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.