use crate::{
Variant,
utils::{WMIError, WMIResult},
variant::IUnknownWrapper,
};
use std::{
iter::Iterator,
ptr::{NonNull, null_mut},
};
use windows::Win32::System::Com::SAFEARRAY;
use windows::Win32::System::Ole::{SafeArrayAccessData, SafeArrayUnaccessData};
use windows::Win32::System::Variant::*;
use windows::{
Win32::Foundation::VARIANT_BOOL,
core::{BSTR, IUnknown, Interface},
};
#[derive(Debug)]
pub struct SafeArrayAccessor<T> {
arr: NonNull<SAFEARRAY>,
p_data: *mut T,
}
impl<T> SafeArrayAccessor<T> {
pub unsafe fn new(arr: NonNull<SAFEARRAY>) -> WMIResult<Self> {
let mut p_data = null_mut();
if unsafe { (*arr.as_ptr()).cDims } != 1 {
return Err(WMIError::UnimplementedArrayItem);
}
unsafe { SafeArrayAccessData(arr.as_ptr(), &mut p_data)? };
Ok(Self {
arr,
p_data: p_data as *mut T,
})
}
pub fn len(&self) -> u32 {
unsafe { (*self.arr.as_ptr()).rgsabound[0].cElements }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn iter(&self) -> impl Iterator<Item = &'_ T> + '_ {
let element_count = self.len();
(0..element_count).map(move |i| unsafe { &*self.p_data.offset(i as isize) })
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &'_ mut T> + '_ {
let element_count = self.len();
(0..element_count).map(move |i| unsafe { &mut *self.p_data.offset(i as isize) })
}
}
impl<T> Drop for SafeArrayAccessor<T> {
fn drop(&mut self) {
unsafe {
let _result = SafeArrayUnaccessData(self.arr.as_ptr());
}
}
}
pub unsafe fn safe_array_to_vec_of_strings(arr: NonNull<SAFEARRAY>) -> WMIResult<Vec<String>> {
let accessor = unsafe { SafeArrayAccessor::<BSTR>::new(arr)? };
accessor
.iter()
.map(|item| item.try_into().map_err(WMIError::from))
.collect()
}
pub unsafe fn safe_array_to_vec(
arr: NonNull<SAFEARRAY>,
item_type: VARENUM,
) -> WMIResult<Vec<Variant>> {
fn copy_type_to_vec<T, F>(
arr: NonNull<SAFEARRAY>,
variant_builder: F,
) -> WMIResult<Vec<Variant>>
where
T: Copy,
F: Fn(T) -> Variant,
{
let accessor = unsafe { SafeArrayAccessor::<T>::new(arr)? };
Ok(accessor.iter().map(|item| variant_builder(*item)).collect())
}
match item_type {
VT_I1 => copy_type_to_vec(arr, Variant::I1),
VT_I2 => copy_type_to_vec(arr, Variant::I2),
VT_I4 => copy_type_to_vec(arr, Variant::I4),
VT_I8 => copy_type_to_vec(arr, Variant::I8),
VT_UI1 => copy_type_to_vec(arr, Variant::UI1),
VT_UI2 => copy_type_to_vec(arr, Variant::UI2),
VT_UI4 => copy_type_to_vec(arr, Variant::UI4),
VT_UI8 => copy_type_to_vec(arr, Variant::UI8),
VT_R4 => copy_type_to_vec(arr, Variant::R4),
VT_R8 => copy_type_to_vec(arr, Variant::R8),
VT_BSTR => {
let v = unsafe { safe_array_to_vec_of_strings(arr) }?;
Ok(v.into_iter().map(Variant::String).collect())
}
VT_BOOL => copy_type_to_vec::<VARIANT_BOOL, _>(arr, |item| Variant::Bool(item.as_bool())),
VT_UNKNOWN => {
let accessor = unsafe { SafeArrayAccessor::<*mut _>::new(arr)? };
accessor
.iter()
.map(|item| {
unsafe { IUnknown::from_raw_borrowed(item) }
.cloned()
.map(|item| Variant::Unknown(IUnknownWrapper::new(item)))
.ok_or(WMIError::NullPointerResult)
})
.collect()
}
_ => Err(WMIError::UnimplementedArrayItem),
}
}