Skip to content

Commit

Permalink
Merge pull request #100 from ohadravid/chore/variant-and-safearray-im…
Browse files Browse the repository at this point in the history
…provements

Improve Variant and SafeArray implementations
  • Loading branch information
ohadravid authored Sep 9, 2024
2 parents 51a8fe4 + c85b8fa commit 5ba4022
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 54 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wmi"
version = "0.13.4"
version = "0.14.0"
authors = ["Ohad Ravid <[email protected]>"]
edition = "2021"
license = "MIT OR Apache-2.0"
Expand Down
1 change: 1 addition & 0 deletions src/de/wbem_class_de.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer {

#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions src/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ impl WMIConnection {

#[allow(non_snake_case)]
#[allow(non_camel_case_types)]
#[allow(dead_code)]
#[cfg(test)]
mod tests {
use super::*;
Expand Down
9 changes: 3 additions & 6 deletions src/result_enumerator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ use serde::{
use std::ptr;
use windows::core::VARIANT;
use windows::Win32::System::Ole::SafeArrayDestroy;
use windows::Win32::System::Variant::VariantClear;
use windows::Win32::System::Wmi::{
IEnumWbemClassObject, IWbemClassObject, CIMTYPE_ENUMERATION, WBEM_FLAG_ALWAYS,
WBEM_FLAG_NONSYSTEM_ONLY, WBEM_INFINITE,
Expand Down Expand Up @@ -46,7 +45,7 @@ impl IWbemClassWrapper {
)
}?;

let res = safe_array_to_vec_of_strings(unsafe { &*p_names });
let res = unsafe { safe_array_to_vec_of_strings(unsafe { &*p_names }) };

unsafe { SafeArrayDestroy(p_names) }?;

Expand All @@ -72,8 +71,6 @@ impl IWbemClassWrapper {
let property_value = Variant::from_variant(&vt_prop)?
.convert_into_cim_type(CIMTYPE_ENUMERATION(cim_type))?;

VariantClear(&mut vt_prop)?;

Ok(property_value)
}
}
Expand Down Expand Up @@ -149,8 +146,8 @@ impl<'a> Iterator for QueryResultEnumerator<'a> {
&objs[0]
);

let mut objs = objs.into_iter();
let pcls_ptr = objs.next().unwrap().ok_or(WMIError::NullPointerResult);
let [obj] = objs;
let pcls_ptr = obj.ok_or(WMIError::NullPointerResult);

match pcls_ptr {
Err(e) => Some(Err(e)),
Expand Down
57 changes: 23 additions & 34 deletions src/safearray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,16 @@ use crate::{
utils::{WMIError, WMIResult},
Variant,
};
use std::{iter::Iterator, ptr::null_mut, slice};
use std::{iter::Iterator, ptr::null_mut};
use windows::core::BSTR;
use windows::Win32::System::Com::SAFEARRAY;
use windows::Win32::System::Ole::{
SafeArrayAccessData, SafeArrayGetLBound, SafeArrayGetUBound, SafeArrayUnaccessData,
};
use windows::Win32::System::Ole::{SafeArrayAccessData, SafeArrayUnaccessData};
use windows::Win32::System::Variant::*;

#[derive(Debug)]
pub struct SafeArrayAccessor<'a, T> {
arr: &'a SAFEARRAY,
p_data: *mut T,
lower_bound: i32,
upper_bound: i32,
}

/// An accessor to SafeArray, which:
Expand All @@ -38,34 +34,34 @@ impl<'a, T> SafeArrayAccessor<'a, T> {
///
/// This function is unsafe as it is the caller's responsibility to verify that the array is
/// of items of type T.
pub fn new(arr: &'a SAFEARRAY) -> WMIResult<Self> {
pub unsafe fn new(arr: &'a SAFEARRAY) -> WMIResult<Self> {
let mut p_data = null_mut();

let lower_bound = unsafe { SafeArrayGetLBound(arr, 1)? };
let upper_bound = unsafe { SafeArrayGetUBound(arr, 1)? };
if arr.cDims != 1 {
return Err(WMIError::UnimplementedArrayItem);
}

unsafe { SafeArrayAccessData(arr, &mut p_data)? };

Ok(Self {
arr,
p_data: p_data as *mut T,
lower_bound,
upper_bound,
})
}

/// Return a slice which can access the data of the array.
pub fn as_slice(&self) -> &[T] {
// upper_bound can be -1, in which case the array is empty and we will return a 0 length slice.
let data_slice =
unsafe { slice::from_raw_parts(self.p_data, (self.upper_bound + 1) as usize) };
/// Return an iterator over the items of the array.
pub fn iter(&self) -> impl Iterator<Item = &'_ T> + '_ {
// Safety: We required the caller of `new` to ensure that the array is valid and contains only items of type T (and one dimensional).
// `SafeArrayAccessData` returns a pointer to the data of the array, which can be accessed for `arr.rgsabound[0].cElements` elements.
// See: https://learn.microsoft.com/en-us/windows/win32/api/oleauto/nf-oleauto-safearrayaccessdata#examples
let element_count = self.arr.rgsabound[0].cElements;

&data_slice[(self.lower_bound as usize)..]
(0..element_count).map(move |i| unsafe { &*self.p_data.offset(i as isize) })
}
}

impl<'a, T> Drop for SafeArrayAccessor<'a, T> {
fn drop(&mut self) {
// TOOD: Should we handle errors in some way?
unsafe {
let _result = SafeArrayUnaccessData(self.arr);
}
Expand All @@ -75,7 +71,7 @@ impl<'a, T> Drop for SafeArrayAccessor<'a, T> {
/// # Safety
///
/// The caller must ensure that the array is valid and contains only strings.
pub fn safe_array_to_vec_of_strings(arr: &SAFEARRAY) -> WMIResult<Vec<String>> {
pub unsafe fn safe_array_to_vec_of_strings(arr: &SAFEARRAY) -> WMIResult<Vec<String>> {
let items = safe_array_to_vec(arr, VT_BSTR)?;

let string_items = items
Expand All @@ -91,22 +87,16 @@ pub fn safe_array_to_vec_of_strings(arr: &SAFEARRAY) -> WMIResult<Vec<String>> {

/// # Safety
///
/// The caller must ensure that the array is valid.
pub fn safe_array_to_vec(arr: &SAFEARRAY, item_type: VARENUM) -> WMIResult<Vec<Variant>> {
/// The caller must ensure that the array is valid and contains elements on the specified type.
pub unsafe fn safe_array_to_vec(arr: &SAFEARRAY, item_type: VARENUM) -> WMIResult<Vec<Variant>> {
fn copy_type_to_vec<T, F>(arr: &SAFEARRAY, variant_builder: F) -> WMIResult<Vec<Variant>>
where
T: Copy,
F: Fn(T) -> Variant,
{
let mut items = vec![];

let accessor = SafeArrayAccessor::<T>::new(arr)?;

for item in accessor.as_slice().iter() {
items.push(variant_builder(*item));
}
let accessor = unsafe { SafeArrayAccessor::<T>::new(arr)? };

Ok(items)
Ok(accessor.iter().map(|item| variant_builder(*item)).collect())
}

match item_type {
Expand All @@ -121,13 +111,12 @@ pub fn safe_array_to_vec(arr: &SAFEARRAY, item_type: VARENUM) -> WMIResult<Vec<V
VT_R4 => copy_type_to_vec(arr, Variant::R4),
VT_R8 => copy_type_to_vec(arr, Variant::R8),
VT_BSTR => {
let mut items = vec![];
let accessor = unsafe { SafeArrayAccessor::<BSTR>::new(arr)? };

for item_bstr in accessor.as_slice().iter() {
items.push(Variant::String(item_bstr.try_into()?));
}
Ok(items)
accessor
.iter()
.map(|item| item.try_into().map(Variant::String).map_err(WMIError::from))
.collect()
}
// TODO: Add support for all other types of arrays.
_ => Err(WMIError::UnimplementedArrayItem),
Expand Down
19 changes: 6 additions & 13 deletions src/variant.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
};
use serde::Serialize;
use std::convert::TryFrom;
use windows::core::{IUnknown, Interface, BSTR, VARIANT};
use windows::core::{IUnknown, Interface, VARIANT};
use windows::Win32::Foundation::{VARIANT_BOOL, VARIANT_FALSE, VARIANT_TRUE};
use windows::Win32::System::Variant::*;
use windows::Win32::System::Wmi::{self, IWbemClassObject, CIMTYPE_ENUMERATION};
Expand Down Expand Up @@ -75,11 +75,10 @@ macro_rules! cast_num {
impl Variant {
/// Create a `Variant` instance from a raw `VARIANT`.
///
/// # Safety
///
/// This function is unsafe as it is the caller's responsibility to ensure that the VARIANT is correctly initialized.
pub fn from_variant(vt: &VARIANT) -> WMIResult<Variant> {
let vt = vt.as_raw();
/// Note: this function is safe since manipulating a `VARIANT` by hand is an *unsafe* operation,
/// so we can assume that the `VARIANT` is valid.
pub fn from_variant(variant: &VARIANT) -> WMIResult<Variant> {
let vt = variant.as_raw();
let variant_type = unsafe { vt.Anonymous.Anonymous.vt };

// variant_type has two 'forms':
Expand All @@ -102,13 +101,7 @@ impl Variant {
// Rust can infer the return type of `vt.*Val()` calls,
// but it's easier to read when the type is named explicitly.
let variant_value = match VARENUM(variant_type) {
VT_BSTR => {
let bstr_ptr = unsafe { BSTR::from_raw(vt.Anonymous.Anonymous.Anonymous.bstrVal) };
let bstr_as_str = bstr_ptr.to_string();
// We don't want to be the ones freeing the BSTR.
let _ = bstr_ptr.into_raw();
Variant::String(bstr_as_str)
}
VT_BSTR => Variant::String(variant.to_string()),
VT_I1 => {
let num = unsafe { vt.Anonymous.Anonymous.Anonymous.cVal };

Expand Down

0 comments on commit 5ba4022

Please sign in to comment.