use std::{convert::TryFrom, ptr};
use log::trace;
use widestring::WideCString;
use winapi::{shared::{guiddef::IID_NULL, minwindef::{UINT, WORD}, winerror::NOERROR, wtypes::{VT_ARRAY, VT_SAFEARRAY}}, um::{
oaidl::{DISPID, DISPID_PROPERTYPUT, DISPPARAMS, EXCEPINFO, LPDISPATCH, VARIANT},
oleauto::{
VariantClear, VariantInit, DISPATCH_METHOD, DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT,
},
winnls::GetUserDefaultLCID,
}};
use win_variant::{free_variant, free_variants, VariantArg, VariantResult};
use super::Error;
#[derive(PartialEq)]
enum DispatchOperation {
Method,
GetProperty,
PutProperty,
}
impl From<DispatchOperation> for WORD {
fn from(value: DispatchOperation) -> WORD {
match value {
DispatchOperation::Method => DISPATCH_METHOD,
DispatchOperation::GetProperty => DISPATCH_PROPERTYGET,
DispatchOperation::PutProperty => DISPATCH_PROPERTYPUT,
}
}
}
pub struct DispatchInterface {
dispatch: LPDISPATCH,
released: bool,
}
impl DispatchInterface {
pub fn new(dispatch: LPDISPATCH) -> DispatchInterface {
DispatchInterface {
dispatch,
released: false,
}
}
fn get_ids_of_name(&self, name: &str) -> Result<DISPID, Error> {
let mut wide: Vec<u16> = WideCString::from_str(name)?.into_vec();
let mut names = vec![wide.as_mut_ptr()];
let mut id: DISPID = 0;
let result = unsafe {
if let Some(r) = self.dispatch.as_ref() {
r.GetIDsOfNames(
&IID_NULL,
names.as_mut_ptr(),
1,
GetUserDefaultLCID(),
&mut id,
)
} else {
return Err(Error::NullDispatchPointer);
}
};
if result != NOERROR {
return Err(result.into());
}
Ok(id)
}
fn invoke(
&self,
operation: DispatchOperation,
name: &str,
args: Option<Vec<VariantArg>>,
) -> Result<VARIANT, Error> {
let command_id = self.get_ids_of_name(name)?;
let mut args: Option<Vec<VARIANT>> = match args {
Some(mut args) => {
args.reverse();
Some(args.into_iter().map(|a| a.into()).collect())
}
None => None,
};
let mut params: DISPPARAMS = DISPPARAMS::default();
if let Some(args) = args.as_mut() {
params.cArgs = args.len() as UINT;
params.rgvarg = &mut args[0];
}
let mut named_params = if operation == DispatchOperation::PutProperty {
Some(vec![DISPID_PROPERTYPUT])
} else {
None
};
if let Some(named_params) = named_params.as_mut() {
params.rgdispidNamedArgs = &mut named_params[0];
params.cNamedArgs = named_params.len() as UINT;
}
let mut result: VARIANT = VARIANT::default();
unsafe { VariantInit(&mut result) };
let mut expect = EXCEPINFO::default();
let mut arg_error: UINT = 0;
let hr = unsafe {
if let Some(r) = self.dispatch.as_ref() {
r.Invoke(
command_id,
&IID_NULL,
GetUserDefaultLCID(),
operation.into(),
&mut params,
&mut result,
&mut expect,
&mut arg_error,
)
} else {
VariantClear(&mut result);
if let Some(args) = args.as_mut() {
free_variants(args);
}
return Err(Error::NullDispatchPointer);
}
};
if let Some(args) = args.as_mut() {
free_variants(args);
}
if hr != NOERROR {
return Err(hr.into());
}
Ok(result)
}
pub fn call(&self, name: &str, args: Option<Vec<VariantArg>>) -> Result<VariantResult, Error> {
match self.invoke(DispatchOperation::Method, name, args) {
Err(e) => Err(e),
Ok(mut v) => {
let result = VariantResult::try_from(v);
unsafe {
let vt = v.n1.n2().vt as u32;
if vt != VT_SAFEARRAY && vt&VT_ARRAY == 0 {
free_variant(&mut v);
}
}
Ok(result?)
}
}
}
pub fn get(&self, name: &str, args: Option<Vec<VariantArg>>) -> Result<VariantResult, Error> {
match self.invoke(DispatchOperation::GetProperty, name, args) {
Err(e) => Err(e),
Ok(mut v) => {
let result = VariantResult::try_from(v);
unsafe {
let vt = v.n1.n2().vt as u32;
if vt != VT_SAFEARRAY && vt&VT_ARRAY == 0 {
free_variant(&mut v);
}
}
Ok(result?)
}
}
}
pub fn put(&self, name: &str, args: Option<Vec<VariantArg>>) -> Result<VariantResult, Error> {
match self.invoke(DispatchOperation::PutProperty, name, args) {
Err(e) => Err(e),
Ok(mut v) => {
let result = VariantResult::try_from(v);
unsafe {
let vt = v.n1.n2().vt as u32;
if vt != VT_SAFEARRAY && vt&VT_ARRAY == 0 {
free_variant(&mut v);
}
}
Ok(result?)
}
}
}
fn release(&mut self) {
if self.released {
return;
}
unsafe {
if let Some(r) = self.dispatch.as_ref() {
r.Release();
}
self.released = true;
self.dispatch = ptr::null_mut();
}
}
}
impl Drop for DispatchInterface {
fn drop(&mut self) {
trace!("dropping Dispatch interface");
self.release();
}
}