microsoft/win32metadata

repository·main·Indexed 23 days ago

https://github.com/microsoft/win32metadata

A project providing automated, high-coverage metadata for Win32 APIs used to generate idiomatic, strongly-typed projections for languages such as C# (cswin32) and Rust (windows-rs). It utilizes a three-layer architecture consisting of a Scraper layer (using ClangSharp and ConstantsScraper), an Emitter layer (using ClangSharpSourceCompilation), and the WinmdGenerator MSBuild project SDK to convert C/C++ header files into .winmd metadata files.

Tokens
17.4K
Snippets
22
Records
83
Agent score
78%

What's inside win32metadata

  1. Overview of the win32metadata project

    main

    The win32metadata repository is an open-source project designed to generate machine-readable metadata for the entire Win32 API surface. It produces a Windows.Win32.winmd file in the ECMA-335 binary format, which is distributed via the Microsoft.Windows.SDK.Win32Metadata NuGet package.

    Developers use this metadata to power language projections (such as C#, Rust, Python, and Zig) that automatically generate idiomatic Win32 API bindings. This approach replaces the need for manual, error-prone P/Invoke definitions.

  2. What is Win32Metadata and how to use it

    main

    Win32Metadata is an open-source project that generates machine-readable metadata (Windows.Win32.winmd in ECMA-335 format) for the entire Win32 API surface. This metadata is used by language projection tools to auto-generate idiomatic API bindings for various programming languages.

    Key Output Packages

    • Microsoft.Windows.SDK.Win32Metadata (NuGet): Contains the Windows.Win32.winmd file, which provides the ECMA-335 metadata for the Win32 API.
    • Microsoft.Windows.WinmdGenerator (NuGet): An MSBuild SDK used to generate .winmd files from C/C++ headers.
    • Microsoft.Windows.SDK.Win32Docs (NuGet): Contains API documentation in MessagePack binary format.

    Supported Language Projections

    • C#: via CsWin32
    • Rust: via windows-rs
    • Community: Python, Zig, D, Dart, Beef, and .NET Standard.
  3. Understand the win32metadata architecture

    main

    The win32metadata project is organized into three primary layers that convert C/C++ header files into .winmd metadata files:

    1. Scraper Layer: Traverses header files and generates intermediate C# files. It uses ClangSharp for general header traversal and ConstantsScraper for extracting constants via regex.
    2. Emitter Layer: Traverses the generated C# files and produces the final Windows.Win32.winmd files. It uses ClangSharpSourceCompilation to transform CLR-compliant C# (which has specific restrictions on unsafe structs) into language-agnostic metadata.
    3. WinmdGenerator: An MSBuild project SDK that packages the Scraper and Emitter into a single interface, allowing developers to convert C/C++ projects to winmds using a no-code project file configuration.
  4. Identify the outputs of the win32metadata repository

    main

    The repository produces three primary outputs distributed via NuGet packages:

    1. Windows.Win32.winmd (Package: Microsoft.Windows.SDK.Win32Metadata): An ECMA-335 metadata binary describing the entire Win32 API surface.
    2. WinmdGenerator SDK (Package: Microsoft.Windows.WinmdGenerator): An MSBuild project SDK used to generate .winmd files from arbitrary C/C++ projects.
    3. API Documentation (Package: Microsoft.Windows.SDK.Win32Docs): A MessagePack binary (apidocs.msgpack) containing Win32 API documentation.

    These outputs are consumed by language projections such as CsWin32 (C#) and windows-rs (Rust), as well as community projections for Dart, Python, Zig, D, and Beef.

  5. How win32metadata architecture and generation works

    main

    The project uses a sophisticated pipeline to transform C/C++ Win32 headers into a structured .winmd file. Understanding these core concepts is essential for working with the metadata:

    Partition-based Namespaces

    The Win32 API is organized into approximately 240 partitions. Each partition maps specific header files to a logical Windows.Win32.* namespace, providing a structured way to navigate thousands of APIs.

    Multi-Architecture Scraping

    To handle architecture-specific type differences, headers are scraped independently for x64, x86, and arm64. A CrossArchTreeMerger then combines these into a unified representation, applying [SupportedArchitecture] attributes to relevant types.

    Layered Configuration

    Configuration follows a hierarchy from general to specific:

    1. baseSettings.rsp (SDK-provided)
    2. scraper.settings.rsp (Project-wide)
    3. Individual partition settings.rsp (Per-partition customization)

    Manual Overrides

    For complex APIs that automated tools like ClangSharp cannot handle correctly (such as certain COM interfaces or complex DirectX types), the project maintains a manual/ folder containing hand-written C# files to ensure accuracy.

  6. How the Emitter layer works

    main

    The Emitter layer converts generated C# files into ECMA-335 compliant .winmd files. Because ClangSharp generates code intended to be compilable by the CLR, it often uses patterns (like representing COM objects as structs instead of interfaces) that are not ideal for language-agnostic metadata. The Emitter corrects this.

    ClangSharpSourceCompilation

    Orchestrates the manipulation and compilation of C# files using several specialized classes:

    • NamesToCorrectNamespacesMover: Moves APIs to specific namespaces based on requiredNamespacesForNames.rsp.
    • MetadataSyntaxTreeCleaner: Visits C# Abstract Syntax Tree (AST) nodes to apply remaps and custom attributes.
    • CrossArchTreeMerger: Merges C# files from multiple architectures to identify architecture-specific APIs.

    ClangSharpSourceWinmdGenerator

    Walks the final C# AST and writes each node to the Windows.Win32.winmd file.

  7. Handle different enum member naming patterns

    main

    When migrating #define groups to enums, you will encounter three primary patterns:

    1. Renamed Members: If the enum member name differs from the #define name, simply reference the #define directly as the value. No macro conflicts occur.
    2. Same-Name Members: If the enum member name is identical to the existing #define, use #pragma push_macro, #undef, and #pragma pop_macro to suppress the macro expansion inside the enum declaration. This prevents the preprocessor from expanding the name before the enum can define it.
    3. Signed/Unsigned Overflow: For values that require casting (e.g., 0xFFFFFFFF into a signed int), use a C cast. ClangSharp will automatically generate the necessary unchecked() block in the resulting metadata.
    // Pattern 2: Same-name members
    #pragma push_macro("FILE_ATTRIBUTE_READONLY")
    #undef FILE_ATTRIBUTE_READONLY
    
    enum class [[clang::flag_enum]] FILE_ATTRIBUTE_FLAGS : DWORD {
        FILE_ATTRIBUTE_READONLY = 0x00000001,
    };
    DEFINE_ENUM_FLAG_OPERATORS(FILE_ATTRIBUTE_FLAGS)
    
    #pragma pop_macro("FILE_ATTRIBUTE_READONLY")
    
    // Pattern 3: Signed/unsigned overflow
    enum class OBJECT_IDENTIFIER : int {
        OBJID_WINDOW  = 0,
        OBJID_SYSMENU = (int)0xFFFFFFFF, // Generates unchecked() in metadata
    };
  8. Configure bitmask enums with Clang attributes and macros

    main

    To ensure bitmask enums are correctly identified as [Flags] in metadata and support bitwise operations in C++, use a combination of two mechanisms:

    1. [[clang::flag_enum]]: A Clang attribute that marks the enum as a bitmask. This provides compiler diagnostics and signals intent to the metadata generator.
    2. DEFINE_ENUM_FLAG_OPERATORS(NAME): A Windows SDK convention (from winnt.h) that enables bitwise operators (|, &, ^, ~) on the enum type. The ConstantsScraper tool detects this macro to apply the [Flags] attribute to the generated C# code.
  9. Win32MetadataScraper Component Overview

    main

    The scraping architecture consists of three primary components:

    Win32MetadataScraper

    Located in sources/Win32MetadataScraper/Program.cs, this is the main entry point. It:

    • Parses RSP files for PInvokeGenerator settings.
    • Walks the AST using RemapDiscovery.WalkTranslationUnit.
    • Merges auto-discovered remaps with configured ones.
    • Runs PInvokeGenerator.GenerateBindings().
    • Writes the results to a .remaps sidecar file.

    RemapDiscovery

    Located in sources/Win32MetadataScraper/RemapDiscovery.cs, this static class handles the AST logic:

    • WalkTranslationUnit(): Finds tag→typedef and function pointer prototype→alias relationships.
    • ResolveTagRemaps(): Uses heuristics (stripping _ or tag prefixes, case-insensitive matching) to disambiguate tags.
    • FilterTagRemaps(): Removes identity remaps, built-in defaults, or conflicts.
    • ResolveFunctionPointerFixups(): Classifies pairs by pointer prefixes (LP, PFN, P) to determine the correct remap direction.

    ScrapeHeaders

    Located in sources/GeneratorSdk/MetadataTasks/ScrapeHeaders.cs, this manages the build process:

    • Spawns Win32MetadataScraper.dll in isolated dotnet processes per partition.
    • Aggregates .remaps sidecar files into thread-safe dictionaries.
    • Writes the final merged RSP files via WriteAutoRemapsRsp().
  10. Understand the win32metadata pipeline

    main

    The win32metadata project derives Windows Metadata (.winmd) from Windows SDK C/C++ headers using a three-stage pipeline:

    1. ClangSharp: Parses headers via Clang's AST to generate C# declarations for functions, structs, COM interfaces, and typedefs.
    2. ConstantsScraper: Performs raw text/regex scanning of headers to extract #define constants and assemble enums that are not present in the AST.
    3. The Emitter (ClangSharpSourceToWinmd): Compiles the generated C# sources, applies transformations (remaps, attribute additions), and writes the final Windows.Win32.winmd file.
    SDK C/C++ Headers
          │
          ├──[ClangSharp]──────────> Generated C# (functions, structs, COM interfaces)
          │                               │
          ├──[ConstantsScraper]────> Generated C# (constants, enums)
          │                               │
          └──[Manual C# overrides]────────┤
                                          │
                                  [Emitter (ClangSharpSourceToWinmd)]
                                          │
                                          ▼
                                  Windows.Win32.winmd
  11. Understand the Shift-Left Metadata Plan

    main

    The 'Shift-Left Metadata' initiative aims to move metadata currently stored in sidecar files (like .rsp files and autoTypes.json) directly into Windows SDK headers using C-style annotations. This improves metadata durability and reduces the maintenance burden of external mapping files.

    Key Metadata Categories:

    • Category A (Shift-Left Target): Metadata that belongs in headers (e.g., _Sets_last_error_, _Min_os_version_, _Must_close_with_).
    • Category B (Header Refactoring): Metadata requiring structural header changes (e.g., converting #define constants to enum types).
    • Category C (Tooling Configuration): Metadata that stays in sidecar files because it is specific to win32metadata tooling (e.g., namespace organization, documentation URLs, and scraper settings).
  12. Use SAL-style annotations for metadata

    main

    To embed tool-readable metadata into C/C++ headers without affecting compilation, use a SAL-like annotation mechanism. This allows metadata to be embedded in functions, parameters, struct fields, and return values.

    When using Clang (which ClangSharp utilizes), these annotations are implemented via __attribute__((annotate(...))). When using MSVC, they use __declspec(...). If no analysis tool is running (e.g., _PREFAST_ is not defined), these annotations expand to nothing, ensuring zero runtime or compilation cost.

    // When Clang is the compiler (ClangSharp uses Clang):
    #if __clang__
      #define _SA_annotes0(n)   __attribute__((annotate("Name=" #n)))
      #define _SA_annotes1(n,p) __attribute__((annotate("Name=" #n "; p1=" #p)))
    
    // When MSVC is the compiler:
    #else
      #define _SA_annotes0(n)   __declspec(#n)
      #define _SA_annotes1(n,p) __declspec(#n "(" #p ")")
    #endif
    
    // When no analysis tool is running (_PREFAST_ not defined):
    // All of the above expand to nothing — zero cost