Windows Kit: A new Swift projection for Windows APIs

Hi all,

I would like to share an open-source project I have been working on called Windows Kit, which aims to provide a Swift language projection for both Win32 (C and COM) and WinRT APIs from the Windows SDK, Windows App SDK, and Windows Driver Kit.

See the README for a more approachable and detailed introduction to the background for this project.

The features I have implemented in the projection generator so far include:

  • Pure Swift and cross-platform
  • A Windows Metadata parser made with Swift Binary Parsing (only implementing the subset of ECMA-335 used by Windows Metadata, which saves a lot of work)
  • Downloads Windows Metadata packages from the NuGet Server API in .nupkg
    format (which is just a renamed .zip file) and caches the metadata files
    locally
  • A ZIP format parser also made with Swift Binary Parsing (used to extract the NuGet packages)
  • A Deflate decompressor (used in the ZIP parser) made with a BitSpan
    non-copyable LSB bit reader inspired by ParserSpan from Swift Binary Parsing
    and a two-level lookup table for Huffman decoding like zlib
  • Generates and prints the desired Swift interfaces for classes, enums, and structs using SwiftSyntax

The differences to the existing WinRT projection by The Browser Company from a user's perspective will be the expanded API surface available and distribution as pre-made Swift packages instead of requiring a local CMake setup to run the projection generator. I also plan to use C++ interop for COM and WinRT instead of C interop (as COM was designed to be a subset of the C++ ABI, and WinRT is based on COM) which will be simpler and possibly easier for the compiler to optimise. And it will take more advantage of Clang attributes to let the compiler do much of the projection work on its own.

The main work to do next will be working out how the generated C, C++, and Swift code will interact, completing the projection of each of the constructs in Windows Metadata, and handling the unique aspects of the NuGet packages for each SDK.

The codebase has lots of comments documenting what I've learned about Windows Metadata and keeping it as easy to understand as possible has been a major goal. I have also written a document explaining the Windows Metadata format and strategies I used to maintain safety when parsing it.

I am open to collaborating if anyone is interested. Thanks for reading!

12 Likes

Really nice work here — the parser especially. The Index type is a good call: making the initializer failable so a raw 0 (the reserved null) can’t produce a value means a non-optional Index is itself proof the row exists, and CodedIndex composing on top of it keeps the tag/table dimension type-safe too. That’s the right instinct for a format this full of indices.

I’ve spent a fair bit of time in this same area, so a few notes that might be useful — some of it is recent enough that you may not have seen it yet.

There’s active work toward first-class COM interop in Swift: a @COM attribute where the compiler handles the vtable layout, QueryInterface as as?, AddRef/Release via ARC, and HRESULTthrows — so a COM interface becomes just a @COM protocol, with no annotated-C++-header layer in between. Some of the compiler pieces are already landing behind an experimental flag, and there’s a vision pitch up for it with the two companion design docs linked, if you’d like to dig in: Pitch: A vision for COM Interoperability in Swift

Two things that follow from how it’s structured:

  • The C surface is elevated through the Clang importer (HRESULT → throws, property synthesis, name translation, MIDL array handling), with APINotes supplying accurate SDK types — rather than generated headers, and not gated on any new language feature.

  • WinRT is a pure-library layer on top of the COM foundation — explicitly no compiler changes — so it largely follows once the core lands.

I mention it because a couple of the next steps you described — working out how the generated C/C++/Swift interact, and leaning on Clang attributes for the projection — overlap heavily with what this design takes on directly. Once a COM interface is just a @COM protocol, much of that annotated-header middle layer falls away, which may save you the trickiest part of what you’ve scoped.

For full transparency on where I’m coming from: the existing BCNY projection is built on earlier work of mine, and my plan for it is to move onto the @COM support plus swift-winmd. I’m sharing that so you can see the direction I’m working in, not to tell you where yours should go.

The piece of your work I’d genuinely encourage you to push on is the NuGet handling. Completed and landed in SwiftPM, it would meaningfully improve NuGet support for system packages — valuable well beyond this projection, and independent of the COM work. The per-SDK packaging quirks are real, reusable infrastructure, and I’d be glad to see you own that. It’s something I’d like to see happen, so if you take it on I’m happy to be a sounding board.

5 Likes

Thanks for the detailed reply, Saleem. I've read your pitch and am not sure about some of the details; I would love to hear your perspective on this.

My project relies on metadata from Microsoft's win32metadata project, which describes both C and COM APIs in the same structured format as WinRT with additional semantic information. While my understanding of your plan is for the Clang importer to read C/C++ COM headers in the Windows SDK and automatically elevate them to idiomatic Swift. I think there will be some major challenges with this:

  • A lot of semantic information is lost when MIDL is compiled to C/C++ headers, including [in]/[out] direction and array parameter linking (size_is).
  • Many Win32 COM APIs were authored manually in C/C++ headers without MIDL, for example in Direct2D and DirectWrite.
  • Microsoft has used SAL (Source Code Annotation Language) macros to annotate some of this missing information, but these macros are expanded by the C preprocessor into complex vendor pragmas and are very inconsistent in their application.
  • A lot of semantic information from win32metadata was never in MIDL to begin with, such as mapping disparate macros into [Flags] bitfields, providing memory ownership and lifecycle information, and organising the flat API namespace into logical namespace hierarchies.

You also mention APINotes which could fill in some of this missing information, but the Windows API surface is unimaginably vast and achieving good coverage would effectively require rewriting win32metadata by hand, specifically for Swift.

Microsoft created the win32metadata project because they realised that creating manual API definitions for every programming language was a doomed, unscalable effort and scraping C/C++ headers was fragile and lacked the semantic information needed for modern, safe languages like C# and Rust.

I also think there are a few reasons why COM support at the language level may not be the best solution:

  • Swift's C++ interop already puts it ahead of every other programming language (besides C++ itself) for COM interop since it is a subset of the C++ ABI, and efforts in the compiler to improve it have a strong cross-platform benefit and wide applications. New features like Clang attributes for foreign reference counting are directly applicable to COM.
  • The COM APIs that 99% of people want to use are Windows APIs, which have metadata available for projection generators to use. A @COM attribute in Swift would make authoring COM components easier, but COM interfaces that must be implemented by users to call Windows APIs are also defined in the metadata and projection generators can turn these into Swift protocols just as well. This would not help for authoring or calling third-party COM components with custom interfaces, but that is a very niche activity and I think a separate code generator would be more appropriate than supporting this use case in the language.
  • Windows APIs are a 30+ year archaeological dig of legacy edge cases that I think would be better suited for a library with less strict constraints than the compiler.
2 Likes

We’re after the same thing — idiomatic, safe Swift bindings for the Windows APIs — so this is really a question of which layer solves which part. Let me lay out how I see the layers; I think several of the challenges you’ve listed dissolve depending on where the line is drawn.

Strong agreement on the premises: MIDL → header translation is lossy, plenty of Win32 COM (Direct2D, DirectWrite) was hand-authored without MIDL, and win32metadata carries semantics that are either absent from the headers ([Flags] groupings, namespace hierarchy) or only present inconsistently through SAL (size_is, in/out direction, ownership). No argument there.

That’s the case for the split, not against it. win32metadata describes both a C surface and a COM surface, and they want different treatment:

  • The C surface is imported by the ClangImporter, with APINotes reshaping it. Where we don’t import something well today, the fix belongs in the importer — it lands for every platform’s C interop, not just Windows. A library that reshapes imports scopes that benefit to Windows alone. The Clang attribute work for foreign reference counting is a good example: cross-platform by construction, with COM as one consumer rather than the reason for it.

  • The COM surface is queried directly from the metadata rather than reconstructed from lossy MIDL-derived headers — the metadata is the input, so nothing here asks anyone to hand-author APINotes across the COM surface.

The detail that matters most: C++ interop isn’t a sufficient substrate for COM, because COM is an ABI and the compiler owns the ABI. And “the C++ ABI” isn’t one target — the Itanium ABI on most Unix platforms and the MSVC ABI on Windows differ in exactly the areas COM leans on (object layout, vtable shape, calling convention). That divergence has already been a recurring source of issues for C++ interop; supporting multiple C++ ABIs within a single build is work we started and haven’t finished.

A library that recreates the vtable/object-layout contract re-derives what the compiler already knows per-ABI, tracking the compiler on every layout or calling-convention change rather than being correct by construction. DXSample, an old experimental demo, is a concrete instance: the headers and documentation described one ABI, but the implementation diverged at the ABI layer — precisely because of the C ↔ C++ bridging — and I had to bridge away by hand to reconcile them. The bridging itself can produce an ABI that doesn’t match what’s written down. When the compiler owns the bridge, an object-layout change can’t silently break the projection, because the layout isn’t being guessed at in a library.

So @COM and a metadata-driven projection aren’t competitors — they’re different layers. @COM, the compiler-side work already landing behind the experimental flag, owns the ABI bridge: vtable layout, AddRef/Release via ARC, QueryInterface as as?, HRESULTthrows. A generator owns which interfaces exist and what they mean, emitting against that bridge instead of re-implementing the ABI plumbing itself. That’s how I’d draw the line.

One thing that might save you real time: the WinMD parsing you’re building on Swift Binary Parsing is a large undertaking, and much of it already exists in swift-winmd. It’s a zero-copy ECMA-335 reader — tables and heaps read in place as ~Escapable borrowed views over the caller’s bytes — that projects the metadata as a read-only relational database queryable with typed Swift combinators or SQL, with the COM-interface rules expressed as declarative SQL views rendered through templates. If it covers what you need, it’s yours to build on; if it doesn’t, I’d like to know where it falls short so it can be fixed for everyone.

The NuGet handling is the part that stands apart: not covered elsewhere, reusable well beyond this projection, and independent of how the parser and @COM questions resolve.

The C++ interop and importer work is another area wide open for contribution — the lossy bridging, the multi-ABI divergence, the reference-counting attributes all live there, and improvements benefit every platform rather than Windows alone. Worth digging into if it appeals to you.

3 Likes

I can see how language-level COM interop and projections could work together, but I'm not convinced that the former would be necessary for idiomatic Swift bindings on Windows.

On supporting multiple C++ ABIs in a single build, Clang on Windows defaults to the MSVC ABI, and mixing MSVC and Itanium object files in a single executable isn't possible (or desirable). If you are referring to cross-compilation, that is a compiler implementation hurdle to overcome for C++ interop generally, but doesn't preclude using it for COM on Windows today.

In C++, a COM interface is just a struct or class with pure virtual methods. The COM headers in the Windows SDK are just C++ headers with these declarations, and the APIs they describe just use a subset of the MSVC C++ ABI (what I meant earlier). A projection generator can simply generate the same headers but without the legacy macros and with Clang attributes added for automatic reference counting and other improvements. QueryInterface becomes a generic queryInterface<T>() -> T? method, as done by windows-rs for Rust. It would not need to manually recreate ABI details like the vtable and object layout, as Clang already interoperates with the MSVC ABI on Windows. The compiler retains ownership and optimisation opportunities.

Because the projection generator is strictly necessary anyway to handle win32metadata, it can just as easily synthesize the C++ boilerplate and Clang attributes along the way. So if all the required mechanics already exist in Swift's C++ interop and the generator is already required to synthesize the API semantics, does COM interoperability need to be a first-class language feature?

On swift-winmd, it is great work, but I'm going to keep using my parser for a few reasons:

  • It is already finished, so I'm just focusing on API projection now
  • It only implements the subset of ECMA-335 used by Windows Metadata, which I found by studying the metadata parser in windows-rs (it made the same design choice). This saves validation in the projection generator.
  • My usage of the metadata (and that of windows-rs) is not expressive enough to need combinators or SQL queries
  • I'm very biased since I wrote it, but I find my parser easier to understand which is good for debugging and I think Swift Binary Parsing is really well designed.

On NuGet, the SwiftPM integration is certainly an exciting possibility, but I'm not sure what the benefits would be for Swift users (most NuGet packages are for .NET) or existing NuGet users (who are mostly .NET developers). However, my usage of it is going to become essentially a reimplementation of the NuGet client and I would be happy to work on wider applications if there is a use case for it once Windows Kit is feature-complete.

1 Like

Really great work. @TwirlySeal

One problem we have when using TBCNY's swift-winrt projected code is can't build with Swift 6, because there are many global/static variables initialization conflict with concurrency check. Is that can be fixed in your projection?

Thanks! My projection is not ready for use just yet, but it will definitely support Swift 6 and onwards, and Swift concurrency. I am currently working on code generation for the projection.