Core COM and WinRT type support shared by the windows-* crates.
- 📦 crates.io
- 📖 docs.rs
- 🚀 Getting started
- 📁 Source
windows-core is the foundation that nearly every other crate builds on. It provides the COM/WinRT
runtime machinery - IUnknown, IInspectable, the Interface trait, reference counting, agile
references, and weak references - along with GUID and re-exports of the
result and string types.
It is also where you declare and implement your own COM and WinRT interfaces. The #[interface]
and #[implement] macros live in the separate windows-interface and
windows-implement crates only because Rust requires procedural macros to
ship in their own proc-macro crate. They are part of windows-core - re-exported from it behind
the default proc-macros feature and documented here rather than as standalone crates.
Declare a COM interface as an unsafe trait deriving from IUnknown with #[interface] and its
GUID, then provide a Rust type for it with #[implement]:
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() {
let object: IValue = Value { value: 42 }.into();
assert_eq!(unsafe { object.value() }, 42);
// COM identity: `cast` queries for another interface on the same object.
let unknown: IUnknown = object.cast().unwrap();
let again: IValue = unknown.cast().unwrap();
assert_eq!(unsafe { again.value() }, 42);
}#[interface] generates the vtable, the safe caller-side wrappers, and the IValue_Impl trait.
#[implement] generates the Value_Impl wrapper that carries the vtable pointers and reference
count; you write the methods in impl IValue_Impl for Value_Impl. Converting the struct .into()
an interface yields a reference-counted COM object, and cast moves between interfaces on the same
object.
init_mta places an uninitialized calling thread into the multithreaded apartment and keeps
the process MTA alive until the process exits:
fn main() -> windows_core::Result<()> {
windows_core::init_mta()?;
Ok(())
}The call does not change a thread that is already initialized in another apartment. Use the generated Win32 COM APIs directly when explicit MTA usage lifetime management is required.
Disabling the default proc-macros feature drops the syn/quote/proc-macro2 build
dependencies. The interface_decl! and implement_decl! declarative macro_rules! macros then
cover the common case - an always-agile type implementing one or more IUnknown-derived
interfaces - without proc macros. They are more verbose (every identifier and the IID must be
spelled out) and narrower in scope; see the interface_macro and implement_macro module docs for
the grammar and limits. windows-core uses them internally so it can build with proc-macros
disabled.
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.
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.
Run cargo test -p windows-core; see also the workspace test crates.