use super::SafeVariant;
use crate::errors::{SageError, SageResult};
use windows::{core::*, Win32::System::Com::*, Win32::System::Ole::*, Win32::System::Variant::*};
pub struct SafeDispatch<'a> {
dispatch: &'a IDispatch,
}
impl<'a> SafeDispatch<'a> {
pub fn new(dispatch: &'a IDispatch) -> Self {
SafeDispatch { dispatch }
}
#[allow(dead_code)] pub fn call_method(&self, method_id: i32, method_name: &str) -> SageResult<SafeVariant> {
self.call_method_with_params(method_id, method_name, &[])
}
pub fn call_method_with_params(
&self,
method_id: i32,
method_name: &str,
params: &[SafeVariant],
) -> SageResult<SafeVariant> {
unsafe {
let mut result = VARIANT::default();
let mut excep_info = EXCEPINFO::default();
let mut arg_err: u32 = 0;
let mut variant_params = Vec::new();
for param in params {
variant_params.push(param.to_variant()?);
}
let dispparams = DISPPARAMS {
rgvarg: if variant_params.is_empty() {
std::ptr::null_mut()
} else {
variant_params.as_ptr() as *mut VARIANT
},
rgdispidNamedArgs: std::ptr::null_mut(),
cArgs: variant_params.len() as u32,
cNamedArgs: 0,
};
let hr = self.dispatch.Invoke(
method_id,
&GUID::zeroed(),
0,
DISPATCH_METHOD | DISPATCH_PROPERTYGET,
&dispparams,
Some(&mut result),
Some(&mut excep_info),
Some(&mut arg_err),
);
match hr {
Ok(_) => SafeVariant::from_variant(result),
Err(e) => {
let error_msg = if !excep_info.bstrDescription.is_empty() {
excep_info.bstrDescription.to_string()
} else {
format!("Erreur COM: {}", e.message().to_string_lossy())
};
Err(SageError::method_call(method_name, method_id, &error_msg))
}
}
}
}
#[allow(dead_code)] pub fn get_property(&self, property_id: i32, property_name: &str) -> SageResult<SafeVariant> {
unsafe {
let mut result = VARIANT::default();
let mut excep_info = EXCEPINFO::default();
let mut arg_err: u32 = 0;
let dispparams = DISPPARAMS {
rgvarg: std::ptr::null_mut(),
rgdispidNamedArgs: std::ptr::null_mut(),
cArgs: 0,
cNamedArgs: 0,
};
let hr = self.dispatch.Invoke(
property_id,
&GUID::zeroed(),
0,
DISPATCH_PROPERTYGET,
&dispparams,
Some(&mut result),
Some(&mut excep_info),
Some(&mut arg_err),
);
match hr {
Ok(_) => SafeVariant::from_variant(result),
Err(e) => {
let error_msg = if !excep_info.bstrDescription.is_empty() {
excep_info.bstrDescription.to_string()
} else {
format!("Erreur COM: {}", e.message().to_string_lossy())
};
Err(SageError::method_call(
property_name,
property_id,
&error_msg,
))
}
}
}
}
#[allow(dead_code)] pub fn set_property(
&self,
property_id: i32,
property_name: &str,
value: SafeVariant,
) -> SageResult<()> {
unsafe {
let mut excep_info = EXCEPINFO::default();
let mut arg_err: u32 = 0;
let variant_value = value.to_variant()?;
let named_arg_id = DISPID_PROPERTYPUT;
let dispparams = DISPPARAMS {
rgvarg: &variant_value as *const VARIANT as *mut VARIANT,
rgdispidNamedArgs: &named_arg_id as *const i32 as *mut i32,
cArgs: 1,
cNamedArgs: 1,
};
let hr = self.dispatch.Invoke(
property_id,
&GUID::zeroed(),
0,
DISPATCH_PROPERTYPUT,
&dispparams,
None,
Some(&mut excep_info),
Some(&mut arg_err),
);
match hr {
Ok(_) => Ok(()),
Err(e) => {
let error_msg = if !excep_info.bstrDescription.is_empty() {
excep_info.bstrDescription.to_string()
} else {
format!("Erreur COM: {}", e.message().to_string_lossy())
};
Err(SageError::method_call(
property_name,
property_id,
&error_msg,
))
}
}
}
}
pub fn get_method_id(&self, method_name: &str) -> SageResult<i32> {
unsafe {
let name_wide: Vec<u16> = method_name
.encode_utf16()
.chain(std::iter::once(0))
.collect();
let mut dispatch_id = 0i32;
let name_ptr = PCWSTR(name_wide.as_ptr());
self.dispatch
.GetIDsOfNames(&GUID::zeroed(), &name_ptr, 1, 0, &mut dispatch_id)
.map_err(|e| {
SageError::method_call(
method_name,
-1,
&format!(
"Méthode '{}' non trouvée: {}",
method_name,
e.message().to_string_lossy()
),
)
})?;
Ok(dispatch_id)
}
}
pub fn call_method_by_name(
&self,
method_name: &str,
params: &[SafeVariant],
) -> SageResult<SafeVariant> {
let method_id = self.get_method_id(method_name)?;
self.call_method_with_params(method_id, method_name, params)
}
#[allow(dead_code)] pub fn get_property_by_name(&self, property_name: &str) -> SageResult<SafeVariant> {
let property_id = self.get_method_id(property_name)?;
self.get_property(property_id, property_name)
}
pub fn set_property_by_name(&self, property_name: &str, value: SafeVariant) -> SageResult<()> {
let property_id = self.get_method_id(property_name)?;
self.set_property(property_id, property_name, value)
}
pub fn call_property_put(&self, property_name: &str, params: &[SafeVariant]) -> SageResult<()> {
if params.len() != 1 {
return Err(SageError::MethodCallError {
method_name: property_name.to_string(),
method_id: -1,
message: format!(
"PROPPUT nécessite exactement 1 paramètre, {} fourni",
params.len()
),
});
}
self.set_property_by_name(property_name, params[0].clone())
}
}
pub trait FromDispatch: Sized {
fn from_dispatch(dispatch: IDispatch) -> SageResult<Self>;
}
#[cfg(test)]
mod tests {
#[test]
fn test_safe_dispatch_structure() {
}
}