rs_dispatch/com/
com_module.rs

1#![allow(dead_code)]
2
3use anyhow::{anyhow, Context};
4use std::env;
5use windows::core::*;
6use windows::Win32::System::Com::*;
7use windows::Win32::System::Ole::*;
8
9const LOCALE_USER_DEFAULT: u32 = 0x0400;
10const LOCALE_SYSTEM_DEFAULT: u32 = 0x0800;
11
12pub struct Variant(VARIANT);
13
14impl From<bool> for Variant {
15    fn from(value: bool) -> Self {
16        Self(value.into())
17    }
18}
19
20impl From<i32> for Variant {
21    fn from(value: i32) -> Self {
22        Self(value.into())
23    }
24}
25
26impl From<i64> for Variant {
27    fn from(value: i64) -> Self {
28        Self(value.into())
29    }
30}
31
32impl From<f64> for Variant {
33    fn from(value: f64) -> Self {
34        Self(value.into())
35    }
36}
37
38impl From<&str> for Variant {
39    fn from(value: &str) -> Self {
40        Self(BSTR::from(value).into())
41    }
42}
43
44impl From<&String> for Variant {
45    fn from(value: &String) -> Self {
46        Self(BSTR::from(value).into())
47    }
48}
49
50impl Variant {
51    pub fn bool(&self) -> anyhow::Result<bool> {
52        Ok(bool::try_from(&self.0)?)
53    }
54
55    pub fn int(&self) -> anyhow::Result<i32> {
56        Ok(i32::try_from(&self.0)?)
57    }
58    pub fn long(&self) -> anyhow::Result<i64> {
59        Ok(i64::try_from(&self.0)?)
60    }
61    pub fn float(&self) -> anyhow::Result<f64> {
62        Ok(f64::try_from(&self.0)?)
63    }
64
65    pub fn string(&self) -> anyhow::Result<String> {
66        Ok(BSTR::try_from(&self.0)?.to_string())
67    }
68
69    pub fn idispatch(&self) -> anyhow::Result<IDispatchW> {
70        Ok(IDispatchW(IDispatch::try_from(&self.0)?))
71    }
72
73    pub fn vt(&self) -> u16 {
74        unsafe { self.0.as_raw().Anonymous.Anonymous.vt }
75    }
76}
77/// The IDispatchW is a wrapper structure for the [`windows::Win32::System::Com::IDispatch`] interface.   
78/// It will be used for **all** of the IDispatch related actions and should always be called.   
79/// @0:[IDispatch] -> Pass the raw IDispatch.
80pub struct IDispatchW(pub IDispatch);
81impl IDispatchW {
82    /// This is the general invoke method for the idispatch.   
83    /// Instead of using this one you can use the [IDispatchW::get()] [IDispatchW::put()]   
84    /// or use the relative data type for more convenient cast.   
85    /// Use vec![] for empty args and .into() to convert it to Variant.
86    pub fn invoke(
87        &self,
88        flags: DISPATCH_FLAGS,
89        name: &str,
90        mut args: Vec<Variant>,
91    ) -> anyhow::Result<Variant> {
92        unsafe {
93            let mut dispatch_id = 0;
94            self.0
95                .GetIDsOfNames(
96                    &GUID::default(),
97                    &PCWSTR::from_raw(HSTRING::from(name).as_ptr()),
98                    1,
99                    LOCALE_USER_DEFAULT,
100                    &mut dispatch_id,
101                ).with_context(|| "GetIDsOfNames Failiure!")?;
102            let mut dispatch_param = DISPPARAMS::default();
103            let mut dispatch_named = DISPID_PROPERTYPUT;
104            if !args.is_empty() {
105                args.reverse();
106                dispatch_param.cArgs = args.len() as u32;
107                dispatch_param.rgvarg = args.as_mut_ptr() as *mut VARIANT;
108                if (flags & DISPATCH_PROPERTYPUT) != DISPATCH_FLAGS(0) {
109                    dispatch_param.cNamedArgs = 1;
110                    dispatch_param.rgdispidNamedArgs = &mut dispatch_named;
111                }
112            }
113            let mut result = VARIANT::default();
114            self.0
115                .Invoke(
116                    dispatch_id,
117                    &GUID::default(),
118                    LOCALE_SYSTEM_DEFAULT,
119                    flags,
120                    &dispatch_param,
121                    Some(&mut result),
122                    None,
123                    None,
124                ).with_context(|| "Invokation Failure!")?;
125            Ok(Variant(result))
126        }
127    }
128
129    pub fn get(&self, name: &str,args:Vec<Variant>) -> anyhow::Result<Variant> {
130        self.invoke(DISPATCH_PROPERTYGET, name, args)
131    }
132
133    pub fn int(&self, name: &str,args:Vec<Variant>) -> anyhow::Result<i32> {
134        let result = self.get(name,args)?;
135        result.int()
136    }
137
138    pub fn bool(&self, name: &str,args:Vec<Variant>) -> anyhow::Result<bool> {
139        let result = self.get(name,args)?;
140        result.bool()
141    }
142    pub fn float(&self, name: &str,args:Vec<Variant>) -> anyhow::Result<f64> {
143        let result = self.get(name,args)?;
144        result.float()
145    }
146
147    pub fn string(&self, name: &str,args:Vec<Variant>) -> anyhow::Result<String> {
148        let result = self.get(name,args)?;
149        result.string()
150    }
151
152    pub fn put(&self, name: &str, args: Vec<Variant>) -> anyhow::Result<Variant> {
153        self.invoke(DISPATCH_PROPERTYPUT, name, args)
154    }
155
156    pub fn call(&self, name: &str, args: Vec<Variant>) -> anyhow::Result<Variant> {
157        self.invoke(DISPATCH_METHOD, name, args)
158    }
159}
160
161/// Automatic uninitilize for the com object i case the memory was droped.   
162/// can also be done manually with [RSCom::close_api()]
163pub struct DeferCoUninitialize;
164impl Drop for DeferCoUninitialize {
165    fn drop(&mut self) {
166        unsafe {
167            CoUninitialize();
168        }
169    }
170}
171/// This structure hold the pointer for the COM IDispatch interface.
172///
173/// @api:[IDispatchW] -> The main interface for the COM.
174pub struct RSCom {
175    pub api: IDispatchW,
176}
177
178impl RSCom {
179    /// Initializing the structure [`crate::com_module::RSCom`] and append for each pointer its interface.   
180    /// @com_name:[&str] -> The com application name you want to use, for example:"Excel.Application".
181    pub fn init(com_name:&str) -> anyhow::Result<RSCom> {
182        let mut args = env::args();
183        let _ = args.next();
184        unsafe {
185            // API Data
186            // Init the thread
187            let res = CoInitializeEx(None, COINIT_APARTMENTTHREADED);
188            if res.is_err() {
189                return Err(anyhow!("error: {}", res.message()));
190            }
191            //let _com = DeferCoUninitialize;
192            // Get CLSID of the com
193            let clsid = CLSIDFromProgID(PCWSTR::from_raw(
194                HSTRING::from(com_name).as_ptr(),
195            ))
196            .with_context(|| "wrong clsid of api")?;
197            println!("printing api id {:?}", clsid);
198            // Create the instance of the COM
199            let _api_dispatch = CoCreateInstance(&clsid, None, CLSCTX_LOCAL_SERVER)
200                .with_context(|| "CoCreateInstance of api")?;
201            // Cast from IDispatch to the IDispatchWrapper
202            let api_dispatch = IDispatchW(_api_dispatch);
203            Ok(RSCom { api: api_dispatch })
204        }
205    }
206    /// Method for Uninitialize the Com Object.
207    pub fn close_api(&self) -> () {
208        unsafe { CoUninitialize() }
209    }
210}
211unsafe impl Send for RSCom {}