1use crate::convert::VariantConversionError;
4use crate::variant::Variant;
5use thiserror::Error;
6use widestring::U16CString;
7use windows::core::{Error as WinError, GUID, PCWSTR};
8use windows::Win32::Foundation::{DISP_E_EXCEPTION, DISP_E_PARAMNOTFOUND, DISP_E_TYPEMISMATCH};
9use windows::Win32::System::Com::{
10 IDispatch, DISPATCH_FLAGS, DISPATCH_METHOD, DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT,
11 DISPPARAMS, EXCEPINFO,
12};
13use windows::Win32::System::Ole::DISPID_PROPERTYPUT;
14use windows::Win32::System::Variant::VARIANT;
15
16pub trait IDispatchExt {
17 fn get(&self, name: &str) -> Result<Variant, IDispatchError>;
18 fn put(&self, name: &str, value: Variant) -> Result<(), IDispatchError>;
19 fn call(&self, name: &str, args: Vec<Variant>) -> Result<Variant, IDispatchError>;
20}
21
22#[derive(Error, Debug)]
23pub enum IDispatchError {
24 #[error("Couldn't convert args to VARIANT")]
25 VariantConversion(#[from] VariantConversionError),
26 #[error("Couldn't convert string to BSTR")]
27 StringConversion(#[from] widestring::error::ContainsNul<u16>),
28 #[error("Win32 error")]
29 GenericWin32(#[from] WinError),
30 #[error("COM exception")]
31 Exception(EXCEPINFO),
32 #[error("COM argument error {0} for argument {1}")]
33 Argument(ComArgumentError, usize),
34}
35
36#[derive(Error, Debug)]
37pub enum ComArgumentError {
38 #[error("The value's type does not match the expected type for the parameter")]
39 TypeMismatch,
40 #[error("A required parameter was missing")]
41 ParameterNotFound,
42}
43
44const LOCALE_USER_DEFAULT: u32 = 0x0400;
45const LOCALE_SYSTEM_DEFAULT: u32 = 0x0800;
46
47fn invoke(
48 obj: &IDispatch,
49 name: &str,
50 dp: &mut DISPPARAMS,
51 flags: DISPATCH_FLAGS,
52) -> Result<Variant, IDispatchError> {
53 let mut name = U16CString::from_str(name).map_err(IDispatchError::StringConversion)?;
54 let mut id = 0i32;
55 unsafe {
56 obj.GetIDsOfNames(
57 &GUID::default(),
58 &PCWSTR(name.as_mut_ptr()),
59 1,
60 LOCALE_USER_DEFAULT,
61 (&mut id) as *mut i32,
62 )
63 }
64 .map_err(IDispatchError::GenericWin32)?;
65
66 let mut excep = EXCEPINFO::default();
67 let mut arg_err = 0;
68 let mut result = VARIANT::default();
69
70 let res = unsafe {
71 obj.Invoke(
72 id,
73 &GUID::default(),
74 LOCALE_SYSTEM_DEFAULT,
75 flags,
76 dp,
77 Some(&mut result),
78 Some(&mut excep),
79 Some(&mut arg_err),
80 )
81 };
82
83 match res {
84 Ok(_) => result.try_into().map_err(Into::into),
85 Err(e) => Err(match e.code() {
86 DISP_E_EXCEPTION => IDispatchError::Exception(excep),
87 DISP_E_TYPEMISMATCH => {
88 IDispatchError::Argument(ComArgumentError::TypeMismatch, arg_err as usize)
89 }
90 DISP_E_PARAMNOTFOUND => {
91 IDispatchError::Argument(ComArgumentError::ParameterNotFound, arg_err as usize)
92 }
93 _ => IDispatchError::GenericWin32(e),
94 }),
95 }
96}
97
98#[macro_export]
106macro_rules! get {
107 ($obj:expr, $name:ident) => {{
108 use variant_rs::dispatch::IDispatchExt;
109 $obj.get(stringify!($name))
110 }};
111}
112
113#[macro_export]
121macro_rules! put {
122 ($obj:expr, $name:ident, $value:expr) => {{
123 use variant_rs::dispatch::IDispatchExt;
124 let val: Variant = $value.into();
125 $obj.put(stringify!($name), val)
126 }};
127}
128
129#[macro_export]
137macro_rules! call {
138 ($obj:expr, $name:ident($($arg:expr),*)) => {
139 {
140 use variant_rs::dispatch::IDispatchExt;
141 let args = vec![$((&$arg).into()),*];
142 $obj.call(stringify!($name), args)
143 }
144 };
145}
146
147impl IDispatchExt for IDispatch {
148 fn get(&self, name: &str) -> Result<Variant, IDispatchError> {
152 let mut dp = DISPPARAMS::default();
153 invoke(self, name, &mut dp, DISPATCH_PROPERTYGET)
154 }
155
156 fn put(&self, name: &str, value: Variant) -> Result<(), IDispatchError> {
160 let mut value = value.try_into()?;
161 let mut dp = DISPPARAMS {
162 cArgs: 1,
163 rgvarg: &mut value,
164 cNamedArgs: 1,
165 ..Default::default()
166 };
167 let mut id = DISPID_PROPERTYPUT;
168 dp.rgdispidNamedArgs = &mut id as *mut _;
169 invoke(self, name, &mut dp, DISPATCH_PROPERTYPUT)?;
170 Ok(())
171 }
172
173 fn call(&self, name: &str, args: Vec<Variant>) -> Result<Variant, IDispatchError> {
177 let mut dp = DISPPARAMS::default();
178 let args: Vec<VARIANT> = args
179 .into_iter()
180 .rev()
181 .map(|v| v.try_into().map_err(IDispatchError::from))
182 .collect::<Result<_, _>>()?;
183 dp.cArgs = args.len() as u32;
184 dp.rgvarg = args.as_ptr() as *mut _;
185 invoke(self, name, &mut dp, DISPATCH_METHOD)
186 }
187}