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(())
}

注意事项

  1. com-rs库已被弃用,新项目应使用微软官方的com crate
  2. 实际使用时需要处理错误和资源释放
  3. 示例中的CLSID和接口定义需要根据实际COM对象替换
  4. 所有COM方法调用都需要在unsafe块中执行
  5. 必须正确初始化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(())
}

示例说明

  1. 接口定义:使用interfaces!宏定义了IMyCustomInterface接口,继承自IUnknown
  2. 类实现:使用class!宏实现了COM类MyComObject,包含原子计数器状态
  3. 线程安全:使用ComRc实现跨线程安全访问
  4. 错误处理:展示了HRESULT错误处理方式
  5. 字符串处理:演示了COM中常见的宽字符串处理方式

构建说明

在Cargo.toml中添加以下依赖:

[dependencies]
com = "0.2"
widestring = "0.4"

这个完整示例演示了从COM组件定义、实现到使用的完整流程,包含了线程安全和错误处理等关键方面。

回到顶部