-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for setting IWbemContext #103
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
use crate::{WMIConnection, WMIResult}; | ||
use log::debug; | ||
use windows::Win32::System::{ | ||
Com::{CoCreateInstance, CLSCTX_INPROC_SERVER}, | ||
Wmi::{IWbemContext, WbemContext}, | ||
}; | ||
use windows_core::{BSTR, VARIANT}; | ||
|
||
#[derive(Debug, Clone)] | ||
#[non_exhaustive] | ||
pub enum ContextValueType { | ||
String(String), | ||
I4(i32), | ||
R8(f64), | ||
Bool(bool), | ||
} | ||
|
||
impl From<ContextValueType> for VARIANT { | ||
fn from(value: ContextValueType) -> Self { | ||
match value { | ||
ContextValueType::Bool(b) => Self::from(b), | ||
ContextValueType::I4(i4) => Self::from(i4), | ||
ContextValueType::R8(r8) => Self::from(r8), | ||
ContextValueType::String(str) => Self::from(BSTR::from(str)), | ||
} | ||
} | ||
} | ||
|
||
#[derive(Clone, Debug)] | ||
pub struct WMIContext(pub(crate) IWbemContext); | ||
|
||
impl WMIContext { | ||
/// Creates a new instances of [`WMIContext`] | ||
pub(crate) fn new() -> WMIResult<WMIContext> { | ||
debug!("Calling CoCreateInstance for CLSID_WbemContext"); | ||
|
||
let ctx = unsafe { CoCreateInstance(&WbemContext, None, CLSCTX_INPROC_SERVER)? }; | ||
|
||
debug!("Got context {:?}", ctx); | ||
|
||
Ok(WMIContext(ctx)) | ||
} | ||
|
||
/// Sets the specified named context value for use in providing additional context information to queries. | ||
/// | ||
/// Note the context values will persist across subsequent queries until [`WMIConnection::delete_all`] is called. | ||
pub fn set_value(&mut self, key: &str, value: impl Into<ContextValueType>) -> WMIResult<()> { | ||
let value = value.into(); | ||
unsafe { self.0.SetValue(&BSTR::from(key), 0, &value.into())? }; | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Clears all named values from the underlying context object. | ||
pub fn delete_all(&mut self) -> WMIResult<()> { | ||
unsafe { self.0.DeleteAll()? }; | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
||
impl WMIConnection { | ||
/// Returns a mutable reference to the [`WMIContext`] object | ||
pub fn ctx(&mut self) -> &mut WMIContext { | ||
&mut self.ctx | ||
} | ||
} | ||
|
||
macro_rules! impl_from_type { | ||
($target_type:ty, $variant:ident) => { | ||
impl From<$target_type> for ContextValueType { | ||
fn from(value: $target_type) -> Self { | ||
Self::$variant(value.into()) | ||
} | ||
} | ||
}; | ||
} | ||
|
||
impl_from_type!(&str, String); | ||
impl_from_type!(i32, I4); | ||
impl_from_type!(f64, R8); | ||
impl_from_type!(bool, Bool); | ||
|
||
#[allow(non_snake_case)] | ||
#[allow(non_camel_case_types)] | ||
#[allow(dead_code)] | ||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use crate::COMLibrary; | ||
use serde::Deserialize; | ||
|
||
#[test] | ||
fn verify_ctx_values_used() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Super cool! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you also add an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also: since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added the async test. With respect to |
||
let com_con = COMLibrary::new().unwrap(); | ||
let mut wmi_con = | ||
WMIConnection::with_namespace_path("ROOT\\StandardCimv2", com_con).unwrap(); | ||
|
||
#[derive(Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug)] | ||
struct MSFT_NetAdapter { | ||
InterfaceName: String, | ||
} | ||
|
||
let mut orig_adapters = wmi_con.query::<MSFT_NetAdapter>().unwrap(); | ||
assert!(!orig_adapters.is_empty()); | ||
|
||
// With 'IncludeHidden' set to 'true', expect the response to contain additional adapters | ||
wmi_con.ctx().set_value("IncludeHidden", true).unwrap(); | ||
let all_adapters = wmi_con.query::<MSFT_NetAdapter>().unwrap(); | ||
assert!(all_adapters.len() > orig_adapters.len()); | ||
|
||
wmi_con.ctx().delete_all().unwrap(); | ||
let mut adapters = wmi_con.query::<MSFT_NetAdapter>().unwrap(); | ||
adapters.sort(); | ||
orig_adapters.sort(); | ||
assert_eq!(adapters, orig_adapters); | ||
} | ||
|
||
#[tokio::test] | ||
async fn async_verify_ctx_values_used() { | ||
let com_con = COMLibrary::new().unwrap(); | ||
let mut wmi_con = | ||
WMIConnection::with_namespace_path("ROOT\\StandardCimv2", com_con).unwrap(); | ||
|
||
#[derive(Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug)] | ||
struct MSFT_NetAdapter { | ||
InterfaceName: String, | ||
} | ||
|
||
let mut orig_adapters = wmi_con.async_query::<MSFT_NetAdapter>().await.unwrap(); | ||
assert!(!orig_adapters.is_empty()); | ||
|
||
// With 'IncludeHidden' set to 'true', expect the response to contain additional adapters | ||
wmi_con.ctx().set_value("IncludeHidden", true).unwrap(); | ||
let all_adapters = wmi_con.async_query::<MSFT_NetAdapter>().await.unwrap(); | ||
assert!(all_adapters.len() > orig_adapters.len()); | ||
|
||
wmi_con.ctx().delete_all().unwrap(); | ||
let mut adapters = wmi_con.async_query::<MSFT_NetAdapter>().await.unwrap(); | ||
adapters.sort(); | ||
orig_adapters.sort(); | ||
assert_eq!(adapters, orig_adapters); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add to this a
non_exhaustive
attribute, so adding more options won't be a breaking changeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also replace
ContextValueType
withVariant
/impl Into<Variant>
, but then it'll be better to haveimpl_try_from_variant
generate both impls (the new one beingimpl From<$target_type> for Variant
), as well asInto<VARIANT> for Variant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had initially started with using
Variant
but settled forContextValueType
because only a subset of theVARIANT
types are supported as per doc.I guess the benefit of
ContextValueType
is the caller can't accidentally pass in an unsupported type whereas they can withVariant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 about the doc - this makes a lot of sense.