键盘快捷键

在章节间导航

S/ 在书中搜索

? 显示此帮助

Esc 隐藏此帮助

如何实现现有 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);
}

这是一个完整示例