Skip to content

Latest commit

 

History

History
105 lines (79 loc) · 4.22 KB

File metadata and controls

105 lines (79 loc) · 4.22 KB

windows-core

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

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.

Declaring and implementing interfaces

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.

Initializing COM for apartment-agnostic code

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.

Without the proc macros

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.


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.