win_idispatch/
dispatch.rs

1use std::{convert::TryFrom, ptr};
2
3use log::trace;
4use widestring::WideCString;
5use winapi::{shared::{guiddef::IID_NULL, minwindef::{UINT, WORD}, winerror::NOERROR, wtypes::{VT_ARRAY, VT_SAFEARRAY}}, um::{
6        oaidl::{DISPID, DISPID_PROPERTYPUT, DISPPARAMS, EXCEPINFO, LPDISPATCH, VARIANT},
7        oleauto::{
8            VariantClear, VariantInit, DISPATCH_METHOD, DISPATCH_PROPERTYGET, DISPATCH_PROPERTYPUT,
9        },
10        winnls::GetUserDefaultLCID,
11    }};
12
13use win_variant::{free_variant, free_variants, VariantArg, VariantResult};
14
15use super::Error;
16
17#[derive(PartialEq)]
18enum DispatchOperation {
19    Method,
20    GetProperty,
21    PutProperty,
22}
23
24impl From<DispatchOperation> for WORD {
25    fn from(value: DispatchOperation) -> WORD {
26        match value {
27            DispatchOperation::Method => DISPATCH_METHOD,
28            DispatchOperation::GetProperty => DISPATCH_PROPERTYGET,
29            DispatchOperation::PutProperty => DISPATCH_PROPERTYPUT,
30        }
31    }
32}
33
34/// Holds the pointer to the IDispatch interface. Drop is implemented to
35/// correctly release the pointer when dropped.
36pub struct DispatchInterface {
37    dispatch: LPDISPATCH,
38    released: bool,
39}
40
41impl DispatchInterface {
42    /// Creates a new Dispatch Interface using the provided pointer. The pointer
43    /// should not be managed by anything else after provided to the interface.
44    pub fn new(dispatch: LPDISPATCH) -> DispatchInterface {
45        DispatchInterface {
46            dispatch,
47            released: false,
48        }
49    }
50
51    fn get_ids_of_name(&self, name: &str) -> Result<DISPID, Error> {
52        let mut wide: Vec<u16> = WideCString::from_str(name)?.into_vec();
53        let mut names = vec![wide.as_mut_ptr()];
54        let mut id: DISPID = 0;
55        let result = unsafe {
56            if let Some(r) = self.dispatch.as_ref() {
57                r.GetIDsOfNames(
58                    &IID_NULL,
59                    names.as_mut_ptr(),
60                    1,
61                    GetUserDefaultLCID(),
62                    &mut id,
63                )
64            } else {
65                return Err(Error::NullDispatchPointer);
66            }
67        };
68        if result != NOERROR {
69            return Err(result.into());
70        }
71        Ok(id)
72    }
73
74    fn invoke(
75        &self,
76        operation: DispatchOperation,
77        name: &str,
78        args: Option<Vec<VariantArg>>,
79    ) -> Result<VARIANT, Error> {
80        let command_id = self.get_ids_of_name(name)?;
81        let mut args: Option<Vec<VARIANT>> = match args {
82            Some(mut args) => {
83                // Arguments must be reversed
84                args.reverse();
85                Some(args.into_iter().map(|a| a.into()).collect())
86            }
87            None => None,
88        };
89        let mut params: DISPPARAMS = DISPPARAMS::default();
90        if let Some(args) = args.as_mut() {
91            params.cArgs = args.len() as UINT;
92            params.rgvarg = &mut args[0];
93        }
94        let mut named_params = if operation == DispatchOperation::PutProperty {
95            Some(vec![DISPID_PROPERTYPUT])
96        } else {
97            None
98        };
99        if let Some(named_params) = named_params.as_mut() {
100            params.rgdispidNamedArgs = &mut named_params[0];
101            params.cNamedArgs = named_params.len() as UINT;
102        }
103
104        let mut result: VARIANT = VARIANT::default();
105        unsafe { VariantInit(&mut result) };
106
107        let mut expect = EXCEPINFO::default();
108
109        let mut arg_error: UINT = 0;
110        let hr = unsafe {
111            if let Some(r) = self.dispatch.as_ref() {
112                r.Invoke(
113                    command_id,
114                    &IID_NULL,
115                    GetUserDefaultLCID(),
116                    operation.into(),
117                    &mut params,
118                    &mut result,
119                    &mut expect,
120                    &mut arg_error,
121                )
122            } else {
123                VariantClear(&mut result);
124                if let Some(args) = args.as_mut() {
125                    free_variants(args);
126                }
127                return Err(Error::NullDispatchPointer);
128            }
129        };
130        if let Some(args) = args.as_mut() {
131            free_variants(args);
132        }
133        if hr != NOERROR {
134            return Err(hr.into());
135        }
136        Ok(result)
137    }
138    /// This executes a dispatch method operation for the name provided and
139    /// passes the arguments to the operation.
140    pub fn call(&self, name: &str, args: Option<Vec<VariantArg>>) -> Result<VariantResult, Error> {
141        match self.invoke(DispatchOperation::Method, name, args) {
142            Err(e) => Err(e),
143            Ok(mut v) => {
144                let result = VariantResult::try_from(v);
145                unsafe {
146                    let vt = v.n1.n2().vt as u32;
147                    if vt != VT_SAFEARRAY && vt&VT_ARRAY == 0 {
148                        free_variant(&mut v);
149                    }
150                }
151                Ok(result?)
152            }
153        }
154    }
155
156    /// This executes a property get operation for the name provided and
157    /// passes the arguments to the operation.
158    pub fn get(&self, name: &str, args: Option<Vec<VariantArg>>) -> Result<VariantResult, Error> {
159        match self.invoke(DispatchOperation::GetProperty, name, args) {
160            Err(e) => Err(e),
161            Ok(mut v) => {
162                let result = VariantResult::try_from(v);
163                unsafe {
164                    let vt = v.n1.n2().vt as u32;
165                    if vt != VT_SAFEARRAY && vt&VT_ARRAY == 0 {
166                        free_variant(&mut v);
167                    }
168                }
169                Ok(result?)
170            }
171        }
172    }
173    /// This executes a property put operation for the name provided and
174    /// passes the arguments to the operation.
175    pub fn put(&self, name: &str, args: Option<Vec<VariantArg>>) -> Result<VariantResult, Error> {
176        match self.invoke(DispatchOperation::PutProperty, name, args) {
177            Err(e) => Err(e),
178            Ok(mut v) => {
179                let result = VariantResult::try_from(v);
180                unsafe {
181                    let vt = v.n1.n2().vt as u32;
182                    if vt != VT_SAFEARRAY && vt&VT_ARRAY == 0 {
183                        free_variant(&mut v);
184                    }
185                }
186                Ok(result?)
187            }
188        }
189    }
190
191    fn release(&mut self) {
192        if self.released {
193            return;
194        }
195        unsafe {
196            if let Some(r) = self.dispatch.as_ref() {
197                r.Release();
198            }
199            self.released = true;
200            self.dispatch = ptr::null_mut();
201        }
202    }
203}
204
205impl Drop for DispatchInterface {
206    fn drop(&mut self) {
207        trace!("dropping Dispatch interface");
208        self.release();
209    }
210}