windows-rs Documentation

Official Rust developer resources and documentation for microsoft/windows-rs with 13k stars. Verified technical context for AI.

12,832 stars Rust Rust 1 file · ~87,017 tokens
RAW Doc

Technical Documentation: microsoft/windows-rs

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`docs/readme.md`)

windows-rs provides Rust crates for Windows APIs, from COM support and system services to UI and
raw bindings.

Prefer the focused crates below when they cover the functionality you need. For additional APIs,
windows-bindgen can generate a minimal project-specific binding,
while windows and windows-sys provide broad API
coverage behind feature flags for binary applications.

Reusable libraries should avoid the windows and windows-sys umbrella crates. They add
dependency weight and version churn, and several libraries can bring incompatible versions of the
same large crate into one dependency tree. Prefer the focused crates for shared Windows types and
generate any remaining private bindings with windows-bindgen.

Each crate page covers usage and maintenance. Generated API documentation is available on
docs.rs.

Crates

Core types and interop

Crate Description
windows-core COM and WinRT types, traits, and authoring macros.
windows-result Windows error handling and propagation.
windows-strings Windows string types, conversions, and macros.
windows-link Links C-style functions without import libraries.

Values and collections

Crate Description
windows-collections WinRT collections.
windows-numerics Graphics vectors and matrices.
windows-reference Implementation of WinRT IReference<T>.
windows-time WinRT DateTime and TimeSpan types.

Async and threading

Crate Description
windows-future WinRT asynchronous operations as Rust futures.
windows-threading Safe wrapper over the Win32 thread pool.

System services

Crate Description
windows-registry Safe Windows registry access.
windows-services Support for authoring Windows services.
windows-version Queries the Windows version at runtime.

UI and graphics

Crate Description
windows-reactor Declarative UI library backed by WinUI 3.
windows-canvas 2D graphics built on Direct2D.
windows-composition Windows composition visuals.
windows-webview Safe wrapper around the WebView2 browser control.
windows-window Window creation and message dispatch.
windows-pickers Windows file and folder pickers.
windows-animation Wrapper around the Windows Animation Manager.
windows-reactor-setup Stages the Windows App Runtime.

Code generation and metadata

Crate Description
windows-clang Generates RDL from C and C++ headers using libclang.
windows-default Embedded Windows metadata for build tools.
windows-metadata Reads and writes ECMA-335 metadata.
windows-rdl Parses RDL and generates ECMA-335 metadata.
cppwinrt Packages the C++/WinRT compiler.

Macro implementation

These crates implement macros exported by windows-core. Applications should use the
windows-core exports rather than depend on them directly.

Crate Description
windows-implement Implements the #[implement] macro.
windows-interface Implements the #[interface] macro.

Windows API bindings

Use windows-bindgen when the focused crates above do not cover the APIs you need. Binary
applications may instead prefer the convenience of the pre-generated windows or windows-sys
umbrella crate.

Crate Description
windows-bindgen Generates Rust bindings from Windows metadata.
windows Typed bindings for C-style, COM, and WinRT APIs.
windows-sys Raw bindings for C-style Windows APIs.

Overview (`metadata/win32/readme.md`)

Win32 metadata snapshot

This directory holds the canonical RDL snapshot of the Win32 API surface that
windows-clang scrapes from the Windows SDK headers. The whole
API lives in a single flat Windows.Win32 namespace, split one file per defining header -
e.g. wingdi.rdl holds exactly the declarations wingdi.h defines - written in the human-readable
RDL text format. The partitioning follows the source (the clang
cursor's defining header), not a hand-curated namespace layout.

The merged binary Windows.Win32.winmd that downstream tooling consumes is not checked in (winmd
is a binary format, and *.winmd is git-ignored). This text snapshot is committed instead so the
API surface can be browsed directly and so changes show up as readable diffs in pull requests - the
same reason the generated windows and windows-sys crates are committed.

Generated - do not edit by hand

These files are produced by tool-win32:

sh
cargo run -p tool-win32

Re-run the tool after changing the manifest (the const slices in
crates/tools/win32/src/main.rs), the hand-authored vocabulary seed (metadata/metadata.rdl), or
the scraper, and commit the resulting diff. Hand edits will be overwritten on the next run - the
tool clears the generated partitions in this directory and re-emits the whole closure. The seed
lives one level up (metadata/metadata.rdl) so it survives that clear.

See docs/crates/windows-clang.md for the scraper design -
how the metadata matches the SDK headers, header partitioning, and the documented deviations from
the reference - and docs/crates/windows-rdl.md for the winmd
artifact and layout of the in-house metadata pipeline that replaces the external win32metadata
project.


Overview (`crates/libs/animation/readme.md`)

windows-animation

Windows Animation wraps the Windows Animation Manager (IUIAnimationManager) for variable
interpolation, transitions, and storyboards. It does not depend on a UI or rendering framework.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-animation]
version = "0.100"
rust,no_run
use windows_animation::*;

fn main() -> Result<()> {
    let manager = Manager::new()?;
    let opacity = manager.create_variable(0.0)?;

    // Animate from 0.0 to 1.0 over half a second.
    let library = TransitionLibrary::new()?;
    let fade_in = library.linear(0.5, 1.0)?;

    let storyboard = manager.create_storyboard()?;
    storyboard.add_transition(&opacity, &fade_in)?;
    storyboard.schedule(0.0)?;

    // Each frame, advance the clock and read the current value.
    manager.update(0.25)?;
    let value = opacity.value()?;
    assert!((0.0..=1.0).contains(&value));
    Ok(())
}

Overview (`crates/libs/bindgen/readme.md`)

windows-bindgen

The windows-bindgen crate generates Rust bindings from
Windows metadata.

Add the generator as a build dependency and the generated code's runtime dependency:

toml
[dependencies.windows-link]
version = "0.100"

[build-dependencies.windows-bindgen]
version = "0.100"

Generate a flat file in OUT_DIR from build.rs:

rust,no_run
use std::path::Path;

fn main() {
    let out_dir = std::env::var("OUT_DIR").unwrap();
    windows_bindgen::builder()
        .output(Path::new(&out_dir).join("bindings.rs"))
        .filter("GetTickCount")
        .flat()
        .sys()
        .write();
}

Filters name the functions, constants, or types you need. Signature dependencies are included
automatically. See the guide for qualified names, member filters, and filter files.

Use the generated bindings from src/main.rs:

rust,ignore
mod bindings {
    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}

fn main() {
    unsafe {
        println!("{}", bindings::GetTickCount());
    }
}

For a published library, generate and commit src/bindings.rs from a separate tool instead of
running the generator in consumer builds. Formatting is best effort; install rustfmt when
generating checked-in source.


Overview (`crates/libs/canvas/readme.md`)

windows-canvas

Windows Canvas wraps Direct2D, Direct3D 11, DXGI, DirectWrite, and WIC for 2D drawing.

The core crate is host-neutral. Enable system for the typed windows-window swap-chain helper,
reactor for Canvas controls in Reactor, and composition to draw into Composition surfaces. The
system and reactor host adapters can coexist unless composition selects their incompatible
Composition backends.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-canvas]
version = "0.100"
rust,no_run
use windows_canvas::*;

fn main() -> Result<()> {
    let device = GpuDevice::new_warp()?;
    let mut swap_chain = device.create_swap_chain(640, 480)?;

    let session = swap_chain.begin_draw()?;
    session.clear(ColorF::CORNFLOWER_BLUE);

    let brush = session.create_solid_brush(ColorF::rgb(1.0, 0.5, 0.0))?;
    session.fill_rect(&Rect::new(20.0, 20.0, 200.0, 120.0), &brush);
    drop(session);

    swap_chain.present()?;
    Ok(())
}

Overview (`crates/libs/clang/readme.md`)

windows-clang

The windows-clang crate extracts declarations from C and
C++ source with libclang and emits RDL. It is the
header-facing stage of the Windows metadata pipeline:

text
headers -> windows-clang -> RDL -> windows-rdl -> WinMD

Add the crate to your Cargo.toml:

toml
[dependencies.windows-clang]
version = "0.100"

For ordinary header-to-RDL generation, configure and run the high-level builder:

rust,no_run
windows_clang::clang()
    .input("Example.h")
    .args(["-x", "c++", "--target=x86_64-pc-windows-msvc"])
    .reference_default()
    .namespace("Example")
    .library("example.dll")
    .output("Example.rdl")
    .write()
    .unwrap();

The builder reads inputs and references, invokes the extractor, emits RDL, and writes the output.
Use Input, extract, and EmitOptions directly when a generator needs to inspect or
combine immutable snapshots before emission.

The caller owns libclang installation, compiler arguments, package versions, import-library
discovery, architecture merging, output promotion, and RDL-to-WinMD compilation. See the crate
documentation
for both APIs and the extraction model.


Overview (`crates/libs/collections/readme.md`)

windows-collections

The windows-collections crate implements Windows
collection interfaces for Rust collections.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-collections]
version = "0.100"
rust
use windows_collections::*;

let numbers = IIterable::<i32>::from(vec![1, 2, 3]);

for value in numbers {
    println!("{value}");
}

Overview (`crates/libs/composition/readme.md`)

windows-composition

Windows Composition wraps the retained-mode Windows composition engine for standalone windows and
WinUI 3 applications.

The default system feature hosts Composition in a windows-window window. Use the reactor
feature for a Composition host inside Reactor, with default features disabled. The stacks are
mutually exclusive. Both support Canvas drawing surfaces through windows-canvas[composition];
select Canvas's matching system or reactor feature.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-composition]
version = "0.100"
rust,no_run
use windows_composition::*;

fn build(compositor: &Compositor) -> SpriteVisual {
    let visual = compositor.create_sprite_visual();
    visual.set_size(200.0, 120.0);

    let brush = compositor.create_color_brush(CompositionColor::rgb(0, 120, 215));
    visual.set_brush(&brush);
    visual
}

Overview (`crates/libs/core/readme.md`)

windows-core

The windows-core crate provides COM and WinRT types,
traits, and macros for the windows-* family of crates.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-core]
version = "0.100"

Use the core types as needed:

rust
use windows_core::{h, Result, HRESULT, HSTRING};

// WinRT reference-counted strings.
let name: &HSTRING = h!("Windows.Foundation.Uri");
assert_eq!(name, &HSTRING::from("Windows.Foundation.Uri"));

// HRESULT-based results.
fn check(code: HRESULT) -> Result<()> {
    code.ok()
}

assert!(check(HRESULT(0)).is_ok());
assert!(check(HRESULT(-2147467259)).is_err());

create_instance activates an in-process COM class and returns a requested interface. The
application must initialize the calling thread for COM before using it.

Use init_mta for apartment-agnostic code. UI threads that require a single-threaded apartment can
hold the value returned by init_sta for the lifetime of their COM objects.


Overview (`crates/libs/cppwinrt/readme.md`)

cppwinrt

The cppwinrt crate bundles the C++/WinRT compiler for use in
Rust.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.cppwinrt]
version = "0.100"

Use cppwinrt function as needed:

rust,ignore
println!("{}", cppwinrt::cppwinrt(["-help"]));

Overview (`crates/libs/default/readme.md`)

windows-default

The windows-default crate provides the default metadata
for Windows APIs as embedded byte slices. Build tools can use WINRT and WIN32 without
locating or distributing separate .winmd files.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-default]
version = "0.100"
rust
use windows_default::{WIN32, WINRT};

assert!(!WINRT.is_empty());
assert!(!WIN32.is_empty());

Overview (`crates/libs/future/readme.md`)

windows-future

The windows-future crate adapts WinRT asynchronous
operations to Rust futures.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-future]
version = "0.100"

Use the Windows async types as needed:

rust,no_run
use windows_future::*;

// This result will be available immediately.
let ready = IAsyncOperation::ready(Ok(123));
assert_eq!(ready.join().unwrap(), 123);

let ready = IAsyncOperation::spawn(|| {
    // Some lengthy operation goes here...
    Ok(456)
});

assert_eq!(ready.join().unwrap(), 456);

Overview (`crates/libs/implement/readme.md`)

windows-implement

The windows-implement crate provides the
#[implement] macro for implementing COM and WinRT interfaces in Rust.

Start by adding the following to your Cargo.toml file:

toml
[dependencies]
windows-core = "0.100"
windows-implement = "0.100"
rust
use windows_core::{IUnknown, interface};
use windows_implement::implement;

#[interface("57f61c8b-b400-4035-a7c2-abbddf9e3559")]
unsafe trait IValue: IUnknown {
    fn get(&self) -> u32;
}

#[implement(IValue)]
struct Value(u32);

impl IValue_Impl for Value_Impl {
    unsafe fn get(&self) -> u32 {
        self.0
    }
}

let value: IValue = Value(42).into();
assert_eq!(unsafe { value.get() }, 42);

Overview (`crates/libs/interface/readme.md`)

windows-interface

The windows-interface crate provides the
#[interface] macro for declaring COM interfaces in Rust.

Start by adding the following to your Cargo.toml file:

toml
[dependencies]
windows-core = "0.100"
windows-interface = "0.100"
rust
use windows_core::IUnknown;
use windows_interface::interface;

#[interface("57f61c8b-b400-4035-a7c2-abbddf9e3559")]
unsafe trait IValue: IUnknown {
    fn get(&self) -> u32;
}

The windows-link crate provides the link macro that
simplifies linking. The link macro uses raw-dylib and thus does not require import lib files.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-link]
version = "0.100"

Use the link macro to import functions and their matching function-pointer types. Here the loader
functions are linked normally while MessageBoxA is resolved at runtime:

rust,no_run
use std::ffi::c_void;
use windows_link::link;

link!("kernel32.dll" "system" fn LoadLibraryA(name: *const u8) -> *mut c_void);
link!("kernel32.dll" "system" fn GetProcAddress(
    library: *mut c_void,
    name: *const u8,
) -> *mut c_void);
link!("user32.dll" "system" fn MessageBoxA(
    hwnd: *mut c_void,
    text: *const u8,
    caption: *const u8,
    kind: u32,
) -> i32);

unsafe {
    let module = LoadLibraryA(b"user32.dll\0".as_ptr());
    let address = GetProcAddress(module, b"MessageBoxA\0".as_ptr());

    if !address.is_null() {
        let message_box: MessageBoxA = std::mem::transmute(address);

        message_box(
            std::ptr::null_mut(),
            b"text\0".as_ptr(),
            b"caption\0".as_ptr(),
            0,
        );
    }
}

Overview (`crates/libs/metadata/readme.md`)

windows-metadata

The windows-metadata crate reads and writes the
ECMA-335 metadata format used by .NET, WinRT, and Win32 metadata.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-metadata]
version = "0.100"

Query a type with the metadata reader:

rust,no_run
use windows_metadata::*;

let index = reader::Index::read("Windows.winmd").unwrap();

let def = index.expect("Windows.Foundation", "Point");
assert_eq!(def.namespace(), "Windows.Foundation");
assert_eq!(def.name(), "Point");

let extends = def.extends().unwrap();
assert_eq!(extends.namespace(), "System");
assert_eq!(extends.name(), "ValueType");

let fields: Vec<_> = def.fields().collect();
assert_eq!(fields.len(), 2);
assert_eq!(fields[0].name(), "X");
assert_eq!(fields[1].name(), "Y");
assert_eq!(fields[0].ty(), Type::F32);
assert_eq!(fields[1].ty(), Type::F32);

Overview (`crates/libs/numerics/readme.md`)

windows-numerics

The windows-numerics crate provides Windows vector,
matrix, plane, and quaternion types.

These are the same graphics math types used by composition, Direct2D, and Direct3D
APIs, so values flow into those APIs without conversion.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-numerics]
version = "0.100"

Use the vector and matrix types as needed:

rust
use windows_numerics::*;

let a = Vector2::new(3.0, 4.0);
assert_eq!(a.length(), 5.0);

let b = Vector3::new(1.0, 0.0, 0.0);
let c = Vector3::new(0.0, 1.0, 0.0);
assert_eq!(b.cross(&c), Vector3::new(0.0, 0.0, 1.0));

let transform = Matrix3x2::translation(10.0, 20.0);
assert_eq!(transform.m31, 10.0);
assert_eq!(transform.m32, 20.0);

Overview (`crates/libs/pickers/readme.md`)

windows-pickers

Windows Pickers provides file and folder pickers backed by the Windows Common Item Dialog. It
supports standalone Windows applications and components built with windows-reactor.

Start by adding the following to your Cargo.toml file:

toml
[dependencies]
windows-pickers = "0.100"
windows-window = "0.100"
rust,no_run
# #[cfg(feature = "system")]
# mod example {
use windows_pickers::{OpenFilePicker, Result};
use windows_window::Window;

fn pick_rust_file(window: &Window) -> Result<()> {
    let path = OpenFilePicker::new()
        .title("Open a Rust source file")
        .filter_extensions("Rust source", ["rs"])
        .filter_all()
        .show(window)?;

    if let Some(path) = path {
        println!("{}", path.display());
    }

    Ok(())
}
# }

The calling thread must be a COM single-threaded apartment (STA). Cancellation returns Ok(None);
other Windows failures are returned as errors.


Overview (`crates/libs/rdl/readme.md`)

windows-rdl

The windows-rdl crate compiles RDL (Rust Definition
Language) - a Rust-like text format for describing Windows APIs - into ECMA-335 .winmd metadata,
and back again.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-rdl]
version = "0.100"

Use the reader to compile .rdl source into a .winmd, and the writer to regenerate canonical
.rdl from a .winmd:

rust,no_run
windows_rdl::reader()
    .input("example.rdl")
    .output("example.winmd")
    .write()
    .unwrap();

windows_rdl::writer()
    .input("example.winmd")
    .output("example.rdl")
    .write()
    .unwrap();

Overview (`crates/libs/reactor-setup/readme.md`)

windows-reactor-setup

Windows Reactor Setup stages the Windows App SDK runtime files needed by a
windows-reactor application that runs
fully self-contained.

Add it as a build dependency:

toml
[build-dependencies]
windows-reactor-setup = "0.100"

Call the setup function from build.rs:

rust,no_run
windows_reactor_setup::as_self_contained();

Runtime extraction caches are separate for each target architecture and are reused only after
successful extraction and required-file checks. Download, extraction, and copy errors fail the
build rather than leaving it to succeed with missing runtime files.


Overview (`crates/libs/reactor/readme.md`)

windows-reactor

Windows Reactor is a typed declarative UI library for building native WinUI 3 applications in
Rust. Components own Rust state, describe the current view, and receive typed messages from
controls.

Add Reactor to Cargo.toml:

toml
[dependencies.windows-reactor]
version = "0.100"
rust,no_run
use windows_reactor::*;

#[derive(Clone, Copy)]
enum Message {
    Increment,
    Reset,
}

struct Counter {
    count: i32,
}

impl Component for Counter {
    type Input = ();
    type Message = Message;

    fn create(_input: &(), _context: &ComponentContext<Self>) -> Self {
        Self { count: 0 }
    }

    fn update(&mut self, message: Message, _context: &ComponentContext<Self>) {
        match message {
            Message::Increment => self.count += 1,
            Message::Reset => self.count = 0,
        }
    }

    fn view(&self, _input: &(), context: &mut ViewContext<Self>) -> View {
        let content = StackPanel::new().spacing(8.0).children((
            format!("Count: {}", self.count),
            Button::new()
                .on_click(context.message(Message::Increment))
                .content("Increment"),
            Button::new()
                .on_click(context.message(Message::Reset))
                .content("Reset"),
        ));

        context.window_frame("Counter", content)
    }
}

fn main() {
    App::run_component::<Counter>(()).unwrap();
}

Run with cargo run. Applications use the default features and an installed Windows App SDK
runtime. See the setup guide for runtime prerequisites and self-contained deployment.

The getting-started guide covers editable state, child components, background work, images,
and application windows. Browse the samples for complete applications and focused examples.


Overview (`crates/libs/reference/readme.md`)

windows-reference

The windows-reference crate implements
IReference<T> for boxed Windows values.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-reference]
version = "0.100"
rust
use windows_reference::*;

let value = IReference::<i32>::from(42);
assert_eq!(value.Value().unwrap(), 42);

Overview (`crates/libs/registry/readme.md`)

windows-registry

The windows-registry crate reads and writes the Windows
registry.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-registry]
version = "0.100"
rust,no_run
use windows_registry::*;

fn main() -> Result<()> {
    let key = CURRENT_USER.create(r"software\windows-rs")?;

    key.set_u32("number", 123)?;
    key.set_string("name", "Rust")?;

    println!("{}", key.get_u32("number")?);
    println!("{}", key.get_string("name")?);

    Ok(())
}

Overview (`crates/libs/result/readme.md`)

windows-result

The windows-result crate provides Windows error types for
Win32, COM, and WinRT APIs.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-result]
version = "0.100"

Use the HRESULT, Error, and specialized Result types as needed:

rust
use windows_result::*;

const S_OK: HRESULT = HRESULT(0);
const ERROR_CANCELLED: WIN32_ERROR = WIN32_ERROR(1223);
const E_CANCELLED: HRESULT = ERROR_CANCELLED.to_hresult();

fn main() -> Result<()> {
    S_OK.ok()?;
    let e = Error::new(E_CANCELLED, "test message");
    assert_eq!(e.code(), E_CANCELLED);
    assert_eq!(e.message(), "test message");
    Ok(())
}

Overview (`crates/libs/services/readme.md`)

windows-services

The windows-services crate implements Windows service
processes.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-services]
version = "0.100"
rust,no_run
windows_services::Service::new()
    .can_pause()
    .can_stop()
    .run(|_service, _command| {})
    .unwrap();

Overview (`crates/libs/strings/readme.md`)

windows-strings

The windows-strings crate provides Windows string types
and literals.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-strings]
version = "0.100"

Use the Windows string types as needed:

rust
use windows_strings::*;

const A: PCSTR = s!("ansi");
const W: PCWSTR = w!("wide");

fn main() {
    let b = BSTR::from("bstr");
    let h = HSTRING::from("hstring");

    assert_eq!(b, "bstr");
    assert_eq!(h, "hstring");

    assert_eq!(unsafe { A.to_string().unwrap() }, "ansi");
    assert_eq!(unsafe { W.to_string().unwrap() }, "wide");
}

Overview (`crates/libs/sys/readme.md`)

windows-sys

The windows-sys crate provides raw Windows API declarations, structs, and constants without
wrappers or helper traits.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-sys]
version = "0.100"
features = [
    "handleapi",
    "synchapi",
    "winuser",
]

Make use of any Windows APIs as needed:

rust,no_run
use windows_sys::{core::*, Win32::*};

unsafe {
    let event = CreateEventW(std::ptr::null_mut(), 1, 0, std::ptr::null());
    SetEvent(event);
    WaitForSingleObject(event, 0);
    CloseHandle(event);

    MessageBoxA(0 as _, s!("Ansi"), s!("Caption"), MB_OK as u32);
    MessageBoxW(0 as _, w!("Wide"), w!("Caption"), MB_OK as u32);
}

Overview (`crates/libs/threading/readme.md`)

windows-threading

The windows-threading crate wraps the Windows thread
pool.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-threading]
version = "0.100"

Submit work to the default pool:

rust,no_run
windows_threading::submit(|| {
    println!("thread: {}", windows_threading::thread_id());

    loop {
        println!(".");
        windows_threading::sleep(1000);
    }
});

Process an iterator in parallel:

rust,no_run
let counter = std::sync::RwLock::<usize>::new(0);

windows_threading::for_each(0..10, |value| {
    println!("thread: {}, value: {value}", windows_threading::thread_id());
    let mut counter = counter.write().unwrap();
    *counter += value;
});

assert_eq!(*counter.read().unwrap(), 45);

Overview (`crates/libs/time/readme.md`)

windows-time

The windows-time crate provides Windows TimeSpan and
DateTime values with Rust constructors, arithmetic, and std::time conversions.

TimeSpan is a duration and DateTime is a point in time. Both use 100-nanosecond ticks.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-time]
version = "0.100"

Use the time types as needed:

rust
use windows_time::*;

let span = TimeSpan::from_minutes(90);
assert_eq!(span.whole_seconds(), 5400);

let start = DateTime::from_unix_secs(1_000_000_000);
let later = start.checked_add(span).unwrap();
assert_eq!(later.unix_secs(), 1_000_005_400);

Overview (`crates/libs/version/readme.md`)

windows-version

The windows-version crate reads the Windows version
without relying on the application manifest.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-version]
version = "0.100"
rust,no_run
use windows_version::*;

println!("Current version: {:?}", OsVersion::current());

if is_server() {
    println!("Running on a Windows Server release.");
}

if OsVersion::current() >= OsVersion::new(10, 0, 0, 12345) {
    println!("Can use a feature available on this version or later.")
}

Overview (`crates/libs/webview/readme.md`)

windows-webview

Windows WebView wraps the WebView2 COM APIs to host Microsoft Edge
(Chromium) in a window.

The crate has no default UI framework. Enable system for a complete windows-window host or
reactor for a WebView2 control in a Reactor view. Enable both when an application uses both host
types.

Start by adding the following to your Cargo.toml file:

toml
[dependencies]
windows-webview = { version = "0.100", features = ["system"] }

WebViewWindowBuilder::run owns the COM apartment, parent window, WebView2 environment and
controller, resize handling, shutdown order, and the application's windows-window message loop:

rust,ignore
use windows_webview::*;

fn main() -> Result<()> {
    WebViewWindow::new("WebView2")
        .size(1000, 700)
        .run(|host| {
            host.webview()
                .navigate("https://github.com/microsoft/windows-rs")?;
            Ok(())
        })
}

Use WebViewHost::builder when another framework owns the parent HWND and message loop. It chains
environment and controller creation into one callback and returns one host object for the
framework to retain. The raw-window sample shows the raw HWND
contract. The reactor sample embeds WebView2 in a Reactor view,
while reactor-window uses Reactor controls to drive a
WebView hosted in a separate windows-window.


Overview (`crates/libs/window/readme.md`)

windows-window

The windows-window crate provides basic window creation
and message-loop support for Canvas, WebView2, and custom rendering.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows-window]
version = "0.100"
rust,no_run
use windows_window::*;

fn main() -> Result<()> {
    let window = Window::new("Hello")
        .client_size(800, 600)
        .on_resize(|width, height| {
            println!("resized to {width} x {height}");
        })
        .on_close(|| {
            println!("release HWND-hosted resources here");
        })
        .quit_on_close(true)
        .create()?;

    // `window.hwnd()` can be handed to windows-canvas, WebView2, Direct2D, etc.
    println!("created window {:?}", window.hwnd());

    run();
    Ok(())
}

Overview (`crates/libs/windows/readme.md`)

windows

The windows crate provides Rust bindings for Win32, COM, and
WinRT APIs generated from
Windows metadata.

Start by adding the following to your Cargo.toml file:

toml
[dependencies.windows]
version = "0.100"
features = [
    "Data_Xml_Dom",
    "handleapi",
    "synchapi",
    "winuser",
]

Make use of any Windows APIs as needed:

rust,no_run
use windows::{core::*, Data::Xml::Dom::*, Win32::*};

fn main() -> Result<()> {
    let doc = XmlDocument::new()?;
    doc.LoadXml(h!("<html>hello world</html>"))?;

    let root = doc.DocumentElement()?;
    assert!(root.NodeName()? == "html");
    assert!(root.InnerText()? == "hello world");

    unsafe {
        let event = CreateEventW(None, true, false, None);
        if event.is_null() {
            return Err(Error::from_thread());
        }
        SetEvent(event).ok()?;
        WaitForSingleObject(event, 0);
        CloseHandle(event).ok()?;

        MessageBoxA(None, s!("Ansi"), s!("Caption"), MB_OK as u32);
        MessageBoxW(None, w!("Wide"), w!("Caption"), MB_OK as u32);
    }

    Ok(())
}

Overview (`crates/tests/bench/readme.md`)

test_bench

A side-by-side WinRT projection benchmark. Three consumers call the identical Rust WinRT
component, so every per-call delta is projection cost.

Consumer Column Projection
rust windows-rs The generated windows-rs projection.
cpp cppwinrt The header-only C++/WinRT projection.
cswinrt2 CsWinRT 2 The stable C#/WinRT projection with RCW-backed managed classes.

The cswinrt project is a focused CsWinRT 3.0 preview probe rather than a benchmark column. The
preview package builds on plain net10.0, but it does not contain the WinRT.Interop assembly
that its delegate marshaller loads. The probe fails at the first event subscription and its Cargo
test remains ignored until a complete preview ships.

The component (component -> bench_component.dll) is a real WinRT component: RDL -> winmd ->
windows-bindgen -> #[implement], activated registration-free via DllGetActivationFactory. It
exposes one activatable class, Bench.Widget, with scalar, string, metadata Object, nullable
reference, async, event, vector, map, and view operations. A non-default INonDefault interface
provides the QueryInterface fixture, and LiveCount reports native ownership balance.

Running

powershell
# Full run (10,000,000 iterations, median of three runs by default)
crates/tests/bench/run.ps1

# Quick run
crates/tests/bench/run.ps1 -Iterations 100000 -Runs 1

run.ps1 builds the component first so its metadata writer does not race a consumer build, builds
the three consumers, stages bench_component.dll as Bench.dll, and prints median throughput,
memory, and leak values. The lower native result and the CsWinRT result are bold.

Each consumer also has a cargo test that runs the same path with a small iteration count, so CI
exercises the matrix wherever the .NET SDK and MSVC toolchain are present.

Results

Measured on August 5, 2026 with .NET SDK 10.0.302 and CsWinRT 2.3.1. Times are milliseconds for
10,000,000 operations (median of three runs); Error uses 1,000,000 operations. Lower is better.

Metric C++/WinRT windows-rs CsWinRT 2
Create 624 567 11,075
Int32 25 24 45
String 298 291 1,588
Add 14 17 25
Interface 14 17 64
Object 141 138 1,205
Event 219 218 929
AddRemove 353 781 24,526
Vector 119 121 254
IterateVector 1,287 129 4,516
GetMany 2 2 183
Map 722 579 17,012
Lookup 219 183 297
VectorView 19 24 139
MapView 152 167 246
Reference 2,323 766 24,049
Async 579 490 54,547
Error 21,263 7 2,858
Memory (bytes/object) 8 8 296
Leak (live objects) 0 0 0

Metrics

Metric What it measures
Create Activate a fresh Widget.
Int32 Set and get the Int32 property.
String Set and get the String property, including HSTRING conversion.
Add Call a method with two Int32 arguments and a return value.
Interface Acquire INonDefault once, then call Value repeatedly.
Object Set and get metadata Object (IInspectable).
Event Subscribe one handler and raise Changed through Signal.
AddRemove Subscribe and unsubscribe a Changed handler each iteration.
Vector Read one element per iteration from an IVector.
IterateVector Iterate the whole IVector for a bounded number of passes.
GetMany Copy a vector into caller-owned storage through the public bulk-copy surface.
Map Enumerate values from an IMap<string, int>.
Lookup Read one value per iteration from an IMap.
VectorView Read one element per iteration from an IVectorView.
MapView Read one value per iteration from an IMapView.
Reference Box and unbox a nullable Int32 through IReference.
Async Consume an already-completed IAsyncOperation.
Error Propagate a failing HRESULT with a reduced iteration count.
Memory Client-side allocated bytes per retained live object.
Leak Component live-instance count above baseline after the run.

The timed bodies use the same loop bounds, inputs, result retention, collection sizes, and reduced
error count wherever the public projection surfaces permit. CsWinRT finalization is forced outside
affected timers so cleanup does not contaminate the following metric.

The GetMany row requests the same final result through each public projection. windows-rs and
C++/WinRT expose the WinRT GetMany method directly. CsWinRT maps IVector<T> to IList<T>, so
its public CopyTo adapter loops over GetAt.

The Error row highlights projection error models. Rust observes a failed HRESULT as a Result
branch. C++/WinRT and CsWinRT throw and catch exceptions, so this row uses a reduced loop count.

The leak row checks native AddRef/Release balance. Rust and C++/WinRT release deterministically
at scope exit. CsWinRT returns to baseline after GC.Collect() and
WaitForPendingFinalizers() because an RCW retains its native reference until finalization.

To add a metric, emit a matching Name: <ms> ms line from every consumer and add its name to the
$Metrics array in run.ps1.


Overview (`crates/samples/reactor/content-dialog/readme.md`)

ContentDialog default button

Run cargo run -p reactor-content-dialog.

Choose a default button, then press Enter in the dialog. WinUI highlights the default button and
normally focuses it when the dialog opens. The result is shown after the dialog closes; Cancel
returns ContentDialogResult::None.

The editor case shows the focus exception: a multiline TextBox receives focus and consumes Enter
to insert a line instead of closing the dialog. Tab moves focus to the buttons.

ContentDialogButton::None removes the default-button designation, not the buttons' own keyboard
behavior. A focused button can still respond to Enter.


Overview (`crates/samples/reactor/font-icon/readme.md`)

FontIcon size and foreground

Run cargo run -p reactor-font-icon.

rust
FontIcon::new()
    .glyph("\u{E80F}")
    .font_size(48.0)
    .foreground(ThemeBrush::PrimaryText)

Compare small red and large blue glyphs, a theme-aware foreground, and the native defaults on the
same icon. "Toggle theme" switches the window between light and dark.

Without a foreground override, the preview inherits its color from the surrounding HyperlinkButton.
"Native defaults" removes both overrides, restoring the native font size and inherited foreground.
font_size accepts finite positive values. foreground accepts a Color or a ThemeBrush.


This sample is the windows-reactor port of the WinUI control gallery. It contains 65
destinations across 11 categories:

  • Basic Input
  • Collections
  • Date and Time
  • Design Guidance
  • Dialogs and Flyouts
  • Layout
  • Media
  • Menus and Toolbars
  • Navigation
  • Status and Info
  • Text

Run it with:

powershell
cargo run -p reactor-gallery

The shell uses a generated TitleBar and NavigationView. The pane lists categories by default.
Select a category to open its card grid, then select a card to open the control page. Search
replaces the category links with matching control destinations. Back navigation follows the
shell's page history.

Each control destination is a component under src/pages/. Pages use controlled state for live
examples such as text input, sliders, selection controls, dialogs, progress indicators, and layout
changes. Page-owned state is recreated when a destination is retired and later reopened. Window
state, including the selected theme and backdrop material, belongs to the shell and persists across
page replacement.

For implementation examples, read the compiled page components in src/pages/.
The gallery does not duplicate them as display-only source snippets, which can drift from the
working examples.

The gallery is organized as follows:

File Purpose
src/shell.rs Window visuals, title bar, navigation pane, search, and history
src/registry.rs Category and destination metadata
src/router.rs Leaf destination to component mapping
src/controls.rs Shared page headers, sample cards, card grids, and assets
src/pages/ Category modules and control page components

Bundled images in assets/ are loaded through Image::source_file. Sample cards use WinUI theme
brushes so their surfaces follow the selected light, dark, or system theme. The Materials page
changes the live window backdrop between Mica, Mica Alt, Acrylic, and a solid background.

The gallery uses generated GridView selection feedback, BreadcrumbBar item-click events, and
the Viewbox child slot. Flyout, SplitButton, TabView, and InfoBar examples use their native event
and ownership contracts rather than substituting pointer handlers.

Recording-runtime tests mount the full gallery, replace pages through shell navigation, mount every
registered destination, drive representative controlled input, reject events from retired pages,
and verify that shell-owned theme and backdrop state survives page replacement.


Overview (`crates/samples/reactor/navigation/readme.md`)

This sample qualifies navigation and window isolation by opening a secondary workspace from the
primary at runtime. A SplitView hosts the navigation pane and current page through generated
typed slots. Each window owns its page, editor text, focus reference, background work, and
component queue. The windows share a small application model that broadcasts theme changes and
window-lifecycle notifications.

Run it with:

powershell
cargo run -p reactor-navigation

Try this sequence:

  1. Choose Open secondary window, then open the editor in each window and enter different text.
  2. Move between Home and Editor and confirm each window retains its own text.
    The window title follows the current page.
  3. Change the shared theme and confirm both windows update.
  4. Start background work in the secondary window, then choose Close this window.
  5. Confirm the primary window remains responsive and reports the secondary closure.

ComponentContext::open_window stages an independent root with the current component publication.
After publication, the host registers a pending open and mounts that root in a new Pump. The
opener's scope does not own the new Pump. Close requests use the same transactional boundary and
are applied only after publication. Each workspace declares its native title from component state.
Declarative size configuration remains deferred rather than being hidden in the sample with direct
WinUI calls.


Overview (`crates/samples/reactor/number-box/readme.md`)

NumberBox options

Run cargo run -p reactor-number-box.

rust
NumberBox::new()
    .small_change(0.25)
    .spin_button_placement_mode(NumberBoxSpinButtonPlacementMode::Inline)

Compare hidden, inline, and compact spinner buttons. The compact popup appears when the control
receives focus. Up/Down and the spinner buttons use small_change; values remain between 0 and 20.
small_change accepts finite f64 values.

The step button switches between 0.25 and 2. "Use native defaults" removes both properties from the
existing controls, restoring a step of 1 and hidden buttons. The headers identify the custom modes;
all three use hidden buttons while the overrides are removed.


Overview (`crates/samples/reactor/startup_perf/readme.md`)

Windows Reactor startup tracing sample

This blank app demonstrates TraceLogging events across the Windows Reactor startup lifecycle. It
renders one static TextBlock in a 1000x1000 window.

The app uses the ETW team's tracelogging crate to emit TraceLogging events from the
BenchmarkSyntheticApps provider:

  • Provider GUID: FD80D616-E92B-4B2B-9BED-131ADA36A8FD
  • Keyword: 0x0000400000000000
  • App name: blank_windows_reactor
  • Process name: blank-windows-reactor.exe
Event Location
wWinMainEntry Before Windows App SDK bootstrap
XamlAppLoaded Root component creation
WindowLoaded First post-commit effect
FirstRender First CompositionTarget::Rendering callback after commit
FirstIdle Low-priority dispatcher callback after FirstRender
ProcessStop Immediately after the final window closes

Every event includes AppName, Seq, and Pid fields.

Run the framework-dependent app with:

powershell
cargo run -p reactor-startup-perf --release

MSIX package

Run these commands from the repository root:

  1. Install Windows App Development CLI 0.5.0 or later, or upgrade an existing installation:

    powershell
    # First installation:
    winget install Microsoft.WinAppCli --source winget
    
    # Existing installation:
    winget upgrade Microsoft.WinAppCli --source winget
    
    winapp --version
  2. Build the app and prepare the package layout:

    powershell
    cargo build -p reactor-startup-perf --release --quiet
    $work = "target\reactor-startup-msix"
    $layout = "$work\layout"
    Remove-Item $layout -Recurse -Force -ErrorAction Ignore
    New-Item $layout -ItemType Directory | Out-Null
    Copy-Item `
        target\release\blank-windows-reactor.exe `
        $layout
    $output = "$work\blank-windows-reactor_x64.msix"
    $manifest = "crates\samples\reactor\startup_perf\package\Package.appxmanifest"
  3. Create an unsigned package:

    powershell
    winapp pack $layout --manifest $manifest --output $output --skip-pri

    To sign without a timestamp, use one winapp pack call:

    powershell
    $certificate = "C:\certificates\package.pfx"
    $securePassword = Read-Host "Certificate password" -AsSecureString
    $certificatePassword = [Net.NetworkCredential]::new("", $securePassword).Password
    winapp pack $layout `
        --manifest $manifest `
        --output $output `
        --skip-pri `
        --cert $certificate `
        --cert-password $certificatePassword

    winapp pack cannot timestamp a signature. For timestamped signing, package first and then
    sign:

    powershell
    $certificate = "C:\certificates\package.pfx"
    $timestampUrl = "https://timestamp.example.com"
    $securePassword = Read-Host "Certificate password" -AsSecureString
    $certificatePassword = [Net.NetworkCredential]::new("", $securePassword).Password
    winapp pack $layout --manifest $manifest --output $output --skip-pri
    winapp sign $output `
        $certificate `
        --password $certificatePassword `
        --timestamp $timestampUrl

The checked-in manifest uses version 1.0.0.0 and the Microsoft corporate publisher. Update its
Identity before packaging if either value needs to change. The publisher must exactly match the
signing certificate subject.

WinApp CLI accepts local and UNC certificate paths, but it passes the path directly to signtool;
copy the PFX locally first if the signing environment does not allow signtool to read the share.
Certificate passwords are command-line arguments because WinApp CLI does not provide a secure input
or environment-variable option.

The package remains framework-dependent. Install Microsoft.WindowsAppRuntime.2.msix on the target
machine before launching it.


Overview (`crates/samples/reactor/text-block-alignment/readme.md`)

TextBlock alignment

Run cargo run -p reactor-text-block-alignment.

rust
TextBlock::new()
    .text("Centered text")
    .text_alignment(TextAlignment::Center)

text_alignment positions text within the TextBlock's available width. horizontal_alignment
positions the TextBlock itself. The sample compares left, centered, and right text in stretched
controls with a centered, content-sized control. Resize the window to see the difference.

TextBlock and TextBox share Reactor's TextAlignment enum. Canvas has a separate TextAlignment
for DirectWrite; qualify the type or use an import alias when using both crates.

Updating or omitting text_alignment keeps the native TextBlock. Omitting a previously declared
alignment clears its local value and lets WinUI supply the alignment again.


Overview (`crates/samples/reactor/text-box-read-only/readme.md`)

Read-only TextBox

Run cargo run -p reactor-text-box-read-only.

Both fields allow selection and copying without editing. The second uses text_alignment to center
its text inside the stretched control. Resize the window to see the difference.


Overview (`crates/samples/reactor/translation-transition/readme.md`)

Translation transition

Run cargo run -p reactor-translation-transition.

Open and close the panel, or click again while it is moving to reverse direction. Translation is a
visual offset in DIPs; it does not change the space reserved by layout or remove the panel.


Overview (`crates/samples/reactor/virtual/readme.md`)

Virtual task editor

This sample qualifies windows-reactor virtualization with application behavior rather than
static labels. It includes:

  • keyed row components with controlled TextBox and ToggleSwitch values;
  • front insertion, removal, move-to-end, reversal, and a 1,000-item reset;
  • direct selection input for each keyed row;
  • validation and queued typed focus;
  • keyed row effects and cleanup on recycle;
  • background loading with scope-owned cancellation;
  • conditional row content;
  • same-key payload updates and key-changing source resets.

Run it with:

powershell
cargo run -p reactor-virtual

The same component model has a feature-gated RecordingRuntime performance driver. Run it in
release mode:

powershell
cargo run -p reactor-virtual --bin reactor-virtual-perf `
    --features perf --release -- --samples 500

The driver measures local editing, broad and redundant parent messages, an unchanged root-component
memo hit, a forced value-equal root recomposition, 32-row recycle/realize batches, background
completion, and a mixed virtual cycle. It reports allocator traffic and median, p95, and p99
Rust-side turn time. It also measures value-equal declaration scaling at 1,000, 10,000, and 100,000
tasks. It does not include WinUI control work, layout, rendering, or presentation.

The live driver adds CompositionTarget::Rendering frame intervals and host/native phase timing:

powershell
cargo run -p reactor-virtual --bin reactor-virtual-live-perf `
    --features perf --release -- --samples 300
cargo run -p reactor-virtual --bin reactor-virtual-live-perf `
    --features perf --release -- --baseline --samples 300

The active run moves by 32 virtual indices per frame, alternates selection, edits a
controlled row every six frames, and delivers a background completion every 30 frames. The
baseline opens the same 1,000-item editor without scheduling those actions. Each command writes a
report next to the executable. After the measured frames, the driver also verifies that queued
recycle and source-reset work left no retired WinUI shell tokens. A machine that emits no
composition frames writes an explicit failure report and exits unsuccessfully after 20 seconds.

The durable task title belongs to TaskEditor, not the realized row component. A row component is
retained across key-stable payload updates, but native virtualization or a source reset may recycle
it. Non-empty controlled drafts update that model immediately; a blank validation draft remains
row-local and falls back to the last valid model value after recycling. The recording-runtime test
enters edit mode, checks queued focus, edits a task, explicitly recycles it, reverses the source,
and verifies that its title survives each re-realization with one cleanup per retired row effect.

The buttons provide a deterministic manual stress sequence:

  1. Select a row, enter edit mode, and change its title.
  2. Reverse or move the source and confirm the title remains in the task model.
  3. Add and remove rows at the front.
  4. Load another 100 rows in the background.
  5. Reset to 1,000 rows and repeat edits while scrolling.

Overview (`crates/samples/windows/dcomp/readme.md`)

This sample first appeared in Kenny's DirectComposition course on Pluralsight. Check it out to learn all about DirectComposition, Direct2D, and DirectWrite, image decoding, and animation.


Overview (`crates/samples/windows/direct3d12/readme.md`)


Overview (`crates/samples/windows/spellchecker/readme.md`)

Windows SpellChecker

A sample showing how to use the Windows spell-checking API (ISpellCheckerFactory, ISpellChecker)
from the windows crate. It checks a line of text from the command line and prints corrections to
standard out.

Running

Make sure you have Rust installed, and then run the following:

powershell
cargo run -- "This is text with a word missspelled!"

Overview (`crates/samples/windows/uiautomation/readme.md`)

UI Automation

A simple example using the new UI Automation API. This sample requires Windows 11.


Overview (`crates/tests/libs/reactor/readme.md`)

Reactor tests

test-reactor is the home for the new Cargo-based Reactor tests. It lives alongside the existing
selftest, integration, and performance crates without depending on their harnesses or Reactor's
test feature. Add new cases here and migrate existing coverage individually. Remove an old case
only after its replacement proves the same behavior; retire old helpers when their consumers are
gone.

The crate uses the public Reactor API, direct Windows UI Automation, and the Rust standard library.
build.rs generates only the required native bindings into OUT_DIR using windows-bindgen and
stages the host's private Windows App Runtime using windows-reactor-setup.
There are no generated test cases, external runners, or dependencies on the windows crate.

Running

text
cargo test -p test-reactor

On x64 this runs the automated suite, including live WinUI cases and harness failure checks. It requires
an interactive desktop. Cargo deploys the runtime; no machine-wide runtime installation is needed.
The first build needs network access to download the pinned runtime packages. Five native drag/drop
cases are explicitly ignored; see Opt-in drag/drop coverage. Foreground
or focus failures do not silently skip tests. Do not interact with other applications while the
suite runs. Standard Cargo name filters can select individual cases during development.

The crate also runs in the ordinary workspace test command. Other workspace packages can enable
Reactor's legacy test feature through Cargo feature unification; this fixture still uses only the
public application API and real WinUI. The package-only command covers the default-feature build.

The ui integration target is x64-only. The existing x64 workspace job runs the live suite; x86 and
ARM64 build the fixture and run windows-reactor-setup's filesystem and multiprocess tests as part
of workspace testing. This is an explicit architecture boundary, not a runtime skip when focus
or foreground is unavailable.

Layout

Location Purpose
build.rs Native binding generation and self-contained runtime deployment
src/lib.rs Small public-API applications and process-failure probes
src/main.rs Entry point for the isolated WinUI host
tests/ui/main.rs Named behavioral tests driven through UI Automation
tests/ui/automation.rs Scoped lookup, native actions, condition waits, and diagnostics
tests/ui/menu.rs Button-menu callback, content, attachment, and owner lifetimes
tests/ui/process.rs Child ownership, deadlines, shutdown, and harness failure checks
tests/ui/pointer.rs Native mouse injection, owned button cleanup, and physical coordinates
src/pointer.rs Property-only pointer capture and focus fixtures
src/drag/ Property-only drop target and owned native OLE text source
tests/ui/drag.rs Opt-in drag negotiation, policy changes, and cancellation

The library contains the application fixtures. Cargo's integration-test harness supplies the
host executable's path without filesystem searches or a child mode in the test runner. Keeping the
host separate gives WinUI its own main thread and application lifetime. The binary's test = false
disables its empty unit-test harness, not the behavioral tests.

Each UI case launches the Cargo-built test-reactor executable in a fresh process. The suite
serializes desktop cases even with Cargo's default thread count. UI Automation runs on an MTA
worker, while the test thread watches the child and enforces a 30-second deadline, including blocked
UIA calls. Success requires completed assertions and a successful exit after closing the fixture's
own window. Failure cancels further driver work and terminates the owned child.

Case Assertion
menu_callback_replacement_and_removal An open item's native identity survives callback replacement; invoking it reaches the new callback exactly once. A retired item's invocation does not change the displayed count during a 250 ms observation period.
menu_content_replacement_retires_open_items Changing content closes the old popup and creates a new item on the same owner. The old item stays inert before and after opening the replacement.
menu_reattachment_does_not_revive_retired_items Removing and reattaching a menu on the same native owner creates new items without reviving retained providers.
menu_owner_replacement_retires_open_items Replacing the keyed button closes its popup and changes native owner/item identity. A callback update on the replacement preserves its open item and reaches the current callback.
text_box_native_edit_and_controlled_reset UIA Value edits reach component state; declarative reset clears the native value; focused Unicode keyboard input reaches both state and the native TextBox.
keyed_reorder_moves_only_displaced_children Moving the last keyed TextBox to the front keeps every element's native identity and produces the new layout order. The focused TextBox that triggered the reorder is not displaced, so it keeps keyboard focus.
early_successful_exit_is_rejected Exiting with code zero before the driver finishes is a failure, not a passing test.
child_panic_is_reported A child panic produces a failed exit and captured stdout and stderr.
hung_child_is_terminated A ready but unresponsive child times out and is terminated.
driver_timeout_cancels_worker The supervisor deadline cancels a cooperative driver and reaps its child without consuming the cleanup allowance.
driver_timeout_bounds_blocked_worker_cleanup The supervisor reaps the child and returns after its cleanup allowance even when the driver remains blocked.
pointer_capture_policy_at_mount / pointer_capture_policy_added_to_existing_border With only the capture policy, movement outside reaches the pressed Border; after release it reaches the outside surface.
pointer_focus_policy_at_mount / pointer_focus_policy_added_to_existing_border With only the release/focus policy, the Border receives direct native pointer focus after release, not on press.
pointer_button_is_released_after_driver_failure A driver failure while pressing the mouse releases the injected button before the fixture is stopped.
text_data_transfers_owned_unicode_memory The data source rejects unsupported formats and returns independent, nul-terminated UTF-16 allocations that outlive its COM object. This unit test runs normally.

The driver timeout regressions use the same supervisor as live UI cases, with short private
deadlines and synthetic workers rather than COM calls. Each case releases and joins its worker
before asserting the outcome. Live cases retain the 30-second deadline and five-second cleanup
allowance.

The driver uses exact automation IDs, or exact name and control type for menu items, scoped to the
child's windows. Repeated entries for the same native element count once; distinct matching elements
are an error. Waits inspect current UI state; they do not force layout or synthesize Reactor
callbacks. UIA invocation, Value edits, and keyboard input are separate operations. Keyboard injection
checks the foreground process, focused element, and modifier keys.

Button-menu cases use UIA Invoke to exercise WinUI's menu actions, not synthetic Reactor events.
They retain old invoke providers across content, attachment, and owner changes, check popup closure
including the submenu entry, and invoke retired providers before and after opening a replacement.
Retired invocations must leave both the callback count and the replacement popup unchanged. Current
items must deliver the expected key and callback generation exactly once; callback counts are
observed for 250 ms to detect extra deliveries. These tests cover button menus. Dropdown, menu-bar,
command-bar, and tray surfaces remain in the legacy integration fixture.

Pointer cases use SendInput with physical virtual-desktop coordinates derived from UIA anchor
bounds. The fixture's Border has no UIA peer; short text anchors locate its bare background without
clicking the text itself. Guards check foreground ownership, the window under button input, and
held buttons/modifiers. The supervisor owns button cleanup, so cancellation or a blocked UIA worker
cannot leave cleanup dependent on that worker returning. A case that finishes with an injected
button still pressed fails.

The policy fixtures install no press/release callbacks on the target. Capture and release are
observed through native movement routing, not a stored policy or capture flag. Focus is observed
through the target's direct native focus event; setting focus on the baseline button is a separate
driver action. Dynamic cases omit the policy entirely at mount, exercise that state, and then enable
it while preserving the UIA anchor's identity. They do not set the policy to false initially, since
that setter can itself activate a lazy native subscription.

Cargo captures driver steps and assertion failures. On a condition timeout the driver also prints
the fixture's UI tree. Child stdout and stderr are written to separate files to avoid blocked pipes;
failures report the log directory under the system temporary directory. Successful cases remove
their logs. The parent deadline still applies if reading the UI tree stalls.

Opt-in drag/drop coverage

The native drag/drop cases are preserved from PR #5115, but are not part of the default automated
suite. WinUI does not support this drag/drop path in elevated processes
(microsoft/microsoft-ui-xaml#7690).
GitHub-hosted Windows runners run as administrator. The tests use #[ignore = "..."] so Cargo reports
them as ignored, not passed. Explicitly running them elevated fails before launching a fixture.
No runner-specific skip, token manipulation, or automatic elevation change is used.

Run the policy and failure-cleanup cases from a non-elevated terminal on an idle interactive desktop:

text
cargo test -p test-reactor --test ui drop_policy -- --ignored
cargo test -p test-reactor --test ui drag_source_is_stopped_after_driver_failure -- --ignored

The cancellation case also has an unresolved intermittent UIA timeout (0x80131505) after
cancellation. Keeping the source window and its STA alive across drags has not eliminated that
failure. It remains an opt-in regression case, not evidence that cancellation is reliable:

text
cargo test -p test-reactor --test ui cancelled_drag_can_be_restarted -- --ignored --nocapture

To run all five opt-in cases, including the unresolved cancellation regression:

text
cargo test -p test-reactor --test ui -- --ignored
Opt-in case Assertion
drop_policy_at_mount / drop_policy_added_to_existing_border A callback-free Border accepts native text drags through its policy, including activation after an initially rejected drop.
drop_policy_changes_during_drag Source feedback follows Copy-to-Move policy changes while hovering; switching back before release produces a Copy drop that reads the text.
cancelled_drag_can_be_restarted Three cancelled hovers preserve the source's native UIA identity; another drag then drops successfully. Subject to the unresolved timeout above.
drag_source_is_stopped_after_driver_failure Failure during an accepted OLE hover releases the injected button and reaps the fixture process, including its source thread.

The source is a native window on a dedicated OLE STA in the fixture process. It receives injected
input and runs DoDragDrop, leaving WinUI free to render policy changes and read dropped data.
It advertises only CF_UNICODETEXT. Assertions require source-reported hover operations, the final
OLE operation, and a text read, not just a Copy cursor. The target has no drag/drop callbacks that
could mask policy-only activation.

The source window and STA live for the fixture session. Each drag owns its data and feedback
objects. Normal shutdown joins the UIA driver before closing the fixture, which stops and joins the
source thread. The OLE callback checks cancellation and a 20-second deadline; the existing parent
supervisor still bounds blocked native calls. No UIA timeout is retried or treated as success.

App-process coverage

The pilot uses compiler instrumentation and the llvm-tools Rust toolchain component, without a
Cargo coverage plugin. In a fresh PowerShell session at the repository root:

powershell
$ErrorActionPreference = "Stop"
$PSNativeCommandUseErrorActionPreference = $true
$target = Join-Path $PWD "target\reactor-ui-coverage"
$profiles = Join-Path $target ("profiles-" + [Guid]::NewGuid())
New-Item -ItemType Directory $profiles | Out-Null
$env:RUSTFLAGS = "-C instrument-coverage"
$env:LLVM_PROFILE_FILE = Join-Path $profiles "%p-%m.profraw"
cargo test -p test-reactor --target-dir $target
$sysroot = rustc --print sysroot
$hostTriple = (rustc -vV | Select-String "^host: ").ToString().Substring(6)
$llvm = Join-Path $sysroot "lib\rustlib\$hostTriple\bin"
$raw = @(Get-ChildItem $profiles -Filter "*.profraw")
& "$llvm\llvm-profdata.exe" merge -sparse $raw.FullName -o "$profiles\ui.profdata"
& "$llvm\llvm-cov.exe" show "$target\debug\test-reactor.exe" `
    "-instr-profile=$profiles\ui.profdata" `
    '--include-filename-regex=native[\\/]+winui[\\/]+(hosting|events)\.rs$'

The unique profile directory prevents earlier runs from contributing counts. Child processes
inherit the profile pattern, and %p-%m separates their output. Report against test-reactor.exe,
not just the integration-test driver: the native Reactor code runs in the fixture. The source filter
above shows the menu callback update and native text setter paths exercised by these cases. This
is a pilot path report, not an overall Reactor coverage percentage or a replacement for the core
coverage job. Close the PowerShell session afterwards to discard the instrumentation environment.


Overview (`crates/tests/libs/reactor_bench/readme.md`)

Reactor benchmark

This benchmark measures Reactor retained updates, component scheduling, lifecycle work,
virtualization, and memory attribution without WinUI layout or rendering.

text
cargo run -p test-reactor-bench --release --quiet
cargo run -p test-reactor-bench --release --quiet -- --count 4096 --samples 40 --batch 4
cargo run -p test-reactor-bench --release --quiet -- --architecture-gate
cargo run -p test-reactor-bench --profile profiling --quiet -- --retained-memory --count 512

The output includes median and p95 update time, allocated bytes and allocation count per update,
retained bytes per object, isolated component updates, and retained component-scope memory.
Component rows also cover isolated and all-consumer typed-context invalidation, same-type updates,
and retained-root type replacement.

The recursive component rows build balanced trees, target the deepest leaf, and report retained
bytes per scope, update latency, allocations, allocated bytes, and mutation count. The timed
updates disable recording-adapter batch validation because that diagnostic intentionally clones
the complete adapter state.

The --architecture-gate mode uses six samples by default and reports:

  • Keyed no-change and one-property-change updates across two 50,000-object graphs in one timed
    operation. Reactor enforces a 65,536-object limit per graph.
  • Controlled 513-node memory fixtures that separate declaration construction, retained
    graph storage, string properties, events, TreeNode relation shape, and recording-adapter storage.
  • Root-type replacement and middle-child removal in a 16,384-component graph.

Use --gate-samples or --component-count to override the gate defaults.

The --retained-memory mode releases benchmark scratch buffers after mounting and reports live
retained-graph allocations separately from declarations and adapter state. It prints release
layout sizes and additive slot, relation, child-vector, property/event, virtual, and retirement
storage for keyed and unkeyed flat trees, a deep chain, relation-heavy controls, TreeNode
structural objects, a virtual source, and an active retirement.

No-op allocation, changed-update allocation, retained memory, and large-tree scaling are separate
gates.


Overview (`crates/tests/libs/reactor_integration/readme.md`)

Reactor public integration tests

These isolated executables exercise the public application API without Reactor's test feature.
Build this package separately from privileged selftests and benchmarks so Cargo does not unify
their diagnostic features into this build.

text
cargo check -p test-reactor-integration --all-targets
cargo run -p test-reactor-integration --bin application_lifecycle -- replacement
cargo run -p test-reactor-integration --bin application_lifecycle -- multiple
cargo run -p test-reactor-integration --bin application_lifecycle -- startup-error
cargo run -p test-reactor-integration --bin application_menu
cargo run -p test-reactor-integration --bin application_menus
cargo run -p test-reactor-integration --bin menu_lifetimes -- dropdown
cargo run -p test-reactor-integration --bin application_resources -- lifecycle
cargo run -p test-reactor-integration --bin application_dispatch -- application
cargo run -p test-reactor-integration --bin application_dispatch -- notification
cargo run -p test-reactor-integration --bin tray_flyout
cargo run -p test-reactor-integration --bin menu_position -- thread-unaware
cargo run -p test-reactor-integration --bin canvas_integration
cargo run -p test-reactor-integration --bin webview_integration
cargo run -p test-reactor-integration --bin window_state
cargo run -p test-reactor-integration --bin window_observers
cargo run -p test-reactor-integration --bin window_theme
cargo run -p test-reactor-integration --bin title_bar

Each executable owns its application lifetime and watchdog. The lifecycle fixture covers
last-window replacement, multiple-window shutdown, and startup errors. The menu fixture checks
application-lifetime work after its Reactor window closes. The application-resource fixtures cover
declarative window and icon lifetimes, nested dispatch, and menu replacement and selection.
The notification dispatch mode sends interactions and recovery requests to its own callback window
during nested component and application updates, and checks that queued events cannot outlive icon
removal.
The tray-flyout fixture uses an application-owned icon and checks that its menu overlaps the icon's
monitor, extends outside that monitor's work area, and closes during application shutdown.
It measures menu-item bounds through UI Automation rather than the popup window's shadow bounds,
and requires a running Windows shell with a notification area. Companion-crate fixtures exercise
Canvas drawing and WebView initialization. The window-state fixture observes size and icon changes
through the public window callback and Win32 APIs. The window-observer fixture covers callback
replacement without replay, real changes, removal, re-subscription, and independent subscriptions
for color scheme, size, and placement. It also covers removal and re-subscription within one drain,
handoff from a parent to a child, and replacement of the subscribing child. The title-bar fixture
opens a second window from a running application and checks through UI Automation that its
TitleBar right header keeps the same
caption-button inset as the first window.

Run window_theme on an interactive desktop. It temporarily makes its windows topmost without
activation to sample their rendered backgrounds.

Run menu_lifetimes with dropdown, menubar, command, or tray on an interactive desktop.
It uses process-scoped UI Automation and notification messages sent to its own icon. Button-menu
lifetimes are covered by the named Cargo tests in test-reactor.

The menu-position fixture covers pmv2, unaware, system, and thread-unaware DPI modes. It
checks exact physical host bounds and restoration of the caller's thread context, including when
a duplicate menu request is rejected. Run on a display scaled above 100% to exercise coordinate
virtualization.

Use --features self-contained to run with bundled Windows App SDK dependencies. Otherwise,
the Windows App Runtime must be installed. WebView tests also need the WebView2 runtime.


Overview (`crates/tests/libs/reactor_perf/readme.md`)

Reactor stress benchmark

test_reactor_perf compares windows-reactor with the C# Reactor
StressPerf.ReactorOptimized workload:

  • 70 columns x 70 rows = 4,900 TextBlock cells;
  • deterministic .NET Random(42) data and update sequence;
  • a 33 ms update timer;
  • at least one randomly selected cell per tick, controlled by --percent;
  • cached cell declarations rebuilt only for changed indices;
  • snapshot-driven root component updates;
  • phase, frame, allocation, and working-set reporting.

Run the live benchmark in release mode:

powershell
cargo run -p test_reactor_perf --release -- --headless --percent 10 --duration 10

The report is written next to the executable as StressPerf.Reactor.report.txt. Add --json to
also write StressPerf.Reactor.metrics.json.

The in-app frame counter uses CompositionTarget::Rendering, matching the C# workload. As with the
C# harness, render completion is a better no-admin throughput proxy, and ETW presents are required
for a final user-visible frame-rate comparison.


Overview (`crates/tests/libs/reactor_selftest/readme.md`)

Reactor native self-test

This executable runs the Reactor WinUI adapter inside the Reactor application host. Generated
coverage constructs and clears every schema property and event contract. Handwritten phases cover
controlled input, ordered native events, attachments, images, references, pointer injection,
retirement, TreeView content, ListView data, and virtualization.

text
cargo run -p test-reactor-selftest --quiet
cargo run -p test-reactor-selftest --quiet -- --headless

A watchdog terminates the process with an error if WinUI hangs or the application fails to exit.
Application lifecycle, menu, Canvas, WebView, and window-state fixtures live in
test-reactor-integration. They use the public application
API without enabling Reactor's diagnostic test feature.

Asynchronous phases wait for native readiness rather than assuming a timer tick is sufficient.
The ContentDialog check requests opening before its owner window exists, waits for Opened, then
calls Hide and requires Closed.
Use assertions for failures in component callbacks. Calling process::exit there can run
application teardown while callback state is still borrowed.

CI also runs the self-test and native library tests in debug mode with
RUSTFLAGS="-D warnings --cfg windows_cast_diagnostics". check_casts.ps1 checks their captured
output for unexpected same-pointer QueryInterface calls. Its allowlist covers agility checks,
canonical COM identity, activation, and known conversions from IInspectable; these still require
queries even when the returned pointer is unchanged. Review new diagnostics before adding an
exception. A clean result covers only the paths exercised by those tests.

Run every default Reactor, Composition, and WebView sample long enough to catch startup failures:

text
powershell -File crates\tests\libs\reactor_selftest\run_samples.ps1

The script builds each package independently, captures runtime stdout and stderr, requires the
process to remain alive for three seconds, and then closes or terminates that process. Use
-RunSeconds, -Family, or -Package to narrow or lengthen a run, and -KeepLogs to retain all
captured output.

Use -Package with -ChangedSince <commit> to run the named packages plus samples changed between
that commit and HEAD. Added files and deleted files within surviving packages count as changes;
removed packages are not run. -List prints the selection without building or launching anything.
The comparison commit must be available locally.

CI

PRs and master pushes run the native self-test once, with cast diagnostics, and retain the native
regressions, public integration fixtures, deployment checks, isolated feature checks, and public
API snapshot. Workspace unit tests and generation checks belong to test.yml and gen.yml.

The smoke gate runs Counter, Gallery, Workbench, the Reactor Composition host, standalone system
Composition, and Reactor WebView, plus changed or new samples. PRs compare against their base commit;
pushes compare against the previous branch tip, including every commit in the push. Each package
is built independently, and GUI processes run serially.

Scheduled and manually dispatched runs also enforce coverage floors, repeat the native self-test,
and launch every sample. The routine gate targets roughly ten minutes; the exhaustive runs can take
longer. CLI tools use pinned cargo install versions. The public API tool has a separate cache saved
immediately after installation, so a later test failure does not discard the compiled tool.

Run the sample selection checks without starting any windows:

text
pwsh -File crates\tests\libs\reactor_selftest\test_samples.ps1

Code Of Conduct (`docs/code_of_conduct.md`)

Code of conduct

This project has adopted the Microsoft Open Source Code of Conduct.

Resources:


Contributing (`docs/contributing.md`)

Contributing

This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.

When you submit a pull request, a CLA bot will determine whether you need to provide a CLA and
decorate the PR appropriately (for example, with a status check or comment). Follow the bot's
instructions. You only need to do this once across all repositories using our CLA.

This project has adopted the
Microsoft Open Source Code of Conduct. For more
information, see the
Code of Conduct FAQ or contact
[email protected].

Before Contributing

Start by
opening a new issue or commenting on
an existing issue to discuss a feature or bug
fix. This lets maintainers and contributors agree on an approach before you spend time on a pull
request. The pull request template therefore asks you to refer to an existing issue.


Dependencies (`docs/dependencies.md`)

Dependencies

This page tracks every external SDK, header set, metadata file, and runtime the build and tooling
depend on: the version, where it is pinned, how it is obtained, and how the pin is validated. It
covers build/tooling and runtime dependencies (Windows SDK, WDK, WinRT contracts, libclang,
Windows App SDK, WebView2). It does not cover the crates.io Rust dependencies in Cargo.toml,
which are already centralized under [workspace.dependencies].

The model: one owner, validated by running

There is no central version registry. Every external dependency has a single owning tool that
declares the pin as a const, downloads/consumes the artifact at that exact version, and runs in CI
(.github/workflows/gen.yml) - so running the tool proves the pin is current, in one of two ways:

  • Generators (tool-win32, tool-winrt, tool-webview, tool-reactor-metadata,
    tool-reactor) regenerate
    committed artifacts; gen.yml runs each then git diff --exit-code, so a stale pin produces a
    diff and fails.
  • Validators (tool-clang, and guards inside generators) assert invariants and write nothing; a
    violation panics loudly, failing the job with a clean tree.

When one crate must track a pin another crate owns, the owner reads the peer's constant straight
from source with helpers::read_str_const and asserts they
agree. Pins are never copied - they are read back and checked. windows-clang stays a clean
libclang library and is not a shared home for SDK/runtime versions.

At a glance

Dependency Version Owner (pin) Obtained by Validated by
libclang 22.1.8 LIBCLANG_VERSION - crates/libs/clang/src/provision.rs download (NuGet: libclang.runtime.win-<arch> + git sparse checkout of LLVM headers) tool-clang validator + runtime assert
Windows SDK 10.0.28000.2270 SDK_VERSION - crates/tools/win32/src/main.rs download (NuGet) tool-win32 zero-diff regen
Windows WDK 10.0.28000.1839 WDK_VERSION - crates/tools/win32/src/km.rs download (NuGet) tool-win32 zero-diff regen
SDK Contracts (WinRT) 10.0.28000.2270 CONTRACTS_VERSION - crates/tools/winrt/src/main.rs download (NuGet) tool-winrt zero-diff regen
WebView2 SDK headers 1.0.4078.44 WEBVIEW2_VERSION - crates/tools/webview/src/main.rs download (NuGet) tool-webview zero-diff regen
WinUI / Windows App SDK metadata (.winmd files) 2.5.1 WINDOWS_APP_SDK_VERSION - crates/tools/reactor-metadata/src/main.rs download (NuGet) tool-reactor-metadata zero-diff regen of the committed metadata
Windows App SDK runtime 2.5.1 RUNTIME_VER - crates/libs/reactor-setup/src/lib.rs download (NuGet) tool-reactor-metadata guard: == WINDOWS_APP_SDK_VERSION, and reactor.yml matches
WebView2 runtime projection 1.0.4078.44 WEBVIEW2_VER - crates/libs/reactor-setup/src/lib.rs download (NuGet) tool-reactor-metadata guard: == WEBVIEW2_VERSION
LLVM / libclang (CI) 22.1.8 LIBCLANG_VERSION - crates/libs/clang/src/provision.rs download (NuGet) via tool-clang path tool-clang: loads the pin and asserts its version

Toolchain: libclang

The header scrapers (tool-win32, tool-webview) parse C/C++ with libclang. The
version is pinned because clang's macro-capture behavior drifts across majors, which would silently
change the generated metadata.

  • Owner: provision.rs declares LIBCLANG_VERSION = "22.1.8". libclang.dll comes from the
    libclang.runtime.win-<arch> NuGet packages (dotnet/clangsharp, .NET Foundation) fetched at
    that version. The paired clang builtin resource headers (needed only for the non-x64 arch
    passes, to reconcile the aarch64 __prefetch builtin) come from a blobless, shallow, sparse git
    checkout of clang/lib/Headers at the derived llvmorg-<ver> tag - so the DLL and headers are
    the same single-const pin and can never drift.
  • Obtained: if LIBCLANG_PATH is unset, ensure_libclang fetches the pinned
    libclang.runtime.win-<arch> package via nuget_package (the shared NuGet global cache, same as
    the SDK/WDK/WebView2 pins) and points LIBCLANG_PATH at its runtimes/<rid>/native/; non-x64
    passes also fetch the pinned LLVM resource headers via git. LIBCLANG_PATH /
    CLANG_RESOURCE_DIR override for offline machines. All three scrapers call it, so their gen.yml
    jobs need no LLVM install - they always parse with the pinned 22.1.8, in CI and on a fresh
    checkout alike.
  • CI: every workflow self-provisions the pinned libclang from NuGet - no CI job installs LLVM.
    The gen.yml scrapers call ensure_libclang; clippy.yml loads no libclang at all
    (cargo clippy never parses); and test.yml, whose windows-clang tests load libclang at
    runtime,
    exports LIBCLANG_PATH from the same pin via `echo "LIBCLANG_PATH=$(cargo run -q -p tool-clang --
    path)"

    "$GITHUB_ENV". tool-clang pathprintshelpers::libclang_dir(), keeping the unsafe set_var` off the multithreaded test runner. The Linux CI jobs build code that needs no libclang.

  • Validated by tool-clang: fetches, loads, and version-asserts the pin (the same provisioning
    the scrapers run). Writes nothing.
  • To update: bump LIBCLANG_VERSION - a single const that drives both the NuGet DLL and the
    llvmorg-<ver> git tag for the headers, so there is nothing else to touch. Run tool-clang (must
    pass) and regenerate all metadata. No prebuilt-asset ceiling: the DLL comes from NuGet and the
    headers from a git tag, both of which track current LLVM.

Windows SDK, WDK, and WinRT contracts

Each feeds an in-house generator producing a committed .winmd in windows-default. The metadata
is embedded for Rust build tools and remains available as files for external tools. Provenance is
documented in crates/libs/default/readme.md.

windows-bindgen, windows-rdl, and windows-clang depend on windows-default so their builders
can select this metadata without filesystem paths. Binaries linking those crates include both
metadata files.

Package Owner (pin) Produces
Microsoft.Windows.SDK.CPP[.<arch>] SDK_VERSION - tool-win32 um scrape half of Windows.Win32.winmd
Microsoft.Windows.WDK.x64 WDK_VERSION - tool-win32 km scrape half of Windows.Win32.winmd
Microsoft.Windows.SDK.Contracts CONTRACTS_VERSION - tool-winrt Windows.winmd
  • Obtained: windows_clang::nuget_package(id, version) restores from the NuGet global cache or
    downloads from nuget.org (NUGET_PACKAGES overrides the cache).
  • tool-win32 runs both scrapes, so the WDK km scrape reuses the same SDK_VERSION headers as
    the um scrape (they share the crate); WDK_VERSION is a separate pin in km.rs.
  • The "marketing" include/lib folder nested in each package (e.g. 10.0.28000.0) is derived
    from the version via helpers::marketing_dir (first three components + .0), so the version is
    the single edit - never a second folder constant to keep in sync.
  • To update: bump the owning constant, run cargo run -p tool-<win32|winrt>, and commit the
    regenerated .rdl snapshot(s) and .winmd.

CONTRACTS_VERSION happens to share the 10.0.28000 build number with SDK_VERSION, but it is a
distinct NuGet package with its own pin - the two are not coupled and can diverge.

WebView2

WebView2 ships only C/C++ SDK headers, so windows-webview is scraped from them. The WinRT Core
metadata and runtime projection DLL are separate artifacts.

Artifact Owner / location Used by
WebView2.h, WebView2Interop.h WEBVIEW2_VERSION - tool-webview (downloaded, not vendored) tool-webview -> webview/src/bindings.rs
Microsoft.Web.WebView2.Core.winmd regenerated into crates/tools/reactor-metadata/winmd/ by tool-reactor-metadata at WEBVIEW2_VERSION tool-webview, tool-reactor
Runtime projection (Core.dll), WEBVIEW2_VER crates/libs/reactor-setup/src/lib.rs self-contained apps
Evergreen runtime .github/workflows/webview.yml CI test host
  • Headers are downloaded, not vendored: tool-webview fetches the pinned NuGet package via
    nuget_package and parses the headers from it. A bump is a one-line WEBVIEW2_VERSION edit.
  • Pinned libclang: like tool-win32, tool-webview calls ensure_libclang +
    assert_libclang_version, so it parses with the exact pinned 22.1.8 (its gen.yml job needs no
    LLVM install - only the SDK include paths for the system headers WebView2.h pulls in).
  • Runtime projection: bump WEBVIEW2_VER in reactor-setup; it must equal
    WEBVIEW2_VERSION - tool-reactor-metadata asserts this.
  • Core.winmd is refreshed into the shared Reactor metadata by tool-reactor-metadata at
    WEBVIEW2_VERSION (see below).

For the full pipeline and COM<->WinRT bridge, see windows-webview.

WinUI / Windows App SDK

windows-reactor is generated from WinUI / Windows App SDK .winmd; windows-reactor-setup stages
the matching runtime so reactor apps run. Metadata and runtime are two faces of one release, tied to
a single number.

Metadata is regenerated, not hand-copied. tool-reactor-metadata owns
WINDOWS_APP_SDK_VERSION = "2.5.1". On every run it downloads the umbrella
Microsoft.WindowsAppSDK metapackage at that version, reads the exact component versions
(Foundation / InteractiveExperiences / WinUI) pinned in its nuspec, downloads each component, and
copies their .winmd - plus Microsoft.Web.WebView2.Core.winmd at WEBVIEW2_VERSION - into the
committed metadata at crates/tools/reactor-metadata/winmd/. gen.yml re-runs the tool and fails
on any diff, so the metadata provably matches the pin. (extras.winmd in that directory is
generated by tool-reactor-metadata from Windows.Win32.winmd, not a package.) The metadata stays
committed because tool-reactor, tool-webview, and tool-composition read it.

Artifact Owner / location Used by
WinUI / Windows App SDK .winmd + WebView2.Core.winmd regenerated into crates/tools/reactor-metadata/winmd/ at WINDOWS_APP_SDK_VERSION tool-reactor-metadata, tool-reactor, tool-webview, tool-composition
Microsoft.WindowsAppSDK.Runtime, RUNTIME_VER crates/libs/reactor-setup/src/lib.rs app runtime deploy
app.manifest, runtime.txt crates/libs/reactor-setup/assets/ (committed) runtime staging
Runtime installer .github/workflows/reactor.yml CI test host

windows-reactor-setup is a published runtime helper with no generated artifact, so its pins can't
be proven by regen. Instead tool-reactor-metadata guards them on every run, asserting (and
failing loudly on drift) that:

  • WINDOWS_APP_SDK_VERSION (metadata) equals RUNTIME_VER (runtime) - one number drives both;

  • WEBVIEW2_VER equals WEBVIEW2_VERSION, so the staged WebView2 runtime matches the ABI the
    bindings target; and

  • reactor.yml's installer URL installs RUNTIME_VER (.../windowsappsdk/<major.minor>/<ver>/),
    so CI's self-tests exercise the runtime apps ship.

  • To update metadata + runtime: bump WINDOWS_APP_SDK_VERSION (tool-reactor-metadata) and
    RUNTIME_VER (reactor-setup) together, update the reactor.yml installer URL, then run
    cargo run -p tool-reactor-metadata and commit the refreshed metadata. The guard enforces the
    version agreement; the regen enforces the metadata.

  • assets/app.manifest is a generated activation asset with no committed generator -
    app.manifest transforms the App SDK package.appxfragment files into SxS fusion format (source
    versions in its header). It is forward-compatible, so refreshed only when the reactor control set
    needs newly-moved classes, not on every bump.

  • resources.pri is staged from the App SDK runtime package only for self-contained deployment via
    as_self_contained; framework-dependent staging does not copy it.

See windows-reactor and
windows-reactor-setup.

Download mechanisms

Two independent NuGet paths, both using https://www.nuget.org/api/v2/package/{id}/{version}:

  • windows_clang::nuget_package - used by the scraping/codegen tools (tool-win32,
    tool-winrt, tool-webview, tool-reactor-metadata). Restores into the NuGet global cache
    (NUGET_PACKAGES overrides), else downloads via bundled curl/tar. Layout-agnostic - each
    caller indexes the subtree it needs.
  • reactor-setup's staging - runs in the build.rs of every consuming app to stage the App SDK
    runtime and WebView2 projection. Dependency-free (std + the curl.exe/tar.exe shipped with
    Windows), which is why it does not reuse nuget_package.

The `tool-*` / pin pairing

Tool Proves How
tool-win32 SDK_VERSION, WDK_VERSION zero-diff regen of Windows.Win32.winmd (um + km scrapes and their merge)
tool-winrt CONTRACTS_VERSION zero-diff regen of Windows.winmd
tool-webview WEBVIEW2_VERSION zero-diff regen of webview/src/bindings.rs
tool-clang LIBCLANG_VERSION (drives NuGet DLL + llvmorg-<ver> header tag) pure-check assertion
tool-reactor-metadata WINDOWS_APP_SDK_VERSION; reactor-setup sync zero-diff regen of the winmd files; guard reads reactor-setup constants
tool-reactor Reactor schema and WinUI projection zero-diff regen of declarations, native code, bindings, canvas bridge bindings, and live coverage

All cross-file reads go through helpers::read_str_const, so each pin is declared once by its owner
and read back everywhere else.


Security (`docs/security.md`)

Security

Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include Microsoft, Azure, DotNet, AspNet, Xamarin, and many more.

If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's definition of a security vulnerability, please report it to us as described below.

Reporting Security Issues

Please do not report security vulnerabilities through public GitHub issues.

Instead, please report them to the Microsoft Security Response Center (MSRC) at https://msrc.microsoft.com/create-report.

If you prefer to submit without logging in, send email to [email protected]. If possible, encrypt your message with our PGP key; please download it from the the Microsoft Security Response Center PGP Key page.

You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at microsoft.com/msrc.

Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:

  • Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
  • Full paths of source file(s) related to the manifestation of the issue
  • The location of the affected source code (tag/branch/commit or direct URL)
  • Any special configuration required to reproduce the issue
  • Step-by-step instructions to reproduce the issue
  • Proof-of-concept or exploit code (if possible)
  • Impact of the issue, including how an attacker might exploit the issue

This information will help us triage your report more quickly.

If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our Microsoft Bug Bounty Program page for more details about our active programs.

Preferred Languages

We prefer all communications to be in English.

Policy

Microsoft follows the principle of Coordinated Vulnerability Disclosure.


Crates Cppwinrt (`docs/crates/cppwinrt.md`)

cppwinrt

The C++/WinRT compiler, bundled for use from Rust.

cppwinrt packages the C++/WinRT compiler so it can be
invoked from a Rust build. It is a thin wrapper that runs the bundled cppwinrt.exe with the
arguments you provide, returning its output. This is primarily useful for interop scenarios that
also generate C++/WinRT projection headers.

Rust code should prefer a focused crate or windows-bindgen implementation support. Binary
applications can use the broad windows projection when convenient. Use cppwinrt
when a Rust build also compiles C++ that consumes or implements WinRT types.

Start with the crate README for setup and the help
invocation. The bundled executable runs only on Windows.

First workflow: generate headers for a C++ build

  1. Produce or obtain the component winmd.
  2. Choose an output directory owned by the build, normally OUT_DIR.
  3. Invoke cppwinrt::cppwinrt with -in for the component metadata and any reference metadata,
    then -out for the include directory.
  4. Add that directory to the C++ compiler's include paths.
  5. Link the libraries required by the generated C++ and the component.

crates/samples/robot/component_cpp/build.rs is the component example. It compiles
src/robot.rdl with windows-rdl, passes robot.winmd and the default metadata
directory to C++/WinRT, then compiles component.cpp as C++20. The matching
robot/client_cpp/build.rs consumes the component's winmd and compiles a C++ client.

Input and output model

cppwinrt(args) accepts any iterator of values convertible to OsStr, so paths do not need UTF-8
conversion. The arguments are passed unchanged to the bundled compiler. On success the function
returns standard output as a String; on failure it panics with standard error.

The wrapper copies cppwinrt.exe to a unique temporary path for each call, runs it synchronously,
and removes the executable afterward. C++/WinRT itself writes the requested projection output.
Call cppwinrt(["-help"]) for the option set supported by the bundled compiler version.

Integration and pitfalls

  • windows-rdl can compile a component's reviewable RDL contract to the winmd consumed here.
  • windows-default contains the standard winmd files, but C++/WinRT expects input paths. The robot
    samples pass the crate directory containing those files.
  • windows-bindgen is the Rust projection counterpart when both Rust and C++ consume one contract.
  • Gate build-script use to Windows with the MSVC environment when the build also invokes MSVC.
  • Add rerun-if-changed directives for component metadata and C++ sources. Generated headers alone
    do not tell Cargo when to rerun the build script.
  • The wrapper panics on compiler failure and does not return a structured status. Supply only
    validated arguments and let the build fail visibly.
  • Do not assume options from a newer standalone C++/WinRT release. Check the bundled compiler's
    -help output.

Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use cppwinrt.

How it's built

Packages the prebuilt cppwinrt.exe. src/bindings.rs is generated by tool-bindings from
crates/tools/bindings/src/cppwinrt.txt and supplies the CoCreateGuid call used for temporary
directory names.

Testing

Run cargo test -p cppwinrt; see also the workspace test crates.


Crates Windows Animation (`docs/crates/windows-animation.md`)

windows-animation

A safe wrapper around the Windows Animation Manager (IUIAnimationManager).

When to use it

Use windows-animation when a Windows application needs time-based interpolation for values that
it will draw or apply itself. It works well with immediate-mode rendering: advance the manager on
each frame, read each Variable, and use the values in a canvas, Direct2D, Direct3D, or
DirectComposition scene.

The crate does not create a timer, request frames, draw content, or update a UI property. For
retained WinUI visuals in windows-reactor, Reactor's Composition
transitions usually fit better because the compositor owns their frame updates.

Prerequisites

Initialize COM on the thread that creates the animation objects. For a console or rendering thread,
windows_core::init_mta() is suitable, as shown by the samples. A UI thread may use its existing
apartment. Use one monotonic time origin for scheduling and updates.

The README contains dependency setup and a minimal storyboard. The workflow below focuses on
integrating animation into a real frame loop.

First workflow: drive a rendered property

  1. Create one Manager and one TransitionLibrary.
  2. Create a Variable for each scalar property, using the current rendered value as its initial
    value.
  3. Build a transition and schedule it at an absolute time from the application's animation clock.
  4. On every requested frame, sample that clock and call Manager::update(time) once.
  5. Read all variables, draw or apply the values, and request another frame while animation remains
    active.

For example, the per-frame portion of a renderer can be kept separate from transition setup:

rust,no_run
use windows_animation::{Manager, Result, Variable};

fn draw_frame(manager: &Manager, x: &Variable, now: f64) -> Result<f64> {
    manager.update(now)?;
    let x = x.value()?;
    // Use `x` in the renderer for this frame.
    Ok(x)
}

now is an absolute timestamp in the same time domain used when scheduling, not elapsed time since
the previous frame. Supplying deltas makes the animation clock stall or move incorrectly.

Core API and lifecycle

Manager coordinates variables, transitions, and storyboards. Reuse it for values updated from
the same frame clock. Variable holds the current interpolated f64. TransitionLibrary creates
transition descriptions. Transition and Storyboard are retained COM-backed values and can be
cloned.

There are two scheduling paths:

  • Manager::schedule_transition applies one transition to one variable at an absolute start time.
  • Manager::create_storyboard creates a group that can coordinate several variables or sequence
    transitions before scheduling the group.

Keep the Manager and every Variable needed by rendering alive for the animation lifetime.
Propagate the windows_core::Result from creation, scheduling, updates, and value reads; COM
initialization and invalid transition parameters are reported through those results.

Transition choices

API Use it for
linear(duration, final_value) Constant-rate movement to a value.
accelerate_decelerate(...) Ease-in and ease-out movement.
instantaneous(final_value) A scheduled jump without interpolation.

Acceleration and deceleration are ratios of total duration and must sum to <= 1.0. Durations and
final values use f64.

For direct scheduling:

rust,no_run
use windows_animation::*;

fn retarget(manager: &Manager, library: &TransitionLibrary, x: &Variable, now: f64)
    -> Result<()>
{
    let transition = library.accelerate_decelerate(0.25, 320.0, 0.3, 0.3)?;
    manager.schedule_transition(x, &transition, now)
}

Sequencing with storyboards

Storyboard::add_transition adds a transition and returns a Keyframe marking its end.
add_transition_at_keyframe starts another transition at that point. A keyframe can coordinate a
later transition on the same variable or another variable.

Build the entire storyboard before calling schedule(start_time). Its time is absolute in the
manager's clock domain. The storyboard sample sequences a rise and fall on one variable; the
same pattern can start a fade after a movement finishes.

Frame-loop and error pitfalls

  • Update a manager once per frame before reading its variables. Reading first returns the value
    from the previous update.
  • Use a monotonic high-resolution clock. Wall-clock changes must not move animation time backward.
  • Schedule and update with the same time origin. 0.0 is valid for a deterministic offline
    timeline, while a live app commonly uses seconds since renderer startup.
  • The crate does not report whether more frames are required. The application owns invalidation
    and its stop condition.
  • Do not block the UI thread between updates in an interactive application; missed updates appear
    as skipped visual frames even though interpolation remains time-correct.
  • Treat a failed creation, transition, schedule, update, or value read as an application error.
    Do not continue rendering with a stale value unless that is an explicit fallback.

DirectComposition

Variable::copy_curve copies the variable's curve to an object implementing
IDCompositionAnimation. DirectComposition can then evaluate it on its own composition thread,
without the application reading Variable::value each frame.

This API targets Win32 DirectComposition, not the Windows.UI.Composition engine used by WinUI and
Reactor. The two animation object models are not interchangeable.

Samples

Run the headless sample with:

text
cargo run -p animation-storyboard

The sample schedules one transition directly and chains two more through a storyboard keyframe. It
initializes COM explicitly and prints deterministic values using the absolute-time update pattern.

The canvas/clock sample applies the same API in a live
per-frame rendering loop.


Internal documentation

This section is for contributors to windows-animation.

src/bindings.rs is generated by tool-bindings from
crates/tools/bindings/src/animation.txt. The hand-written Manager, TransitionLibrary,
Storyboard, Transition, Keyframe, and Variable wrappers expose the selected Windows
Animation Manager 2 interfaces.

Manager::new and TransitionLibrary::new create their COM classes with
windows_core::create_instance.
Manager methods delegate scheduling and updates to IUIAnimationManager2. Storyboards add an
explicit keyframe after each transition so the returned opaque Keyframe can anchor another
transition. Variable::copy_curve casts the supplied interface to IDCompositionAnimation before
copying the curve.

Keep the generated filter and the hand-written surface aligned. The headless examples are also the
clearest end-to-end checks of COM creation, direct scheduling, keyframe sequencing, and updates.


Crates Windows Bindgen (`docs/crates/windows-bindgen.md`)

windows-bindgen

Generate Rust bindings for the Windows APIs your project needs.

Use windows-bindgen to generate a focused binding set from Windows metadata (.winmd).
It includes default Win32 and WinRT metadata and can also read custom metadata. For broad
pre-generated bindings, use windows or windows-sys.

The README shows dependency setup, generation from
build.rs, and inclusion of the generated file. This guide covers API selection and generation
options.

Generate bindings

Configure the builder, then call write():

rust,no_run
windows_bindgen::builder()
    .output("src/bindings.rs")
    .filters(["GetTickCount", "GetTickCount64"])
    .flat()
    .sys()
    .write();

The command-line-style adapter does the same thing:

rust,no_run
windows_bindgen::bindgen([
    "--out", "src/bindings.rs",
    "--flat",
    "--sys",
    "--filter", "GetTickCount", "GetTickCount64",
]);

Both forms require an output path and at least one include filter. Paths are relative to the
generator's working directory.

For a published library, run generation from a separate tool and commit src/bindings.rs.
Consumers then need only the generated code's runtime dependencies. Applications can instead
generate into OUT_DIR from build.rs, as shown in the README.

Select APIs

Start with the function, constant, or type name in the Windows API documentation. For example,
.filter("GetTickCount") selects GetTickCount. Include any A or W suffix
in the metadata name; filters do not expand C preprocessor aliases.

Signature dependencies are included automatically. You do not need to list every parameter and
return type. Select an interface or its members explicitly when you also need to call its methods.

The bundled Win32 metadata places APIs in Windows.Win32. Bare names such as GetTickCount and
qualified names such as Windows.Win32.GetTickCount both work. The subsystem paths used by
windows and windows-sys package features, such as Win32_System_Com, are not metadata
namespaces: do not turn them into filters such as Windows.Win32.System.Com.

Filters accept dots or :: between path segments and {...} for grouped names:

Filter Selects
GetTickCount A function by name.
HWND A type by name.
Windows.Win32.GetTickCount A function with its metadata namespace.
Windows::Win32::{GetTickCount, GetTickCount64} A group of functions.
Windows.Foundation A WinRT namespace and its children.
Windows.Storage.Streams.IDataReader::ReadByte One interface method.
Windows.Storage.Streams.IDataReader::{ReadByte, ReadUInt32} A group of methods.
Windows.Storage.Streams.IDataReader::ByteOrder A property's getter and setter.
Windows.Storage.Streams.IDataReader::{} The interface type without callable methods.

Bare names search all input namespaces; qualify a name when you need to distinguish matching
definitions. For custom metadata, use the namespaces declared in that metadata.

Interface shells do not expose iteration. Iteration helpers require First, or Current,
HasCurrent, and MoveNext for an iterator.

For runtime classes, select the instance, static, or factory methods you need. For example,
Windows.Foundation.Uri::CreateUri selects a factory method; a class name alone does not select
its factory methods.

Member filters select methods on a type, not free functions in a subsystem namespace.
Property and event names select their accessor pairs. For type and namespace selections, prefix a
filter with ! to exclude it. More specific rules take precedence; exclusions win ties regardless
of argument order. Prefer narrow filters to generating a namespace and removing items from it.

Keep longer filter lists in a file

Create apis.txt with one filter per line:

text
// APIs used by this project.
GetTickCount
GetTickCount64

Use .filter_file("apis.txt") or --filter-file apis.txt. Blank lines and lines beginning with
// are ignored. A file containing the entire argument list can be read with --etc.

Choose the output

Style controls the generated API. Layout controls where it is written.

Builder option Argument Use
Default style No flag Rich wrappers using windows-core.
sys() --sys Raw bindings using windows-link.
minimal() --minimal Bindings for hand-written wrappers, without most conveniences.
Default layout No flag Nested Rust modules matching metadata namespaces.
flat() --flat One flat file, suitable for a private bindings module.
package() --package A Cargo package with namespace files and features.

sys and minimal are mutually exclusive, as are flat and package. Narrow filters are needed
in every style. Generated APIs may also use focused support crates such as windows-numerics;
add the dependencies referenced by the generated source.

Bit-field accessors

For native bit fields, default and minimal bindings provide getters and setters over the backing
integer fields. Sys bindings keep the backing fields without accessors.

Common options

The builder reference documents all methods, and bindgen lists the command-line-style
arguments.

Builder method Argument Purpose
input(path) --in path Read a custom .winmd file or directory.
input_default() --in default Add the bundled Windows metadata.
output(path) --out path Set the output file or package directory.
filter(name) / filters(names) --filter names... Select APIs.
filter_file(path) --filter-file path Read filters from a text file.
implement(name) --implement name Generate interface implementation traits.
implement_all() --implement Generate implementation traits for all interfaces in scope.
derive(name) --derive name Add a derived trait to generated types.
rustfmt(options) --rustfmt options Pass comma-separated formatter configuration.
extern_fns() --extern Use extern declarations instead of link macros with sys.
compose(name) --compose name Select a WinRT class for composition with minimal.
dead_code() --dead-code Use pub(crate) visibility to detect unused callable bindings.

For compose, also filter the class and its composable factory methods, and select its override
interfaces with implement.

With no explicit input, generation uses the bundled metadata. Adding a custom input replaces
that default; add input_default() if it also needs Windows definitions. Compile RDL with
windows-rdl, or scrape headers with windows-clang,
before passing the resulting .winmd to bindgen.

Formatting uses rustfmt from PATH and is best effort: unavailable or failed formatting leaves
the output unformatted without a warning. rustfmt("max_width=120") sets configuration, not an
executable path. Install rustfmt when generating checked-in source.

Samples

Sample Shows
VSS backup Rich COM bindings in OUT_DIR.
Context Flat sys bindings.
Robot component Custom RDL and implementation support.
Robot client Custom and default metadata together.

Contributing

The generator reads metadata through windows-metadata.
test_bindgen contains the fixtures; run cargo test -p test_bindgen and
cargo test -p windows-bindgen. After changing generation logic, run the affected tool-*
generators and inspect the output diffs. tool-bindings generates focused library bindings;
tool-package generates the published windows and windows-sys packages.


Crates Windows Canvas (`docs/crates/windows-canvas.md`)

windows-canvas

Safe 2D drawing over Direct2D, Direct3D 11, DXGI, DirectWrite, and WIC.

When to use it

Use Canvas for custom 2D graphics, charts, diagrams, image processing, text layout, geometry hit
testing, or animation. It is a drawing API rather than a control toolkit. Combine it with Reactor
when the app also needs WinUI controls.

Use Composition when the scene is primarily a retained tree of visuals that Windows animates and
composes. Use Canvas when the app draws pixels and paths for each requested frame. The two can be
combined by drawing Canvas content into a composition surface.

Canvas is host-neutral by default. Enable system for the typed windows-window swap-chain helper
and system Composition, or reactor for Reactor controls and lifted Composition. Enable
composition with the selected host to draw into Composition surfaces. The host adapters can
coexist when composition is not enabled; the Composition backends cannot.

The basic idea

Canvas is an immediate-mode drawing API. The application receives a drawing session and describes
the current frame with commands such as clear, fill_rect, draw_text, and draw_bitmap.

text
begin a frame -> issue drawing commands -> finish the frame -> present it

There is no retained scene tree. If a shape should still be visible in the next frame, draw it
again. Keep application state - points, colors, text, images - in normal Rust data and turn that
state into drawing commands.

The simplest host is Reactor. It creates the graphics device and swap chain, tracks size and display
scale, and handles device recovery. Enable the feature in Cargo.toml:

toml
windows-canvas = { version = "0.100.0", default-features = false, features = ["reactor"] }
windows-reactor = "0.100.0"

Your first canvas

canvas draws once when the surface is ready and again when its size or display scale changes:

rust,ignore
use windows_canvas::*;
use windows_reactor::App;

fn draw(ctx: &DrawContext) -> Result<()> {
    ctx.clear(ColorF::DARK_SLATE_BLUE);

    let brush = ctx.create_solid_brush(ColorF::CORNFLOWER_BLUE)?;
    let center = Vector2::new(ctx.width / 2.0, ctx.height / 2.0);
    let radius = ctx.width.min(ctx.height) * 0.3;
    ctx.fill_ellipse(&Ellipse::circle(center, radius), &brush);

    Ok(())
}

fn main() -> Result<()> {
    App::run(canvas(draw))
}

The callback receives a DrawContext. Its width and height are the available size in
device-independent pixels. It dereferences to DrawingSession, so drawing methods and resource
creation are available directly on ctx.

Clear the surface, or fully cover it, unless keeping pixels from the previous frame is intentional.

Draw shapes with brushes

A brush supplies paint. Create it once within a frame and reuse it for as many operations as
needed:

rust,ignore
let brush = ctx.create_solid_brush(ColorF::CORNFLOWER_BLUE)?;

ctx.fill_rect(&Rect::from_xywh(20.0, 20.0, 160.0, 100.0), &brush);
ctx.draw_ellipse(
    &Ellipse::circle(Vector2::new(260.0, 100.0), 60.0),
    &brush,
    4.0,
);

brush.set_color(ColorF::WHITE);
ctx.draw_line(
    Vector2::new(20.0, 160.0),
    Vector2::new(320.0, 160.0),
    &brush,
    2.0,
);

Rect, RoundedRect, and Ellipse are lightweight geometry values. Use fill_* for a solid
interior and draw_* for an outline. Gradient brushes use the same drawing methods.

Apply a temporary transform

with_transform applies translation, rotation, or scale only while its closure runs, then restores
the previous transform:

rust,ignore
let transform =
    Matrix3x2::rotation_around(15.0, Vector2::new(ctx.width / 2.0, ctx.height / 2.0));

ctx.with_transform(&transform, || {
    ctx.fill_rect(
        &Rect::from_xywh(ctx.width / 2.0 - 80.0, ctx.height / 2.0 - 40.0, 160.0, 80.0),
        &brush,
    );
});

Scoped transforms make helper functions easier to combine because one drawing operation cannot
leave a transform behind for the next one.

Build a custom path

For a polygon, PathBuilder::polygon is the shortest route:

rust,ignore
let points = [
    Vector2::new(160.0, 40.0),
    Vector2::new(280.0, 240.0),
    Vector2::new(40.0, 120.0),
    Vector2::new(280.0, 120.0),
    Vector2::new(40.0, 240.0),
];

let star = PathBuilder::new(ctx.device())?.polygon(points)?;
let brush = ctx.create_solid_brush(ColorF::new(1.0, 0.8, 0.0, 1.0))?;
ctx.fill_path(&star, &brush);

Use begin, line_to, and bezier_to when the path needs individual segments:

rust,ignore
let curve = PathBuilder::new(ctx.device())?
    .begin_hollow(Vector2::new(40.0, 160.0))
    .bezier_to(
        Vector2::new(120.0, 20.0),
        Vector2::new(240.0, 300.0),
        Vector2::new(320.0, 160.0),
    )
    .end_open()
    .build()?;

ctx.draw_path(&curve, &brush, 3.0);

Paths also support bounds and fill or stroke hit testing, which is useful for diagrams and editors.

Draw text

For ordinary text, create a TextFormat and draw into a rectangle:

rust,ignore
let format = TextFormat::new("Segoe UI", 32.0)?
    .with_alignment(TextAlignment::Center)
    .with_paragraph_alignment(ParagraphAlignment::Center);
let brush = ctx.create_solid_brush(ColorF::WHITE)?;

ctx.draw_text(
    "Hello, Canvas!",
    &format,
    &Rect::new(0.0, 0.0, ctx.width, ctx.height),
    &brush,
);

Use TextLayout when text is drawn repeatedly or needs measurement, reflow, caret bounds, or hit
testing. Cache the layout and rebuild it when its text, format, size, or device changes. The
text_layout sample shows that pattern.

Load and draw an image

load_bitmap decodes an image file:

rust,ignore
let bitmap = ctx.load_bitmap("assets/photo.png")?;
let destination = Rect::from_xywh(20.0, 20.0, bitmap.width(), bitmap.height());
ctx.draw_bitmap(&bitmap, &destination, 1.0);

For generated image data, create_bitmap accepts tightly packed premultiplied BGRA pixels:

rust,ignore
let bitmap = ctx.create_bitmap(&pixels, width, height)?;
ctx.draw_bitmap(
    &bitmap,
    &Rect::from_xywh(0.0, 0.0, width as f32, height as f32),
    1.0,
);

Bitmaps, brushes, paths, and text layouts depend on the graphics device. Creating them inside the
draw callback is the easiest starting point. Cache expensive resources only when profiling shows
the need, and rebuild cached resources when ctx.device_changed() is true.

Redraw when state changes

Use canvas when content changes only because the surface was created or resized. Use
animated_canvas for motion that needs every display frame:

rust,ignore
animated_canvas(|ctx| {
    draw_frame(ctx, elapsed_time())
})

For input-driven content, use canvas_invalidated. Store an Invalidator with the component,
change the drawing data in update, and request one new frame:

rust,ignore
struct Sketch {
    points: Rc<RefCell<Vec<Vector2>>>,
    invalidator: Invalidator,
}

fn update(&mut self, point: Vector2, _context: &ComponentContext<Self>) {
    self.points.borrow_mut().push(point);
    self.invalidator.invalidate();
}

fn draw_points(ctx: &DrawContext, points: &[Vector2]) -> Result<()> {
    ctx.clear(ColorF::BLACK);
    let brush = ctx.create_solid_brush(ColorF::WHITE)?;
    for &point in points {
        ctx.fill_ellipse(&Ellipse::circle(point, 3.0), &brush);
    }
    Ok(())
}

fn view(&self, _input: &(), _context: &mut ViewContext<Self>) -> View {
    let points = Rc::clone(&self.points);
    canvas_invalidated(&self.invalidator, move |ctx| {
        draw_points(ctx, &points.borrow())
    })
}

Invalidation keeps the app idle between changes. Do not use continuous animation for a chart,
drawing, or diagram that changes only after input.

Drive a standalone swap chain

Without Reactor, the application creates a GpuDevice and SwapChain and owns the frame loop. The
essential sequence is:

rust,no_run
use windows_canvas::*;

fn draw_frame(chain: &mut SwapChain) -> Result<bool> {
    let session = chain.begin_draw()?;
    session.clear(ColorF::DARK_SLATE_BLUE);

    let brush = session.create_solid_brush(ColorF::CORNFLOWER_BLUE)?;
    session.fill_rect(&Rect::from_xywh(20.0, 20.0, 200.0, 120.0), &brush);

    drop(session);
    chain.present()
}

The drawing session borrows the swap chain, so finish and drop it before present. A standalone
host must also resize the swap chain, update display scale, schedule frames, and recreate the
device, swap chain, and dependent resources when present returns Ok(false). The
standalone sample connects those pieces to
windows-window. Its resize callback redraws the existing swap chain because the outer render loop
is suspended while Windows runs its modal interactive-sizing loop.

Sample What it shows
circles Drawing, animation, and resize handling
clock Continuous animation

Browse crates/samples/canvas for focused drawing and hosting
examples.


Internal documentation

The remainder of this page describes how the crate is built and maintained. Applications do not
need it to use Canvas.

Architecture

The safe wrappers are hand-written and include GpuDevice, SwapChain, DrawingSession,
geometry, brushes, text, bitmaps, effects, and render targets.

GpuDevice owns the shared Direct3D, Direct2D, DXGI, DirectWrite, and WIC objects. A swap chain
owns one D2D device context and is rendered on its owning thread. WARP allows tests and callers to
render without a physical GPU.

device_lost.rs classifies D2D and DXGI loss codes. Standalone callers perform recovery. The
Reactor host rebuilds its device, swap chain, and cached state after loss.

The Reactor integration lives behind this crate's reactor feature. The dependency direction is
windows-canvas[reactor] -> windows-reactor. Canvas owns rendering resources and receives only
typed panel metrics, rendering notifications, deferred frame callbacks, and attachment completion
through ElementRef<SwapChainPanel>.

The system feature enables windows-window and GpuDevice::create_swap_chain_for_window.
Reactor-only builds do not compile windows-window; raw create_swap_chain_for_hwnd remains
available without either host.

Continuous mode uses WinUI's CompositionTarget::Rendering for its normal animation clock. Layout
and scale changes also request a coalesced dispatcher frame because composition rendering can be
delayed while WinUI processes interactive resize input. Demand mode stays idle until layout, scale,
or Invalidator requests a frame.

Panel binding, swap-chain, and attachment-request generations reject stale completion callbacks.
Resizing buffers preserves the existing panel attachment; only panel replacement or swap-chain
rebuilding requires SetSwapChain. Canvas::on_error reports initialization, attachment, resize,
drawing, presentation, and failed recovery through IntegrationError.

CanvasImageSource uses a borrowed drawing session over a WinUI SurfaceImageSource.
CanvasCompositionExt uses a borrowed drawing session over
ICompositionDrawingSurfaceInterop. Both pair every successful BeginDraw with EndDraw,
including panic cleanup.

If a composition-surface draw reports device loss, create a replacement GpuDevice, call
GpuDevice::replace_graphics_device to rebind the existing CompositionGraphicsDevice, and retry
the draw. The Composition surfaces and visuals remain in place during recovery.

Code generation

src/bindings.rs is generated by tool-bindings from
crates/tools/bindings/src/canvas.txt. It contains minimal flat bindings for Direct2D,
Direct3D 11, DXGI, DirectWrite, and WIC, plus referenced WinRT numerics types.

src/reactor_bindings.rs is generated by tool-reactor for the WinUI bridge. Generated files are
committed and must not be edited by hand. After changing
crates/tools/reactor/src/canvas.txt or the generator, run the corresponding tool and verify
cargo check -p windows-canvas --quiet.

Testing

Canvas tests use the WARP software rasterizer and do not require a physical GPU. The integration
suite is in test_canvas:

text
cargo test -p test_canvas

Use RenderTarget for off-screen drawing tests. Repeated presents on a composition swap chain
without a visual consumer can leave WARP waiting for queued work during device destruction.

Reactor integration behavior is also covered by Reactor's recording and live surface tests. The
Composition Canvas sample covers the system bridge, and the Stacker Reactor sample covers the
lifted bridge.


Crates Windows Clang (`docs/crates/windows-clang.md`)

windows-clang

Generates RDL from C/C++ headers using libclang.

windows-clang is the header-facing stage of the Windows metadata pipeline:

text
headers -> windows-clang -> RDL -> windows-rdl -> WinMD

It extracts declarations and source annotations from C and C++ headers. Compile the resulting
RDL with windows-rdl, then use windows-bindgen to
generate Rust bindings.

Setup

The README shows the Cargo dependency. Install a compatible
libclang and set LIBCLANG_PATH to its directory if it is not discoverable on your system.
The crate loads libclang at runtime; it does not install it or the headers you want to process.

Supply compiler arguments for the language, target architecture, include directories, and
preprocessor definitions your headers require. Metadata references do not supply C/C++ headers
or compiler include paths.

Generate RDL

Configure the builder, then call write():

rust,no_run
windows_clang::clang()
    .input("Example.h")
    .args(["-x", "c++", "--target=x86_64-pc-windows-msvc"])
    .reference_default()
    .namespace("Example")
    .library("example.dll")
    .output("Example.rdl")
    .write()
    .unwrap();

At least one input, a namespace, and an output path are required. Paths are relative to the
generator's working directory.

Builder method Purpose
input(path) / inputs(paths) Read header files as separate translation units.
input_text(source) Add an in-memory translation unit.
args(arguments) Pass compiler arguments to libclang.
filter(suffix) Include declarations from included headers matching a path suffix.
symbols(names) Restrict emitted free functions to the supplied names.
reference(path) Resolve existing declarations from a WinMD file.
reference_default() Use the bundled Windows metadata as references.
namespace(name) Set the output RDL namespace.
library(name) Set the DLL name attached to emitted functions.
output(path) Set the output RDL file.

Declarations from input headers are output roots. Included headers can supply dependencies without
having all their declarations emitted; use filter to include their declarations as roots too.
This is a header-path filter, not a function-name filter.

References let generated RDL use existing types without redefining them. Use reference_default
when your headers depend on Windows APIs and reference for your own metadata.

The builder reference
documents all methods.

Inspect extraction results

Use Input, extract, and EmitOptions directly when a generator needs to inspect declarations,
assign different DLLs to functions, or emit separate header partitions.

rust,no_run
let input = windows_clang::Input::new(
    "Example.h",
    std::fs::read_to_string("Example.h").unwrap(),
);
let snapshot = windows_clang::extract(
    [input],
    &["-x", "c++", "--target=x86_64-pc-windows-msvc"],
)
.unwrap();

The resulting Snapshot owns translation-unit-local facts and constants. facts, constants,
unsupported, and dump expose the extraction result for diagnostics and validation.

Input::new(name, source) treats name as a root. The following methods control which included
headers contribute output roots:

Method Selects
with_roots(paths) Individual header paths.
with_root_dirs(paths) Headers under directory prefixes.
with_root_suffixes(suffixes) Headers matching path suffixes.
with_excluded_roots(paths) Headers excluded from output ownership.

Configure emission

Use Snapshot::emit(namespace) for one namespace, emit_with_library to attach one DLL to all
functions, or emit_with_options for generator policy. emit_by_header_with_options returns a
map of defining-header names to RDL partitions.

EmitOptions controls:

Option Purpose
namespace RDL namespace.
library / libraries Default or per-function DLL mappings.
references External types and enum members available to the projection.
excluded_types Types excluded from root selection.
excluded_functions Functions omitted from the local output.
excluded_constants Constants omitted from the local output.
functions Optional free-function allowlist.

References are explicit TypeReference values classified as Type, Interface, or Enum.
Referenced enum member names let an overlay emit an enum only when it adds members to the base.
MetadataReferences builds that map from WinMD files and records existing type, function, and
constant names for exclusion. apply_reference_exclusions excludes only types with an
unambiguous external reference, while apply_exclusions is available for overlays that must omit
every item from a known base.

Exclusions filter root declarations; they do not establish that a dependency has an external
definition. Supply references for external types; required local dependencies are still emitted.

The caller owns DLL selection, architecture merging, and RDL-to-WinMD compilation. Extraction
does not discover import libraries or combine target architectures automatically.

Known limits

  • RDL cannot represent mixed raw pointer-chain mutability. If any level in a raw pointer run is
    const, every level in that run is emitted as const.
  • Coverage is limited to declarations reachable from configured roots.
  • Conflicting declarations can fail emission as ambiguous; extraction is not a general C++ binding
    generator.
  • SAL projection supports only a subset of annotations and size relationships.
  • Non-finite floating-point constants and interface-valued constants cannot be emitted.
  • Header extraction cannot infer curated handle cleanup, last-error, or documentation policy.
  • Stable Rust has no function-pointer ABI corresponding to every native calling convention.

Contributing

Run cargo test -p windows-clang and cargo test -p test_clang. Header fixtures and expected RDL
live in crates/tests/libs/clang; tests regenerate the expected output, and CI rejects output
changes that are not committed. After changing extraction or emission, run the affected tool-*
generators and review the output diffs.

Repository tooling provisions its pinned libclang with cargo run -q -p tool-clang -- path.
Set LIBCLANG_PATH to the reported directory before running tests.


Crates Windows Collections (`docs/crates/windows-collections.md`)

windows-collections

Stock WinRT collection types backed by Rust containers.

When to use this crate

Use windows-collections when a WinRT API expects a collection interface and your data starts as a
Rust Vec or BTreeMap. The crate supplies stock implementations of the standard WinRT
interfaces, so application and component code do not need to implement their vtables.

This is an ABI adapter, not a replacement for Rust collections. Keep data as Vec or BTreeMap
while it remains inside Rust, and convert at the boundary where a WinRT collection is required.

Getting started

The crate README contains the dependency declaration and
minimal vector and map examples. The stock implementations require the default std feature. For
a first workflow:

  1. Identify the exact interface in the receiving API's signature.
  2. Build the values in a Vec or BTreeMap.
  3. Convert the container with InterfaceType::from.
  4. Pass or return the projected interface and handle its methods as fallible operations.
rust
use windows_collections::IVector;

fn numbers() -> IVector<i32> {
    let values = IVector::<i32>::from(vec![1, 2, 3]);
    values.Append(4).unwrap();
    values
}

In application code, propagate the Result returned by collection methods instead of unwrapping
when the operation can fail.

Choosing a collection interface

Interface Backing input Choose it when the consumer needs
IIterable<T> Vec<T::Default> Forward iteration only
IVectorView<T> Vec<T::Default> Indexed, read-only access
IVector<T> Vec<T::Default> Indexed mutation
IObservableVector<T> Vec<T::Default> Mutation notifications
IMapView<K, V> BTreeMap<K::Default, V::Default> Read-only lookup
IMap<K, V> BTreeMap<K::Default, V::Default> Insert, remove, and lookup
IObservableMap<K, V> BTreeMap<K::Default, V::Default> Map mutation notifications

T::Default is the Rust storage form defined by the type's RuntimeType implementation. For
ordinary value types it is usually T. Projected nullable or interface types may use an
Option<T> storage form, so let the compiler guide the concrete container type.

The mutable interfaces own their Rust containers behind synchronization. View conversions own
read-only values, and GetView on a mutable collection returns a snapshot rather than a live view.

Common tasks

Build and read a map

rust
use std::collections::BTreeMap;
use windows_collections::IMapView;
use windows_strings::{h, HSTRING};

fn main() {
    let values = BTreeMap::from([
        (HSTRING::from("one"), 1),
        (HSTRING::from("two"), 2),
    ]);
    let values = IMapView::<HSTRING, i32>::from(values);

    assert!(values.HasKey(h!("one")).unwrap());
    assert_eq!(values.Lookup(h!("two")).unwrap(), 2);
}

Missing keys and invalid vector indexes return the WinRT E_BOUNDS error. Check with HasKey or
Size when absence is expected rather than using an error as normal control flow.

Iterate projected values

Generated IntoIterator implementations allow a borrowed or owned collection to be used in a
for loop. Each item is still a projected value, and map iteration yields
IKeyValuePair<K, V>:

rust
# use windows_collections::IIterable;
let values = IIterable::<i32>::from(vec![1, 2, 3]);

for value in &values {
    println!("{value}");
}

Iteration uses GetMany in blocks instead of making one iterator ABI call for each item. Reading
each map pair's Key and Value remains a separate ABI operation.

Publish collection changes

Convert a container to IObservableVector or IObservableMap when consumers need
VectorChanged or MapChanged. Register the projected event handler, retain its token, and pass
that token to the corresponding remove method when the subscription ends. Mutating through the
observable interface raises insertion, removal, replacement, or reset notifications.

Important choices and pitfalls

  • Convert to the interface the API asks for. A mutable IVector<T> is unnecessary when the API
    accepts IIterable<T> or IVectorView<T>.
  • Conversion consumes the Rust container. Clone it first only when Rust must retain an independent
    copy.
  • GetView snapshots the current contents. Later writes to the mutable source are not reflected in
    that view.
  • Map iteration snapshots entries when First is called. It does not hold a live traversal of the
    backing BTreeMap.
  • Calls cross a WinRT ABI and return Result; repeated fine-grained lookups can cost more than
    processing the original Rust container before conversion.

Samples and next steps

The collections-collections sample converts a
Vec and a BTreeMap, mutates the projected interfaces, and iterates their values. Continue with
the
docs.rs API for the methods on the interface required by
your Windows API. windows-reference covers boxed WinRT values that can
also appear as collection elements.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-collections.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/collections.txt;
the collection adapters are hand-written. Used internally by the windows crate.

Iterating a collection (for x in &vector) yields through BufferedIterator
(src/buffered_iterator.rs), which fetches elements a block at a time via GetMany rather than one
IIterator::next ABI call per element. windows-bindgen generates the IntoIterator impls that
reference it. The block is sized to keep the buffer near 2 KB regardless of element size.

This batching applies to maps too: IMap/IMapView/IObservableMap implement
IIterable>, so for pair in &map drives the same BufferedIterator (yielding
IKeyValuePair items) - there is no separate, slower map path. A map iteration still costs more
than a vector of scalars, but not because of the iterator: GetMany over IVector
bulk-copies the values inline, whereas over a map it returns a block of IKeyValuePair COM objects
(one AddRef each) and reading every pair.Value()/Key() remains a per-pair ABI crossing - the
IMap ABI offers no bulk key/value read. Separately, the component-side stock map iterator
snapshots its entries once at First() so each step is O(1) rather than re-walking the tree,
keeping a full traversal linear.

Testing

Run cargo test -p windows-collections; see also the workspace test crates.


Crates Windows Composition (`docs/crates/windows-composition.md`)

windows-composition

Safe wrappers for the Windows retained-mode composition engine.

When to use it

Use Composition for a retained tree of lightweight visuals, brushes, shapes, and animations. It is
a good fit for animated backgrounds, overlays, game boards, transitions, and scenes where Windows
should retain and compose objects between updates.

Composition does not provide controls, text input, accessibility behavior, or layout panels. Use
windows-reactor for WinUI controls. Use
windows-canvas when custom drawing is easier as immediate-mode 2D commands.
Composition can host Canvas-drawn surfaces.

The basic idea

Composition is a retained graphics API. You create a tree of visual objects, change their
properties when application state changes, and let Windows draw and animate the result.

text
create visuals -> attach them to a tree -> update properties -> Windows composes the scene

This differs from Canvas, where the application issues drawing commands for each frame. A
Composition visual stays in the tree until it is removed. Changing its offset, size, brush, or
animation is enough to change what appears on screen.

A Compositor creates every object in a scene. The most common visual types are:

Type Purpose
ContainerVisual Groups child visuals without drawing content
SpriteVisual Draws a rectangular brush and can contain children
ShapeVisual Draws retained vector shapes

Start with sprite visuals. They are enough for backgrounds, cards, overlays, game pieces, and many
animations.

Choose how to host the scene

The crate supports two Composition stacks:

Feature Use it when
system (default) The app owns a windows-window window
reactor The scene belongs inside a Reactor view

The two stacks use different Windows APIs and their objects cannot be mixed. Cargo features are
mutually exclusive, so applications selecting Reactor must disable the default system feature:

toml
windows-composition = { version = "0.100.0", default-features = false, features = ["reactor"] }

Both stacks can create a graphics device and drawing surface for windows-canvas. Enable Canvas's
composition feature and select its matching system or reactor feature.

The examples below begin with the default system stack because it shows the visual model with the
least framework code.

Your first visual tree

This complete program creates a window and fills it with a dark sprite:

rust,no_run
use windows_composition::*;
use windows_window::*;

fn main() -> Result<()> {
    let _queue = DispatcherQueueController::create_on_current_thread()?;
    let compositor = Compositor::new()?;

    let window = Window::new("Composition")
        .size(800, 600)
        .create()?;
    let target = compositor.create_desktop_window_target(&window, false)?;

    let root = compositor.create_sprite_visual();
    root.set_relative_size_adjustment(Vector2::new(1.0, 1.0));
    root.set_brush(&compositor.create_color_brush(CompositionColor::rgb(30, 30, 46)));
    target.set_root(&root);

    run();
    Ok(())
}

The dispatcher queue and target must stay alive while the scene is displayed. Declaring them in
main keeps their lifetimes clear.

The Compositor creates both the visual and its brush. Composition objects in one tree should come
from the same compositor.

Build a scene from nested visuals

Visuals form an ordered tree. Insert a child at the top to draw it in front of existing children,
or at the bottom to draw it behind them:

rust,ignore
let card = compositor.create_sprite_visual();
card.set_size(240.0, 160.0);
card.set_offset(40.0, 40.0, 0.0);
card.set_brush(&compositor.create_color_brush(CompositionColor::rgb(0, 120, 215)));

let badge = compositor.create_sprite_visual();
badge.set_size(32.0, 32.0);
badge.set_offset(192.0, 16.0, 0.0);
badge.set_brush(&compositor.create_color_brush(CompositionColor::rgb(255, 255, 255)));

card.children().insert_at_top(&badge);
root.children().insert_at_top(&card);

The badge's offset is relative to the card. Moving the card moves both visuals. Keep handles to
visuals that the application will change later; attached visuals remain in the scene even if a
local clone is dropped.

Use the child collection to change the tree:

rust,ignore
root.children().remove(&card);
root.children().remove_all();

Size and position visuals

set_size uses width and height. set_offset uses x, y, and z:

rust,ignore
visual.set_size(200.0, 120.0);
visual.set_offset(24.0, 48.0, 0.0);

Composition does not lay out a scene like WinUI. Either update exact sizes when the host changes or
make a child track its parent:

rust,ignore
background.set_relative_size_adjustment(Vector2::new(1.0, 1.0));

A relative size of (1.0, 1.0) fills the parent. Relative adjustments can be combined with an
exact size or offset, which is useful for margins:

rust,ignore
panel.set_relative_size_adjustment(Vector2::new(1.0, 1.0));
panel.set_size(-32.0, -32.0);
panel.set_offset(16.0, 16.0, 0.0);

Coordinates are device-independent pixels. The host is still responsible for reporting its own
size changes when exact dimensions are used.

Animate a property

Animations are retained objects too. Create an animation, add key frames, and start it on a visual:

rust,ignore
use std::time::Duration;

let pulse = compositor.create_vector3_key_frame_animation();
pulse.insert_key_frame(0.0, Vector3::new(1.0, 1.0, 1.0));
pulse.insert_key_frame(0.5, Vector3::new(1.2, 1.2, 1.0));
pulse.insert_key_frame(1.0, Vector3::new(1.0, 1.0, 1.0));
pulse.set_duration(Duration::from_millis(800));
pulse.set_iterate_forever();

card.start_animation("Scale", &pulse);

Windows advances the animation without an application frame loop. Use
ScalarKeyFrameAnimation for one-number properties such as "Opacity" and
Vector3KeyFrameAnimation for properties such as "Scale" and "Offset".

Property names are exact, case-sensitive Composition strings. Rust cannot catch a misspelling in
"Scale".

Draw retained shapes

Use a ShapeVisual when the scene needs an ellipse or another vector shape rather than a
rectangular sprite:

rust,ignore
let visual = compositor.create_shape_visual();
visual.set_size(200.0, 200.0);

let geometry = compositor.create_ellipse_geometry();
geometry.set_radius(Vector2::new(80.0, 80.0));

let circle = compositor.create_sprite_shape(&geometry);
circle.set_offset(Vector2::new(100.0, 100.0));
circle.set_fill_brush(&compositor.create_color_brush(CompositionColor::rgb(0, 120, 215)));
visual.shapes().append(&circle);

root.children().insert_at_top(&visual);

Shapes remain editable after they are attached. Change the geometry, brush, or visual properties
instead of rebuilding the scene for every update.

Put Composition inside Reactor

With the reactor feature, a Reactor Grid provides the host. The flow is:

  1. Attach an ElementRef<Grid> to the grid.
  2. Observe the composition host from use_effect.
  3. On CompositionHostEvent::Ready, adopt the supplied compositor with
    Compositor::from_host.
  4. Build a visual tree and attach it with request_set_child_visual.
  5. Update its size on Ready and Metrics events.

The important bridge calls look like this:

rust,ignore
let compositor = Compositor::from_host(compositor)?;
let root = compositor.create_sprite_visual();
root.set_relative_size_adjustment(Vector2::new(1.0, 1.0));
root.set_brush(&compositor.create_color_brush(CompositionColor::rgb(30, 30, 46)));

let _ = host.request_set_child_visual(Some(root.host_visual()), |result| {
    if let Err(error) = result {
        eprintln!("failed to attach composition visual: {error:?}");
    }
});

The observation must live until the effect is cleaned up. The
host sample contains the complete
component lifecycle; start there rather than copying only the bridge calls.

Sample What it shows
circles A larger shape scene
minesweeper Input, layout, shapes, and animation

Browse crates/samples/composition for focused hosting and
animation examples.

CompositionScopedBatch::on_completed returns an event revoker that must remain alive until the
tracked work finishes. In a Reactor component, have the callback send a message and update the
retained scene from Component::update.


Internal documentation

The remainder of this page describes how the crate is built and maintained. Applications do not
need it to use Composition.

Dependencies and feature model

The crate depends on windows-core, windows-numerics, windows-collections, and windows-time,
not the umbrella windows crate. Visual offsets and sizes reuse Vector2 and Vector3. Shape
collections use IVector, implicit animation collections use IMap, and animation timing uses
TimeSpan.

The system feature also depends on windows-window and enables dispatcher queue, HWND target,
graphics-device, and drawing-surface support. The reactor feature selects lifted bindings and
exposes host conversions. Compile errors reject zero or two selected stacks.

Cargo unifies features across a build graph. Unified CI jobs that build Reactor exclude
system-stack consumers, then check those consumers in a separate step. Add new consumers to the
matching CI groups. tool-yml generates the stack-specific MSRV and feature checks.

Wrapper model

Each safe type is a newtype over one owned COM interface. Concrete visual types dereference through
their base hierarchy. Brush, Shape, and Animation are sealed traits that keep private
generated binding types out of public signatures.

Constructors and immediate recoverable operations return windows_core::Result. Retained-object
property and factory conveniences fail fast on COM errors. CompositionColor wraps the flat
Windows.UI.Color ABI value.

Module Contents
compositor.rs Factories for graph objects and stack-specific hosts
visual.rs Visual hierarchy, properties, and child collections
brush.rs Brush hierarchy and implementations
shape.rs Shape visuals, geometry, shapes, and collections
animation.rs Key frames, easing, groups, and implicit animations
batch.rs Scoped batches and batch kinds
surface.rs System and lifted graphics devices and drawing surfaces
stack.rs, target.rs System dispatcher queue and HWND hosting
color.rs Public color value

Code generation

Composition uses tool-composition because it needs system and non-default lifted metadata. The
tool first uses windows-rdl to generate the lifted native interop metadata from interop.rdl,
then runs windows_bindgen twice with --flat --minimal --dead-code.

  • System bindings read the repository's Windows and Win32 metadata and include
    Windows.UI.Composition, dispatcher queue, desktop interop, and surface interop APIs.
  • Lifted bindings read the pinned Microsoft UI and Graphics metadata used by Reactor, the
    repository's Windows and Win32 metadata, and the generated interop metadata. They include
    Microsoft.UI.Composition with shared foundation, color, DirectX, and native interop types.

crates/tools/composition/src/composition.txt is the shared filter. System-only regions are omitted
from lifted generation. Composition, DirectX, and native interop entries are rewritten for the
lifted stack. lib.rs selects bindings.rs or bindings_lifted.rs.

Generated binding files are committed and must not be edited by hand. After changing the filter,
metadata inputs, or tool, run:

text
cargo run -p tool-composition --quiet
cargo check -p windows-composition --quiet

Run cargo run -p tool-yml after changing feature-matrix generation.

Reactor bridge

Reactor reports CompositionHostEvent through a typed ElementRef<Grid>. A ready event contains
an application-safe compositor capability and layout metrics. Compositor::from_host adopts that
capability. Visual::host_visual exports only an application-owned lifted visual for attachment.

Both crates generate lifted bindings from the same pinned Microsoft UI metadata. Host attachment
and observation commands exist in Reactor's native and recording runtimes. Window and node
identities reject late events and completions.

Reactor also consumes lifted key-frame, group, easing, and implicit-animation wrappers for element
lifecycle transitions.

Canvas bridge

Each stack exposes matching compositor, graphics-device, and drawing-surface interop interfaces. A
Canvas device becomes a CompositionGraphicsDevice, which creates a premultiplied BGRA drawing
surface. A composition surface brush paints that surface onto a visual.

windows-canvas implements the drawing half behind its composition feature. It borrows the
Direct2D context returned by begin_draw, applies the backing-atlas offset, and pairs the operation
with end_draw.

Testing

test_composition includes pure value tests and live headless tests. The live tests create a
dispatcher queue and real compositor, then exercise visual, brush, shape, collection, and animation
wrappers without a window. Getter checks also verify that wrapper methods route to the intended
versioned COM interface.

Run:

text
cargo test -p test_composition

Window hosting is covered by the standalone sample. Reactor host behavior is covered by Reactor's
recording tests and live self-test fixtures.


Crates Windows Core (`docs/crates/windows-core.md`)

windows-core

Core COM and WinRT type support shared by the windows-* crates.

When to use this crate

Use windows-core directly when you need the common types behind generated Windows bindings,
author a COM or WinRT interface in Rust, or work with COM identity, apartments, factories, or
references. It provides IUnknown, IInspectable, Interface, GUID, RuntimeType, and the
support used by generated projections.

Most code should start with a focused windows-* crate instead. Those crates already depend on and
re-export the core types they need. Binary applications may use the broad
windows projection. Add a direct windows-core dependency when your own public API
names these types or when no higher-level crate
owns the operation.

Getting started

The crate README has the dependency declaration and a minimal
example using strings and results. For a first COM or WinRT workflow:

  1. Select or generate bindings for the Windows API you want to call.
  2. Initialize the calling thread's COM apartment when the API requires it.
  3. Keep projected interface values as owned Rust values and propagate windows_core::Result.
  4. Use Interface::cast when you need another interface implemented by the same object.

For apartment-agnostic command-line code, init_mta initializes an uninitialized calling thread as
MTA and keeps the process MTA alive:

rust
use windows_core::Result;

fn main() -> Result<()> {
    windows_core::init_mta()?;
    // Create and use projected COM or WinRT objects here.
    Ok(())
}

If a UI framework or host initializes COM for you, follow its apartment model instead. init_mta
does not change a thread that is already initialized in another apartment.

For a standalone UI thread, init_sta initializes COM as a single-threaded apartment. Keep the
returned value alive until after the apartment's COM interface values are dropped:

rust
use windows_core::Result;

fn main() -> Result<()> {
    let _apartment = windows_core::init_sta()?;
    // Create and use apartment-bound COM objects here.
    Ok(())
}

Core API model

API Role
IUnknown Owning pointer to the base COM interface
IInspectable Base interface for WinRT objects
Interface Interface identity, vtable access, casts, and raw-pointer interop
GUID Interface IDs and other Windows GUID values
create_instance Activates an in-process COM class as a requested interface
init_mta, init_sta Initializes COM using the required apartment model
StaApartment Keeps an STA initialized on its owning thread
Result<T>, Error, HRESULT Re-exported Windows error model
HSTRING, PCWSTR, PCSTR Re-exported Windows string model
AgileReference<T> Reference that resolves an apartment-valid proxy
Weak<T> Non-owning reference that can be upgraded while the object is alive
EventRevoker Event registration that unregisters when dropped
#[interface], #[implement] Declare an interface and implement it with a Rust type

Projected interface values are reference-counted owners. Cloning one performs the corresponding
COM reference-count operation, dropping it releases the reference, and cast performs
QueryInterface. Prefer these operations over manual AddRef, Release, or pointer casts.

create_instance::<T>(&class_id) wraps the common non-aggregated
CoCreateInstance(..., CLSCTX_INPROC_SERVER, ...) call. The caller remains responsible for COM
apartment initialization and for choosing a class that supports the calling apartment.

Common tasks

Moving between interfaces

Use cast to request another interface from the same COM identity:

rust
use windows_core::{Interface, IInspectable, IUnknown, Result};

fn as_inspectable(value: &IUnknown) -> Result<IInspectable> {
    value.cast()
}

A failed query is returned as an Error. A Rust type conversion is not a replacement for
QueryInterface unless the generated API provides that conversion.

Sharing an apartment-bound object

Do not assume that every interface can be sent to another apartment. Create an
AgileReference<T>, move or clone that reference, and call resolve in the apartment where the
object will be used:

rust
use windows_core::{AgileReference, Interface, Result};

fn make_agile<T: Interface>(value: &T) -> Result<AgileReference<T>> {
    AgileReference::new(value)
}

Weak<T> serves a different purpose: it avoids keeping an object alive and returns None from
upgrade after the object has been destroyed.

Declaring and implementing an interface

The default proc-macros feature re-exports #[interface] and #[implement]:

rust
use windows_core::*;

#[interface("7e75ffe0-2f8c-4040-953e-b1f83a48f77b")]
unsafe trait IValue: IUnknown {
    unsafe fn value(&self) -> i32;
}

#[implement(IValue)]
struct Value {
    value: i32,
}

impl IValue_Impl for Value_Impl {
    unsafe fn value(&self) -> i32 {
        self.value
    }
}

fn main() -> Result<()> {
    let object: IValue = Value { value: 42 }.into();
    assert_eq!(unsafe { object.value() }, 42);

    let unknown: IUnknown = object.cast()?;
    let object: IValue = unknown.cast()?;
    assert_eq!(unsafe { object.value() }, 42);

    Ok(())
}

#[interface] generates the vtable, caller wrappers, and implementation trait. #[implement]
generates the wrapper that owns the vtables and reference count. The implementation methods remain
responsible for the interface contract and any safety requirements in the ABI.

Important choices and pitfalls

  • The default features are std and proc-macros. Disable proc-macros when avoiding the
    syn, quote, and proc-macro2 build dependencies matters; the narrower interface_decl! and
    implement_decl! macros remain available.
  • Raw interface pointers do not carry an owning lifetime. Use from_raw, from_raw_borrowed,
    into_raw, and related Interface operations only when the ownership contract is known.
  • COM apartment initialization is per thread. Initializing one thread does not initialize worker
    threads.
  • EventRevoker unregisters on drop. Keep it alive for as long as the handler should run; use
    into_token only when another owner will remove the registration.
  • The imp module supports generated code and crate internals. Application code should prefer the
    public projected types and traits.

Samples and next steps

There is no standalone windows-core sample group. The
windows samples show Result, init_mta, strings, and projected
interfaces in complete API calls. The robot sample shows a Rust
component implementing generated COM and WinRT interfaces for multiple clients.

Continue with windows-result for error propagation and
windows-strings for ABI string choices.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-core.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/core.txt. The
hand-written modules (agile_reference, com_object, compose, event) provide the COM runtime
support. The #[implement]/#[interface] proc macros are re-exported from the
windows-implement/windows-interface crates
behind the proc-macros feature, and the implement_macro/interface_macro modules supply the
implement_decl!/interface_decl! declarative equivalents used when that feature is off.

Testing

Run cargo test -p windows-core; see also the workspace test crates.


Crates Windows Default (`docs/crates/windows-default.md`)

windows-default

The default Windows metadata used by the build-time crates.

windows-default embeds the canonical Windows Runtime and Windows API metadata as byte slices.
Build tools use WINRT and WIN32 when they need the standard metadata without locating separate
.winmd files.

Applications should use a focused crate, windows-bindgen, or, for binaries that want a broad
pre-generated surface, windows or windows-sys. Most code
generators should use input_default or reference_default on the builder they already use. Use
windows-default directly only when implementing a metadata tool that accepts in-memory winmd
bytes.

The crate exposes two static byte slices:

Static Embedded file Contents
WINRT Windows.winmd Windows Runtime contracts
WIN32 Windows.Win32.winmd Windows SDK and WDK APIs

Both files are included in the published crate and embedded with include_bytes!. The crate does
not extract files or perform I/O.

First workflow: add standard metadata to a custom index

  1. Construct windows_metadata::reader::File values from WINRT and WIN32.
  2. Add component-specific winmd files.
  3. Build one windows_metadata::reader::Index over the combined files.
  4. Query or transform the index without locating an SDK installation.

tool-reactor-metadata uses this pattern when its metadata resolver combines WinUI winmds with the
standard definitions. If you are compiling RDL or generating Rust, use the neighboring builder
methods instead; they already perform the byte-to-file conversion.

Integration

The build crates depend on windows-default and expose the metadata through their own builders.
Callers therefore do not need a separate dependency or a path into the Windows SDK.

Crate Default behavior
windows-bindgen Implicit with no input; explicit with input_default.
windows-rdl reference_default or writer input_default.
windows-clang reference_default and WinRT-only resolution_default.
windows-metadata Construct reader::File values from the slices.

The windows-bindgen textual adapter accepts --in default. The bindgen, RDL, and Clang builders
use explicit default methods instead; their path-style input methods treat "default" as an
ordinary path. Byte-input APIs remain available for custom metadata that is already in memory.

Programs that link one of these build crates include both metadata payloads in the binary. These
crates are intended for build tools rather than runtime dependencies.

Pitfalls

  • WIN32 is one flat Windows.Win32 metadata set. Published windows and windows-sys feature
    namespaces are created later by tool-package.
  • Adding any explicit bindgen input disables the implicit default input. Call input_default when
    the custom metadata references standard types.
  • reference_default and resolution_default are not synonyms in windows-clang. The former can
    suppress already-defined declarations; the latter only classifies WinRT ABI projections.
  • Linking this crate embeds both payloads even if the tool reads only one static.
  • The bytes are build inputs. They do not provide Windows DLLs or make an API available on the
    running operating system.

Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-default.

How it's built

The crate has no dependencies. src/lib.rs exposes each committed .winmd through
include_bytes!.

File Generator Reviewable source
Windows.winmd cargo run -p tool-winrt metadata/winrt
Windows.Win32.winmd cargo run -p tool-win32 metadata/win32 and metadata/wdk

tool-winrt merges the Windows SDK contract metadata, writes canonical RDL, and compiles that RDL
back to Windows.winmd. tool-win32 scrapes the Windows SDK and WDK headers for X64, Arm64, and
X86, merges the architecture-specific RDL, and writes Windows.Win32.winmd.

For missing SDK coverage, add the declaring header to tool-win32's header list and check that its
exports have an import library in the existing list. Regenerate the metadata with tool-win32,
then run tool-package and tool-features to update the published bindings and feature index.
Do not hand-edit the generated RDL or metadata.

See Dependencies for the package versions
and provenance.

Packaging

The repository .gitignore normally excludes .winmd files, with an exception for this crate's
two committed payloads. Cargo packages tracked files by default, so Cargo.toml does not need a
separate include list. cargo package -p windows-default must contain both .winmd files.

Testing

The metadata is exercised through its consumers:

  • test_bindgen, test_rdl, and the windows-clang integration tests cover default and byte
    inputs.
  • tool-winrt, tool-win32, and tool-roundtrip verify deterministic metadata generation.
  • The tool-yml generated workflows build and document windows-default like the other library
    crates.

After changing the payloads or their generators, run the owning generator and confirm that the
other generators remain output-neutral.


Crates Windows Future (`docs/crates/windows-future.md`)

windows-future

Stock async types for Windows APIs.

windows-future provides IAsyncOperation and the related WinRT async interfaces, with
conveniences for producing and consuming them from Rust.

When to use

Use this crate when an API returns one of the four WinRT async interfaces, or when your own WinRT
API must return one:

  • IAsyncAction
  • IAsyncOperation<T>
  • IAsyncActionWithProgress
  • IAsyncOperationWithProgress<T, P>

Use an async runtime's native task type for Rust-only work that does not cross a WinRT boundary.
windows-future adapts WinRT operations to Rust futures; it is not an async executor.

Getting started

The crate README has the dependency declaration and a minimal
ready/spawn example. A typical first workflow is to receive an IAsyncOperation<T> from a
Windows API, choose how the surrounding code should wait for it, and propagate its
windows_core::Result<T>:

rust,ignore
async fn consume(operation: windows_future::IAsyncOperation<i32>) -> windows_core::Result<i32> {
    let value = operation.await?;
    Ok(value + 1)
}

The operation begins according to the WinRT API's rules; converting it into a future does not
create or schedule the operation.

Consuming an operation

Choose one completion style and let it own the operation's completion handler:

Style Use it when
.await The caller is already async and the default std feature is enabled.
join() Synchronous code may block the current thread until completion.
when(callback) Completion should invoke a Send + 'static callback without blocking.

All three paths return or receive a Result, including failures reported by the WinRT operation.
Avoid join() on a UI thread or another thread that must continue pumping work.

Producing an operation

ready wraps a result that is already available. spawn runs a Send + 'static closure on the
Windows thread pool and exposes its result as a WinRT async object. Pick IAsyncAction for ()
and IAsyncOperation<T> for a value. The progress variants provide the corresponding interface
shape, but closures passed to spawn do not report intermediate progress.

The std feature is enabled by default and supplies ready, spawn, and the IntoFuture
implementation used by .await. Without it, the interface types and the join/when consumption
helpers remain available.

Platform constraints and pitfalls

  • The crate targets Windows and WinRT async interfaces.
  • A WinRT completion handler can be assigned only once. These helpers may assign that handler while
    an operation is running, so do not combine them or a direct SetCompleted call on one operation.
  • spawn is for blocking or synchronous work that is safe on a pool thread. It does not make
    thread-affine UI or COM work safe to move off its owning thread.
  • ready and spawn expose Cancel and Close through IAsyncInfo, but these constructors do
    not interrupt the supplied closure.

Sample and next steps

The spawn sample contrasts an immediately ready operation
with work submitted to the Windows thread pool. Next, inspect the API that produces your operation
to determine its result type, error behavior, cancellation support, and any thread affinity.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-future.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/future.txt and
future_impl.txt; the Future bridge is hand-written.

Testing

Run cargo test -p windows-future; see also the workspace test crates.


Crates Windows Implement (`docs/crates/windows-implement.md`)

windows-implement

The procedural macro that exposes a Rust type through COM or WinRT interfaces.

windows-implement provides #[implement]. The macro builds the vtables, identity object,
reference count, QueryInterface routing, and conversions needed to expose a Rust struct through
one or more generated interfaces.

Intended audience

Use this macro when Rust is the implementation side of COM or WinRT: a component, callback,
activation factory, shell extension, test object, or another object passed to an API as an
interface. Calling an existing interface does not require #[implement].

Application and component code normally imports windows_core::implement. The separate
windows-implement dependency is an implementation detail of windows-core's default
proc-macros feature. Its generated code also depends on windows-core, so direct consumption
offers no standalone runtime.

Interfaces usually come from a focused crate or custom
windows-bindgen output. A binary application may instead use the broad
windows projection. Pair this macro with
#[interface] only for a manually owned or missing interface definition.
See windows-core for a compact end-to-end example.

Implementation model

For #[implement(IValue)] struct Value, the interface declaration or generated bindings provide an
IValue_Impl trait. The macro creates a Value_Impl wrapper. Implement the generated trait for
that wrapper, then convert the original Value into an interface type.

For an inherited interface, implement the *_Impl traits required by the interface and its parent
chain. List each independently exposed interface in #[implement(...)]; do not list a base merely
to compensate for a missing parent relationship in the interface definition.

The resulting interface value owns a COM reference. Cloning an interface increments the reference
count, dropping it decrements the count, and cast uses QueryInterface. The original Rust value
is dropped when the final COM reference is released.

Implementation methods use the projected signatures generated for the interface. Return
windows_core::Result<T> when offered by that trait, and return a precise HRESULT when the trait
uses raw status values. The generated thunk translates Result errors at the ABI boundary.

Threading and agility

Implementations are agile by default. The macro exposes IAgileObject and an agile marshaler
unless the attribute includes Agile = false. This is a COM promise, not a convenience flag.

Before accepting the default, verify that:

  • method calls may arrive from the apartments and threads allowed by the interface contract;
  • shared mutable state is synchronized correctly;
  • borrowed thread-affine resources never escape their owning apartment;
  • destruction is valid on the thread that releases the final reference.

Use Agile = false for an apartment-affine object. Add an interface such as IAgileObject
explicitly only when the object implements that contract itself. For WinRT implementations,
TrustLevel = Partial or TrustLevel = Full controls the value reported by
IInspectable::GetTrustLevel; omit it unless the component's contract requires a nondefault trust
level.

Errors, panics, and pointers

Treat every generated implementation method as an FFI boundary:

  • Validate raw pointers before dereferencing them when the ABI permits validation.
  • Initialize required out parameters on every success path and on failure paths required by the
    interface contract.
  • Convert expected failures into Error, Result, or the documented HRESULT.
  • Do not let a panic cross the generated external function. Remove panic paths from boundary code
    or catch them at a layer that can map them to a defined failure.
  • Keep interface references alive for as long as stored raw pointers derived from them are used.

Error::new or Error::from_hresult preserves an HRESULT for projected Result methods. Use the
error code specified by the interface contract; an arbitrary failure code makes native callers
hard to diagnose.

Reference-counted interface fields can form cycles. COM reference counting does not collect them.
Use weak references where the interface model supports them or define an explicit teardown path.

Practical component workflow

  1. Generate or declare the interfaces and inspect their *_Impl traits.
  2. Design a Rust state type with explicit synchronization and apartment assumptions.
  3. Add #[implement] with only the interfaces the object must expose.
  4. Implement every required *_Impl trait on the generated wrapper.
  5. Convert the value to the narrowest interface returned to the caller.
  6. Test cast for each supported and unsupported interface, not just direct method calls.
  7. Test failure HRESULTs, out-parameter initialization, final release, and any cross-thread use
    promised by agility.

For static factories and exported components, the repository's robot component sample shows
StaticComObject, activation factory output, OutRef, and generated implementation traits. The
implementation test crates contain smaller examples for identity, inheritance, generic WinRT
interfaces, agility, and error propagation.

Pitfalls

  • Implementing the Rust trait does not make an incorrect interface declaration ABI-safe.
  • Interior mutability without synchronization can violate the default agile contract.
  • Returning an interface to native code transfers a reference-counted ownership interest; avoid
    temporary raw pointers that outlive their owner.
  • cast success is governed by the interface list and inheritance graph generated into
    QueryInterface.
  • Generic constraints from the annotated struct are carried into the generated wrapper. Keep them
    compatible with every generated *_Impl trait.

When procedural macros are disabled, windows-core::implement_decl! covers the common always-agile
case with a more explicit and narrower declarative syntax. Use its module documentation as the
grammar reference.


Internal documentation

windows-implement is a syn/quote procedural macro crate. src/lib.rs parses the annotated
struct, interface list, generics, Agile, and TrustLevel options. src/gen.rs emits the wrapper,
vtable chains, identity and reference-counting implementation, interface conversions, and
QueryInterface routing.

The crate depends on the *_Impl traits and vtable constructors emitted by
windows-interface or windows-bindgen. Generated paths target ::windows_core; the proc macro
crate is separated only to satisfy Rust's procedural macro packaging model.

Run cargo test -p windows-implement; the test_implement and test_implement_core crates cover
the runtime contract more broadly.


Crates Windows Interface (`docs/crates/windows-interface.md`)

windows-interface

The procedural macro that turns a Rust trait declaration into a COM interface.

windows-interface provides the #[interface] attribute used to declare a COM interface that can
be called or implemented from Rust.

Intended audience

Prefer interfaces from a focused crate or generated with
windows-bindgen. Binary applications may also use interfaces from the broad
windows projection. Declare an interface manually only when the ABI is owned by the
project, the interface is missing from metadata, or a focused interoperability test needs an
independent declaration.

The macro is normally consumed as windows_core::interface, not by depending on this crate
directly. windows-core re-exports it through its default proc-macros feature and supplies every
runtime type referenced by the generated code. The separate crate exists because procedural macros
must be compiled as a proc-macro crate.

See windows-core for the paired declaration and implementation example. Use
the lower-level interface_decl! macro from windows-core only when the procedural macro feature
is disabled and its narrower grammar is sufficient.

Designing a declaration

An interface declaration records an ABI, not an ordinary Rust trait design. Verify these items
before writing it:

  1. Use the interface's exact IID. Reusing or mistyping an IID can make QueryInterface return an
    object with an incompatible vtable.
  2. Name the direct base interface after :. Use IUnknown for a base COM interface.
  3. Preserve method order, including every inherited method represented by the parent.
  4. Match parameter types, pointer constness, return types, and Windows integer widths exactly.
  5. Mark the trait unsafe; callers and implementors must uphold the ABI contract.

The accepted trait form is intentionally restricted. Methods take &self; async methods,
generics, explicit ABI declarations, variadics, default bodies, and non-documentation attributes
are rejected. One parent path describes the vtable inheritance chain.

For COM methods that return only an HRESULT status, Result<()> provides a projected caller and
implementor shape while the vtable entry returns HRESULT. An explicit HRESULT return preserves
the raw status. Do not replace an ABI out parameter with Result<T> by analogy: declare the
parameter shape expected by the macro and ABI. Ref<T> and OutRef<T> are supported for interface
input and output parameters and project to the corresponding Param and OutParam caller forms.

What the macro generates

For an interface named IValue, #[interface] generates:

  • the transparent IValue interface wrapper;
  • an Interface implementation containing the IID and vtable type;
  • Deref to the declared parent interface;
  • caller methods that dispatch through the vtable;
  • the IValue_Vtbl layout;
  • the IValue_Impl trait used by #[implement];
  • conversions and QueryInterface support needed by windows-core.

Caller methods remain unsafe because the declaration cannot prove pointer validity or
API-specific invariants. The wrapper handles COM identity and reference counting through
windows-core; it does not validate a hand-written ABI.

Practical workflow and pitfalls

Keep a manual interface declaration next to a citation or checked-in definition of its ABI. Add a
test that exercises both a known implementation and QueryInterface through the expected IID when
possible.

Common failures are severe:

  • A wrong method order or signature dispatches through the wrong machine-level contract.
  • A wrong parent produces an invalid inherited vtable layout.
  • A correct IID paired with an incorrect layout can corrupt memory.
  • Rust references in an ABI are valid only when their representation and lifetime are part of the
    agreed Rust-to-Rust contract. Public COM ABIs normally use Windows ABI types and pointers.
  • Result<()> translates an error into HRESULT at the boundary; panics are not COM errors and must
    not unwind across an external call.

Prefer generating the declaration from .winmd with windows-bindgen when metadata can describe
the interface. That keeps caller and implementation projections aligned and avoids maintaining a
vtable by hand.


Internal documentation

windows-interface is a syn/quote procedural macro crate. src/lib.rs parses the restricted
trait and GUID. src/generation.rs emits the interface wrapper, vtable, implementation trait,
inheritance traversal, and Ref<T>/OutRef<T> parameter adapters. src/guid.rs parses the IID.

The generated paths are rooted at ::windows_core, which is why direct use still requires a
compatible windows-core dependency. The implementation macro consumes the generated *_Impl
traits and vtable constructors; changes to either macro must preserve that contract.

Run cargo test -p windows-interface; the test_interface and implementation test crates cover
cross-crate behavior.


A macro for declaring Windows DLL imports without an import library.

windows-link provides the link! macro. It emits a raw external function declaration using
Rust's raw-dylib linking mode and a function-pointer type alias with the same name.

Intended audience

This crate is for binding generators and authors of narrow low-level bindings. The generated
windows and windows-sys crates already declare and link the Windows APIs they expose, so
application code should not redeclare those functions.

Use windows-link directly when:

  • an exported Windows function is absent from the available metadata;
  • a project owns a small reviewed raw binding;
  • generated --sys bindings use link! as their runtime link mechanism;
  • the function-pointer type emitted beside the declaration is useful to an abstraction.

Use windows-bindgen when several metadata-backed declarations are needed.
A binary application may use windows or windows-sys when the
published umbrella crates already contain the API. Reusable libraries should prefer focused crates
and narrow generated bindings.

Unlike #[interface] and #[implement], link! is not a procedural macro normally re-exported for
application use. Rich generated bindings invoke it through windows-core; raw generated bindings
depend on windows-link directly. Most users therefore encounter this crate as binding
infrastructure rather than as an application dependency.

Declaration contract

The README shows the macro grammar and its function-pointer alias. Every token in a declaration is
part of the ABI contract:

  • The library literal must name the DLL that exports the symbol on supported Windows versions.
  • The ABI literal must match the export, commonly "system" for Windows APIs.
  • The optional link-name literal maps the Rust identifier to a differently named export.
  • Parameter and return types must match the export exactly, including pointer constness and integer
    width.
  • The declaration is unsafe even when a particular function happens to accept only integers.

On x86 Windows, the macro requests undecorated import names. On other Windows architectures it
uses a verbatim raw-dylib import. Do not add architecture-specific spelling to compensate without
checking the actual exported symbol and Rust's ABI behavior.

The type alias has the same identifier as the function because Rust has separate value and type
namespaces. This permits code such as let callback: GetTickCount = GetTickCount;. The alias is an
unsafe extern function pointer and carries the same caller obligations as the direct declaration.

Practical workflow

Before adding a declaration:

  1. Search the windows and windows-sys API reference for the symbol.
  2. Confirm the DLL, export name, calling convention, and exact signature from an authoritative
    Windows definition.
  3. Decide which Windows versions and architectures the surrounding crate supports.
  4. Put the declaration in a private FFI module.
  5. Add a safe wrapper only if its preconditions, ownership, result convention, and cleanup can be
    expressed correctly.
  6. Test on each supported architecture when decoration, variadics, or unusual ABI types are
    involved.

link! only arranges static import resolution. It is not delayed loading and does not make an
optional export safe to call on older systems. For optional APIs, resolve the symbol dynamically
and keep the module loaded for at least as long as the function pointer can be called.

Pitfalls

  • A successful link does not prove the Rust signature is ABI-compatible.
  • DLL forwarding and API-set names are Windows implementation details; use the library contract
    documented for the API.
  • The macro does not generate feature gates. Add the surrounding cfg and Cargo feature policy in
    the binding crate.
  • A declaration produced on a non-Windows target does not make the Windows export available there.
  • Variadic and uncommon calling conventions have Rust and architecture restrictions. Prefer
    metadata-backed windows-sys output when it supports the declaration.
  • Do not duplicate a symbol declaration with a different signature in the same program.

Internal documentation

The crate is hand-written and no_std. src/lib.rs contains the three link! expansions: x86
Windows, other Windows architectures, and non-Windows parsing support. The Windows expansions use
kind = "raw-dylib" and +verbatim; x86 also sets import_name_type = "undecorated".

windows-bindgen emits this macro for sys-style bindings unless --extern is selected.
windows-core re-exports the macro for rich and minimal generated bindings, while windows-sys
calls it through its direct dependency.

Run cargo test -p windows-link; generated binding tests exercise its wider use.


Crates Windows Metadata (`docs/crates/windows-metadata.md`)

windows-metadata

A low-level reader and writer for the ECMA-335 metadata format.

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.

Applications should start with a focused crate or windows-bindgen; binary applications may use
windows or windows-sys when they want a broad pre-generated
surface. Use windows-metadata directly when writing a metadata inspector, generator, merger, or
namespace transformation. The API exposes metadata facts; it does not project them into a safe Rust
API.

Start with the crate README for dependency setup and a
focused type query.

First workflow: inspect an API definition

  1. Load a file with reader::Index::read, or construct reader::File values and pass them to
    Index::new.
  2. Locate a type with get when duplicates are possible or expect when exactly one definition is
    an invariant.
  3. Inspect category, extends, fields, methods, signatures, attributes, and nested types.
  4. Keep rows borrowed from the index; use leak or read_static only when a tool requires
    process-lifetime rows.

For methods, match ECMA-335 parameter rows with
MethodDef::params_by_sequence(signature.types.len()). Sequence 0 belongs to the return value;
nonzero sequences are one-based signature positions. params() instead returns physical table
order and is appropriate for lossless copying, not semantic parameter association.

tool-reactor-metadata provides a larger example. Its metadata resolver loads WinUI winmd files,
adds windows-default byte inputs, indexes classes and interfaces, then uses method signatures and
attributes to drive generated UI code. tool-features uses Index::iter_items to enumerate
types, free functions, and constants.

Input and output model

reader::File owns one winmd byte stream. reader::Index combines files into searchable
namespace, type, nested-type, and expanded Win32 Apis indexes. A zero architecture selector keeps
all rows; Index::new_for_architecture selects neutral rows plus one of X86 (1), X64 (2), or Arm64
(4).

The writer API is lower-level. writer::File builds ECMA-335 rows and into_stream returns the
finished bytes. Prefer windows-rdl when a reviewable source format is useful;
use the writer directly when a tool is copying or synthesizing table rows.

Common tool tasks

Task API
Read one winmd reader::File::read or reader::Index::read
Read bytes already in memory reader::File::new
Query several files together reader::Index::new
Select one architecture reader::Index::new_for_architecture
Merge ordinary winmds merge().input(...).output(...).merge()
Merge per-architecture winmds merge().arch_input(path, bits)
Union compatible duplicate enums Merger::union_enums
Remap flat namespaces remap().source(...).routes(...).fallback(...)
Author metadata tables writer::File

The merger accepts files or directories. Architecture merge retains one neutral definition only
when the same shape is present on every merged architecture; divergent shapes are tagged and kept.
The remapper rewrites definitions and references together and splits Win32 Apis members across
their routed target namespaces.

Integration and pitfalls

  • windows-default supplies the standard WinRT and Win32 files as byte
    slices. Construct reader::File values from those bytes when a custom tool needs them.
  • windows-rdl provides a higher-level text authoring and decompilation path.
  • windows-bindgen applies Rust projection policy to these raw metadata
    facts.
  • A metadata parameter marked neither In nor Out is ParamDirection::Unspecified. Do not infer
    input direction in the metadata layer.
  • Optional, reserved, retval, and buffer-count annotations are independent facts. Consumers decide
    how those facts affect their public API.
  • Duplicate type definitions can be valid in an unfiltered multi-architecture index. Do not call
    expect unless uniqueness is guaranteed.
  • Preserve physical row order when copying malformed or sparse metadata; use semantic helpers when
    interpreting it.

The focused fixtures in crates/tests/libs/rdl and crates/tests/libs/bindgen show metadata
shapes. The main tool consumers are tool-winrt, tool-win32, tool-package, tool-features,
and tool-reactor-metadata.


Internal documentation

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.

How it's built

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.

Multi-arch merge

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).

reader::Index::new_for_architecture selects one of those copies while retaining neutral rows.
Its bit values match the metadata attribute: 1 for X86, 2 for X64, and 4 for Arm64. Plain
Index::new keeps every copy for tools that need to inspect or merge the complete metadata.

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 Invoke signature, so a fields-only signature would wrongly collapse arch-divergent
    callbacks into one untagged copy;
  • layout - #pragma pack and ClassLayout;
  • AlignmentAttribute - __declspec(align(N)) raised alignment is encoded only by that
    attribute (ClassLayout can 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.

Unmanaged callbacks have one narrow semantic reconciliation before that split. If at least one
architecture explicitly uses isize or usize and every other copy uses the same native-sized
integer for its architecture (i32/u32 on x86, i64/u64 on x64 or arm64), the callback keeps
one native-sized signature. It becomes arch-neutral when present on every merged architecture;
otherwise it keeps its subset arch tag. This handles SDK declarations such as FARPROC, whose
return is spelled INT_PTR on 64-bit but legacy int on x86. A plain i32/i64 pair does not
qualify: the explicit native-sized spelling is required as semantic evidence.

The merge is deterministic: it stages through BTreeMaps and insertion-ordered Vecs, with no
HashMap reaching the output.

The merger and namespace remapper accept strings, Path, or PathBuf for input and output paths
and retain them as PathBuf. The remapper provides singular/plural input/inputs,
source/sources, and route/routes methods.

Nested type identity

Decoded signature TypeName values retain the outermost namespace and a slash-separated name,
such as Test and Outer/Child. TypeDef::full_name, TypeRef::full_name, and
TypeDefOrRef::full_name expose that identity; their name and namespace accessors still return
the raw table columns. TypeDef names follow NestedClass, while TypeRef names follow enclosing
TypeRef scopes.

The writer turns the qualified name into an enclosing-TypeRef chain. Merge preserves that chain,
and namespace remapping routes its outermost type without changing the nested path. A
NestedClass row alone does not resolve an unqualified, module-scoped reference to a nested type
(ECMA-335 II.7.3 and II.22.38).

Nested generic instantiations are outside the writer's supported input. Both signature and
TypeSpec emission assert against them rather than derive a nested name's arity from the total
generic argument count. Top-level generics and non-generic nested records remain supported.

test_metadata::nested_references checks physical scope chains, signature decoding through both
TypeRef and TypeDef tokens, repeated leaf names under different parents, rejection of nested
generic instantiations, merge, and namespace remapping.

Method parameter association

ECMA-335 Param rows are not positional. Param.Sequence == 0 describes the return value, and a
nonzero sequence identifies a one-based signature parameter. Rows may be absent, sparse, or stored
out of order.

reader::MethodDef::params_by_sequence(parameter_count) returns a separate optional return row and
one Option<MethodParam> per signature position. It reports duplicate sequences and nonzero
sequences outside the signature as MethodParamSequenceError; consumers do not choose a
first-wins or last-wins interpretation. If several rows are invalid, the first invalid physical row
is reported. MethodDef::params() still iterates physical table order. The merge and remap writers
use that physical iterator on purpose so lossless copying preserves row order, sparse rows, and
malformed input for a later validator.

MethodParam::direction() decodes only the In and Out bits into ParamDirection::{Input, Output, InputOutput, Unspecified}. is_optional, is_reserved, and is_retval_attribute expose
their independent metadata facts. These helpers do not inspect pointer mutability, infer a default
for an unspecified direction, combine ReservedAttribute with Optional, or decide whether a
parameter should become a language return value. Array and byte-count attributes remain available
through attributes() because each projection validates different public-surface shapes.

Determinism and the winmd writer

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 HasSemantics coded index and the Property/Event/MethodSemantics tables are emitted
    only when non-empty, and MethodSemantics rows are sorted by Association - strict readers
    reject an unsorted table.
  • write_index asserts on the run-list one-past-end sentinel rather than silently wrapping to 0
    for 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::Bool constants round-trip symmetrically (the reader maps ELEMENT_TYPE_BOOLEAN); the CLR
    element-type vocabulary (ELEMENT_TYPE_*) is hand-authored in src/clr.rs because its defining
    header (corhdr.h) ships only in the .NET SDK and cannot be scraped.

Testing

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) and merge.rs (native-sized callback
reconciliation). method_params.rs authors metadata directly with writer::File and covers dense,
absent, return, sparse, out-of-order, duplicate, and out-of-range parameter rows. It also covers all
four raw directions and verifies that optional, reserved, retval, and count attributes remain
independent facts. remap.rs covers explicit and fallback namespace routing, singular/plural
builder methods, missing outputs, and invalid inputs.


Crates Windows Numerics (`docs/crates/windows-numerics.md`)

windows-numerics

Graphics-oriented vector and matrix math from Windows.Foundation.Numerics.

windows-numerics provides the POD math value types used by Direct2D, composition, and other
graphics APIs: Vector2, Vector3, Vector4, Matrix3x2, and Matrix4x4. Each is a plain
#[repr(C)] struct with inherent methods and operator overloads layered on top.

When to use

Use this crate when a Windows graphics or composition API accepts
Windows.Foundation.Numerics values. The structs have the expected Windows ABI layout, so vectors
and matrices can cross those API boundaries without conversion.

For a Rust-only math engine, consider a general-purpose math crate if you need quaternions, planes,
SIMD-specific operations, or a larger set of transforms. windows-numerics focuses on the five
value types projected by this crate.

Getting started

The crate README contains the dependency declaration and
minimal vector and matrix examples. A useful first workflow is to calculate with the same types
that the target Windows API expects:

rust
use windows_numerics::{Matrix3x2, Vector2};

let position = Vector2::new(20.0, 30.0);
let offset = Vector2::new(5.0, -10.0);
let translated = position + offset;
let transform = Matrix3x2::translation(translated.x, translated.y);

assert_eq!((transform.m31, transform.m32), (25.0, 20.0));

Keep values as f32; these Windows numeric types do not provide f64 variants.

Vectors

Vector2, Vector3, and Vector4 provide constructors, zero/one and axis vectors, dot products,
length and distance operations, and normalization. Vector3 also provides a cross product.
Arithmetic operators work with owned or borrowed vectors; multiplication and division between
vectors are component-wise. Scalar multiplication and division use f32.

Use length_squared or distance_squared when only a comparison is needed, avoiding a square
root. Check for a zero-length vector before calling normalize, since normalization divides by
the length.

Matrices and transforms

Matrix3x2 represents 2D affine transforms. It provides identity, translation, scale, rotation,
and skew constructors, including variants around a center point. Rotation and skew angles are in
degrees.

Matrix4x4 provides 3D translation, Y-axis rotation in degrees, and perspective projection.
Matrix multiplication composes transforms; preserve the order expected by the Windows API and test
the result with representative points rather than assuming another math library's convention.

Platform constraints and pitfalls

  • The types are plain #[repr(C)] structs with public fields. Initialize every field when using a
    struct literal, or prefer the constructors.
  • Equality is exact floating-point equality. Use an application-appropriate tolerance for computed
    values.
  • Normalizing a zero vector and constructing invalid projection inputs can produce non-finite
    values.
  • The default std feature supplies square root and trigonometric methods used by length,
    distance, normalization, rotation, and skew. Basic value types and arithmetic remain available
    without it.

Samples and next steps

The canvas, composition, Direct2D, and DirectComposition samples use these types in real graphics
workflows. Start with the
canvas transform sample for a 2D
matrix and the
composition animation sample
for vectors passed to composition APIs.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-numerics.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/numerics.txt; the
inherent methods and operator trait impls are hand-written.

Testing

Run cargo test -p windows-numerics; see also the workspace test crates.


Crates Windows Pickers (`docs/crates/windows-pickers.md`)

windows-pickers

File and folder pickers backed by the Windows Common Item Dialog.

When to use it

Use windows-pickers when a Windows desktop application needs the system file-open, folder, or
file-save dialog. The crate returns filesystem PathBuf values and uses the Windows Shell for
navigation, filtering, recent locations, and overwrite confirmation.

The crate wraps the common picker workflows rather than every IFileDialog extension point. Use
raw Windows bindings when an application needs custom dialog controls, dialog events,
non-filesystem Shell items, or other advanced Common Item Dialog APIs.

The basic idea

Choose a picker, configure it with consuming builder methods, and show or request it:

Type Result
OpenFilePicker One file, or several files through show_multiple
FolderPicker One folder, or several folders through show_multiple
SaveFilePicker A filesystem path at which to save a file

Showing a picker is synchronous. IFileDialog::Show runs a nested modal message loop and returns
after the user confirms or cancels the dialog. The crate does not read, create, or modify the
selected file.

Choose how to host the picker

The default system feature accepts a borrowed windows_window::Window:

toml
[dependencies]
windows-core = "0.100"
windows-pickers = "0.100"
windows-window = "0.100"

Applications that own an HWND through another windowing library can disable default features and
use show_for_hwnd:

toml
windows-pickers = { version = "0.100", default-features = false }

Enable reactor to request a picker from a Reactor component:

toml
windows-pickers = { version = "0.100", default-features = false, features = ["reactor"] }
windows-reactor = "0.100"

The system and reactor features are additive. An application may enable both when it uses both
host types.

Pick your first file

This complete program initializes COM, creates an owner window, and shows an open-file picker:

rust,no_run
use windows_pickers::{OpenFilePicker, Result};
use windows_window::Window;

fn main() -> Result<()> {
    let _apartment = windows_core::init_sta()?;
    let window = Window::new("Open a file").create()?;

    let path = OpenFilePicker::new()
        .title("Open a Rust source file")
        .filter_extensions("Rust source", ["rs"])
        .filter_all()
        .show(&window)?;

    if let Some(path) = path {
        println!("{}", path.display());
    }

    Ok(())
}

The calling UI thread must be a COM single-threaded apartment (STA). An application with an
existing UI framework normally uses the apartment initialized by that framework rather than
initializing COM again. Keep the owner window alive until show returns.

Pick files and folders

OpenFilePicker::show and FolderPicker::show return one selected path:

rust,ignore
let file = OpenFilePicker::new().show(&window)?;
let folder = FolderPicker::new().show(&window)?;

Use show_multiple when the user may select more than one item:

rust,ignore
let files = OpenFilePicker::new()
    .title("Attach files")
    .commit_label("Attach")
    .filter_all()
    .show_multiple(&window)?;

let folders = FolderPicker::new()
    .title("Add search folders")
    .show_multiple(&window)?;

commit_label changes the confirmation button for a task-specific action such as "Attach",
"Import", or "Choose".

Choose a save path

SaveFilePicker configures the proposed file name, extension, filters, and overwrite behavior:

rust,ignore
let path = SaveFilePicker::new()
    .title("Save report")
    .filter_extensions("Text", ["txt"])
    .suggested_name("report")
    .default_extension("txt")
    .overwrite_prompt(true)
    .show(&window)?;

suggested_name sets the initial name. default_extension supplies the extension when the user
enters a name without one; a leading period is optional. Configured filters restrict the accepted
file types. overwrite_prompt explicitly enables or disables replacement confirmation, while
leaving it unset preserves the native dialog default.

The returned path is only the user's choice. The application remains responsible for opening the
file and handling races, permissions, sharing violations, and any final overwrite policy.

Configure file filters

Add extension filters directly to an open or save picker:

rust,ignore
let picker = OpenFilePicker::new()
    .filter_extensions("Images", ["png", "jpg", "jpeg"])
    .filter_extensions("Portable Network Graphics", ["png"])
    .filter_all()
    .initial_filter(0);

Extensions may include a leading period. Use filter_patterns for native wildcard patterns:

rust,ignore
let picker = OpenFilePicker::new()
    .filter_patterns("Reports", ["report-*.csv", "summary-*.csv"])
    .filter_all();

Each filter must have a nonempty name and at least one nonempty extension or pattern.

initial_filter uses a zero-based index into the configured filters. An out-of-range index is
reported as an error before the dialog opens.

Set the initial location

Each picker provides two path-based and two known-folder methods:

Method Behavior
default_folder(path) Used when the Shell has no remembered location
folder(path) Forces the initially displayed folder
default_location(location) Default using a PickerLocation
location(location) Forced location using a PickerLocation

PickerLocation supports Desktop, Documents, Downloads, Music, Pictures, Videos, Computer, and
3D Objects. When both a default and forced location are configured, the default is applied first
and the forced location determines the initial view.

Path-based locations must resolve to Shell items. An invalid or unavailable configured path is
reported before the dialog opens.

Preserve Shell state

Pass a stable GUID to client_id when the Shell should remember state such as the last visited
folder:

rust,ignore
let picker = OpenFilePicker::new()
    .client_id(GUID::from_u128(0x6d81d46c_77da_4874_8ca7_754546ba6803));

Use the same GUID for picker uses that should share history and different GUIDs for independent
histories. Without a client ID, the crate does not opt the dialog into this persistence mechanism.

Handle cancellation and errors

Single-selection pickers return Result<Option>:

Value Meaning
Ok(Some(path)) The user selected a path
Ok(None) The user cancelled
Err(error) Configuration, COM, or Shell operation failed

Multiple-selection methods return Result<Vec>. An empty vector means cancellation
because the dialog cannot successfully confirm an empty selection. Only the native
ERROR_CANCELLED result is translated into cancellation; other failures remain
windows_core::Error values.

Use a raw HWND

The raw-handle methods work with another Win32 host:

rust,ignore
let path = OpenFilePicker::new().show_for_hwnd(hwnd)?;

The pointer must be a valid, non-null owner HWND on the calling UI thread and remain live until the
method returns. The calling thread must be a COM single-threaded apartment (STA). The raw methods
are available without the system feature.

Request a picker from Reactor

Picker methods cannot call IFileDialog::Show inline while Reactor is publishing component state.
The reactor feature adds request and request_multiple, which stage the modal operation and
map its result into a component message:

rust,ignore
enum Message {
    Open,
    Picked(windows_pickers::Result<Option<PathBuf>>),
}

fn update(&mut self, message: Message, context: &ComponentContext<Self>) {
    match message {
        Message::Open => {
            if !OpenFilePicker::new()
                .filter_all()
                .request(context, Message::Picked)
            {
                self.status = "Another window operation is pending".to_string();
            }
        }
        Message::Picked(Ok(Some(path))) => {
            self.status = path.display().to_string();
        }
        Message::Picked(Ok(None)) => {
            self.status = "Selection cancelled".to_string();
        }
        Message::Picked(Err(error)) => {
            self.status = format!("Picker failed: {error}");
        }
    }
}

A true return means Reactor staged the request, not that the picker has already opened. Reactor
runs it on the owning window's UI thread after the current publication commits and queues the
mapped message for a later component update. It discards the operation if publication fails, the
component retires, or the window starts closing.

Each Reactor window accepts one pending native window operation at a time. Other Reactor windows
remain independent. No additional COM initialization is needed in a Reactor application. The
reactor/message-box sample uses the same
ComponentContext::run_window support with another native modal API.

Samples

Sample What to study
standalone All picker types with windows-window
reactor All picker types through component messages

Run an individual standalone workflow with:

text
cargo run -p pickers-standalone -- open
cargo run -p pickers-standalone -- open-multiple
cargo run -p pickers-standalone -- folder
cargo run -p pickers-standalone -- folder-multiple
cargo run -p pickers-standalone -- save

Run the Reactor sample with:

text
cargo run -p pickers-reactor

Internal documentation

This section is for contributors to windows-pickers.

src/bindings.rs is generated by tool-bindings from
crates/tools/bindings/src/pickers.txt. It contains the minimal Common Item Dialog, Shell item,
COM activation, and string-allocation APIs used by the hand-written wrapper.

Open and folder pickers create FileOpenDialog; the save picker creates FileSaveDialog. All
preserve the native option defaults while adding FOS_FORCEFILESYSTEM. Folder selection adds
FOS_PICKFOLDERS, multiple selection adds FOS_ALLOWMULTISELECT, and save dialogs with filters
add FOS_STRICTFILETYPES.

Shared configuration retains UTF-16 backing storage until Show returns. Filesystem locations are
converted with SHCreateItemFromParsingName, known locations with
SHCreateItemInKnownFolder, and selected IShellItem values with SIGDN_FILESYSPATH. Returned
strings are copied into PathBuf values before CoTaskMemFree.

Client identity is applied before folder configuration so a forced folder still determines the
initial view. Requested option bits are merged with GetOptions rather than replacing unrelated
native defaults.

After changing the binding filter, run:

text
cargo run -p tool-bindings --quiet
cargo check -p windows-pickers --quiet

Unit tests cover configuration and cancellation translation without opening modal Shell UI.


Crates Windows Rdl (`docs/crates/windows-rdl.md`)

windows-rdl

A parser for RDL (Rust Definition Language) and an ECMA-335 metadata generator.

windows-rdl is the front of the metadata authoring pipeline. It parses RDL (Rust Definition
Language), a small Rust-like syntax for Windows APIs. It emits ECMA-335 .winmd metadata for
windows-bindgen. It also writes canonical RDL from .winmd files.

Most applications should use existing bindings rather than author metadata. Use windows-rdl when
you are defining a custom WinRT or Win32 surface, adapting header output from
windows-clang, or maintaining reviewable text for a winmd generator.

Start with the crate README for setup and the two basic builder
calls. The full language reference is in
crates/libs/rdl/rdl.md.

First workflow: define a custom component surface

  1. Create an .rdl file with one or more #[winrt] or #[win32] modules.
  2. Add standard metadata as a reference if the declarations mention Windows types.
  3. Compile the RDL to a winmd with reader.
  4. Feed the winmd to windows-bindgen or C++/WinRT.
  5. Treat the RDL as the reviewed source and regenerate the binary winmd.

crates/samples/robot/component follows this path. Its build script compiles src/robot.rdl,
references standard Windows metadata, then asks bindgen for the Robotics namespace and WinRT
implementation support. crates/samples/robot/component_cpp sends the same kind of output to
cppwinrt.

Input and output model

Direction Builder Inputs Output
RDL -> winmd reader() .rdl files, directories, or text One .winmd file
winmd -> RDL writer() .winmd files, directories, or bytes One file or partition

Reader references resolve names but are not emitted as definitions. Use reference_default for
the bundled standard metadata, or the path and byte reference methods for custom dependencies.
Writer inputs are definitions to render; input_default renders the bundled metadata.

Writer filters accept namespace prefixes, qualified or unqualified names, and ! exclusions.
split writes one file per namespace. partition accepts an item-name -> file-stem map and writes
one file per defining header. item_names reads the names declared under one namespace without
compiling the file.

RDL syntax

RDL uses Rust tokens and module syntax, but it describes metadata rather than executable Rust.
A top-level mod is a metadata namespace. Tag it #[winrt] or #[win32] to select the type
system. Items include classes, interfaces, delegates, callbacks, enums, structs, unions, typedefs,
constants, and imported functions.

text
#[win32]
mod Example {
    #[repr(i32)]
    enum Color {
        Red = 1,
        Green = 2,
        Blue = 3,
    }

    struct Point {
        x: i32,
        y: i32,
    }

    const MAX: u32 = 42;

    #[library("example.dll")]
    extern fn GetPoint() -> Point;

    #[guid(0x00000001_0002_0003_0004_000000000005)]
    interface ICustom : Windows::Win32::System::Com::IUnknown {
        fn Method(&self, value: i32) -> i32;
    }
}

Most attributes name a metadata attribute type directly. Some attributes use short pseudo-attribute
names. The reader expands those names to full metadata attributes. See PSEUDO_ATTRS in
windows-rdl.

Struct bit fields use their own syntax. A run of bit fields packed into one backing integer is
written as a C-like block on that field. Each member uses Name: width. Anonymous padding uses
_: width.

text
struct D3D11_VIDEO_PROCESSOR_COLOR_SPACE {
    _bitfield: u32 {
        Usage: 1,
        RGB_Range: 1,
        YCbCr_Matrix: 1,
        YCbCr_xvYCC: 1,
        Nominal_Range: 2,
        Reserved: 26,
    },
}

Member offsets are implicit. Each offset is the total width of earlier members, including padding.
The reader writes one Windows.Win32.Metadata.NativeBitfieldAttribute(name, offset, width) custom
attribute per named member. The writer renders it back to block form.

See windows-clang for how the scraper emits bit
fields. See windows-bindgen for the accessors they
drive.

WinRT types use the #[winrt] namespace flavor. They also add runtime-class and property syntax.

text
#[winrt]
mod Robotics {
    #[Activatable(1)]
    class Robot {
        IRobot,
    }

    #[ExclusiveTo(Robot)]
    interface IRobot {
        fn Speak(&self, message: String);
        Name: String;
    }
}

The crates/tests/libs/rdl/input directory has focused .rdl files for syntax examples. It covers
structs, flags, delegates, generic interfaces, unions, and more.

Common tasks and neighboring crates

The complete source-to-binding path is:

text
C/C++ headers -- clang() --> .rdl -- reader() --> .winmd -- bindgen() --> bindings.rs
 (windows-clang)             (windows-rdl)        (windows-bindgen)
  • Skip RDL when a suitable winmd already exists.
  • Use windows-clang to create RDL from headers; tool-webview demonstrates the full path.
  • Use windows-metadata for table-level inspection, merge, and namespace remapping.
  • Use writer().split() to maintain namespace-partitioned reviewable metadata.
  • Use merge_arch_rdl only for generators that have per-architecture RDL directories and winmds.
    It merges structural differences and restores the defining-header partition.
  • tool-reactor-metadata compiles hand-authored extras.rdl to fill metadata gaps before binding
    WinUI.

Pitfalls

  • RDL references must be supplied separately; naming an external type does not locate its winmd.
  • The reader rejects unsupported types, constants, callback ABIs, variadic callback parameters,
    and function ABIs instead of dropping them.
  • Pointer chains have one constness bit plus depth. Uniform *mut *mut T and
    *const *const T chains work; mixed chains do not.
  • len_param and size_param store raw zero-based signature positions. Update the attribute when
    parameters move.
  • RDL cannot spell a metadata parameter with neither In nor Out. Omitting direction invokes the
    type-based default.
  • Attributes on a void return row cannot round-trip because there is no return type to carry them.
  • split and partition clear existing .rdl files in the output directory before writing.

Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-rdl.

How it's built

The RDL grammar uses syn, quote, and proc-macro2. It reuses Rust's tokenizer so the syntax
stays Rust-shaped. The reader lowers the syntax tree to ECMA-335 and emits .winmd through
windows-metadata. The writer reads metadata through the same crate and
writes canonical RDL.

The clang path uses clang-sys to parse C or C++ translation units. It projects the declarations
into the RDL syntax tree. The header path and hand-authored RDL path share the same lowering code.
The formatter module pretty-prints generated RDL.

Testing

Dedicated test crates cover the crate:

  • test_rdl covers RDL to winmd round trips with input/*.rdl fixtures and invalid input with
    errors/input/*.rdl and errors/expected/*.txt diagnostic fixtures.
  • The windows-clang integration tests cover header-to-RDL output.
  • tool-roundtrip re-derives committed RDL files from committed winmd files. The gen workflow
    enforces a clean git diff.
  • test_bindgen covers the .winmd to Rust step that consumes this crate's output.

Run targeted tests with:

sh
cargo test -p test_rdl
cargo test -p windows-clang

Default metadata files

windows-rdl builds the default metadata files used by the in-repo generators and library crates.

File Source Writer
crates/libs/default/Windows.winmd Merged SDK contract winmds tool-winrt
crates/libs/default/Windows.Win32.winmd Scraped SDK and WDK headers tool-win32

The committed RDL files are the reviewable source for these metadata files:

  • metadata/winrt/*.rdl is partitioned by namespace.
  • metadata/win32/*.rdl is partitioned by defining header.
  • metadata/wdk/*.rdl is partitioned by defining header.

The binary winmd files are derived artifacts. Generation is deterministic. The metadata writer
stages tables in BTreeMaps and uses a fixed zero GUID for the module MVID.

Every maintained crate that needs Win32 metadata resolves against the in-repo Windows.Win32.winmd.
Minimal-binding crates and windows-reactor use it directly. The windows and windows-sys crates
use it through tool-package.

Multi-arch merge

tool-win32 scrapes x64, arm64, and x86 into separate RDL sets. Then merge_arch_rdl combines them
into one winmd. A type with the same shape on every architecture is emitted once. A type that
differs by architecture is split into per-architecture copies tagged #[arch(X86|X64|Arm64)].

The merge compares type structure through windows-metadata.
merge_arch_rdl handles orchestration. It reads each architecture's RDL, runs the merge, and writes
the combined output. ArchInput stores its RDL directory and winmd as PathBuf.

Published crates and namespace remap

The in-repo Win32 and WDK metadata lives in flat namespaces. Published windows and windows-sys
APIs are partitioned behind many Cargo features.

tool-package remaps the flat metadata into header-stem namespaces under target/package/. It uses
the committed metadata/win32 RDL directory as the routing signal. Then it runs windows-bindgen
over that partition. tool-features uses the same remap so feature search reports the same header
stems.

The in-repo WinRT Windows.winmd is projected with the remapped Win32 and WDK metadata.

Round-trip rules

RDL is the reviewable source for WinRT, Win32, and WDK metadata. The .winmd files are derived
artifacts. The gen workflow runs the generators and tool-roundtrip. It fails when regeneration
changes tracked files.

Family External source RDL layout Winmd build path
WinRT SDK winmds metadata/winrt, per namespace merge -> RDL -> winmd
Win32 SDK headers metadata/win32, per header scrape -> arch merge -> winmd
WDK WDK headers metadata/wdk, per header scrape -> arch merge -> winmd

tool-roundtrip validates the reverse direction:

  • WinRT uses writer(Windows.winmd).split() to write metadata/winrt.
  • Win32 and WDK cannot recover header files from flat winmd alone. The tool reads the committed RDL
    layout to map type names back to header stems. Then it writes metadata/win32 or metadata/wdk
    with writer(winmd).partition(map).

Current normalization rules

Some metadata forms have canonical RDL spellings. These rules are intentional. They keep generated
RDL stable.

Form RDL spelling Reason
WinRT System.Char Char16 Keeps the type distinct from u16.
Property setter parameter value Shorthand does not retain its original name.
Event add parameter handler Shorthand stores the event shape.
Event remove parameter names token The remove accessor takes an event token.
Property and event accessors Shorthand The writer tracks consumed accessor methods.
Other interface methods Full method form Non-accessor methods stay explicit.
Input direction #[in] The reader accepts #[r#in]; the formatter emits #[in].
Raw identifiers, GUIDs, delegate ABIs Canonical output Equivalent text has one spelling.

The reader rejects unsupported forms with errors. It does not silently drop them. Examples include
unsupported types, constants, callback ABIs, variadic callback parameters, and function ABIs.

Inline nested records

Inline structs and unions have nested TypeDefs linked by NestedClass rows. Their field
signatures reference those definitions through enclosing-TypeRef chains, including every level
of nesting. The writer matches the qualified identity to the containing record and renders the
same inline syntax. It also accepts the unscoped leaf references in existing metadata.

test_rdl::nested_references checks physical TypeRef scopes before and after the text round trip,
alongside the canonical struct_nested_anon.rdl fixture. test_metadata::nested_references
independently covers the underlying signature reader, merge, and namespace remapper.

Method parameter rows

The winmd writer uses MethodDef::params_by_sequence from
windows-metadata. Sequence 0 supplies return-type attributes. Nonzero rows
are matched to one-based signature positions, so sparse or out-of-order metadata cannot rename or
reflag another parameter. Every signature parameter is emitted; a missing row uses pN with no
explicit direction or optional marker, which the RDL reader lowers with its existing type-based
direction default. Duplicate and out-of-range sequences stop the write with a diagnostic.

Direction and optionality come from MethodParam::direction() and is_optional(). Reserved,
retval, and count attributes remain independent pseudos or custom attributes. The writer applies
RDL's format boundary only when spelling them: In+Out emits both markers, while Unspecified emits
the input spelling because RDL has no literal unspecified-direction syntax.

test_rdl::method_params authors sparse and out-of-order rows directly, checks parameter and
return pseudos in the generated RDL, and compiles the RDL back to dense metadata with the same
associations. It also checks that every non-return Param row in the committed WinRT, Win32, and
WDK metadata has at least one representable direction flag.

Lossless round-trip limits

  • Direction flags: RDL has no spelling for a Param row with neither In nor Out. Omitting both
    invokes the type-based default, so a metadata row with neither flag reads back as In.
  • Void return rows: Return attributes are written after ->. A void method has no return type
    to carry them, so attributes on its sequence 0 row are not represented.
  • Count relationships: #[len_param(N)] and #[size_param(N)] store raw zero-based signature
    positions. Reordering parameters without updating N changes the relationship.
  • Pointer constness: metadata::Type stores one constness bit with a pointer depth. Uniform
    chains such as *mut *mut T and *const *const T round-trip. Mixed chains are rejected before
    metadata is written, including chains nested inside a reference.

Crates Windows Reactor Setup (`docs/crates/windows-reactor-setup.md`)

windows-reactor-setup

Build-time staging for self-contained Windows Reactor applications.

When to use it

Use windows-reactor-setup only when a
windows-reactor executable must carry a private Windows App SDK runtime
beside the executable. It is a build dependency called from build.rs; it is not a runtime API and
does not belong in [dependencies].

Do not use it for a framework-dependent Reactor application. windows-reactor already contains
the framework bootstrap that resolves an installed Windows App SDK framework package at startup.
That deployment model stages no private runtime files.

Do not use it for a plain windows-webview HWND host. The WebView2 Evergreen runtime supplies
that COM-only path. Reactor's XAML WebView2 control has an additional projection DLL requirement,
which this setup helper handles for self-contained Reactor apps.

Prerequisites

  • The Cargo target OS must be Windows.
  • The build needs network access the first time each pinned NuGet package is staged.
  • %SystemRoot%\System32\curl.exe and tar.exe must be available.
  • The target must use MSVC, or the LLVM-based GNU ABI supported by the manifest linker arguments.
  • Supported target architecture mappings are x86 -> x86, aarch64 -> arm64, and other
    Rust target architectures -> x64.

The README contains the build-dependency declaration and one-line build.rs.

First workflow: produce a self-contained build

  1. Add windows-reactor-setup under [build-dependencies].
  2. Create build.rs and call windows_reactor_setup::as_self_contained().
  3. Build the application normally with Cargo.
  4. Run the executable from its Cargo profile output directory to confirm the staged runtime is
    used.
  5. Package the executable together with all staged DLLs and runtime directories from that output
    directory. Preserve their relative layout.
  6. Test the packaged directory on a machine that does not provide the framework package expected
    by a framework-dependent build.

The reactor/self_contained sample is the reference
project layout. The webview/reactor sample shows the same
setup for a Reactor app containing the XAML WebView2 control.

What the build step does

as_self_contained performs these operations during the application build:

  1. Resolve Cargo's profile output directory from OUT_DIR and PROFILE.
  2. Download and cache the pinned Microsoft.WindowsAppSDK.Runtime NuGet package.
  3. Extract the MSIX for the target architecture.
  4. Copy the allow-listed Windows App Runtime files to the profile output directory.
  5. Download and cache the pinned Microsoft.Web.WebView2 NuGet package.
  6. Copy the target architecture's native_uap/Microsoft.Web.WebView2.Core.dll beside the
    executable.
  7. Write an application manifest containing the self-contained deployment marker.
  8. Pass linker arguments that embed the manifest in binary targets.

Downloads and extracted packages are cached under
%LOCALAPPDATA%\windows-reactor-setup\temp\v1 when LOCALAPPDATA is available. Cargo may rerun the
build script, but the cached package and extraction directories avoid downloading on every build.
The versioned cache does not reuse unchecked entries in the parent temp directory. Its first
use requires a fresh download. MSIX extraction caches are separate for each target architecture.

Deployment and shared target directories

The Cargo profile directory can contain outputs from several packages. Stage the application from
a clean, known build profile and copy every runtime file and subdirectory required beside the
executable. Copying only the .exe does not produce a self-contained deployment.

A destination-owned file lock serializes runtime and WebView2 copying by build scripts sharing
the same Cargo profile directory. Cache locks alone do not protect this shared output.

The embedded manifest includes a windows-reactor-self-contained description marker. Reactor reads
that marker to select the private runtime. A framework-dependent executable ignores private files
left by a self-contained build in the same Cargo target directory and uses its inlined framework
bootstrap instead.

Microsoft.Web.WebView2.Core.dll is always staged. This allows a self-contained app to add
windows-webview's reactor feature without another deployment step. It is the WinRT projection
assembly used by the XAML control, not the webview2loader.dll used by COM-only hosting.

Failures and cleanup

as_self_contained has no Result return. Unsupported target configuration, missing Cargo
environment variables, and staging errors panic during the build. Download, extraction, directory
access, and copy errors stop staging before the helper emits manifest linker arguments. Diagnostics
include the failing path or command.

Cache entries are published only after extraction succeeds and required files are present.
Failed or interrupted staging leaves an unpublished .partial directory, which the next attempt
discards and rebuilds. A failed copy can leave some files in the Cargo output directory, but the
build fails and the next attempt copies the runtime again.

The first build therefore requires reliable NuGet access. In offline build environments, populate
the helper's package cache before disconnecting or use a build environment with the required cache.
Do not delete the cache while another build is using it.

Staged files live in the Cargo profile output directory, outside the package's OUT_DIR.
cargo clean removes the target output but not the package cache under LOCALAPPDATA. Remove that
specific cache directory manually only when forcing a fresh package download.

The helper copies fixed package versions. Update the crate rather than replacing staged files by
hand so the manifest, runtime allow-list, and WebView2 projection stay compatible.


Internal documentation

This section is for contributors to windows-reactor-setup.

as_self_contained validates CARGO_CFG_TARGET_OS, derives the target directory from the nearest
ancestor matching PROFILE, and falls back to the conventional ancestor depth. The unit tests
cover standard and split-package OUT_DIR layouts.

stage_pkg downloads a .nupkg inside a staging directory, extracts it, verifies the requested
architecture's MSIX or WebView2 DLL, and removes the archive before publishing the directory.
Runtime MSIX extraction uses .msix_extract-<arch> and checks for the core runtime and XAML DLLs
before publication.

cached_dir holds a file lock for each cache entry while checking, populating, and publishing it.
Extraction happens in a sibling .partial directory, followed by a rename to the final name.
The lock is released when its file handle is dropped, including after a failed or terminated build.
Concurrent builds share the completed entry rather than modifying the same staging directory.

copy_runtime_to copies only top-level entries named in assets/runtime.txt, then recursively
preserves any selected directory. Keep that allow-list in sync with the pinned Windows App SDK
runtime. Filesystem tests cover architecture isolation, failed extraction and retry, required-file
checks, concurrent cache publication, concurrent deployment processes, and copy failures.
Archive tests use local ZIP fixtures and the system tar.exe; they do not download packages or
launch WinUI.

The application manifest template is assets/app.manifest. The function inserts the deployment
marker after the opening assembly element, writes the result to OUT_DIR, and emits binary-only
manifest linker arguments for MSVC or LLVM GNU targets.

deploy_webview2 copies Microsoft.Web.WebView2.Core.dll from the pinned WebView2 package's
per-architecture native_uap directory. Keep its package version compatible with the WinRT
metadata and XAML WebView2 bridge used by windows-webview and Reactor.


Crates Windows Reactor (`docs/crates/windows-reactor.md`)

windows-reactor

A declarative WinUI 3 library built around components, typed messages, and native controls.

Windows Reactor lets Rust state drive native WinUI 3 controls. Start with a counter, then add
editable state, child components, and background work. Later sections cover common application
tasks and link to complete samples.

Reactor is currently a preview, so releases may still refine public APIs.

Create a project

Create a binary crate and add Reactor:

text
cargo new counter
cd counter
cargo add [email protected]

Use the default features. An installed Windows App SDK runtime is required for the default
deployment model; see the setup guide for prerequisites.

For a GUI executable that should not open a console window, add this to the top of main.rs:

rust,ignore
#![windows_subsystem = "windows"]

Leave it out while diagnosing startup problems so console output remains visible.

Add a component

Replace main.rs with:

rust,no_run
use windows_reactor::*;

#[derive(Clone, Copy)]
enum Message {
    Increment,
    Reset,
}

struct Counter {
    count: i32,
}

impl Component for Counter {
    type Input = ();
    type Message = Message;

    fn create(_input: &(), _context: &ComponentContext<Self>) -> Self {
        Self { count: 0 }
    }

    fn update(&mut self, message: Message, _context: &ComponentContext<Self>) {
        match message {
            Message::Increment => self.count += 1,
            Message::Reset => self.count = 0,
        }
    }

    fn view(&self, _input: &(), context: &mut ViewContext<Self>) -> View {
        let content = StackPanel::new().spacing(8.0).children((
            format!("Count: {}", self.count),
            Button::new()
                .on_click(context.message(Message::Increment))
                .content("Increment"),
            Button::new()
                .on_click(context.message(Message::Reset))
                .content("Reset"),
        ));

        context.window_frame("Counter", content)
    }
}

fn main() {
    App::run_component::<Counter>(()).unwrap();
}

Run it:

text
cargo run

The component loop is:

text
native event -> typed message -> update state -> describe the next view
Method Purpose
create Build the initial state
update Handle a message and change the state
view Describe the controls for the current state

view uses typed builders. StackPanel::children accepts a tuple of different control types, and
strings convert directly to text views. The button callbacks enqueue messages for update; they
do not mutate state inside the native event callback.

context.window_frame publishes the window title and returns the standard title-bar layout. For a
static view with no state or events, call App::run(view) instead.

Add editable state

To drive an editable control from component state, declare its current value and send changes
back as messages.

rust,ignore
TextBox::new().text(self.name.clone())
    .placeholder_text("Type your name")
    .on_text_changed(context.callback(Message::SetName))

Handle Message::SetName(name) in update by assigning self.name = name. Reactor suppresses
feedback from its own writes while still delivering user edits.

Only declared properties are controlled; subscribing to changes does not take ownership. Omitting
a previously declared property clears its native assignment once. An explicit None is a declared
value, not an omitted property. Store values in component state when they must survive control
replacement.

The controlled sample shows the complete pattern.

Split up the view

Use a function returning View for a stateless piece. Use a child component when a subtree needs
its own state or lifecycle:

rust,ignore
View::component::<Greeting>(GreetingInput {
    name: self.name.clone(),
})

Component input must implement Clone + PartialEq.

Children at fixed positions keep identity by position and type. Items that can move need stable
application keys:

rust,ignore
StackPanel::new().keyed_children(
    self.tasks
        .iter()
        .map(|task| (task.id, task.title.clone())),
)

Do not use list indices as keys when items can move. Stable keys keep state and focus attached to
the correct item.

Do work without blocking the window

Rendering and component updates run on the UI thread. Use set_timeout for delayed messages and
spawn_background for blocking or CPU-intensive work. Background work returns a message rather
than touching controls or component state directly.

Use an effect when an external subscription or resource should exist only while part of the view
is present. See the use-effect and
async-state samples for complete lifecycle patterns.

Application state and notification icons

Use App::run_application::<A>(input) for application state that must survive closing individual
windows. Implement Application with create, update, and view, using typed messages and
callbacks as with Component. An ApplicationView declares windows and notification icons
without requiring a hidden application window.

Declaration or command Behavior
view.window::<C>("main", input) Registers a reopenable window without opening it.
context.show_window("main") Opens, restores, or activates that window; repeated requests coalesce.
Updated window input Reaches the existing component without recreating its state.
User closes a window Drops its component state; the declaration remains available to reopen.
Omit a window key Closes its instance and removes its declaration.
view.notify_icon("tray", icon) Creates or updates the keyed Shell registration.
Omit an icon key Removes its registration and dismisses its menu.
context.exit() Ends the application and releases all owned resources.

Keys must be unique within each resource kind. A live window key must retain its component type.
Only keys declared in the application's view can be opened.

Construct a notification declaration with NotifyIcon::new(path), then attach tooltip,
on_activate, and menu. The icon path names an .ico file. Activation callbacks receive a
ScreenPoint; attaching a Menu handles both mouse and keyboard context-menu positioning without
application-side coordinate conversion.

Windows and notification icons have independent lifetimes. The application keeps running while
either remains; registered but closed window slots do not keep it alive.

The notification icon sample demonstrates independent
window and icon lifetimes with typed inputs and callbacks.
The basic sample contains only a notification icon
and an Exit menu.

Notification menus follow the Windows taskbar theme. For a menu independent of a notification icon
or control, AppContext::show_menu_at accepts a Menu and physical screen coordinates.

Position and restore windows

Publish startup placement through context.window_visuals. The first committed declaration is
applied before the native window becomes visible, for both primary and secondary windows.

Setting Meaning
initial_position(ScreenPoint) Initial outer-window top-left in physical screen pixels
initial_placement(WindowPlacement) Restored outer bounds in physical screen pixels, plus maximized state
client_size(width, height) Client-area size in DIPs; later explicit changes still resize
constraints(WindowConstraints) Client-size limits in DIPs, including when restoring placement

Full placement takes precedence over initial position and initial client size. Later changes to
either initial setting do not move or resize an open window. Load saved placement before opening
the window; an asynchronous update after startup is too late. Negative coordinates are valid on a
multi-monitor desktop. Windows adjusts fully off-screen bounds to an available monitor.

rust,ignore
context.window_visuals(WindowVisuals::new().initial_placement(saved_placement));
context.on_window_placement(context.callback(Message::Placement));

on_window_placement reports the initial placement and native changes while subscribed.
Notifications contain the restored outer bounds even when maximized and exclude minimized state.
Applications own persistence: save observations in the message handler rather than waiting for a
final shutdown notification. Keeping a saved value in initial_placement does not move the window
back after a user move.

on_color_scheme also reports the current value when subscribed; on_window_size reports changes
only. Replacing an observer callback does not replay the current value. Observers end when omitted
from the view or when their component closes.

Use WindowVisuals::theme to choose System, Light, or Dark. With no backdrop, transparent content
shows the window's themed background. Use WindowVisuals::backdrop to select Mica, MicaAlt, or
Acrylic; an opaque application background covers the material.

Sample Shows
window-placement Open a window at an observed placement
window-placement-registry Save placement across process restarts
window-observers Subscribe to size, placement, and color-scheme changes
window-theme Themes, backdrops, and application backgrounds

Display images

Image and ImageIcon support these source builders:

Builder Source
source(uri) A URI; returns a Result
source_file(path) A file path; returns a Result
source_data(bytes) Encoded bitmap bytes
source_svg(bytes) SVG bytes

Use EncodedImage::from_static for embedded assets or EncodedImage::new for owned bytes.
URI and file sources select SVG by the .svg extension. In-memory sources choose their decoder
through the builder:

rust,no_run
# use windows_reactor::*;
let svg = EncodedImage::from_static(
    br#"<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24">
    <circle cx="12" cy="12" r="10" fill="red"/></svg>"#,
);
StackPanel::new().children((
    Image::new().source_svg(svg.clone()),
    AppBarButton::new().icon(Icon::image_svg(svg)),
));

Keep owned bytes in component state and clone the value when describing the view. Equal sources
share native images within a runtime. Image::on_opened and on_failed report loading outcomes
for each assignment, including when a shared source has already loaded.

The image-sources sample compares the source
builders across images and icon slots. image-reuse
shows page replacement with shared sources.

Animate a visual

Border pairs opacity, scale, and translation(x, y) with matching
opacity_transition, scale_transition, and translation_transition durations. Translation uses
DIPs and changes visual position without changing layout space.

New or replaced visuals start at their declared values without animating from native defaults.
Later transition settings apply before target changes in the same render, regardless of builder order.
Removing a target restores its default using the current transition. Removing a transition affects
future target changes; an animation already running can finish. Native transitions respect the
Windows animation setting. See the
translation sample for sliding and reversal.

Add icon content

Use Icon to supply a control's icon content:

rust,no_run
# use windows_reactor::*;
StackPanel::new().children((
    TitleBar::new()
        .title("Search")
        .icon(Symbol::Home)
        .left_header(TextBlock::new().text("Files")),
    AutoSuggestBox::new().query_icon(Icon::font("\u{E721}")),
));

Use Icon::symbol, Icon::font, Icon::bitmap, Icon::image_*, or Icon::path for control icon
slots. Use SymbolIcon, FontIcon, BitmapIcon, ImageIcon, and PathIcon when the icon is a
standalone visual that needs layout or other visual properties.

Choose a deployment model

A framework-dependent application uses an installed Windows App SDK framework package and needs
only windows-reactor.

For a self-contained application, add:

toml
[build-dependencies]
windows-reactor-setup = "0.100"

Then create build.rs:

rust,ignore
fn main() {
    windows_reactor_setup::as_self_contained();
}

The setup guide covers prerequisites, WebView2, and packaging.

Explore larger samples

Sample What it demonstrates
calculator State, messages, and Grid layout
solitaire A larger application with keyed state
stacker Reactor, Canvas, and Composition together
gallery Available controls and navigation

Browse the sample directory for smaller examples of controlled
input, context, effects, multiple windows, virtualization, WebView2, and deployment.
The navigation sample demonstrates pane
content and visibility settings.

Contributing

Controls are generated from crates/tools/reactor/src/schema.toml and the committed WinUI metadata.
Edit the schema or generator, not the generated files. tool-reactor-metadata refreshes the
metadata; tool-reactor generates declarations, native adapters, bindings, and control coverage.
The native binding filter is crates/tools/reactor/src/bindings.txt.

Use metadata names unless a shared Reactor name matches the public value, role, and behavior.
Container names distinguish one owned visual (content), ordered visuals (children), and item
declarations (items).

After a generator or schema change, regenerate and inspect the output:

text
cargo run -p tool-reactor --quiet
cargo check -p windows-reactor --quiet
cargo test -p windows-reactor --all-features --lib

Changes to the public API also require updating public-api.txt.


Crates Windows Reference (`docs/crates/windows-reference.md`)

windows-reference

A stock implementation of the WinRT IReference<T> interface.

When to use this crate

Use windows-reference when a WinRT signature explicitly requires IReference<T> and you need to
box a Rust value for that call or component boundary. WinRT uses this interface for boxed value
types, similar to Nullable<T> in other projections.

Do not use it for ordinary Rust borrowing. &T, Box<T>, and Option<T> express Rust ownership
and optionality without creating a WinRT object. Add this crate only at an interop boundary that
names IReference<T>.

Getting started

The crate README has the dependency declaration and
minimal boxing example. The public implementation is available on Windows with the default std
feature. The value type must implement RuntimeType + Clone + 'static.

The first workflow is to convert a value with IReference::<T>::from, pass the resulting projected
interface to the WinRT API, and call Value when reading one received from an API:

rust
use windows_reference::IReference;

fn round_trip() -> i32 {
    let boxed = IReference::<i32>::from(42);
    boxed.Value().unwrap()
}

In application code, propagate the Result from Value instead of unwrapping when the interface
call can fail.

Core API model

IReference<T> is a reference-counted WinRT object. Creating one clones or moves the Rust value
into a stock implementation, and each call to Value returns a clone. Cloning the
IReference<T> itself clones the interface reference, not the boxed value.

The object implements the WinRT identity and runtime type information for
Windows.Foundation.IReference<T>. It also supports IPropertyValue for scalar value types, but
the public Rust wrapper exposes Value as the supported way to retrieve the boxed value.

Common tasks

Box scalar and string values

rust
use windows_reference::IReference;
use windows_strings::HSTRING;

let count = IReference::<u32>::from(3);
assert_eq!(count.Value().unwrap(), 3);

let label = IReference::<HSTRING>::from("ready");
assert_eq!(label.Value().unwrap(), "ready");

IReference<HSTRING> accepts HSTRING, &HSTRING, String, and &str. Other supported value
types use their normal From<T> conversion. Add windows-strings directly when the surrounding
crate does not already provide its string types.

Use a boxed value in a component

Keep IReference<T> in the component signature when the metadata declares a boxed value. Accept
the interface by the projected parameter form, call Value once, and continue with the plain Rust
value. When returning one, construct it at the return boundary:

rust
use windows_reference::IReference;

fn boxed_count(value: u32) -> IReference<u32> {
    IReference::from(value)
}

This keeps WinRT reference counting at the boundary and avoids repeated ABI calls in the rest of
the implementation.

Important choices and pitfalls

  • IReference<T> boxes a present value. Optionality is represented by whether the interface
    itself is present in the surrounding projected signature.
  • Value is fallible because it is a WinRT interface call, even for an object created locally.
  • The value is cloned into and out of the stock object. Avoid using it as a high-frequency wrapper
    for large values when a different API shape is under your control.
  • The generic type determines the interface IID and must match the receiving signature exactly.
    IReference and IReference are different WinRT interface types.
  • Array accessors on the object's internal IPropertyValue implementation are not supported.
    Use the typed Value method.

Samples and next steps

The reference-reference sample boxes scalar and
string values and reads them through IReference<T>. For related interop patterns, see
windows-collections for collection interfaces and the
robot sample for a component boundary implemented in Rust.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-reference.

How it's built

src/bindings.rs is generated; the IReference<T> implementation is hand-written.

Testing

Run cargo test -p windows-reference; see also the workspace test crates.


Crates Windows Registry (`docs/crates/windows-registry.md`)

windows-registry

Simple, safe, and efficient access to the Windows registry.

windows-registry wraps the Win32 registry APIs behind a small, safe surface. Start from one of the
predefined roots, then open a key and work with typed values.

When to use

Use this crate when a Windows application must read or write registry configuration and wants owned
keys, typed values, iteration, transactions, or explicit registry-view selection. Use a portable
configuration format when settings must work on non-Windows systems or travel with the application.

Getting started

The crate README has the dependency declaration and minimal
read/write and transaction examples. For a first workflow, read application settings under
CURRENT_USER and propagate missing-key, access, type, and data errors to the layer that can decide
how to handle them:

rust,no_run
use windows_registry::{CURRENT_USER, Result};

fn read_launch_count() -> Result<u32> {
    let key = CURRENT_USER.open(r"Software\Contoso\Widget")?;
    key.get_u32("LaunchCount")
}

Create keys during installation or first-run setup, and remove test data when finished.

Keys and access

The predefined roots include CLASSES_ROOT, CURRENT_CONFIG, CURRENT_USER, LOCAL_MACHINE, and
USERS. open requests read access. create opens or creates a key with read and write access.
Both return an owned Key that closes its handle on drop.

Use options() when the defaults are not appropriate:

rust,no_run
use windows_registry::{CURRENT_USER, Result};

fn open_for_update() -> Result<windows_registry::Key> {
    CURRENT_USER
        .options()
        .read()
        .write()
        .wow64_64()
        .open(r"Software\Contoso\Widget")
}

The builder can add access rights, create a volatile key, or associate the operation with a
Transaction.

Reading, writing, and enumerating

Typed helpers cover u32, u64, strings, expandable strings, multi-strings, and binary data.
get_value and set_value preserve a value's registry Type when the typed helpers are not
enough. keys() enumerates child names and values() enumerates names and values.

Registry operations are not atomic as a group. To update several keys together, create a
Transaction, open or create each key through options associated with it, and call commit.
Dropping an uncommitted transaction rolls it back.

Platform constraints and pitfalls

  • The crate is Windows-only. Access to protected locations such as parts of LOCAL_MACHINE
    depends on the process token and may require elevation.
  • A 32-bit process and a 64-bit process can see different redirected views. Select wow64_32() or
    wow64_64() when both processes must address a known view. They are mutually exclusive; the last
    call wins.
  • Expandable strings are returned as stored. Reading one does not expand environment variables.
  • Value names may be empty for a key's default value.
  • remove_tree is recursive. Scope the parent and path carefully before deleting.
  • Kernel Transaction Manager support and policy can vary by Windows environment. Surface
    transaction creation and commit failures to the caller.

Samples and next steps

The read-write sample creates, reads, and removes a
test key. The transaction sample performs the write
through a transaction.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-registry.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/registry.txt;
Key, Value, OpenOptions, and Transaction are hand-written safe wrappers.

Testing

Run cargo test -p windows-registry; see also the workspace test crates.


Crates Windows Result (`docs/crates/windows-result.md`)

windows-result

Efficient Windows error handling and propagation for Win32, COM, and WinRT.

When to use this crate

Use windows-result when code works directly with Windows status values or exposes Windows errors
without depending on the larger windows-core crate. Generated windows-* APIs normally return
these types through a re-export, so application code often does not need a separate dependency.

Choose this model when callers need the original HRESULT, Win32 error code, or COM error
information. Convert to a domain-specific error at your application boundary when callers do not
need the Windows details.

Getting started

The crate README contains the dependency declaration and a
minimal example. A typical first workflow is to keep windows_result::Result<T> as the return type,
convert the status value immediately after a Windows call, and use ? for propagation:

rust
use windows_result::{HRESULT, Result};

fn check_status(status: HRESULT) -> Result<()> {
    status.ok()?;
    Ok(())
}

On Windows, status-to-message conversion asks the operating system for descriptive text. The core
status and error types also compile without std; integrations such as std::error::Error and
std::io::Error require the default std feature.

Core API model

Type Success rule Typical source
HRESULT Any value >= 0 COM and WinRT methods
BOOL Any nonzero value Win32 functions with GetLastError on failure
WIN32_ERROR Zero GetLastError and Win32 error constants
NTSTATUS Any value >= 0 Native system APIs
RPC_STATUS Zero RPC APIs
Error Always represents an error Rich Rust error with code and optional error info
Result<T> Result<T, Error> Windows operations exposed to Rust

The wrappers preserve the status representation until it needs to become an Error. Their ok
methods apply the correct success rule, and conversions normalize failures to an HRESULT.

Common tasks

Convert a status and preserve its code

rust
use windows_result::{Error, Result, WIN32_ERROR};

fn cancelled() -> Result<()> {
    let code = WIN32_ERROR(1223);
    Err(Error::from(code))
}

let error = cancelled().unwrap_err();
assert_eq!(WIN32_ERROR::from_error(&error), Some(WIN32_ERROR(1223)));

WIN32_ERROR::to_hresult uses the standard FACILITY_WIN32 mapping. Use
WIN32_ERROR::from_error when you need to recover a mapped Win32 code.

Attach context to a failure

Use Error::new when you are creating the failure and have a useful message:

rust
use windows_result::{Error, WIN32_ERROR};

let code = WIN32_ERROR(1223).to_hresult();
let error = Error::new(code, "operation cancelled");

assert_eq!(error.code(), code);
assert_eq!(error.message(), "operation cancelled");

Error::from_hresult stores only the code. Converting an HRESULT with Error::from can capture
thread-local COM error information when it is available. Error::message falls back to the system
description for the code.

Work with last-error APIs

Call BOOL::ok, WIN32_ERROR::from_thread, or Error::from_thread immediately after the failing
Windows call. Another Windows call may overwrite the thread's last-error value.

rust
use windows_result::{BOOL, Result};

fn check_win32_result(value: BOOL) -> Result<()> {
    value.ok()
}

Only use this pattern for APIs whose documentation says failure details come from GetLastError.
Some Win32 functions use other return conventions.

Important choices and pitfalls

  • HRESULT(1) is success, not failure. Test it with is_ok or ok rather than comparing with
    zero.
  • BOOL(0).ok() reads the current thread's last-error value. Do not construct a false BOOL later
    and expect it to retain the original error.
  • Error equality and ordering compare the HRESULT, not the message or COM error information.
  • Converting a mapped Win32 Error to std::io::Error recovers the underlying Win32 code.
    Other facilities retain the full HRESULT as the raw OS error.
  • --cfg=windows_slim_errors is a whole-program policy that stores only the four-byte HRESULT.
    It reduces error size but discards extended COM and WinRT error information.

Samples and next steps

The result-result sample creates an error with a message,
propagates status failures, and inspects the resulting error. Run it from the workspace with:

text
cargo run -p result-result

For error handling alongside projected COM types, continue with
windows-core.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-result.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/result.txt. The
com, bstr, and strings modules add COM error-info support and are gated on windows (and
disabled under the windows_slim_errors cfg).

Testing

Run cargo test -p windows-result; see also the workspace test crates.


Crates Windows Services (`docs/crates/windows-services.md`)

windows-services

A simple, safe way to implement Windows services in Rust.

windows-services lets you write a Windows service process with a builder. Use Service::new to
declare accepted controls, then run to connect the process to the Service Control Manager (SCM).

When to use

Use this crate to implement the runtime side of a Windows service: service startup, status changes,
and control callbacks. It does not install, configure, start, or remove the service registration.
Use the SCM command-line tools or an installer for that lifecycle.

Use a normal console application for foreground tools and short-lived scheduled work. A service is
appropriate when Windows must manage a long-running background process and deliver service control
events.

Getting started

The crate README has the dependency declaration and minimal
builder example. The first practical workflow is:

  1. Build a service executable.
  2. Register its absolute path with the SCM.
  3. Start it through the SCM, not by launching it directly.
  4. Handle Command::Start and every control enabled on the builder.
  5. Stop and remove the test registration when finished.

The simple sample prints the corresponding
sc create, sc start, sc query, sc stop, and sc delete commands when run outside the SCM.

Commands and state

Service::new accepts only startup by default. can_stop enables stop and shutdown controls.
can_pause enables pause and resume. can_accept(mask) enables other SCM control classes, which
arrive as Command::Extended.

The callback receives &Service and a Command. The crate moves through the normal pending and
stable states around start, stop, pause, and resume callbacks. Use state() to inspect the current
state and set_state() only when the service needs to report a state outside that automatic flow.

Keep the callback responsive. Start worker threads or signal existing workers instead of doing
lengthy work on the control callback. On Stop, signal shutdown and release resources promptly.

Running outside the SCM

Without configuration, run returns an error when the process was not started by the SCM.
can_fallback installs a closure for intentional console or development execution. In fallback
mode the crate sends start and stop commands around that closure, allowing one executable to share
service setup with a local test path.

Platform constraints and pitfalls

  • The crate and service process are Windows-only.
  • Registering or changing a service normally requires administrative rights.
  • run owns the service dispatch lifecycle. Arrange application initialization before calling it.
  • Enable only controls the callback handles. Other accepted controls arrive with raw event data in
    ExtendedCommand; interpret that pointer only according to the matching Windows control code and
    during the callback.
  • A service normally has a different working directory, environment, account, and desktop access
    than an interactive process. Use absolute paths and avoid assumptions about an interactive user.
  • Log to the Windows Event Log or another service-safe destination rather than standard output.

Samples and next steps

Start with the simple sample. The
time-change sample shows can_accept, an extended
time-change control, and decoding its event data with the matching Win32 definitions.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-services.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/services.txt; the
service runtime and dispatch glue are hand-written.

Testing

Run cargo test -p windows-services; see also the workspace test crates.


Crates Windows Strings (`docs/crates/windows-strings.md`)

windows-strings

The string types used across the Windows APIs.

When to use this crate

Use windows-strings when a Windows ABI requires a Windows string type and you do not otherwise
need windows-core. Most projected APIs re-export these types, so use the type named by the
function signature instead of converting every string to one preferred representation.

The main choice is ownership: HSTRING and BSTR own their contents, while PCSTR, PCWSTR,
PSTR, and PWSTR are pointer wrappers whose storage belongs elsewhere.

Getting started

The crate README has the dependency declaration and a
minimal example for each common representation. Before the first API call, inspect its signature:

  1. Use HSTRING for WinRT strings.
  2. Use BSTR for COM Automation strings.
  3. Use PCWSTR or PCSTR for borrowed, null-terminated input.
  4. Use PWSTR or PSTR only when the API writes through the pointer.

HSTRING, the pointer wrappers, and the literal macros are available across targets. BSTR is
Windows-only because allocation and deallocation use the COM Automation ABI. Conversions involving
OsStr, OsString, or Path require Windows and the default std feature.

Core API model

Type or macro Encoding and ownership Common use
HSTRING Owned, immutable, reference-counted UTF-16 WinRT parameters and results
BSTR Owned, length-prefixed UTF-16 COM Automation parameters and results
PCWSTR / PWSTR Borrowed const / mutable UTF-16 pointer Win32 wide-string ABIs
PCSTR / PSTR Borrowed const / mutable 8-bit pointer Win32 8-bit string ABIs
h! Static &HSTRING literal Reused WinRT string constant
w! Static null-terminated PCWSTR literal Wide input constant
s! Static null-terminated UTF-8 PCSTR literal 8-bit input constant
HStringBuilder Preallocated mutable UTF-16 buffer Build one immutable HSTRING

Length-prefixed types can represent data independently of a trailing null. Pointer types depend on
a null terminator and do not record a length or lifetime.

Common tasks

Create owned strings

rust
use windows_strings::{BSTR, HSTRING};

let runtime_name = HSTRING::from("Windows.Foundation.Uri");
assert_eq!(runtime_name, "Windows.Foundation.Uri");

#[cfg(windows)]
{
    let automation = BSTR::from("report");
    assert_eq!(automation, "report");
}

HSTRING::from_wide accepts UTF-16 code units directly. to_string_lossy and display are useful
when diagnostics should continue through invalid UTF-16; equality against str compares the
encoded contents without first allocating a Rust String.

Pass string literals

rust
use windows_strings::{h, s, w, HSTRING, PCSTR, PCWSTR};

const UTF8_NAME: PCSTR = s!("name");
const WIDE_NAME: PCWSTR = w!("name");

let runtime_name: &HSTRING = h!("Windows.Foundation.Uri");

The literal macros append the required terminator at compile time. h! returns a borrowed static
HSTRING; clone it or construct HSTRING::from only when an owned value is required.

Read a pointer returned by Windows

Pointer conversion is unsafe because the wrapper cannot prove that the pointer is non-null, valid,
terminated, or still alive:

rust
use windows_strings::PCWSTR;

unsafe fn copy_wide(value: PCWSTR) -> Result<String, std::string::FromUtf16Error> {
    unsafe { value.to_string() }
}

Check the producing API's ownership rules before reading or freeing its pointer. A null pointer is
not an empty string: calling len, as_wide, as_bytes, or to_string on it is invalid.

Fill an `HSTRING` buffer

Use HStringBuilder::new when another operation fills a known number of UTF-16 code units. Write
through its mutable slice, call trim_end if unused trailing elements remain zero, and convert the
builder into HSTRING.

Important choices and pitfalls

  • Prefer wide Windows APIs when both 8-bit and wide variants exist. A PCSTR type alone does not
    define which legacy code page a particular Win32 function applies.
  • PCSTR, PCWSTR, PSTR, and PWSTR do not own their buffers. Keep the backing allocation alive
    for the entire call.
  • Do not pass an immutable literal through PSTR or PWSTR; writable pointer types require
    writable storage sized as the API specifies.
  • Use BSTR only for APIs that transfer or borrow a COM Automation string according to the stated
    ownership contract.
  • The default std feature adds standard-library conversions. Disable it only if the remaining
    alloc-based API meets the target's needs.

Samples and next steps

The string samples cover HSTRING, BSTR, and the wide and 8-bit literal macros. Run them from the
workspace with:

text
cargo run -p strings-hstring
cargo run -p strings-bstr
cargo run -p strings-wide-ansi

Continue with windows-core when the string is part of a projected COM or WinRT
call.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-strings.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/strings.txt; the
string types and macros are hand-written.

Testing

Run cargo test -p windows-strings; see also the workspace test crates.


Crates Windows Sys (`docs/crates/windows-sys.md`)

windows-sys

Raw Windows API declarations, constants, and ABI types.

windows-sys projects Windows metadata as raw FFI. Functions keep pointer parameters and native
integer conventions, structs are plain ABI values, and the crate does not provide owning COM or
WinRT wrappers.

When to use it

Prefer a focused crate when one covers the task. For APIs not covered there, a reusable library
should generate a narrow private binding set with windows-bindgen.

Choose windows-sys when a binary application wants a pre-generated broad API and its Rust layer
should stay close to a C signature, such as:

  • sharing layouts and callbacks with existing C or C++ code;
  • writing a narrow wrapper where ownership and error translation are handled locally;
  • calling a raw API shape that the rich projection cannot represent.

Choose windows when a binary application needs a similarly broad projection with
reference-counted interfaces, parameter conversion traits, and projected results.

Reusable libraries should avoid both umbrella crates. They add dependency weight and version
churn, and several libraries can bring incompatible versions into one dependency tree. Cargo
features limit what compiles, but they do not remove those dependency-management costs.

Do not choose windows-sys on the assumption that windows adds runtime overhead to every call.
The meaningful difference is API shape and who must maintain the unsafe contract.

Features and API discovery

The same metadata drives windows and windows-sys, but raw Win32 feature names usually follow
lowercase header groups. For example, a program using event handles may need synchapi,
handleapi, and type features such as winnt or minwinbase.

Use the API reference and the generated signature:

  1. Search for the native API name.
  2. Confirm that the result is available in windows-sys; rich-only COM and WinRT projections do
    not have equivalent raw interface wrappers.
  3. Enable the feature shown for the function and any feature needed by types in its signature.
  4. Inspect compiler errors before adding broad features. Generated feature dependencies already
    include many prerequisites.
  5. Verify operating-system availability separately from crate availability.

Keep imports explicit at an FFI boundary. windows_sys::Win32::* is convenient in a small sample,
but named imports make it clear which constants, structs, and extern functions a wrapper exposes.

Raw ABI patterns

The core aliases show the intended level of abstraction:

  • BOOL, HRESULT, NTSTATUS, and RPC_STATUS are integer aliases.
  • PCSTR and PCWSTR are const pointers.
  • PSTR and PWSTR are mutable pointers.
  • BSTR and HSTRING are raw ABI handles, not owning Rust strings.
  • IUnknown_Vtbl and IInspectable_Vtbl describe base vtable layouts.

Generated functions do not add Option, slices, Rust bool, automatic out values, or resource
guards. Pass null pointers, lengths, discriminants, and callbacks in the exact form required by the
Windows documentation. Callback functions generally need extern "system" and must obey the
documented lifetime and threading rules.

Use #[repr(C)] for your own structs that cross the boundary. Do not transmute between
windows-sys and windows types merely because their current layouts appear equal. Convert
through documented raw representations at one reviewed boundary.

Safety and ownership

Treat each extern call as a three-part contract:

  1. Inputs must remain valid for the duration documented by the API.
  2. The return value must be interpreted using that API's success convention.
  3. Every acquired resource must be paired with its documented release operation.

Raw handle aliases are often pointers, but pointer shape does not establish ownership. A returned
value may be borrowed, process-owned, closed with CloseHandle, released by another function, or
compared with a special sentinel. Build a private RAII guard as soon as an owned resource enters
Rust, and transfer ownership explicitly when the Windows API takes it.

Keep unsafe inside a safe wrapper wherever the contract can be expressed and checked in Rust.
Validate buffer sizes and integer conversions before the call. Avoid constructing slices from raw
pointers until nullability, alignment, initialization, and element count have all been established.

Errors

windows-sys returns native status values. It does not translate them into
windows_core::Result:

  • For BOOL, check against zero only when the API documents zero as failure.
  • For handles, distinguish null from INVALID_HANDLE_VALUE; different functions use different
    sentinels.
  • For HRESULT values, use the documented success and failure ranges rather than treating every
    nonzero value as an error.
  • For last-error APIs, read GetLastError immediately after the failing return and before another
    Windows call.
  • Some APIs set last error only for selected failures. Do not report a stale last-error value when
    the API does not promise one.

A wrapper may translate these results into its own error type or use the focused
windows-result crate. Keep the native status available when callers need to
make API-specific decisions.

Strings and buffers

The s! and w! macros create null-terminated literal pointers. They do not allocate dynamic
storage. For runtime UTF-16 input, encode into a Vec, append a zero, and keep the vector
alive and unchanged until the call returns. Reject or handle interior nulls according to the target
API.

Output strings require the pattern documented by the function. Common forms include a caller-owned
buffer and length, a size query followed by allocation, or a pointer allocated by Windows and
released by a specific function. Do not assume that PWSTR implies any one of these forms.

Lengths may count bytes, code units, characters, or include the trailing null. Preserve the native
unit in variable names and check conversions to the API's integer width.

A practical boundary

A maintainable raw wrapper usually follows this order:

  1. Define the smallest public Rust operation needed by the application.
  2. Import only the native items needed for that operation.
  3. Convert Rust values and establish pointer lifetimes.
  4. Make one or a small group of related calls inside a narrow unsafe block.
  5. Capture status or last error immediately.
  6. Put owned resources in guards before any fallible follow-up work.
  7. Convert outputs into owned Rust values before returning.

The windows-sys service sample is useful for callback and struct layout patterns, but production
code should also check every fallible API result and guard each acquired handle.


Internal documentation

The remainder of this page is for repository contributors.

How it is built

tool-package generates the crate by driving windows-bindgen in --sys --package mode from
crates/tools/package/src/sys.txt. src/lib.rs and the small src/core ABI module are
hand-maintained; the Windows namespace files and Cargo feature graph are generated.

Sys package generation prunes namespaces that would contain no raw items. In particular, a
namespace containing only projected COM interfaces does not produce an empty module. Generated
extern functions link through windows-link.

Do not edit generated source under crates/libs/sys/src/Windows. Change metadata, filters, or the
generator and run the appropriate repository tool.

Testing

Run cargo test -p windows-sys; the workspace test crates and windows-sys samples cover wider ABI
usage.


Crates Windows Threading (`docs/crates/windows-threading.md`)

windows-threading

Simple, safe access to the Windows thread pool.

windows-threading exposes the Windows thread pool through a small, safe API. submit runs a
closure on a pool thread, while Pool provides ownership and sizing controls.

When to use

Use this crate for CPU or blocking work that should run on the Windows thread pool without bringing
in a Rust async runtime. It is also useful when scoped tasks need to borrow local data.

Use std::thread when each task needs a dedicated thread, thread-local initialization, or a stable
thread identity. Use an async runtime's task APIs when the application already depends on that
runtime and the work is primarily asynchronous I/O.

Getting started

The crate README contains the dependency declaration and
minimal examples for all three submission styles. For a first workflow, decide whether the caller
must wait:

rust,no_run
let inputs = [2, 3, 4];
let outputs = std::sync::Mutex::new(Vec::new());

windows_threading::Pool::with_scope(|scope| {
    for input in inputs {
        let outputs = &outputs;
        scope.submit(move || outputs.lock().unwrap().push(input * input));
    }
});

assert_eq!(outputs.lock().unwrap().len(), inputs.len());

with_scope does not return until its submissions finish, so the closures can borrow inputs and
outputs.

Choosing a submission API

API Waiting and lifetime behavior
submit Uses the process-wide default pool and returns immediately; the closure is 'static.
for_each Submits every iterator item in parallel and waits for all of them.
Pool::submit Uses a private pool and returns immediately; the closure is 'static.
Pool::scope Accepts borrowing closures and joins the pool before returning.

Dropping a Pool also waits for its outstanding submissions. Calling join waits explicitly.
scope and join wait for all work on that private pool, including work submitted from another
thread.

Pool sizing and task design

Windows chooses the private pool size unless set_thread_limits(min, max) is called. Set limits
only when the workload has measured concurrency requirements. Keep pool closures finite, avoid
waiting on other work queued to the same constrained pool, and protect shared mutable state with
normal synchronization primitives.

thread_id() returns the current Windows thread identifier. sleep(milliseconds) blocks the
current worker; it does not schedule a timer or yield an async task.

Platform constraints and pitfalls

  • This crate wraps the Windows thread-pool APIs and is for Windows targets.
  • submit and Pool::submit cannot borrow stack data because they may outlive the caller. Use
    Pool::scope for borrowed data.
  • A panic in a submitted closure crosses a system callback boundary and must not be used as an
    error-reporting strategy. Handle expected failures inside the closure.
  • Thread-pool allocation failures are treated as unrecoverable and panic.

Samples and next steps

The for-each sample shows parallel aggregation. The
pool sample shows thread limits, scoped borrowing, and how
work is distributed among pool threads.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-threading.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/threading.txt;
Pool and the submit/for_each helpers are hand-written.

Testing

Run cargo test -p windows-threading; see also the workspace test crates.


Crates Windows Time (`docs/crates/windows-time.md`)

windows-time

WinRT TimeSpan and DateTime with idiomatic Rust conversions.

windows-time provides the two WinRT time primitives as plain #[repr(C)] Rust types: TimeSpan
(a duration) and DateTime (a point on the WinRT UTC timeline).

When to use

Use these types when exchanging time values with WinRT APIs or when code benefits from their signed,
100-nanosecond representation. Use std::time::Duration for nonnegative Rust-only durations and
std::time::SystemTime when no Windows ABI value is needed.

Getting started

The crate README has the dependency declaration and a minimal
arithmetic example. A practical workflow is to keep calculations in DateTime and TimeSpan, use
checked arithmetic at input boundaries, and convert only where another API requires a std type:

rust
use windows_time::{DateTime, TimeSpan};

let started = DateTime::now();
let timeout = TimeSpan::from_seconds(30);
let deadline = started.checked_add(timeout).expect("deadline out of range");

assert!(deadline > started);

`TimeSpan` durations

Construct spans from ticks, microseconds, milliseconds, seconds, minutes, hours, or days. Unit
constructors saturate if multiplication exceeds the i64 tick range. TimeSpan can be negative,
so it can represent a difference that std::time::Duration cannot.

Use checked_add, checked_sub, checked_mul, checked_div, and checked_neg when overflow is
possible. The operator forms panic on overflow or division by zero. Saturating addition,
subtraction, and negation are also available.

Conversions to std::time::Duration reject negative spans. Conversion in either direction has
100-nanosecond precision, so finer source precision is truncated.

`DateTime` points

DateTime stores ticks since 1601-01-01 00:00:00 UTC. Use from_unix_secs or
from_unix_millis for Unix timestamps, and unix_secs, unix_millis, or unix_nanos for the
reverse direction. DateTime::now() and SystemTime conversions require the default std
feature.

Add or subtract a TimeSpan to move along the timeline. Subtract two DateTime values to obtain a
signed TimeSpan. Prefer the checked methods for values from external input.

Display emits an ISO-8601 UTC timestamp. to_local() shifts the stored ticks so the component
methods return local calendar fields. Treat that result as a local-field carrier; it is not a new
UTC instant to serialize with Display or convert back to SystemTime.

Platform constraints and pitfalls

  • Both types use signed i64 ticks, where one tick is 100 nanoseconds.
  • TimeSpan and DateTime are ABI values. Do not substitute Duration or SystemTime in a WinRT
    signature.
  • Unit and Unix constructors saturate on overflow. Use explicit range validation when saturation
    would hide invalid input.
  • DateTime::now() returns DateTime::MAX if the system clock cannot be represented.
  • Disable the default std feature only if now, SystemTime conversion, and the standard error
    implementation are not needed. Local-time conversion still calls a Windows API.

Sample and next steps

The time-time-types sample demonstrates duration
construction, checked date arithmetic, Unix timestamps, and formatting.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-time.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/time.txt; the
conversion logic is hand-written.

Why not substitute the type at the projection layer

C++/WinRT replaces TimeSpan with std::chrono::duration> and
DateTime with the matching time_point. That works only because those chrono types are a single
int64_t and therefore bit-for-bit ABI-compatible with the WinRT struct.

Rust has no equivalent. core::time::Duration is { u64 seconds, u32 nanos } (96 bits, unsigned) -
it can't represent negative TimeSpan values and isn't ABI-compatible with i64, and
std::time::SystemTime is opaque and platform-defined. So the ABI struct stays a single i64, and
windows-time provides explicit conversions to and from the std types instead of aliasing them.

Testing

Run cargo test -p windows-time; see also the workspace test crates.


Crates Windows Version (`docs/crates/windows-version.md`)

windows-version

Reliable OS version information without an application manifest.

windows-version reports the real operating system version even when the process has no version
manifest.

When to use

Use this crate when Windows documentation gives an OS build requirement and the program must make a
version decision that is not affected by application-manifest version virtualization. It also
provides a workstation/server distinction and the update build revision (UBR).

Prefer direct capability detection when Windows exposes it: attempt the API, query the interface,
or check the feature itself. Version checks are appropriate when the platform contract is stated
only in terms of a minimum Windows version or build.

Getting started

The crate README contains the dependency declaration and a
minimal comparison. In application code, name the required version and keep the decision next to
the feature it guards:

rust,no_run
use windows_version::OsVersion;

const REQUIRED: OsVersion = OsVersion::new(10, 0, 0, 22_000);

fn feature_is_available() -> bool {
    OsVersion::current() >= REQUIRED
}

This is easier to audit than scattering comparisons against individual fields.

Reading version information

OsVersion::current() returns major, minor, service-pack pack, and build. OsVersion
implements Ord, with comparisons proceeding in that field order. Construct a threshold with
OsVersion::new.

is_server() distinguishes Windows Server from workstation releases. revision() reads the UBR
from the registry. Treat the revision separately from OsVersion: it is not part of Ord.

For diagnostics, report both values when the revision matters:

rust,no_run
let version = windows_version::OsVersion::current();
let revision = windows_version::revision();
println!(
    "{}.{}.{}.{}",
    version.major, version.minor, version.build, revision
);

Platform constraints and pitfalls

  • The crate is Windows-only.
  • A version threshold does not guarantee that an optional component, driver, policy, or hardware
    capability is present. Query those requirements separately.
  • revision() returns 0 when the registry query fails as well as when the stored revision is
    zero. Do not use it where those cases must be distinguished.
  • Windows product names are marketing labels, not API versions. Compare the documented numeric
    build rather than parsing a product name.
  • Do not use is_server() as a proxy for installed roles or individual server capabilities.

Next steps

Find the minimum supported build in the documentation for the Windows API or feature being used.
Keep that requirement in one named constant, and add capability checks for requirements not
captured by the OS build.

The version-version sample prints the current version,
revision, product type, and the result of a named build comparison.


Internal documentation

The remainder of this page covers how the crate is built and maintained. It is for contributors and
is not needed to use windows-version.

How it's built

src/bindings.rs is generated by tool-bindings from crates/tools/bindings/src/version.txt;
OsVersion and the comparison logic are hand-written.

Testing

Run cargo test -p windows-version; see also the workspace test crates.


Crates Windows Webview (`docs/crates/windows-webview.md`)

windows-webview

A safe Rust wrapper around the WebView2 COM APIs for hosting
Microsoft Edge in a window.

When to use it

Use windows-webview when a Windows desktop application needs to host web content, exchange
messages with JavaScript, or use browser facilities such as profiles, cookies, downloads, and the
Chrome DevTools Protocol. The crate has no default UI framework. Enable system to host WebView2
through windows-window, or reactor to place the WinUI XAML WebView2 control in a
windows-reactor view. The features are additive.

The crate wraps a selected WebView2 surface rather than exposing the complete SDK. Use raw WebView2
bindings when an application needs APIs that are not represented here.

UI operation model

Low-level WebView2 creation and script operations complete through callbacks. Starting an
operation returns Result<()>: an error means the operation did not start, while success means its
result may later be passed to the callback on the same UI thread. A callback runs at most once and
may be dropped without running if its native operation or UI apartment ends first.

No operation runs a nested message loop. Win32, WinUI, Reactor, and other UI hosts retain control
of message preprocessing, dispatch, and shutdown. WebViewWindowBuilder::run is a separate
convenience for applications that choose windows-window as their top-level host; it owns the one
application message loop.

This differs from windows-pickers: a Common Item Dialog is a synchronous
native modal operation whose Show method owns its modal loop. Reactor stages such modal work with
ComponentContext::run_window; it uses ElementRef requests for callback-completed control work.

The basic idea

A low-level WebView host has three main objects:

Type Purpose
Environment Owns the browser process and user-data context
Controller Places a browser inside a native parent window
WebView Navigates, runs scripts, exchanges messages, and exposes page events
WebViewHost Owns all three objects while another framework owns the parent
WebViewWindow Adds an owned windows-window parent and lifecycle handling

WebViewHostBuilder creates the first three as one callback-completed operation. Use the individual
types when an application shares an environment or needs a custom creation sequence.

The Microsoft Edge WebView2 runtime must be installed for native window hosting. Low-level hosts
need a live message loop on a COM single-threaded apartment. Standalone applications initialize
one with windows_core::init_sta(). WebViewWindowBuilder::run handles this for its owned loop.

Host your first page

The system feature provides a complete windows-window host:

rust,no_run
use windows_webview::*;

fn main() -> Result<()> {
    WebViewWindow::new("WebView2")
        .size(1000, 700)
        .run(|host| {
            host.webview()
                .navigate("https://learn.microsoft.com/windows/apps/")?;
            Ok(())
        })
}

The host resizes the controller with its parent and closes WebView2 before the HWND is destroyed.
Call host.retain or host.retain_all for event registrations that should remain active for the
host lifetime. WebViewWindowBuilder::on_close reports normal close processing, including
programmatic closure, when another UI framework coordinates several windows. Direct destruction
during drop does not call it.

Use WebViewHostBuilder when a framework already owns the parent HWND and UI loop:

rust,ignore
WebViewHost::builder().create_for_hwnd(parent, move |result| {
    match result {
        Ok(host) => framework.attach_webview(host),
        Err(error) => framework.report_webview_error(error),
    }
})?;

The builder chains environment and controller creation without blocking or pumping messages.
WebViewHost owns the resulting environment, controller, browser, and retained event
registrations. The framework stores that one value and remains responsible for parent lifetime,
layout, position notifications, and shutdown. The raw Environment and Controller callback APIs
remain available when a framework needs separate environment reuse or custom controller creation.

Keep event registrations alive

Every on_* method returns an EventRegistration. The registration unsubscribes when it is
dropped, so store it for as long as the callback should run:

rust,ignore
let navigation = webview.on_navigation_completed(|args| {
    println!("navigation succeeded: {}", args.is_success());
})?;

webview.navigate("https://github.com/microsoft/windows-rs")?;

// Keep `navigation` alive while the message loop runs.

For several events, a vector is convenient:

rust,ignore
let registrations = vec![
    webview.on_document_title_changed(|title| {
        println!("the title changed to {title}");
    })?,
    webview.on_process_failed(|args| {
        eprintln!("browser process failed: {:?}", args.kind());
    })?,
];

Callbacks run on the UI thread. Keep them short so the window and page remain responsive.

The common navigation methods are direct:

rust,ignore
webview.navigate("https://example.com")?;
webview.reload()?;
webview.go_back()?;
webview.go_forward()?;

Use source and document_title after navigation:

rust,ignore
let page = webview.clone();
let navigation = webview.on_navigation_completed(move |args| {
    if args.is_success() {
        println!("{} - {}", page.document_title(), page.source());
    }
})?;

on_navigation_starting can inspect or cancel a request before it begins:

rust,ignore
let starting = webview.on_navigation_starting(|args| {
    if !args.uri().starts_with("https://") {
        _ = args.set_cancel(true);
    }
})?;

Keep a process-failure handler in a long-running host. A failed render process can usually be
followed by reload; a failed browser process requires a new WebView.

Exchange messages with the page

Page JavaScript sends a value to Rust with:

javascript
window.chrome.webview.postMessage({ action: "save", value: 42 });

The host receives it through on_web_message_received:

rust,ignore
let messages = webview.on_web_message_received(|args| {
    println!("{} sent {}", args.source(), args.web_message_as_json());
})?;

Inspect source before trusting messages from content that can navigate. Use
try_web_message_as_string when the protocol accepts only strings.

Rust sends a value in the other direction with:

rust,ignore
webview.post_web_message_as_json(r#"{"status":"saved"}"#)?;
webview.post_web_message_as_string("refresh")?;

The page receives those values with:

javascript
window.chrome.webview.addEventListener("message", event => {
    console.log("host sent", event.data);
});

Use execute_script when Rust needs to run a specific expression:

rust,ignore
webview.execute_script("document.title", |result| {
    println!("title as JSON: {result:?}");
})?;

Script results are JSON encoded. The callback runs later on the UI thread.

Add script before each document loads

add_script_to_execute_on_document_created installs script before page JavaScript runs and
returns its identifier through a callback:

rust,ignore
let page = webview.clone();
webview.add_script_to_execute_on_document_created(
    "document.documentElement.dataset.host = 'windows-rs';",
    move |result| {
        let script = result.unwrap();
        println!("registered {}", script.as_str());
        page.navigate("https://example.com").unwrap();
    },
)?;

Save the returned ScriptId if the script may need to be removed later:

rust,ignore
webview.remove_script_to_execute_on_document_created(&saved_script)?;

Wait for successful registration before navigating when the script must apply to the first
document.

Host local files

Map a folder to a virtual HTTPS host rather than navigating to a file: URL:

rust,ignore
let folder = concat!(env!("CARGO_MANIFEST_DIR"), "\\web");
webview.set_virtual_host_name_to_folder_mapping(
    "app.example",
    folder,
    HostResourceAccessKind::Deny,
)?;
webview.navigate("https://app.example/index.html")?;

The page can now load scripts, styles, and images relative to that origin. The access kind controls
whether other origins may request the folder's content.

For generated or embedded content, intercept a URL pattern and return a response:

rust,ignore
let resources = webview.on_web_resource_requested("https://app.example/*", |request| {
    if request.uri().ends_with("/style.css") {
        Some(
            WebResourceResponse::new("body { font-family: Segoe UI, sans-serif; }")
                .content_type("text/css"),
        )
    } else {
        None
    }
})?;

Returning None lets WebView2 continue the request normally. The callback is synchronous, so
prepare expensive content outside it.

Put a WebView inside Reactor

Enable the reactor feature when the browser should participate in a Reactor layout:

toml
windows-webview = { version = "0.100.0", default-features = false, features = ["reactor"] }

webview returns a normal Reactor View and sends the initialized browser through a callback:

rust,ignore
use windows_reactor::*;
use windows_webview::{EventRegistration, WebView, webview};

struct Browser {
    webview: Option<WebView>,
    navigation: Option<EventRegistration>,
}

#[derive(Clone)]
enum Message {
    Initialized(WebView),
}

fn update(&mut self, message: Message, _context: &ComponentContext<Self>) {
    let Message::Initialized(webview) = message;
    self.navigation = webview.on_navigation_completed(|_| {}).ok();
    _ = webview.navigate("https://learn.microsoft.com/windows/apps/");
    self.webview = Some(webview);
}

fn view(&self, _input: &(), context: &mut ViewContext<Self>) -> View {
    webview(context.callback(Message::Initialized))
}

These methods belong inside the corresponding Component implementation. Initialization happens
after the XAML control enters a live visual tree, not during Component::create.

The convenience function panics on a native initialization error. Use webview_result when the
component should receive and display that error. A self-contained Reactor app must also deploy
Microsoft.Web.WebView2.Core.dll; windows-reactor-setup stages it.
The reactor sample embeds a WinUI WebView2 control in a
Reactor view and exercises navigation, scripts, messaging, DevTools, and events through the shared
WebView API. The reactor-window sample instead
uses a Reactor window as a toolbar for a callback-driven WebViewWindow. It sets
quit_on_close(false) so both windows share Reactor's UI thread and message loop, and uses
on_close to keep the Reactor component synchronized with the external window. This sample also
exercises controller zoom and parent-window lifecycle, which the WinUI WebView2 control does not
expose through supported interop.

Run an example with cargo run -p webview-<name>.

Example What it shows
minimal Complete WebViewWindow hosting
reactor WebView2 embedded in a Reactor view

Browse crates/samples/webview for focused browser features and
other hosting models.


Internal documentation

This section is for contributors to windows-webview.

The system feature enables the typed windows-window controller helpers. The reactor feature
depends on windows-reactor and bridges its WinUI WebView2 control to the shared browser API.
Raw HWND controller methods remain available without either host feature.

Binding generation

WebView2 ships C/C++ headers rather than Windows metadata. tool-webview builds the committed
bindings in three stages:

Stage Implementation Output
Headers -> RDL windows_clang::clang() target/webview/WebView2.rdl
RDL -> winmd windows_rdl::reader() target/webview/WebView2.winmd
winmd -> Rust windows_bindgen crates/libs/webview/src/bindings.rs

The tool downloads the pinned Microsoft.Web.WebView2 NuGet package. It parses WebView2.h and
WebView2Interop.h as separate inputs because the collector emits declarations owned by each
input, then merges both translation units. It uses Windows.Win32.winmd for referenced Win32
types and targets x86_64-pc-windows-msvc with Microsoft extensions. Regenerate with
cargo run -p tool-webview; never edit src/bindings.rs.

Bindings use --flat --minimal and the filter in crates/tools/webview/src/webview.txt. Filter
method names are raw metadata names such as put_Bounds and get_CoreWebView2, not projected
names. Implemented interfaces belong in --implement, without method filters. The --dead-code
option keeps interface methods crate-private. The small Win32 filter supplies COM string allocation
and memory-stream support without a dependency on the full windows crate.

Wrapper implementation

Completion handlers and event adapters in handler.rs use implement_decl!, avoiding the
windows-core proc-macro dependencies. OptionsObject uses the same mechanism to implement the
caller-provided environment options interfaces. Its string getters allocate with the COM task
allocator because WebView2 takes ownership.

All WebView2 completion operations use the same callback contract. Their public methods report
immediate initiation failures and retain one-shot handlers for native completion. No operation
owns or nests the host's message loop.

Event adapters convert COM add/remove tokens into EventRegistration. Resource interception also
removes its request filter when the registration drops. protocol.rs converts response bytes to
an IStream with SHCreateMemStream, keeping streams out of the public API.

string.rs handles borrowed UTF-16 input, borrowed WebView2 callback strings, owned LPWSTR
results that must be freed with CoTaskMemFree, and task-allocator output strings returned by
implemented interfaces.

Reactor bridge

The WinUI XAML control exposes a WinRT CoreWebView2, while this crate wraps the COM
ICoreWebView2. ICoreWebView2Interop2::GetComICoreWebView2 is the supported bridge; the two
objects do not convert through a plain interface cast.

With the reactor feature, src/reactor.rs requests initialization after the control is loaded,
retains the loaded-event and asynchronous-operation state through Reactor, crosses the interop
bridge, and reports one result. The canonical WinRT metadata is
winmd/Microsoft.Web.WebView2.Core.winmd. Reactor setup supplies the matching projection DLL for
self-contained deployment.

Maintenance

Generated bindings carry casing lint expectations and allow dead code only when the Reactor bridge
is disabled. Keep those expectations synchronized with generator output.

test-webview runs the raw host and browser feature fixtures against a live WebView2 runtime.
The self-contained test-reactor-integration WebView fixture covers WinUI control initialization
and the COM bridge. Both packages run with real windows and message pumps.
test-webview --headless suppresses its interactive UI; the Reactor integration fixture manages
and closes its own test windows.


Crates Windows Window (`docs/crates/windows-window.md`)

windows-window

Minimal window creation and message-loop support for content hosted by a raw HWND.

When to use it

Use windows-window when a Windows desktop application needs a small top-level window and message
loop to host a swap chain, WebView2 controller, Direct2D or Direct3D renderer, or another API that
accepts an HWND. It avoids depending on the full windows crate or generating
application-specific bindings for basic windowing.

The crate is not a general UI toolkit. Menus, controls, input policy, multi-window coordination,
and specialized message handling remain the application's responsibility.

Prerequisites

The crate targets Windows desktop applications. Create and drive a window on the thread that owns
its message queue. Interop code receiving Window::hwnd() must not retain the handle beyond the
Window lifetime.

Window creation attempts to set process DPI awareness to per-monitor v2. Set any different process
DPI policy before creating a Window.

The README contains dependency setup and the minimal create-and-run example.

First workflow: host resize-sensitive content

Most integrations need the following sequence:

  1. Put shared renderer or controller state behind Rc<RefCell<_>> or another UI-thread owner.
  2. Build the window with on_resize and on_close closures that update and release the hosted
    content.
  3. Call create, then use client_size for the initial content size.
  4. Pass hwnd to the hosting API.
  5. Choose run for an event-driven host or run_with for a render loop.
  6. Drop hosted resources before the Window when their API requires the parent handle to remain
    valid.

on_resize receives client-area width and height in physical pixels. The callback also handles the
initial resize messages that arrive after the builder installs its state.

Window creation and ownership

Window::new(title) returns a WindowBuilder. size sets the initial outer window size, while
client_size sets the size excluding non-client borders. style and ex_style replace the
defaults with raw WS_* and WS_EX_* values. The defaults are WS_OVERLAPPEDWINDOW and no
extended style. no_redirection_bitmap adds WS_EX_NOREDIRECTIONBITMAP for content supplied by
composition. visible(false) creates a hidden top-level window that can receive messages for
integrations such as notification-area icons.

on_message receives (hwnd, message, wparam, lparam) and returns Option. Return
Some(result) only when the application fully handled the message. Return None to use the
crate's built-in handling and DefWindowProcW. on_resize and on_move are focused alternatives
for WM_SIZE and WM_MOVE. If on_message handles either message, its focused callback does not
run.

on_close runs before default WM_CLOSE processing destroys the window. Use it to close or drop
hosted resources whose APIs require a live parent HWND. If on_message handles WM_CLOSE,
on_close does not run.

quit_on_close controls whether closing the window posts WM_QUIT. It defaults to true for
single-window applications. Dropping a window does not post WM_QUIT, so cleanup and failed
creation cannot terminate an application-owned loop. Set the option to false when window lifetime
and application lifetime differ, then call quit explicitly when the application should exit.

create registers the shared window class, creates the window, shows it when configured as
visible, and returns an error if creation fails. After native destruction, Window::hwnd returns
null and Window::client_size returns (0, 0). Window::close sends WM_CLOSE through the
configured close and quit behavior. Dropping a live Window calls DestroyWindow directly
without posting WM_QUIT.

Choosing a message loop

API Use it when Behavior
run() Event-driven updates. Blocks in GetMessageW until quit.
run_with(render) Consecutive frames may be needed. Drains messages, then calls render.
pump() An external operation owns the wait. Dispatches pending messages; never blocks.
quit() Application state requires loop termination. Posts WM_QUIT to the current thread.

The run_with closure returns Result. Return Ok(true) to request another immediate frame,
or Ok(false) to block until a message arrives. Propagating an error exits the loop. This lets a
renderer switch between animation and an idle or occluded state without busy-waiting.

pump returns false after consuming WM_QUIT; the caller should then stop its outer loop.
Repeatedly calling pump without another wait mechanism spins the CPU.

Messages, reentrancy, and panics

Message dispatch is reentrant: a handler can call a Win32 API that sends another message before the
first callback returns. The crate temporarily removes user handlers while one runs. Nested messages
therefore use default processing instead of re-entering a closure or borrowing its captured
RefCell again. A nested WM_CLOSE is deferred until the active handler returns so hosted
resources can shut down before the native window is destroyed.

This also means a nested WM_SIZE triggered inside a handler does not invoke on_resize. Apply any
state update needed by that synchronous operation directly.

Handlers run across an extern "system" window-procedure boundary without catch_unwind. A panic
that escapes a handler aborts the process rather than unwinding through Win32. Return errors
through captured application state or catch a panic inside the closure if recovery is required.

Do not perform long blocking work in a message handler. It prevents painting, input, timers, and
other components on the same UI thread from progressing.

Interop and common options

  • Use hwnd() only with APIs that accept a borrowed parent or target handle.
  • Use client_size() after creation to size the initial swap chain or child content.
  • Forward resize callbacks to Controller::set_bounds for
    windows-webview, or resize the relevant swap-chain buffers.
  • A composition host can use no_redirection_bitmap() to avoid allocating an unused DWM
    redirection surface.
  • Raw input, paint, keyboard, mouse, DPI, and position behavior can be implemented through
    on_message. This crate intentionally does not project message-specific argument types.

Samples

Sample What to study
window-message Creation and message handling.
standalone Swap-chain hosting and resize flow.

Internal documentation

This section is for contributors to windows-window.

src/bindings.rs is generated by tool-bindings from
crates/tools/bindings/src/window.txt. It contains the minimal flat Win32 surface needed for class
registration, creation, DPI setup, destruction, and message dispatch. The hand-written
window.rs depends only on windows-core.

One class is registered lazily for the process. A boxed state containing optional message, resize,
move, and close handlers and a shared liveness bit is stored in GWLP_USERDATA after
CreateWindowExW. wndproc clears the bit and removes the state on WM_NCDESTROY. Window::drop
destroys only the original native window while it remains live. This prevents a late drop from
acting on an HWND
value that Windows has recycled for another window.

Before invoking a callback, wndproc takes all handlers out of state. After the callback it reads
GWLP_USERDATA again because synchronous handling may have destroyed the window and freed the
state. It restores the handlers only when the state still exists. Keep this ordering when changing
dispatch behavior.

The crate does not catch panics in wndproc, add message-specific wrappers, or coordinate several
top-level windows. Those boundaries keep the crate small and its ownership rules explicit.


Crates Windows (`docs/crates/windows.md`)

windows

A broad projected Windows API for Rust binary applications.

The windows crate projects Win32, COM, and WinRT metadata into Rust. It keeps the Windows API
shape while adding typed handles, COM reference counting, string and parameter conversions, and
Result-returning wrappers where the metadata describes an HRESULT result.

Choosing a binding crate

Need Choose Why
Focused crate covers the task That focused crate Small, stable dependency and safer API
Reusable library needs another API windows-bindgen Owned binding set
Binary application needs broad rich bindings windows Pre-generated projected API
Binary application needs broad raw bindings windows-sys Pre-generated FFI

Start with the focused crates listed in the crate index. They provide smaller
dependencies and APIs designed around a particular task. If a reusable library needs APIs they do
not expose, generate a private, reviewed binding set with windows-bindgen.

Use windows when a binary application benefits more from a pre-generated broad projection than
from owning generated bindings. Cargo features limit what compiles, but the umbrella crate still
adds dependency weight and version churn. Different libraries can otherwise bring incompatible
versions of the same large crate into one dependency tree.

It is possible to mix windows and windows-sys, but their similarly named types are distinct.
Keep conversions at a small boundary rather than passing values from both crates throughout an
application.

Finding an API and its features

Windows APIs retain their metadata organization:

  • WinRT namespaces use Rust modules such as windows::Data::Xml::Dom.
  • Win32 header groups are exposed below windows::Win32, such as handleapi, synchapi, and
    winuser.
  • Shared runtime types and traits are under windows::core.

Use this workflow when adding an API:

  1. Find the Windows API by its native name in the
    API reference.
  2. Read the projected Rust signature rather than translating the C signature by hand.
  3. Enable the feature shown for the item. WinRT features follow namespace names with _ separators,
    such as Data_Xml_Dom. Win32 features usually follow lowercase header groups, such as
    handleapi.
  4. Import narrowly while learning the API. A path such as windows::Win32::CreateEventW makes
    missing features and name collisions easier to diagnose than a glob import.
  5. Add supporting features reported by the compiler or reference. Cargo feature dependencies pull
    in many prerequisite types automatically, but a function and the types in its signature may be
    gated separately.

Keep the feature list near the code that motivates it. For a workspace, a shared dependency can
centralize the version while each package selects only the features it calls. Avoid enabling every
feature as a discovery shortcut: it slows builds and hides the intended API boundary.

If a documented item does not appear after enabling its feature, check the target first. The
windows crate is Windows-only, and availability in Windows metadata does not guarantee that an
API exists on every supported Windows version. Runtime version checks and documented fallback
behavior remain the application's responsibility.

Reading projected signatures

The Rust signature is the contract. Projection changes common ABI details in useful ways:

  • Nullable pointers may become Option.
  • C BOOL inputs may become Rust bool.
  • String and interface inputs may accept Param<T> implementations rather than one exact type.
  • COM and WinRT out parameters may become return values.
  • HRESULT-returning methods may become windows::core::Result<T>.
  • Slice-shaped pointer and length pairs may become Rust slices.

These changes do not make every Windows call safe. Generated functions remain unsafe when the
caller must uphold pointer, lifetime, threading, initialization, or API-specific invariants. Keep
the unsafe block small and document the invariant in the safe wrapper around it.

Ownership and types

COM and WinRT interface values are owning, reference-counted wrappers. Cloning one calls AddRef,
dropping one calls Release, and Interface::cast performs QueryInterface. Prefer those
operations over manipulating raw interface pointers.

Win32 handle types such as HANDLE and HWND improve type checking but do not imply ownership.
The API documentation determines whether a returned handle is borrowed, owned, or a sentinel and
which function releases it. Pair acquisition and release in one abstraction so early returns do
not leak the resource.

Generated structs follow the Windows ABI. Initialize them with Default when supported, then set
required size, version, discriminant, or pointer fields exactly as the native API requires.
Constants sometimes need a cast because the metadata preserves the native constant type while a
function accepts another integer type.

Errors

windows::core::Result<T> and windows::core::Error cover HRESULT-based failures. The ? operator
is the normal path for projected methods that return Result.

Win32 APIs use several other failure conventions. A function may return BOOL, a null handle,
INVALID_HANDLE_VALUE, zero, or another sentinel. Do not apply one check to every function:

  • Use .ok()? when a projected BOOL or HRESULT is documented as success/failure.
  • Check handle and integer sentinels exactly as documented.
  • Call Error::from_thread() immediately after an API reports a last-error failure. Another
    Windows call may overwrite the thread's last-error value.
  • Do not treat every zero as failure. Some APIs use zero as a valid result or require a separate
    status query.

The getting-started example demonstrates both patterns: CreateEventW requires a null-handle
check, while SetEvent and CloseHandle return values support .ok()?.

Strings

Choose a string representation from the parameter type and ownership contract:

Form Use
w!("text") Static null-terminated UTF-16 input (PCWSTR)
s!("text") Static null-terminated narrow input (PCSTR)
h!("text") Static WinRT HSTRING
HSTRING Owned WinRT string, including dynamic text
BSTR Owned Automation string
PCWSTR / PCSTR Borrowed const pointer with externally managed lifetime
PWSTR / PSTR Mutable pointer, commonly for caller-provided output storage

Prefer the W variant of a Win32 API for new code. The A variant uses an API-specific Windows
code page, not Rust UTF-8. The literal macros include the required trailing null where applicable.
For dynamic pointer strings, keep the backing buffer alive and immovable for the entire call and
add a trailing null yourself. A pointer string has no ownership; never free it unless the API says
the caller owns the allocation.

A practical wrapper workflow

When a binary application wraps an API from this crate:

  1. Keep generated types and calls in a Windows-specific module.
  2. Validate Rust inputs before entering unsafe.
  3. Convert strings and buffers once, immediately before the call.
  4. Check the API's documented success convention before making another Windows call.
  5. Move owned handles or interfaces into a type whose Drop implementation uses the matching
    release function.
  6. Return ordinary Rust values and Result from the public wrapper.

This boundary makes API version checks, resource cleanup, and platform-specific tests visible. It
also prevents raw pointers and similarly named windows and windows-sys types from spreading
through the rest of the program. A reusable library should apply the same boundary to focused
crates and private windows-bindgen output instead of depending on this umbrella crate.


Internal documentation

The remainder of this page is for repository contributors.

How it is built

The published crate is generated by tool-package, which drives windows-bindgen in --package
mode from crates/tools/package/src/windows.txt. It emits src/Windows/mod.rs and one file per
metadata namespace. src/lib.rs is hand-maintained and supplies the docs.rs stub that redirects to
the external API reference.

The generated Cargo features mirror namespaces and Win32 header groups. Feature dependency edges
are generated from type and namespace dependencies. The crate re-exports windows-core as
windows::core and depends on the focused collections, future, numerics, reference, and time
crates used by the projection.

Do not edit generated source under crates/libs/windows/src/Windows. Change metadata, filters, or
the generator and run the appropriate repository tool.

Testing

Run cargo test -p windows; the workspace test crates and Windows samples cover broader API and
projection behavior.


--- METRICS ---