use windows::core::{GUID, Result as WinResult, PCWSTR};
use windows::Win32::System::Com::{
CoInitializeEx,
CoUninitialize,
COINIT_APARTMENTTHREADED,
IDispatch,
CLSIDFromProgID,
CoCreateInstance,
CLSCTX_ALL,
EXCEPINFO,
DISPPARAMS,
DISPATCH_FLAGS,
DISPATCH_METHOD,
DISPATCH_PROPERTYPUT,
DISPATCH_PROPERTYGET,
};
use windows::Win32::System::Ole::DISPID_PROPERTYPUT;
use windows::core::VARIANT;
use crate::variant::{variant_empty};
pub struct ComApartment;
impl ComApartment {
pub fn init_sta() -> WinResult<Self> {
unsafe { CoInitializeEx(None, COINIT_APARTMENTTHREADED).ok()?; }
Ok(Self)
}
}
impl Drop for ComApartment {
fn drop(&mut self) {
unsafe { CoUninitialize(); }
}
}
fn to_wide_null(s: &str) -> Vec<u16> {
s.encode_utf16().chain(std::iter::once(0)).collect()
}
pub fn is_progid_registered(progid: &str) -> WinResult<bool> {
let wide = to_wide_null(progid);
unsafe {
match CLSIDFromProgID(PCWSTR(wide.as_ptr())) {
Ok(_) => Ok(true),
Err(err) => {
let code = err.code().0;
if code as u32 == 0x800401F3u32 {
Ok(false)
} else {
Err(err)
}
}
}
}
}
pub fn get_dispid(dispatch: &IDispatch, name: &str) -> WinResult<i32> {
let mut dispid = 0i32;
let wide = to_wide_null(name);
let names = [PCWSTR(wide.as_ptr())];
let riid = GUID::zeroed();
unsafe {
dispatch.GetIDsOfNames(
&riid,
names.as_ptr(),
1,
0,
&mut dispid,
)?;
}
Ok(dispid)
}
pub fn invoke(
dispatch: &IDispatch,
name: &str,
flags: DISPATCH_FLAGS,
args: &mut [VARIANT],
named_ids: Option<&mut [i32]>,
) -> WinResult<VARIANT> {
args.reverse();
let dispid = get_dispid(dispatch, name)?;
let mut excep = EXCEPINFO::default();
let (named_ptr, named_len) = {
let mut ptr = std::ptr::null_mut();
let mut len = 0u32;
if let Some(ids) = named_ids {
if !ids.is_empty() {
ptr = ids.as_mut_ptr();
len = ids.len() as u32;
}
}
(ptr, len)
};
let mut arg_dp = DISPPARAMS {
rgvarg: if args.is_empty() { std::ptr::null_mut() } else { args.as_mut_ptr() },
rgdispidNamedArgs: named_ptr,
cArgs: args.len() as u32,
cNamedArgs: named_len,
};
let mut result = variant_empty();
unsafe {
dispatch.Invoke(
dispid,
&GUID::zeroed(),
0,
flags,
&mut arg_dp,
Some(&mut result as *mut VARIANT),
Some(&mut excep as *mut EXCEPINFO),
Some(std::ptr::null_mut()),
)?;
}
Ok(result)
}
#[derive(Clone)]
pub struct ComObject {
pub disp: IDispatch,
}
impl ComObject {
pub fn new(disp: IDispatch) -> Self { Self { disp } }
pub fn get(&self, prop: &str) -> WinResult<VARIANT> {
let mut args: [VARIANT; 0] = [];
invoke(&self.disp, prop, DISPATCH_PROPERTYGET, &mut args, None)
}
pub fn put(&self, prop: &str, value: VARIANT) -> WinResult<()> {
let mut args = [value];
let mut named = [DISPID_PROPERTYPUT];
let _ = invoke(&self.disp, prop, DISPATCH_PROPERTYPUT, &mut args, Some(&mut named))?;
Ok(())
}
pub fn call(&self, method: &str, mut args: Vec<VARIANT>) -> WinResult<VARIANT> {
invoke(&self.disp, method, DISPATCH_METHOD, &mut args[..], None)
}
}
pub fn com_create(progid: &str) -> WinResult<ComObject> {
let wide = to_wide_null(progid);
let clsid = unsafe { CLSIDFromProgID(PCWSTR(wide.as_ptr()))? };
let disp: IDispatch = unsafe { CoCreateInstance(&clsid, None, CLSCTX_ALL)? };
Ok(ComObject::new(disp))
}