use alloc::{string::String, vec::Vec};
use core::{
ffi::c_void,
ptr::{copy_nonoverlapping, null_mut},
};
use windows_core::Interface;
use windows_sys::Win32::{
Foundation::{SysFreeString, VARIANT_FALSE, VARIANT_TRUE},
System::{
Com::{SAFEARRAY, SAFEARRAYBOUND},
Ole::{
SafeArrayAccessData, SafeArrayCreate,
SafeArrayCreateVector, SafeArrayPutElement,
SafeArrayUnaccessData,
},
Variant::{
VARIANT, VT_ARRAY, VT_BOOL,
VT_BSTR, VT_I4, VT_I8, VT_UI1,
VT_UNKNOWN, VT_VARIANT,
},
},
};
use crate::error::{ClrError, Result};
use crate::string::ComString;
pub trait Variant {
fn to_variant(&self) -> VARIANT;
fn var_type() -> u16;
}
impl Variant for String {
fn to_variant(&self) -> VARIANT {
let bstr = self.to_bstr();
let mut variant = unsafe { core::mem::zeroed::<VARIANT>() };
variant.Anonymous.Anonymous.vt = Self::var_type();
variant.Anonymous.Anonymous.Anonymous.bstrVal = bstr;
variant
}
fn var_type() -> u16 {
VT_BSTR
}
}
impl Variant for &str {
fn to_variant(&self) -> VARIANT {
let bstr = self.to_bstr();
let mut variant = unsafe { core::mem::zeroed::<VARIANT>() };
variant.Anonymous.Anonymous.vt = Self::var_type();
variant.Anonymous.Anonymous.Anonymous.bstrVal = bstr;
variant
}
fn var_type() -> u16 {
VT_BSTR
}
}
impl Variant for bool {
fn to_variant(&self) -> VARIANT {
let mut variant = unsafe { core::mem::zeroed::<VARIANT>() };
variant.Anonymous.Anonymous.vt = Self::var_type();
variant.Anonymous.Anonymous.Anonymous.boolVal =
if *self { VARIANT_TRUE } else { VARIANT_FALSE };
variant
}
fn var_type() -> u16 {
VT_BOOL
}
}
impl Variant for i32 {
fn to_variant(&self) -> VARIANT {
let mut variant = unsafe { core::mem::zeroed::<VARIANT>() };
variant.Anonymous.Anonymous.vt = Self::var_type();
variant.Anonymous.Anonymous.Anonymous.lVal = *self;
variant
}
fn var_type() -> u16 {
VT_I4
}
}
impl Variant for i64 {
fn to_variant(&self) -> VARIANT {
let mut variant = unsafe { core::mem::zeroed::<VARIANT>() };
variant.Anonymous.Anonymous.vt = Self::var_type();
variant.Anonymous.Anonymous.Anonymous.llVal = *self;
variant
}
fn var_type() -> u16 {
VT_I8
}
}
impl Variant for windows_core::IUnknown {
fn to_variant(&self) -> VARIANT {
let mut variant = unsafe { core::mem::zeroed::<VARIANT>() };
variant.Anonymous.Anonymous.vt = VT_UNKNOWN;
variant.Anonymous.Anonymous.Anonymous.punkVal = self.as_raw() as *mut _;
variant
}
fn var_type() -> u16 {
VT_UNKNOWN
}
}
pub fn create_safe_array_args<T: Variant>(args: Vec<T>) -> Result<*mut SAFEARRAY> {
unsafe {
let vartype = T::var_type();
let psa = SafeArrayCreateVector(vartype, 0, args.len() as u32);
if psa.is_null() {
return Err(ClrError::NullPointerError("SafeArrayCreateVector"));
}
for (i, arg) in args.iter().enumerate() {
let variant = arg.to_variant();
let index = i as i32;
let value_ptr = match vartype {
VT_BOOL => {
&variant.Anonymous.Anonymous.Anonymous.boolVal as *const _ as *const c_void
}
VT_I4 => &variant.Anonymous.Anonymous.Anonymous.lVal as *const _ as *const c_void,
VT_BSTR => variant.Anonymous.Anonymous.Anonymous.bstrVal as *const c_void,
_ => return Err(ClrError::VariantUnsupported),
};
let hr = SafeArrayPutElement(psa, &index, value_ptr);
if hr != 0 {
return Err(ClrError::ApiError("SafeArrayPutElement", hr));
}
if vartype == VT_BSTR {
SysFreeString(variant.Anonymous.Anonymous.Anonymous.bstrVal);
}
}
let args = SafeArrayCreateVector(VT_VARIANT, 0, 1);
let mut var_array = core::mem::zeroed::<VARIANT>();
var_array.Anonymous.Anonymous.vt = VT_ARRAY | vartype;
var_array.Anonymous.Anonymous.Anonymous.parray = psa;
let index = 0;
let hr = SafeArrayPutElement(
args,
&index,
&mut var_array as *const VARIANT as *const c_void,
);
if hr != 0 {
return Err(ClrError::ApiError("SafeArrayPutElement (2)", hr));
}
Ok(args)
}
}
pub fn create_safe_args(args: Vec<VARIANT>) -> Result<*mut SAFEARRAY> {
unsafe {
let arg = SafeArrayCreateVector(VT_VARIANT, 0, args.len() as u32);
for (i, var) in args.iter().enumerate() {
let index = i as i32;
let mut variant = *var;
let hr = SafeArrayPutElement(
arg,
&index,
&mut variant as *const VARIANT as *const c_void
);
if hr != 0 {
return Err(ClrError::ApiError("SafeArrayPutElement", hr));
}
}
Ok(arg)
}
}
pub fn create_safe_array_buffer(data: &[u8]) -> Result<*mut SAFEARRAY> {
let len: u32 = data.len() as u32;
let bounds = SAFEARRAYBOUND {
cElements: data.len() as _,
lLbound: 0,
};
unsafe {
let sa = SafeArrayCreate(VT_UI1, 1, &bounds);
if sa.is_null() {
return Err(ClrError::NullPointerError("SafeArrayCreate"));
}
let mut p_data = null_mut();
let mut hr = SafeArrayAccessData(sa, &mut p_data);
if hr != 0 {
return Err(ClrError::ApiError("SafeArrayAccessData", hr));
}
copy_nonoverlapping(data.as_ptr(), p_data as *mut u8, len as usize);
hr = SafeArrayUnaccessData(sa);
if hr != 0 {
return Err(ClrError::ApiError("SafeArrayUnaccessData", hr));
}
Ok(sa)
}
}