Core COM and WinRT type support shared by the windows-* crates.
- 📦 crates.io
- 📖 docs.rs
- 🚀 Getting started
- 📁 Source
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.
The crate README has the dependency declaration and a minimal example using strings and results. For a first COM or WinRT workflow:
- Select or generate bindings for the Windows API you want to call.
- Initialize the calling thread's COM apartment when the API requires it.
- Keep projected interface values as owned Rust values and propagate
windows_core::Result. - Use
Interface::castwhen 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:
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:
use windows_core::Result;
fn main() -> Result<()> {
let _apartment = windows_core::init_sta()?;
// Create and use apartment-bound COM objects here.
Ok(())
}| 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.
Use cast to request another interface from the same COM identity:
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.
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:
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.
The default proc-macros feature re-exports #[interface] and #[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() -> 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.
- The default features are
stdandproc-macros. Disableproc-macroswhen avoiding thesyn,quote, andproc-macro2build dependencies matters; the narrowerinterface_decl!andimplement_decl!macros remain available. - Raw interface pointers do not carry an owning lifetime. Use
from_raw,from_raw_borrowed,into_raw, and relatedInterfaceoperations only when the ownership contract is known. - COM apartment initialization is per thread. Initializing one thread does not initialize worker threads.
EventRevokerunregisters on drop. Keep it alive for as long as the handler should run; useinto_tokenonly when another owner will remove the registration.- The
impmodule supports generated code and crate internals. Application code should prefer the public projected types and traits.
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.
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.