如何实现现有 COM 接口?
在某些情况下,您可能需要实现现有 COM 接口,而不仅仅是调用操作系统提供的现有实现。这时,implement
功能和宏就派上用场了。windows crate 提供可选的实现支持,隐藏在 implement
功能之后。一旦启用,implement 宏可用于实现任意数量的 COM 接口。该宏负责实现 IUnknown
本身。
让我们实现一个由 Windows 定义的简单接口来说明。IPersist
接口定义在 Win32::System::Com
模块中,所以我们首先添加对 windows
crate 的依赖,并包含 Win32_System_Com
功能。
[dependencies.windows]
version = "0.52"
features = [
"implement",
"Win32_System_Com",
]
implement
功能解锁了实现支持。
implement
宏包含在 windows::core
模块中,所以我们将通过以下方式简单地全部包含它
#![allow(unused)] fn main() { use windows::{core::*, Win32::System::Com::*}; }
现在是实现的时候了
#![allow(unused)] fn main() { #[implement(IPersist)] struct Persist(GUID); }
implement
宏将为 IUnknown
接口的生命周期管理和接口发现提供必要的实现,无论属性中包含哪些接口。在这种情况下,只实现 IPersist
。
实现本身由遵循 <interface name>_Impl
模式的 trait 定义,我们需要为我们的实现实现它,如下所示
#![allow(unused)] fn main() { impl IPersist_Impl for Persist_Impl { fn GetClassID(&self) -> Result<GUID> { Ok(self.0) } } }
IPersist 接口,最初在此处文档化,有一个返回 GUID
的单一方法,所以我们只需通过返回我们实现中包含的值来实现它。window
crate 和 implement
宏将通过提供实际的 COM 虚函数调用和虚函数表布局来完成其余工作,以将其转换为堆分配和引用计数的 COM 对象。
剩下的就是通过 Into
trait 将实现移动或装箱到 implement
宏提供的 COM 实现中。
#![allow(unused)] fn main() { let guid = GUID::new()?; let persist: IPersist = Persist(guid).into(); }
此时,我们可以简单地将 persist
视为它所是的 COM 对象。
#![allow(unused)] fn main() { let guid2 = unsafe { persist.GetClassID()? }; assert_eq!(guid, guid2); println!("{:?}", guid); }
这是一个完整示例。