use crate::utils::check_hres;
use crate::Variant;
use failure::Error;
use std::iter::Iterator;
use std::slice;
use widestring::WideCStr;
use winapi::{
shared::wtypes::*,
shared::{
minwindef::UINT,
ntdef::{LONG, NULL},
winerror::HRESULT,
wtypes::BSTR,
},
um::{oaidl::SAFEARRAY, oleauto::{
SafeArrayAccessData,
SafeArrayUnaccessData,
SafeArrayGetLBound,
SafeArrayGetUBound,
}},
};
#[derive(Debug)]
pub struct SafeArrayAccessor<T> {
arr: *mut SAFEARRAY,
p_data: *mut T,
lower_bound: i32,
upper_bound: i32,
}
impl<T> SafeArrayAccessor<T> {
pub unsafe fn new(arr: *mut SAFEARRAY) -> Result<Self, Error> {
let mut p_data = NULL;
let mut lower_bound: i32 = 0;
let mut upper_bound: i32 = 0;
unsafe {
check_hres(SafeArrayGetLBound(arr, 1, &mut lower_bound as _))?;
check_hres(SafeArrayGetUBound(arr, 1, &mut upper_bound as _))?;
check_hres(SafeArrayAccessData(arr, &mut p_data))?;
}
Ok(Self {
arr,
p_data: p_data as *mut T,
lower_bound,
upper_bound,
})
}
pub fn as_slice(&self) -> &[T] {
let data_slice =
unsafe { slice::from_raw_parts(self.p_data, (self.upper_bound + 1) as usize) };
&data_slice[(self.lower_bound as usize)..]
}
}
impl<T> Drop for SafeArrayAccessor<T> {
fn drop(&mut self) {
unsafe {
let _result = check_hres(SafeArrayUnaccessData(self.arr));
}
}
}
pub fn safe_array_to_vec_of_strings(arr: *mut SAFEARRAY) -> Result<Vec<String>, Error> {
let items = safe_array_to_vec(arr, VT_BSTR)?;
let string_items = items
.into_iter()
.map(|item| match item {
Variant::String(s) => s,
_ => unreachable!(),
})
.collect();
Ok(string_items)
}
pub fn safe_array_to_vec(arr: *mut SAFEARRAY, item_type: u32) -> Result<Vec<Variant>, Error> {
let mut items = vec![];
match item_type {
VT_I4 => {
let accessor = unsafe { SafeArrayAccessor::<i32>::new(arr)? };
for item in accessor.as_slice().iter() {
items.push(Variant::I4(*item))
}
}
VT_BSTR => {
let accessor = unsafe { SafeArrayAccessor::<BSTR>::new(arr)? };
for item_bstr in accessor.as_slice().iter() {
let item: &WideCStr = unsafe { WideCStr::from_ptr_str(*item_bstr) };
items.push(Variant::String(item.to_string()?));
}
}
_ => unimplemented!(),
}
Ok(items)
}