A low-level reader and writer for the ECMA-335 metadata format.
- π¦ crates.io
- π docs.rs
- π Getting started
- π Source
windows-metadata reads and writes the ECMA-335 metadata format used by .NET,
WinRT, and the Win32 metadata. It is the foundation
windows-bindgen builds on. The reader::Index type loads
one or more .winmd files and lets you query namespaces, type definitions, and
their members.
The remainder of this page covers how the crate is built and maintained. It is
for contributors and is not needed to use windows-metadata.
Consumed by windows-bindgen. src/bindings.rs is generated by tool_bindings
from crates/tools/bindings/src/metadata.txt; the ECMA-335 tables and readers
are hand-written.
windows-metadata's merge module coalesces the per-architecture winmds that
tool_win32 scrapes (x64, arm64, x86) into a single winmd. A type identical across
every arch is emitted once as arch-neutral; a type that diverges is split into
per-arch copies, each tagged with a SupportedArchitectureAttribute (spelled
#[arch(X86|X64|Arm64)] in RDL).
The collapse-or-split decision is structural, driven by type_sig() β a hash of
everything that can legitimately differ between architectures:
- fields β name, type, and constant value (so an enum whose members hold different per-arch values splits instead of silently dropping the divergent values);
- method signatures β Win32 callbacks and WinRT delegates have no fields and
diverge only in their
Invokesignature, so a fields-only signature would wrongly collapse arch-divergent callbacks into one untagged copy; - layout β
#pragma packandClassLayout; AlignmentAttributeβ__declspec(align(N))raised alignment is encoded only by that attribute (ClassLayoutcan only lower alignment), so a type that differs solely in forced over-alignment must fold the attribute into the signature or the divergent copy is lost;- flags β the type's attribute flags.
Types present on only a subset of arches still go through the same structural split, so a type that is present on x64+arm64 (but not x86) and diverges between those two is split per arch rather than collapsing to whichever copy happened to be first. A group is emitted arch-neutral only when its signature spans every arch in the run.
The merge is deterministic: it stages through BTreeMaps and insertion-ordered
Vecs, with no HashMap reaching the output.
The writer is the foundation of the pipeline's reproducible builds. It stages
Constant / Attribute / GenericParam (and the bindgen TypeTree) in
BTreeMaps / BTreeSets, and the module MVID is a fixed zero GUID, so regenerating a
winmd is byte-for-byte identical across platforms (CI validates the committed winmds on
Linux).
Notable invariants in the ECMA-335 tables:
- The
HasSemanticscoded index and theProperty/Event/MethodSemanticstables are emitted only when non-empty, andMethodSemanticsrows are sorted byAssociationβ strict readers reject an unsorted table. write_indexasserts on the run-list one-past-end sentinel rather than silently wrapping to0for a target table with exactly 65535 rows (the guard lives on the write side so the reader's width threshold stays ECMA-conformant for external winmds).Value::Boolconstants round-trip symmetrically (the reader mapsELEMENT_TYPE_BOOLEAN); the CLR element-type vocabulary (ELEMENT_TYPE_*) is hand-authored insrc/clr.rsbecause its defining header (corhdr.h) ships only in the .NET SDK and cannot be scraped.
Run cargo test -p windows-metadata; see also the workspace test crates. The
arch-merge collapse/split rules are pinned by arch_roundtrip.rs (divergent fields,
callbacks, forced alignment, enum constant values, subset-present divergence).