use windows::Win32::Foundation::E_FAIL;
use windows::Win32::System::Com::{
CC_STDCALL, ELEMDESC, ELEMDESC_0, FUNC_DISPATCH, FUNCDESC, FUNCFLAGS, IDLDESC,
IMPLTYPEFLAG_FDEFAULT, IMPLTYPEFLAG_FSOURCE, INVOKE_FUNC, INVOKE_PROPERTYGET,
INVOKE_PROPERTYPUT, ITypeInfo, ITypeLib, SYSKIND, TKIND_COCLASS, TKIND_DISPATCH, TYPEDESC,
TYPEDESC_0,
};
use windows::Win32::System::Ole::{
CreateTypeLib2, ICreateTypeInfo, LoadRegTypeLib, LoadTypeLibEx, PARAMDESC, REGKIND_NONE,
REGKIND_REGISTER, TYPEFLAG_FCANCREATE, TYPEFLAG_FDISPATCHABLE, UnRegisterTypeLib,
};
use windows::Win32::System::Variant::VARENUM;
use windows_core::{BSTR, GUID, Interface, PCWSTR};
use crate::dispatch::{self, PUT};
use crate::server::CLSID_DENISE_PANEL;
pub const LIBID_DENISE: GUID = GUID::from_u128(0x5CA2_EE57_C922_483E_8FDA_B0A8_B3D3_B195);
pub const DIID_DENISE_PANEL: GUID = GUID::from_u128(0x4C51_48FF_09F3_4C34_9B77_00C8_50E1_F940);
pub const VERSION: (u16, u16) = (1, 0);
const fn syskind() -> SYSKIND {
#[cfg(target_pointer_width = "64")]
{
windows::Win32::System::Com::SYS_WIN64
}
#[cfg(not(target_pointer_width = "64"))]
{
windows::Win32::System::Com::SYS_WIN32
}
}
fn step<T>(what: &str, result: windows_core::Result<T>) -> windows_core::Result<T> {
result.map_err(|e| windows_core::Error::new(e.code(), format!("{what}: {}", e.message())))
}
fn idispatch_type_info() -> windows_core::Result<ITypeInfo> {
const LIBID_STDOLE: GUID = GUID::from_u128(0x0002_0430_0000_0000_C000_0000_0000_0046);
let stdole = step("LoadRegTypeLib(stdole2)", unsafe {
LoadRegTypeLib(&LIBID_STDOLE, 2, 0, 0)
})?;
step("stdole2::IDispatch", unsafe {
stdole.GetTypeInfoOfGuid(&windows::Win32::System::Com::IDispatch::IID)
})
}
fn inherit_idispatch(info: &ICreateTypeInfo) -> windows_core::Result<()> {
let idispatch = idispatch_type_info()?;
let mut href = 0u32;
unsafe {
step("AddRefTypeInfo(IDispatch)", {
info.AddRefTypeInfo(&idispatch, &mut href as *mut u32 as *const u32)
})?;
step("AddImplType(IDispatch)", info.AddImplType(0, href))?;
}
Ok(())
}
pub fn path_beside(dll: &str) -> String {
match dll.rfind('.') {
Some(dot) => format!("{}.tlb", &dll[..dot]),
None => format!("{dll}.tlb"),
}
}
pub fn build(path: &str) -> windows_core::Result<()> {
let wide = wide(path);
let library = step("CreateTypeLib2", unsafe {
CreateTypeLib2(syskind(), PCWSTR(wide.as_ptr()))
})?;
unsafe {
step("library.SetGuid", library.SetGuid(&LIBID_DENISE))?;
step("library.SetName", library.SetName(&BSTR::from("Denise")))?;
step(
"library.SetVersion",
library.SetVersion(VERSION.0, VERSION.1),
)?;
step("library.SetLcid", library.SetLcid(0))?;
}
let panel = step("CreateTypeInfo(DDenisePanel)", unsafe {
library.CreateTypeInfo(&BSTR::from("DDenisePanel"), TKIND_DISPATCH)
})?;
describe_panel(&panel)?;
let events = step("CreateTypeInfo(DDenisePanelEvents)", unsafe {
library.CreateTypeInfo(&BSTR::from("DDenisePanelEvents"), TKIND_DISPATCH)
})?;
describe_events(&events)?;
unsafe {
step("panel.LayOut", panel.LayOut())?;
step("events.LayOut", events.LayOut())?;
}
let coclass = step("CreateTypeInfo(Panel)", unsafe {
library.CreateTypeInfo(&BSTR::from("Panel"), TKIND_COCLASS)
})?;
describe_coclass(&coclass, &panel, &events)?;
step("coclass.LayOut", unsafe { coclass.LayOut() })?;
step("SaveAllChanges", unsafe { library.SaveAllChanges() })
}
fn describe_panel(info: &ICreateTypeInfo) -> windows_core::Result<()> {
unsafe {
info.SetGuid(&DIID_DENISE_PANEL)?;
info.SetVersion(VERSION.0, VERSION.1)?;
step(
"panel.SetTypeFlags",
info.SetTypeFlags(TYPEFLAG_FDISPATCHABLE.0 as u32),
)?;
}
inherit_idispatch(info)?;
for (index, entry) in dispatch::entries().iter().enumerate() {
let mut argument = [ELEMDESC {
tdesc: TYPEDESC {
Anonymous: TYPEDESC_0 {
lptdesc: core::ptr::null_mut(),
},
vt: VARENUM(entry.vt),
},
Anonymous: ELEMDESC_0 {
paramdesc: PARAMDESC::default(),
},
}];
let returns = if entry.flags == PUT {
dispatch::VOID
} else {
entry.vt
};
let description = FUNCDESC {
memid: entry.dispid,
lprgscode: core::ptr::null_mut(),
lprgelemdescParam: if entry.arguments == 0 {
core::ptr::null_mut()
} else {
argument.as_mut_ptr()
},
funckind: FUNC_DISPATCH,
invkind: match entry.flags {
dispatch::GET => INVOKE_PROPERTYGET,
dispatch::PUT => INVOKE_PROPERTYPUT,
_ => INVOKE_FUNC,
},
callconv: CC_STDCALL,
cParams: entry.arguments as i16,
cParamsOpt: 0,
oVft: 0,
cScodes: 0,
elemdescFunc: ELEMDESC {
tdesc: TYPEDESC {
Anonymous: TYPEDESC_0 {
lptdesc: core::ptr::null_mut(),
},
vt: VARENUM(returns),
},
Anonymous: ELEMDESC_0 {
idldesc: IDLDESC::default(),
},
},
wFuncFlags: FUNCFLAGS(0),
};
let added = unsafe { info.AddFuncDesc(index as u32, &description) };
step(&format!("panel.AddFuncDesc[{index}] {}", entry.name), added)?;
let name = wide(entry.name);
let names = [PCWSTR(name.as_ptr())];
let named = unsafe { info.SetFuncAndParamNames(index as u32, &names) };
step(
&format!(
"panel.SetFuncAndParamNames[{index}] {} with {} name(s), flags {}",
entry.name,
names.len(),
entry.flags
),
named,
)?;
}
Ok(())
}
fn describe_events(info: &ICreateTypeInfo) -> windows_core::Result<()> {
unsafe {
info.SetGuid(&crate::automation::DIID_DENISE_PANEL_EVENTS)?;
info.SetVersion(VERSION.0, VERSION.1)?;
step(
"events.SetTypeFlags",
info.SetTypeFlags(TYPEFLAG_FDISPATCHABLE.0 as u32),
)?;
}
inherit_idispatch(info)?;
for (index, event) in dispatch::EVENTS.iter().enumerate() {
let description = FUNCDESC {
memid: event.dispid,
lprgscode: core::ptr::null_mut(),
lprgelemdescParam: core::ptr::null_mut(),
funckind: FUNC_DISPATCH,
invkind: INVOKE_FUNC,
callconv: CC_STDCALL,
cParams: 0,
cParamsOpt: 0,
oVft: 0,
cScodes: 0,
elemdescFunc: ELEMDESC {
tdesc: TYPEDESC {
Anonymous: TYPEDESC_0 {
lptdesc: core::ptr::null_mut(),
},
vt: VARENUM(dispatch::VOID),
},
Anonymous: ELEMDESC_0 {
idldesc: IDLDESC::default(),
},
},
wFuncFlags: FUNCFLAGS(0),
};
step("events.AddFuncDesc", unsafe {
info.AddFuncDesc(index as u32, &description)
})?;
let name = wide(event.name);
step("events.SetFuncAndParamNames", unsafe {
info.SetFuncAndParamNames(index as u32, &[PCWSTR(name.as_ptr())])
})?;
}
Ok(())
}
fn describe_coclass(
info: &ICreateTypeInfo,
panel: &ICreateTypeInfo,
events: &ICreateTypeInfo,
) -> windows_core::Result<()> {
unsafe {
info.SetGuid(&CLSID_DENISE_PANEL)?;
info.SetVersion(VERSION.0, VERSION.1)?;
info.SetTypeFlags(TYPEFLAG_FCANCREATE.0 as u32)?;
}
for (index, (part, flags)) in [
(panel, IMPLTYPEFLAG_FDEFAULT),
(events, IMPLTYPEFLAG_FDEFAULT | IMPLTYPEFLAG_FSOURCE),
]
.into_iter()
.enumerate()
{
let part: ITypeInfo = part.cast()?;
let mut href = 0u32;
unsafe {
step("coclass.AddRefTypeInfo", {
info.AddRefTypeInfo(&part, &mut href as *mut u32 as *const u32)
})?;
step("coclass.AddImplType", info.AddImplType(index as u32, href))?;
step(
"coclass.SetImplTypeFlags",
info.SetImplTypeFlags(index as u32, flags),
)?;
}
}
Ok(())
}
pub fn register(path: &str) -> windows_core::Result<()> {
let wide = wide(path);
unsafe { LoadTypeLibEx(PCWSTR(wide.as_ptr()), REGKIND_REGISTER) }?;
Ok(())
}
pub fn unregister() -> windows_core::Result<()> {
unsafe { UnRegisterTypeLib(&LIBID_DENISE, VERSION.0, VERSION.1, 0, syskind()) }
}
pub fn panel_type_info(fallback: Option<&str>) -> windows_core::Result<ITypeInfo> {
if let Ok(library) = unsafe { LoadRegTypeLib(&LIBID_DENISE, VERSION.0, VERSION.1, 0) } {
if let Ok(info) = unsafe { library.GetTypeInfoOfGuid(&DIID_DENISE_PANEL) } {
return Ok(info);
}
}
let path = fallback.ok_or(E_FAIL)?;
let wide = wide(path);
let library: ITypeLib = unsafe { LoadTypeLibEx(PCWSTR(wide.as_ptr()), REGKIND_NONE) }?;
unsafe { library.GetTypeInfoOfGuid(&DIID_DENISE_PANEL) }
}
fn wide(text: &str) -> Vec<u16> {
text.encode_utf16().chain(core::iter::once(0)).collect()
}