Rust COM组件交互库com-rs的使用:实现高效Windows组件对象模型(COM)的Rust绑定与调用
Rust COM组件交互库com-rs的使用:实现高效Windows组件对象模型(COM)的Rust绑定与调用
注意:com-rs库已被弃用,建议使用微软官方的com crate替代。
基本信息
- 许可证: MIT OR Apache-2.0
- 大小: 10.4 KiB
- 最后更新时间: 大约6年前(2019年10月)
安装
在你的项目目录中运行以下Cargo命令:
cargo add com-rs
或者在Cargo.toml中添加以下行:
com-rs = "0.2.1"
完整示例代码
以下是一个使用com-rs与COM组件交互的基本示例:
// 导入必要的模块
use com_rs::{ComPtr, IUnknown};
use std::ptr;
// 定义COM接口
#[com_interface("00000000-0000-0000-C000-000000000046")]
pub trait IExample: IUnknown {
unsafe fn example_method(&self, param: i32) -> i32;
}
fn main() -> Result<(), com_rs::ComError> {
// 初始化COM库
com_rs::initialize()?;
// 创建COM对象实例 (这里需要替换为实际的CLSID)
let clsid = com_rs::CLSID::from("00000000-0000-0000-0000-000000000000");
let mut example: ComPtr<dyn IExample> = unsafe { com_rs::create_instance(&clsid)? };
// 调用COM方法
let result = unsafe { example.example_method(42) };
println!("COM方法返回结果: {}", result);
// 释放COM对象
unsafe {
example.release();
}
// 反初始化COM库
com_rs::uninitialize();
Ok(())
}
// 实现COM接口的宏
com_implement! {
impl IExample for MyExample {
unsafe fn example_method(&self, param: i32) -> i32 {
param * 2
}
}
}
完整示例demo
以下是使用com-rs与Windows文件系统COM对象交互的完整示例:
use com_rs::{ComPtr, IUnknown};
use std::ptr;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
// 定义IShellItem接口
#[com_interface("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
pub trait IShellItem: IUnknown {
unsafe fn get_display_name(&self, sigdn_name: u32, ppsz_name: *mut *mut u16) -> i32;
}
fn main() -> Result<(), com_rs::ComError> {
// 初始化COM库
com_rs::initialize()?;
// 获取Shell CLSID
let clsid = com_rs::CLSID::from("C0B4E2F3-BA21-4773-8DBA-335EC946EB8B"); // Shell CLSID
// 创建ShellItem对象
let mut shell_item: ComPtr<dyn IShellItem> = unsafe { com_rs::create_instance(&clsid)? };
// 调用get_display_name方法获取当前目录显示名
let mut name_ptr: *mut u16 = ptr::null_mut();
unsafe {
shell_item.get_display_name(0x80028000, &mut name_ptr); // SIGDN_FILESYSPATH
// 将结果转换为Rust字符串
let mut name_vec = Vec::new();
let mut i = 0;
while *name_ptr.offset(i) != 0 {
name_vec.push(*name_ptr.offset(i));
i += 1;
}
let os_string = OsString::from_wide(&name_vec);
println!("当前路径: {}", os_string.to_string_lossy());
// 释放内存
com_rs::CoTaskMemFree(name_ptr as *mut _);
}
// 释放COM对象
unsafe {
shell_item.release();
}
// 反初始化COM库
com_rs::uninitialize();
Ok(())
}
注意事项
- com-rs库已被弃用,新项目应使用微软官方的com crate
- 实际使用时需要处理错误和资源释放
- 示例中的CLSID和接口定义需要根据实际COM对象替换
- 所有COM方法调用都需要在unsafe块中执行
- 必须正确初始化COM库并在使用后反初始化
1 回复
以下是根据您提供的内容整理的完整示例demo,首先展示内容中的示例,然后提供完整的实现代码:
完整示例demo
内容中提供的示例回顾
1. 调用COM接口示例
// 初始化COM运行时并调用IDispatch接口
use com::interfaces;
use com::sys::{HRESULT, GUID, IID};
use com::runtime::init_runtime;
interfaces! {
#[uuid("00000000-0000-0000-C000-000000000046")]
interface IUnknown {
fn query_interface(&self, riid: *const GUID, ppv: *mut *mut std::ffi::c_void) -> HRESULT;
fn add_ref(&self) -> u32;
fn release(&self) -> u32;
}
#[uuid("00020400-0000-0000-C000-000000000046")]
interface IDispatch: IUnknown {
fn get_type_info_count(&self, pctinfo: *mut u32) -> HRESULT;
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
init_runtime()?;
let dispatch: com::ComPtr<IDispatch> = com::runtime::create_instance(
&CLSID_SomeComponent,
None,
com::sys::CLSCTX_INPROC_SERVER
)?;
let mut type_info_count = 0;
unsafe { dispatch.get_type_info_count(&mut type_info_count)?; }
println!("Type info count: {}", type_info_count);
Ok(())
}
2. 实现COM组件示例
// 自定义COM组件实现
use com::{interfaces, class};
use com::sys::HRESULT;
interfaces! {
#[uuid("12345678-1234-1234-1234-123456789ABC")]
interface IMyComponent: com::interfaces::IUnknown {
fn do_something(&self, param: i32) -> HRESULT;
}
}
class! {
pub class MyComponent: IMyComponent {
data: i32,
}
impl IMyComponent for MyComponent {
fn do_something(&self, param: i32) -> HRESULT {
println!("操作数据: {} 和 {}", self.data, param);
com::sys::S_OK
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
com::runtime::init_runtime()?;
let my_component = MyComponent::allocate(42);
let ptr: com::ComPtr<IMyComponent> = my_component.query_interface()?;
unsafe { ptr.do_something(100)?; }
Ok(())
}
完整实现示例
下面是一个完整的COM组件创建和使用示例,包含跨线程操作和错误处理:
use com::{interfaces, class, runtime::init_runtime, sync::ComRc};
use com::sys::{HRESULT, GUID, CLSCTX_INPROC_SERVER};
use std::thread;
// 1. 定义COM接口
interfaces! {
#[uuid("00000000-0000-0000-C000-000000000046")] // IUnknown
interface IUnknown {
fn query_interface(&self, riid: *const GUID, ppv: *mut *mut std::ffi::c_void) -> HRESULT;
fn add_ref(&self) -> u32;
fn release(&self) -> u32;
}
#[uuid("12345678-1234-5678-1234-567812345678")] // 自定义接口
interface IMyCustomInterface: IUnknown {
fn calculate(&self, x: i32, y: i32, result: *mut i32) -> HRESULT;
fn get_name(&self, name: *mut *const u16) -> HRESULT;
}
}
// 2. 实现COM类
class! {
pub class MyComObject: IMyCustomInterface {
counter: std::sync::atomic::AtomicI32,
}
impl IMyCustomInterface for MyComObject {
fn calculate(&self, x: i32, y: i32, result: *mut i32) -> HRESULT {
unsafe { *result = x + y };
self.counter.fetch_add(1, std::sync::atomic::Ordering::SeqCst);
com::sys::S_OK
}
fn get_name(&self, name: *mut *const u16) -> HRESULT {
let wstr = widestring::U16CString::from_str("MyCOMObject").unwrap();
unsafe { *name = wstr.as_ptr() };
com::sys::S_OK
}
}
}
// 3. 使用COM对象
fn use_com_object(com_ptr: com::ComPtr<IMyCustomInterface>) -> Result<(), com::Error> {
let mut result = 0;
unsafe {
com_ptr.calculate(10, 20, &mut result)?;
println!("Calculation result: {}", result);
let mut name_ptr = std::ptr::null();
com_ptr.get_name(&mut name_ptr)?;
let name = unsafe { widestring::U16CStr::from_ptr_str(name_ptr) };
println!("Component name: {}", name.to_string_lossy());
}
Ok(())
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化COM运行时
init_runtime()?;
// 创建COM对象
let my_object = MyComObject::allocate(std::sync::atomic::AtomicI32::new(0));
let com_ptr: com::ComPtr<IMyCustomInterface> = my_object.query_interface()?;
// 在主线程使用
use_com_object(com_ptr.clone())?;
// 跨线程使用
let thread_safe = ComRc::from(com_ptr);
let handle = thread::spawn(move || {
let com_ptr = thread_safe.to_com_ptr();
if let Err(e) = use_com_object(com_ptr) {
eprintln!("Thread error: {}", e);
}
});
handle.join().unwrap();
// 错误处理示例
let invalid_ptr = com::ComPtr::<IMyCustomInterface>::null();
if let Err(e) = unsafe { invalid_ptr.calculate(1, 2, &mut 0) } {
println!("Expected error occurred: {}", e);
}
Ok(())
}
示例说明
- 接口定义:使用
interfaces!
宏定义了IMyCustomInterface
接口,继承自IUnknown
- 类实现:使用
class!
宏实现了COM类MyComObject
,包含原子计数器状态 - 线程安全:使用
ComRc
实现跨线程安全访问 - 错误处理:展示了HRESULT错误处理方式
- 字符串处理:演示了COM中常见的宽字符串处理方式
构建说明
在Cargo.toml中添加以下依赖:
[dependencies]
com = "0.2"
widestring = "0.4"
这个完整示例演示了从COM组件定义、实现到使用的完整流程,包含了线程安全和错误处理等关键方面。