首页
/ 在windows-rs项目中调用winget WinRT COM API的实践指南

在windows-rs项目中调用winget WinRT COM API的实践指南

2025-05-21 08:49:04作者:邬祺芯Juliet

背景介绍

Windows Package Manager(winget)是微软推出的现代化包管理工具,它通过WinRT COM API提供了丰富的功能接口。对于Rust开发者而言,通过windows-rs项目可以方便地调用这些原生Windows API。本文将详细介绍如何在Rust中使用windows-rs来调用winget的WinRT COM API。

准备工作

首先需要确保开发环境满足以下条件:

  1. 安装最新版本的Windows SDK
  2. 在Cargo.toml中添加必要的依赖项
  3. 获取Microsoft.Management.Deployment.winmd元数据文件

核心实现步骤

1. 项目配置

在Cargo.toml中添加windows和windows-core依赖,并启用必要的特性:

[dependencies]
windows-core = "0.58.0"

[dependencies.windows]
version = "0.58.0"
features = [
    "Foundation_Collections",
    "System",
    "Win32_System_Com",
]

2. 生成Rust绑定

使用windows-bindgen工具从winmd文件生成Rust绑定代码:

// build.rs
fn main() {
    if !cfg!(target_env = "msvc") {
        return;
    }

    println!("cargo:rustc-link-lib=onecoreuap");

    windows_bindgen::bindgen([
        "--in",
        "winmd/Microsoft.Management.Deployment.winmd",
        "--out",
        "src/bindings.rs",
        "--filter",
        "Microsoft.Management.Deployment",
        "--config",
        "no-bindgen-comment",
    ])
    .unwrap();
}

3. 关键实现细节

创建COM实例时需要注意将winmd文件放在可执行文件同级目录下,因为winget使用了Packaged COM技术,系统需要这些元数据来确定接口的封送处理行为。

// 创建COM实例的辅助函数
fn create_standard_instance<T: windows_core::Interface>(clsid: &GUID) -> windows_core::Result<T> {
    unsafe {
        let object: IUnknown = CoCreateInstance(clsid, None, CLSCTX_LOCAL_SERVER)?;
        object.cast::<T>()
    }
}

4. 完整示例代码

以下是一个完整的示例,展示了如何连接winget包管理器并搜索指定包:

#![cfg(target_env = "msvc")]
mod bindings;

use windows::Win32::System::Com::{CoCreateInstance, CoInitialize, CLSCTX_LOCAL_SERVER};
use windows_core::{h, IUnknown, Interface, GUID};

// 定义所有需要的CLSID
static PACKAGE_MANAGER_CLSID: GUID = GUID::from_u128(0xC53A4F16_787E_42A4_B304_29EFFB4BF597);
static FIND_PACKAGES_OPTIONS_CLSID: GUID = GUID::from_u128(0x572DED96_9C60_4526_8F92_EE7D91D38C1A);
static CREATE_COMPOSITE_PACKAGE_CATALOG_OPTIONS_CLSID: GUID =
    GUID::from_u128(0x526534B8_7E46_47C8_8416_B1685C327D37);
static PACKAGE_MATCH_FILTER_CLSID: GUID = GUID::from_u128(0xD02C9DAF_99DC_429C_B503_4E504E4AB000);

fn main() -> windows_core::Result<()> {
    unsafe { CoInitialize(None).ok()?; }

    // 创建包管理器实例
    let package_manager = create_standard_instance::<PackageManager>(&PACKAGE_MANAGER_CLSID)?;

    // 获取winget和msstore目录
    let winget_catalog = package_manager.GetPackageCatalogByName(h!("winget"))?;
    let msstore_catalog = package_manager.GetPackageCatalogByName(h!("msstore"))?;

    // 配置复合目录选项
    let composite_catalog_options = create_standard_instance::<CreateCompositePackageCatalogOptions>(
        &CREATE_COMPOSITE_PACKAGE_CATALOG_OPTIONS_CLSID,
    )?;
    composite_catalog_options.Catalogs()?.Append(&winget_catalog)?;
    composite_catalog_options.Catalogs()?.Append(&msstore_catalog)?;
    composite_catalog_options.SetCompositeSearchBehavior(CompositeSearchBehavior::LocalCatalogs)?;
    composite_catalog_options.SetInstalledScope(PackageInstallScope::Any)?;

    // 连接包目录
    let connect_result = package_manager
        .CreateCompositePackageCatalog(&composite_catalog_options)?
        .Connect()?;
    
    if connect_result.Status()? != ConnectResultStatus::Ok {
        panic!("连接包目录失败");
    }

    // 创建搜索选项
    let find_packages_options = 
        create_standard_instance::<FindPackagesOptions>(&FIND_PACKAGES_OPTIONS_CLSID)?;

    // 添加搜索过滤器
    let match_filter = create_standard_instance::<PackageMatchFilter>(&PACKAGE_MATCH_FILTER_CLSID)?;
    match_filter.SetField(PackageMatchField::Id)?;
    match_filter.SetOption(PackageFieldMatchOption::Equals)?;
    match_filter.SetValue(h!("Git.Git"))?;

    find_packages_options.Selectors()?.Append(&match_filter)?;

    // 执行搜索
    let find_result = connect_result
        .PackageCatalog()?
        .FindPackages(&find_packages_options)?;
    
    // 处理搜索结果
    for result in find_result.Matches()? {
        let catalog_package = result.CatalogPackage()?;
        println!(
            "{} - {}",
            catalog_package.Id()?,
            catalog_package.Name()?
        );
    }

    Ok(())
}

技术要点解析

  1. Packaged COM的特殊处理:winget API使用了Packaged COM技术,这意味着系统不会在注册表中查找接口信息,而是直接从winmd元数据文件中获取。因此必须确保winmd文件与可执行文件位于同一目录。

  2. 接口转换:通过cast方法可以将基础的IUnknown接口转换为具体的接口类型,这是COM编程中的常见模式。

  3. 错误处理:windows-rs提供了良好的错误处理机制,所有COM调用都返回Result类型,便于开发者处理各种错误情况。

  4. 多线程考虑:示例中使用了CoInitialize初始化COM库,在实际应用中需要考虑多线程环境下的COM初始化策略。

高级应用场景

掌握了基础用法后,还可以实现更复杂的功能:

  1. 包安装与卸载:通过InstallOptionsUninstallOptions接口实现包的安装和卸载功能。

  2. 提升权限运行:当需要管理员权限时,可以使用WinGetServerManualActivation_CreateInstance函数创建提升的实例。

  3. 进度监控:通过实现特定的回调接口来监控包操作的进度。

总结

通过windows-rs项目调用winget WinRT COM API是一个强大而灵活的方式,可以让Rust开发者充分利用Windows平台的包管理能力。本文介绍了从环境配置到实际调用的完整流程,并提供了可直接运行的示例代码。掌握这些技术后,开发者可以构建出功能丰富的Windows包管理工具,满足各种自动化部署和软件管理需求。

登录后查看全文
热门项目推荐
相关项目推荐

热门内容推荐

最新内容推荐

项目优选

收起
ohos_react_nativeohos_react_native
React Native鸿蒙化仓库
C++
178
262
RuoYi-Vue3RuoYi-Vue3
🎉 (RuoYi)官方仓库 基于SpringBoot,Spring Security,JWT,Vue3 & Vite、Element Plus 的前后端分离权限管理系统
Vue
866
513
ShopXO开源商城ShopXO开源商城
🔥🔥🔥ShopXO企业级免费开源商城系统,可视化DIY拖拽装修、包含PC、H5、多端小程序(微信+支付宝+百度+头条&抖音+QQ+快手)、APP、多仓库、多商户、多门店、IM客服、进销存,遵循MIT开源协议发布、基于ThinkPHP8框架研发
JavaScript
93
15
openGauss-serveropenGauss-server
openGauss kernel ~ openGauss is an open source relational database management system
C++
129
183
openHiTLSopenHiTLS
旨在打造算法先进、性能卓越、高效敏捷、安全可靠的密码套件,通过轻量级、可剪裁的软件技术架构满足各行业不同场景的多样化要求,让密码技术应用更简单,同时探索后量子等先进算法创新实践,构建密码前沿技术底座!
C
261
302
kernelkernel
deepin linux kernel
C
22
5
cherry-studiocherry-studio
🍒 Cherry Studio 是一款支持多个 LLM 提供商的桌面客户端
TypeScript
598
57
CangjieCommunityCangjieCommunity
为仓颉编程语言开发者打造活跃、开放、高质量的社区环境
Markdown
1.07 K
0
HarmonyOS-ExamplesHarmonyOS-Examples
本仓将收集和展示仓颉鸿蒙应用示例代码,欢迎大家投稿,在仓颉鸿蒙社区展现你的妙趣设计!
Cangjie
398
371
Cangjie-ExamplesCangjie-Examples
本仓将收集和展示高质量的仓颉示例代码,欢迎大家投稿,让全世界看到您的妙趣设计,也让更多人通过您的编码理解和喜爱仓颉语言。
Cangjie
332
1.08 K