dynwinrt
September 16, 2026 · View on GitHub
Call Windows Runtime (WinRT) APIs from JavaScript, TypeScript, or Python — without writing a native extension.
Why dynwinrt?
If you've ever tried to call a modern Windows API (WinAppSDK, Windows AI, notifications, file pickers, sensors, …) from an Electron, Node, or Python app, you've probably hit one of these walls:
- Writing a native extension for each API surface — needs C++, Rust, or C#, the matching Windows SDK, and language-specific build tooling.
- Bridging through another runtime — adds deployment dependencies and a hand-maintained wrapper for every API you expose.
- Waiting for an official projection — Windows ships
.winmdmetadata months before any JavaScript- or Python-friendly projection appears in a published package.
dynwinrt reads the same .winmd metadata shipped by the Windows SDK and WinAppSDK, then calls the underlying COM vtables dynamically at runtime via libffi. The codegen emits typed .js + .d.ts or .py + .pyi wrappers; the matching native runtime invokes them. Consuming applications do not need MSBuild, node-gyp, Cargo, or a native compiler.
Scope —
dynwinrtprimarily targets data-style WinRT APIs. WinUIApplication + Windowhosting is also supported on a caller-managed STA UI thread. Classic COM has a separate preview surface described below.
Quick start
JavaScript / TypeScript
npm install @microsoft/dynwinrt
npm install -D @microsoft/dynwinrt-codegen
# Generate a binding for one class (auto-detects the Windows SDK winmd)
npx dynwinrt-codegen generate \
--namespace Windows.Foundation \
--class-name Uri \
--output ./generated
const { roInitialize } = require('@microsoft/dynwinrt');
const { Uri } = require('./generated');
roInitialize(1); // MTA
const uri = new Uri('https://example.com/path?q=1');
console.log(uri.host); // "example.com"
Python
python -m pip install --pre dynwinrt dynwinrt-codegen
# Generate and install one typed projection package.
dynwinrt-codegen generate `
--namespace Windows.Foundation `
--class-name Uri `
--lang py `
--output .\generated_uri
python -m pip install .\generated_uri
from dynwinrt import RoApartment, projected_lifetime_scope
from generated_uri.windows.foundation import Uri
with RoApartment(1), projected_lifetime_scope():
uri = Uri("https://example.com/path?q=1")
print(uri.host) # "example.com"
Initialize one apartment per thread that uses WinRT. Use RoApartment(1) for a
normal MTA thread and RoApartment(0) for an STA UI thread. The projection
lifetime scope releases generated wrappers before the apartment closes.
Generated API
Both projections expose public WinRT activation metadata as normal constructors, preserve static factory methods, and generate properties, overloads, async operations with progress, collections, structs, enums, delegates, and events. JavaScript uses camelCase names; Python uses snake_case names, native Python values, asyncio-compatible awaitables, and type stubs.
Implementing WinRT interfaces
Node.js and Python can supply synchronous handlers for complete non-generic
WinRT interfaces. Generated .implementation(...) descriptors compose multiple
interfaces into a standalone IInspectable object, and .implement(...) creates
its management handle. impl.value is a stable typed primary interface, and
impl.dispose() manages cleanup (Python also supports with ... as impl).
Public typed interface views can be passed to ordinary native
consumers without a WinUI composable base or OS registration.
The first release is non-agile and owner-thread-only. Reference release and object-wide callback disposal are separate operations. See WinRT interface implementations for both language APIs, supported method/property/event/array/output contracts, ownership, and the separate IBackgroundTask deployment concerns.
WinUI Application + Window
When Microsoft.UI.Xaml.Application is selected, codegen emits helpers for
WinUI metadata and XamlControlsResources. The application remains responsible
for package identity, framework bootstrap, UI-thread ownership, and lifecycle.
For JavaScript:
const { initWinappsdk, roInitialize } = require('@microsoft/dynwinrt');
const { Application, Button, Window } = require('./generated');
initWinappsdk(2, 2);
roInitialize(0); // STA
async function main() {
let app;
await Application.startScheduled(() => {
app = Application.create(() => {
const window = new Window();
window.content = new Button();
window.activate();
});
app.requestedTheme = 1; // Dark
});
}
main().catch(console.error);
Application.startScheduled() enters the WinUI dispatcher loop after the
current JavaScript callback unwinds and resolves when the application exits.
This keeps WinUI async completions and JavaScript Promise checkpoints working
while XAML owns the thread. Application.start() remains available when the
exact blocking WinRT call is required, but it pauses the Node event loop.
Python uses Application.start() inside RoApartment(0) and
projected_lifetime_scope(). See the
Python WinUI hello-world sample and
Python-defined WinUI control sample.
In an unpackaged process,
set WINAPPSDK_BOOTSTRAP_DLL_PATH to the architecture-matched
Microsoft.WindowsAppRuntime.Bootstrap.dll before calling JavaScript
initWinappsdk() or Python init_winappsdk().
Application.create() resolves the bootstrapped framework resources and
configures its UI thread for Per-Monitor V2 DPI awareness. Packaged processes
can omit the bootstrap call.
Classic COM (Preview)
Classic COM support is functional and tested, but remains a preview under
active development. It targets a conservatively validated subset of
IUnknown- and IInspectable-rooted interfaces from Windows.Win32.winmd; it
is not a general Automation or native Win32 projection, and it does not provide
general flat DLL export projection.
The current CI baseline against
Microsoft.Windows.SDK.Win32Metadata 71.0.14-preview is 5,721 of 7,929
eligible interfaces (72.15%) with complete safe code generation. Supported
contracts include generated coclass activation and QueryInterface views,
managed interface ownership, native POD layouts, typed counted buffers,
BSTR/HSTRING, validated VARIANT, SAFEARRAY and PROPVARIANT subsets, the
target-device-independent TYMED_HGLOBAL FORMATETC/STGMEDIUM subset,
variable-length WAVEFORMATEX audio formats with exact CoTaskMem outputs, and
synchronous JavaScript implementations of fully supported callback interfaces.
Separate bounded one-shot audio activation and owned-copy transactions are
also available; copy-only facades are not counted as complete interfaces.
Seventeen stock-Windows Node E2E runners exercise representative Shell,
Automation, stream, callback, HWND, and WinRT interop scenarios.
Safety takes priority over coverage. If metadata does not fully describe an interface's ABI, layout, ownership, allocator, or cleanup contract, generation fails before emitting a partial wrapper. Material gaps still include several common graphics, advanced audio, WMI, non-HGLOBAL/device-specific clipboard and drag-and-drop, derived Automation, union, BYREF/InOut, and output-ownership shapes.
Classic COM generation currently emits JavaScript and TypeScript only. It uses
the separate @microsoft/dynwinrt/com public surface; generated wrappers call
@microsoft/dynwinrt/com/unsafe internally after codegen validates the ABI.
Generated COM modules use the same lowercase/kebab namespace layout as WinRT
under com/, while the COM barrel keeps globally unique short exports.
COM-only generated packages also require the explicit com/ entrypoint; the
generated package root and @microsoft/dynwinrt runtime root remain
WinRT-only.
projectAs(value, InterfaceClass) safely queries a borrowed managed native
value or generated wrapper into a separately owned wrapper of a registered
generated safe COM interface; it accepts neither raw pointers nor unsafe
target classes. IMMDevice now supports typed activate(InterfaceClass) for
the documented in-process, null-parameter audio endpoint subset and
getId(): string. The usage guide shows endpoint discovery through
activate(IAudioClient) and DynComAudioFormat.pcm() format negotiation,
without a per-interface native adapter.
Existing distinguishable overloads keep their APIs unchanged. Otherwise fully
validated normal COM overload groups with colliding JavaScript signatures or
projected buffers now expose every member as
<camelName>AtSlot<absoluteVtableSlot>, with no ambiguous unsuffixed method.
This naming-only projection promotes 24 interfaces; it does not guess ABI
contracts or provide a universal overload parser. See the
overload usage and limits.
Repository layout
dynwinrt/
├── .github/
│ └── workflows/ # CI, coverage, and Python wheel assembly
├── .pipelines/ # Official 1ES npm, PyPI, and GitHub release pipeline
├── crates/dynwinrt/ # Shared WinRT + Classic COM ABI/libffi runtime
├── bindings/
│ ├── js/ # @microsoft/dynwinrt npm runtime (N-API)
│ └── py/ # dynwinrt PyPI runtime (PyO3)
├── tools/
│ └── dynwinrt-codegen/ # npm + PyPI WinRT/Classic COM codegen CLI
├── tests/
│ └── e2e/ # JavaScript, Python, and Classic COM E2E suites
├── benchmarks/
│ ├── electron/ # Electron IPC benchmark app
│ └── js/ # Dynamic and static JS/native benchmarks
├── samples/
│ ├── js/ # JavaScript/TypeScript samples
│ └── python/ # Python samples
├── docs/ # Architecture, benchmark, guide, and status docs
└── eng/
├── coverage/ # Mixed Rust/JavaScript/Python coverage tooling
└── release/python/ # Python release preparation and verification
Build from source
# Core library
cargo build -p dynwinrt
cargo test -p dynwinrt
# JS bindings (napi-rs)
cd bindings/js && npm install && npm run build
# Python bindings (PyO3 + maturin)
cd bindings/py && python -m maturin develop && python -m pytest
# Codegen tool
cargo build -p dynwinrt-codegen --release
cargo run -p dynwinrt-codegen -- generate --namespace Windows.Foundation --class-name Uri --output ./generated
Python dynwinrt runtime wheels target
CPython 3.11–3.14 on Windows x64 and ARM64. The standalone
dynwinrt-codegen wheel includes
the prebuilt generator and requires no Rust installation.
Python runtime, codegen, packaging, and WinUI readiness are tracked in
docs/status/PYTHON_CHECKLIST.md.
Python samples cover files, OCR, cryptography, devices, AppLifecycle, text-to-speech, app notifications, WinUI, and custom WinMD generation.
JavaScript samples include:
- WinUI Tic-Tac-Toe — XAML loading, typed projection, generated controls, events, and Mica.
- WinUI Tic-Tac-Toe (code-only) — a programmatic WinUI application without XAML.
- Windows Hello — WinRT async APIs and HWND-bound Classic COM interop.
- Windows AI OCR — a standalone Node.js sample with
generated TextRecognizer bindings, image picker or
--image, and private WinApp CLI identity. - Aion Instruct chat — local Snapdragon NPU inference with streaming progress, cancellation, and multi-turn context in Electron.
- Share UI — typed WinRT and
IDataTransferManagerInterop. - System Media Controls — interactive, end-to-end SMTC publication and GSMTC loopback control with playlist, artwork, live timeline, session discovery, and automated validation.
For deployment, see Package a dynwinrt Node.js application as MSIX.
Codegen CLI reference
| Argument | Required | Description |
|---|---|---|
--winmd PATH[;PATH...] | No | Path to .winmd file(s) (auto-detects Windows SDK if omitted) |
--winmd-list FILE | No | Newline-separated .winmd paths to emit |
--folder PATH | No | Directory containing .winmd files |
--namespace NAMESPACE | No | WinRT namespace to generate (omit for all non-Windows.* namespaces) |
--class-name NAME[,NAME...] | No | Specific classes or public interfaces; dependencies are resolved transitively |
--ref PATH[;PATH...] | No | Additional .winmd files for type resolution only (no code emitted) |
--ref-list FILE | No | Newline-separated reference metadata paths |
--lang LANG | No | js (default, emits .js + .d.ts) or py (emits .py + .pyi and py.typed) |
--import-name NAME | No | JavaScript runtime import name (default @microsoft/dynwinrt) |
--pyi | No | Explicitly request the default Python type stubs |
--no-pyi | No | With --lang py, emit implementation files without type stubs |
--output DIR | No | Output directory (default ./generated) |
--dry-run | No | Validate input, don't write files |
For the complete language-specific behavior and examples, see the JavaScript/TypeScript and Python codegen package guides.
JavaScript local development — fix generated imports
Generated files import from '@microsoft/dynwinrt'. When iterating against a locally-built runtime, rewrite imports to the relative path:
find generated -name "*.js" -exec sed -i "s|from '@microsoft/dynwinrt'|from '../../dist/winrt.js'|g" {} +
Troubleshooting
| Problem | Solution |
|---|---|
cargo build fails with libffi errors | Ensure you have a C compiler (MSVC) and the Windows SDK installed |
cargo test -p dynwinrt fails | Windows SDK must be installed at the default path with Windows.winmd |
| JS bindings won't build | Run npm install first; requires Node.js 18+ |
| Python bindings won't build | Requires CPython 3.11–3.14 and maturin (python -m pip install maturin) |
| Codegen snapshot tests fail after an intentional change | Set DYNWINRT_UPDATE_SNAPSHOTS=1 for JavaScript snapshots or DYNWINRT_UPDATE_PY_SNAPSHOTS=1 for Python snapshots, then rerun the affected test |
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 automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos 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 opencode@microsoft.com with any additional questions or comments.
Trademarks
This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft trademarks or logos is subject to and must follow Microsoft's Trademark & Brand Guidelines. Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies.
License
This project is licensed under the MIT License.