Skip to content

Commit

Permalink
Change from_variant to take ownership, Improve SafeArray wrapper …
Browse files Browse the repository at this point in the history
…code
  • Loading branch information
ohadravid committed Sep 8, 2024
1 parent bd811e9 commit 130a2f0
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 38 deletions.
7 changes: 2 additions & 5 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 @@ -69,11 +68,9 @@ impl IWbemClassWrapper {
None,
)?;

let property_value = Variant::from_variant(&vt_prop)?
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
44 changes: 22 additions & 22 deletions src/safearray.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ 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,
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,28 +36,29 @@ 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 = *const T> + '_ {
// Safety: We require the caller 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) as *const _ })
}
}

Expand All @@ -75,7 +74,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,19 +90,19 @@ 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)?;
let accessor = unsafe { SafeArrayAccessor::<T>::new(arr)? };

for item in accessor.as_slice().iter() {
items.push(variant_builder(*item));
for item in accessor.iter() {
items.push(variant_builder(unsafe { *item }));
}

Ok(items)
Expand All @@ -124,7 +123,8 @@ pub fn safe_array_to_vec(arr: &SAFEARRAY, item_type: VARENUM) -> WMIResult<Vec<V
let mut items = vec![];
let accessor = unsafe { SafeArrayAccessor::<BSTR>::new(arr)? };

for item_bstr in accessor.as_slice().iter() {
for item_bstr in accessor.iter() {
let item_bstr = unsafe { &*item_bstr };
items.push(Variant::String(item_bstr.try_into()?));
}
Ok(items)
Expand Down
16 changes: 5 additions & 11 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,9 @@ 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` is generally an *unsafe* operation.
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 @@ -103,11 +101,7 @@ impl Variant {
// 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)
Variant::String(variant.to_string())
}
VT_I1 => {
let num = unsafe { vt.Anonymous.Anonymous.Anonymous.cVal };
Expand Down

0 comments on commit 130a2f0

Please sign in to comment.