
windows-bindgen 完全指南用 Windows 元数据生成精准的 Rust 绑定【免费下载链接】windows-rsRust for Windows项目地址: https://gitcode.com/GitHub_Trending/wi/windows-rs本文以 windows-rs 仓库中 docs/crates/windows-bindgen.md 为核心脉络系统讲解windows-bindgen的输入选择、过滤语法、样式与布局决策、完整生成工作流及底层实现原理。读完你不仅能独立为项目生成定制化 Rust 绑定还能理解windows、windows-sys等预生成 crate 背后的生成管线学会如何把生成产物纳入 CI 并保证确定性。windows-bindgen是 windows-rs 元数据管线C/C headers - RDL - .winmd - windows-bindgen - Rust source or a Rust package的最终阶段它读取 ECMA-335 格式的 Windows 元数据.winmd将其转译为 Rust 绑定源码或完整的 Rust package。它是构建期代码生成器而非 Windows API 运行时——生成的是代码不是 API 调用能力本身。何时使用 windows-bindgen当某个聚焦 cratefocused crate没有覆盖项目所需 API 时就轮到windows-bindgen出场。它是可复用库的首选方式让库只拥有自己的一小片绑定集narrow binding set而不必依赖庞大的windows或windows-sys伞形 crate。相比之下二进制应用在依赖体积与版本共享不那么敏感时更倾向于直接使用这两个预生成 crate。一句话概括使用场景可复用库生成并提交一份聚焦的bindings.rs避免把整个库绑死在windows伞形 crate 上自定义元数据支持自定义.winmd输入配合windows-rdl、windows-clang打通从 C/C 头文件一路到 Rust 的完整管线控制生成形态通过样式style、布局layout、过滤器filter精确控制生成的 Rust API 形状。选择输入windows-bindgen 吃什么windows-bindgen读取的是ECMA-335 Windows 元数据.winmd不是头文件也不是 IDL。支持的输入方式如下起点绑定来源标准 Windows API使用隐式默认元数据并添加过滤器filter自定义.winmd通过input添加若其中引用了 Windows 类型还需调用input_defaultRDL 声明先用windows-rdl编译再添加生成的.winmdC/C 头文件用windows-clang抓取经 RDL 编译再绑定内存中的元数据字节使用input_bytes或input_byte_sets关键规则如果不提供任何输入构建器默认读取windows-default捆绑的标准 WinRT 与 Win32 元数据。一旦提供自定义输入就取代了这个隐式选择当自定义定义需要引用标准类型时必须显式调用input_default把默认元数据一并加载进来。第一个工作流拥有一片聚焦绑定对于可复用库把生成过程与普通消费者的构建过程分开判断公共面需要富封装rich wrappers还是原始 FFI把 API 名称放进一份稳定的过滤器清单从一个不发布的 workspace 工具生成src/bindings.rs审查并随库一起提交生成的绑定文件在 CI 中运行该工具拒绝任何生成产物漂移diff。仓库中的tool_bindings正是这套工作流的范本它的入口 crates/tools/bindings/src/main.rs 逐个调用bindgen([--etc, crates/tools/bindings/src/*.txt])从crates/tools/bindings/src/*.txt读取命令文件重写各库已提交的bindings.rs。例如 core.txt 的内容为--out crates/libs/core/src/imp/bindings.rs --flat --minimal --dead-code --filter AGILEREFERENCE_DEFAULT CO_E_NOTINITIALIZED CoCreateFreeThreadedMarshaler CoTaskMemAlloc ... RoGetActivationFactory UuidCreate可以看到聚焦库的典型形态--flat扁平单文件--minimal精简渲染--dead-code未使用调用项标为pub(crate) 一份稳定的过滤器清单。应用侧替代方案见 crates/samples/bindgen/vss_backup/build.rs在build.rs中把扁平文件生成到OUT_DIR作为私有模块引入每次构建都重新生成fn main() { let out_dir std::env::var(OUT_DIR).unwrap(); let bindings format!({out_dir}/bindings.rs); windows_bindgen::builder() .output(bindings) .filters([ CreateVssBackupComponentsInternal, VSS_BT_FULL, IVssBackupComponents, ]) .flat() .write(); }发布库务必选择提交产物committed output这样库的使用者只需要生成代码的运行时支持如windows-link而无需携带生成器与元数据负载。同时库暴露的生成类型应视为其公共契约的一部分变更需谨慎。最小的可运行示例把生成器作为构建依赖、生成代码的运行时作为普通依赖加入Cargo.toml见 crates/libs/bindgen/readme.md[dependencies.windows-link] version 0.100 [build-dependencies.windows-bindgen] version 0.100在build.rs中用 API 方式生成绑定let args [ --out, src/bindings.rs, --flat, --sys, --filter, GetTickCount, ]; windows_bindgen::bindgen(args);之后在源码中使用mod bindings; unsafe { println!({}, bindings::GetTickCount()); }命令参数也可以放进文本文件使用windows_bindgen::bindgen([--etc, bindings.txt])读取包含完整命令含输出与样式选项的--etc适配文件若只是过滤器清单则用Bindgen::filter_file/filter_files或--filter-file。命令文件中空行与以//开头的行会被忽略。用过滤器挑选 API过滤器filter形似 Rust 路径越具体输出越小过滤器结果Windows.Win32.System.Com该命名空间及其子命名空间中的全部内容Windows.Win32.Foundation.HWND完整命名类型Namespace.Type::{}仅名字的外壳name-only shellNamespace.Type::Method单个方法及其必需的依赖类型Namespace.Type::{Method1, Method2}一组方法!Namespace.Type从已包含项中排除匹配项要点属性property与事件event的名称会选中它们的访问器配对选中某个 WinRT 类的CreateInstance成员会附带激活activation支持只选类本身则只投影其默认接口、不含构造函数签名依赖会自动包含进来通常以**外壳shell**形式出现整类型过滤器则保留类型层级组合使用Namespace.Type::Method选中一个方法及其必需类型依赖而Namespace.Type::{}只生成名字外壳。选择样式与布局样式Style控制 API 形状布局Layout控制条目写到哪里两者正交。样式适用场景关键行为默认Default富 WinRT 与 Win32 绑定包装器、句柄类型、继承转发器sys原始 FFI普通结构体与外部函数windows-sys即此样式minimal小型手写包装绑定集省略大部分便捷与继承包装extern_fns把 sys 自由函数从windows-link宏改为extern块sys与minimal互斥只有 sys 样式能输出原生可变参数variadic函数——富包装器无法转发未知参数尾同时稳定版 Rust 也无法声明fastcall可变参数。布局输出默认与元数据命名空间对应的嵌套 Rust 模块flat单个扁平 Rust 源文件package按命名空间拆分的文件 带命名空间 feature 的Cargo.tomlflat与package互斥package模式面向windows、windows-sys这类宽投影聚焦库通常用一个扁平文件即可在 sys 的 package 模式下仅含 COM 接口的空命名空间及其未使用的 feature 条目会被剪除pruned。常见生成任务实现特征用implement或implements为选中的接口生成 WinRT 实现特征implementation traitsimplement_all作用于作用域内所有接口可组合类minimal 模式用compose处理显式过滤的可组合 WinRT 类。注意要分别过滤该类与其工厂方法——implement不会选择组合目标派生特征用derive或derives给生成的类型添加特征格式化器用rustfmt选择格式化器可执行文件内部绑定用dead_code让内部绑定中未使用的可调用项被检测为pub(crate)。WinRT 事件订阅事件 add/remove 配对会投影为一个返回EventRevoker的方法。丢弃 revoker 即取消订阅forget或into_token把该责任转移出去。接口实现仍会提供 ABI 层的两个访问器。常见坑Pitfalls宽命名空间过滤会产生很大的依赖闭包。先从包装器真正拥有的可调用项或类型名开始默认、sys、minimal 输出是三种不同契约不是格式选择。写包装代码之前先定好样式minimal只改渲染不改依赖选择窄过滤器仍然必需精确选中不支持的 variadic 导出会报错宽泛的富过滤器则会直接省略它输出路径相对于生成器当前目录。脚本与 CI 中要固定生成器调用位置别为了让整个库共享一个生成类型而让它依赖windowscrate。要么用聚焦的基础 crate要么明确绑定归属。内部文档生成器如何构建与维护以下内容面向贡献者使用windows-bindgen无需了解。构建方式与输出策略windows-bindgen是手写的通过windows-metadata读取 ECMA-335 元数据windows-default提供捆绑输入。tool_bindings生成聚焦库文件tool_package生成发布的windows与windows-sys包见 crates/tools/bindings/src/main.rs 中的注释Reactor 绑定由tool_reactor生成WebView2 绑定由tool_webview生成。命名的策略方法把样式决策从各 writer 中抽离出来Style::emit_class_methods控制逐类包装方法Style::emit_inherited_forwarders控制继承接口转发方法Style::emit_iterable_into_iterator控制继承的IIterableT桥接minimal_string_input/minimal_string_return映射 minimal 样式下的字符串Config::emit_runtime_name控制 WinRT 运行时名常量Style::derive_std_traits/emit_core_traits控制生成的特征块Style::emit_bare_typedef控制句柄与无作用域枚举的表示。Config::item_vis把dead_code可见性应用于可调用项可命名项保持 public因为手写代码与导出的宏可能重新导出或引用它们。类型选择精确过滤器走TypeClosure::build从选中类型出发、沿签名依赖展开。选中的入口点是完整类型签名依赖默认是外壳除非被直接选中整类型过滤器保留其层级类成员过滤器保留提供该成员的类到接口边纯签名依赖不会引入无关的层级边被选为外壳的接口仍可通过implement提供_Impl脚手架实现闭包保留每个 ABI 方法签名但不发出可调用包装器。当同一个绑定既要调用又要实现某接口时请把整个接口也选中minimal 可组合绑定需要显式的类目标用过滤器选中类的可组合工厂方法、用implement选中覆写接口、再用compose选中类——这能防止每个继承已实现覆写接口的类都变成组合目标宽过滤与 package 生成走TypeMap::filterminimal只影响渲染不改变哪些引用类型被包含。WinRT 与 Win32 生成元数据读取器依据元数据属性分类类型共享代码处理名称、签名、依赖与重映射不同的 writer 保持各自的 ABI 规则WinRT vtable 方法返回HRESULT富输出经Result投影COM 方法保持原生返回形态用ReturnHint处理常见投影模式WinRT 支持泛型、运行时签名、激活与RuntimeTypeWinRT 委托是带Invoke的 COM 接口COM 回调可以是函数指针Win32 还有自由导出、常量、句柄、联合、嵌套类型与架构相关布局。位域访问器Winmd 没有位域语法。头文件管线把每个 run 存储为名为_bitfield、_bitfield1……的整数字段并用NativeBitfieldAttribute条目描述逻辑成员。非 sys 的 bindgen 输出保留后备字段并追加类型化 getter/setter宽度为 1 的成员投影为bool更宽的成员使用后备整数类型读穿过后备类型移位有符号字段符号扩展、无符号字段零扩展写清除目标范围后 OR 进掩码值恒等移位identity shift会被省略保证生成代码在-D warnings下保持干净。RDL 端把同一形状拼写为后备字段上的 block。覆盖测试在test_clang/input/bitfields.h与test_bindgen/input/struct_bitfield.rdl。计数缓冲区Counted buffersNativeArrayInfoAttribute与MemorySizeAttribute描述元素个数、字节数与固定计数。MethodParam::buffer_relationship只解码字面关系bindgen 负责校验投影策略索引相关参数前bindgen 会拒绝负数、越界、自引用self-relative或重复使用的计数索引计数必须是单个输入标量字节计数要求元素为字节大小固定计数必须非负且适配 32 位 Windows 的最大 Rust 对象大小任何校验失败都会保留原始指针/计数对输入或输入/输出缓冲区可以变成切片slice纯输出缓冲区保持裸指针/计数对——因为mut [T]要求调用前已有初始化存储。参数方向与返回值windows-metadata提供原始的方向、可选、保留、retval 与计数事实bindgen 施加 Rust 策略Input与Unspecified仅作输入Output与InputOutput走可输出分支合格的输入/输出缓冲区变成mut [T]。InputOutput保持可变切片形态而仅标记Output的参数保持裸指针/计数对让调用者可提供未初始化存储。必需输入缓冲区可用有符号或无符号切片长度带符号计数的可选缓冲区保持显式因为它们可能使用负数哨兵值。尾参数投影为返回值的条件启发式仅输出output-only、必需required、非保留non-reserved、无计数uncounted、指针形态pointer-shaped。RetValAttribute只绕过对前序输出参数、void 指针目标与 128 位大小限制这三项启发式检查不绕过其余候选检查。可变参数函数Sys 输出在 link-macro 或 extern-block 两种形态下都保留字面...尾与元数据的C或system调用约定。在 X86 上Rust 会把systemC-variadic 声明降级为兼容的 C variadic ABI。富rich与 minimal writer 绝不发出可调用的固定前缀包装器。包剪枝Package pruningSys package 输出中只含 COM 接口的命名空间会变空。package 生成会递归移除该命名空间的模块、文件、feature 与 feature 依赖只要自身或任一子项仍含输出父命名空间就保留。确定性与测试生成会对元数据驱动的 map 排序写盘前先格式化保证输出确定性生成器必须保持输出中立除非有意变更投影——改动 bindgen 后要运行其所属的tool_*生成器并检查全部生成 difftest_bindgen覆盖过滤器闭包、样式、布局、方法、缓冲区、返回值、实现支持与可变参数test_rdl与test_clang覆盖输入阶段CI 会重新生成已提交绑定与包输出并拒绝漂移。示例与相邻工具工具/示例作用vss_backup 示例在OUT_DIR生成富 COM 绑定IVssBackupComponents等context_alignment 示例为CONTEXT生成扁平 sys 绑定--sys --flat见其 build.rscrates/samples/robot/component编译自定义 RDL 并生成实现支持crates/samples/robot/client组合自定义元数据与默认元数据生成 WinRT 客户端tool_package用 package 模式生成发布的windows与windows-syscratetool_webview演示完整的header - RDL - winmd - Rust管线结语windows-bindgen是 windows-rs 元数据管线的收口环节以 ECMA-335 元数据为唯一输入以「过滤器定范围、样式定形状、布局定落点」三个正交维度精确控制输出再配合tool_bindings式的「提交产物 CI 防漂移」工作流让任何可复用库都能拥有一片自洽、可维护、不依赖伞形 crate 的 Rust 绑定。无论你的目标是标准 Win32/WinRT API 的精简绑定还是从自定义 RDL、C/C 头文件一路走到 Rust 的完整自建管线这份指南都已覆盖从入门到源码级原理的全部关键路径。【免费下载链接】windows-rsRust for Windows项目地址: https://gitcode.com/GitHub_Trending/wi/windows-rs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考