Skip to content

Latest commit

 

History

History
300 lines (234 loc) · 14.6 KB

File metadata and controls

300 lines (234 loc) · 14.6 KB

dynwinrt

Call Windows Runtime (WinRT) APIs from JavaScript, TypeScript, or Python — without writing a native extension.

@microsoft/dynwinrt @microsoft/dynwinrt-codegen dynwinrt on PyPI dynwinrt-codegen on PyPI License: MIT

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 .winmd metadata 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.

Scopedynwinrt primarily targets data-style WinRT APIs. WinUI Application + Window hosting 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.

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 project flat DLL exports.

The current CI baseline against Microsoft.Windows.SDK.Win32Metadata 71.0.14-preview is 5,681 of 7,929 eligible interfaces (71.65%) 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, and synchronous JavaScript implementations of fully supported callback 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, audio, WMI, clipboard/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.

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.
  • 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.