Tooling to generate metadata for Win32 APIs in the Windows SDK.
Перейти к файлу
Steve Otteson affae63de8 Fixes #782 - DEFINE_DEVPROPKEY should emit a DEVPROPKEY, not PROPERTYKEY 2022-03-30 18:22:34 -07:00
.github Added CODEOWNERS. 2022-02-10 10:06:02 -08:00
.vscode * Fix several issues caused by the partitions change 2021-01-12 15:11:15 -08:00
BuildTools Move to sdk version 22000 plus a ton of changes in header coverage (#697) 2021-10-13 21:32:51 -07:00
apidocs Get the docs package to work for lower TFM projects (#686) 2021-09-28 15:12:22 -07:00
buildTransitive Create msbuild ProjectionMetadataWinmd item in package import 2021-09-01 10:01:32 -06:00
designs Update the docs to reflect the latest changes 2021-01-20 13:22:09 -08:00
docs Documentation updates. 2021-09-29 12:06:14 -07:00
ext Add API Docs package build to pipeline 2021-06-24 11:52:46 -06:00
generation/WinSDK Fixes #782 - DEFINE_DEVPROPKEY should emit a DEVPROPKEY, not PROPERTYKEY 2022-03-30 18:22:34 -07:00
images User/sotteson/msbuildsdk (#475) 2021-05-13 21:31:28 -07:00
scripts Fixes #782 - DEFINE_DEVPROPKEY should emit a DEVPROPKEY, not PROPERTYKEY 2022-03-30 18:22:34 -07:00
sources Fixes #782 - DEFINE_DEVPROPKEY should emit a DEVPROPKEY, not PROPERTYKEY 2022-03-30 18:22:34 -07:00
tests Fixes #846 - Fix some delegates that weren't emitted correctly 2022-03-30 16:02:26 -07:00
.editorconfig Incremental build system (#670) 2021-09-23 21:38:28 -07:00
.gitattributes Make .zip, .winmd use git lfs (#573) 2021-07-13 13:24:05 -07:00
.gitignore Fixes #837 - Allow for the removal of attributes when emitting the winmd (#857) 2022-03-28 16:57:19 -07:00
.gitmodules * Fix several issues caused by the partitions change 2021-01-12 15:11:15 -08:00
CODE_OF_CONDUCT.md
CONTRIBUTING.md Added notes about removing attributes. 2022-03-28 22:38:24 -07:00
Directory.Build.props Incremental build system (#670) 2021-09-23 21:38:28 -07:00
Directory.Build.targets Adding empty Directory.Build.props/targets to avoid picking up external ones 2021-01-30 09:34:05 -08:00
DoAll.ps1 More namespace cleanup 2021-11-09 00:25:00 -08:00
LICENSE Initial LICENSE commit 2020-06-08 14:52:18 -07:00
README.md Incremental build system (#670) 2021-09-23 21:38:28 -07:00
SECURITY.md
azure-pipelines.yml Update the service name we use for signing 2022-01-27 09:56:19 -08:00
global.json Update global.json to use latest nuget.org release of Microsoft.Windows.WinmdGenerator 2021-09-24 13:13:40 -07:00
nuget.config Build authoring cleanup (#630) 2021-09-02 20:36:15 -07:00
release.yml Fix release pipeline 2021-10-14 08:34:25 -07:00
strongname.snk Add API Docs package build to pipeline 2021-06-24 11:52:46 -06:00
version.json Fixes #782 - DEFINE_DEVPROPKEY should emit a DEVPROPKEY, not PROPERTYKEY 2022-03-30 18:22:34 -07:00

README.md

Overview

Win32 APIs provide functionality that not all languages and frameworks support themselves. When developing for Windows, developers often call into Win32 APIs to access this functionality.

Historically, this has required manually redefining the APIs to make them accessible, which is fragile and error-prone. Community projects like https://github.com/dotnet/pinvoke (.NET) and https://github.com/retep998/winapi-rs (Rust) have taken on the burden of providing strongly-typed and validated API signatures for their frameworks, but the projects are manually maintained, which is hard to sustain and makes it challenging to provide thorough API coverage.

This project aims to provide metadata for Win32 APIs such that idiomatic projections and projects like the ones above can be generated for all languages and frameworks in a more automated way and with more complete API coverage.

To call Win32 APIs from the language of your choice based off of this metadata, use the following language projections:

Note: Community projects are listed here to help with discovery but are not officially validated by Microsoft.

See the roadmap and FAQ for more details.

If you'd like to browse the metadata to see what we're emitting, download Windows.Win32.winmd and Windows.Win32.Interop.dll into the same folder and load Windows.Win32.winmd in ILSpy.

We also publish the metadata via the Microsoft.Windows.SDK.Win32Metadata NuGet package.

ILSpy with winmd

Principles

Below are some principles that guide the metadata that we produce:

  • Provide the broadest API coverage possible
  • Keep the names of the original APIs, but express in metadata additional information that can make them easier to use.
  • Convert non-specific types like uint that use constants into explicit enums to improve usability and discoverability. Keep enum member names consistent with the original constant names to preserve SEO.
  • Express Win32 resources like HANDLE and GDI objects as strongly-typed structs. The definition of these structs include how to dispose of the resources (like CloseHandle or DeleteObject). It is up to language projections to make use of this information in a language-specific way. For example, a C# projection could use SafeHandle objects for HANDLE and GDI objects.

Architecture

This project uses ClangSharp to scrape Windows SDK headers into C# files. It uses libraries from the Windows SDK to figure out what the DLL imports are for each API function. The project is split into partitions that roughly translate into namespaces. ClangSharp creates a .cs file for each partition that it processes.

Once the C# files are written by ClangSharp, the emitter turns these files into a Windows Metadata (.winmd) file. Although this is an ECMA-335 binary, it is not directly loadable by the CLR.

The resulting .winmd is packaged as a NuGet package which can be used to create language projections for other languages such as C#, modern C++, and Rust.

ClangSharp Overview

ClangSharp emits C# as it encounters types found in C/C++ headers. It will only emit types for headers included in its "traverse" list.

Example for Direct3DDxgi:

generation/WinSDK/Partitions/Direct3DDxgi/main.cpp:

#include <winnt.h>
#include <winerror.h>
#include <dxgi.h>
#include <dxgi1_2.h>
#include <dxgi1_3.h>
#include <dxgi1_4.h>
#include <dxgi1_5.h>
#include <dxgi1_6.h>
#include <dxgidebug.h>
#include <dxgitype.h>
#include <dxgicommon.h>
#include <dxgiformat.h>

generation/WinSDK/Partitions/Direct3DDxgi/settings.rsp:

--traverse
<IncludeRoot>/shared/dxgitype.h
<IncludeRoot>/shared/dxgiformat.h
<IncludeRoot>/shared/dxgicommon.h
<IncludeRoot>/shared/dxgi.h
<IncludeRoot>/shared/dxgi1_2.h
<IncludeRoot>/shared/dxgi1_4.h
<IncludeRoot>/shared/dxgi1_6.h
<IncludeRoot>/um/dxgidebug.h
<IncludeRoot>/shared/dxgi1_3.h
<IncludeRoot>/shared/dxgi1_5.h
--namespace
Windows.Win32.Graphics.Dxgi

This means ClangSharp will emit types from the above list of headers when scraping for dxgi.lib. The compiler will see lots of other headers, like what windows.h brings in, but it will only emit types seen in the list above.

ClangSharp and Remaps

ClangSharp emits C# code as it goes. For example, when it sees this type:

typedef struct tagRECT
{
    LONG    left;
    LONG    top;
    LONG    right;
    LONG    bottom;
} RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;

It starts emitting:

public partial struct tagRect
{
    public int left;

It has no way of knowing a typedef is coming (RECT). But, we can feed data into ClangSharp that tells it to rename tagRECT to RECT:

generation/WinSDK/scraper.settings.rsp

tagRECT=RECT

Now when ClangSharp encounters tagRECT it will automatically change the name it uses to RECT.

Winmd Emitter Overview

ClangSharp was designed to create C#-compilable code from Win32 headers. Because its goal is to create C#-compilable code while also preserving pointers, it can't always express things in the way we would like for metadata which is meant to be language-agnostic. For example, the CLR will not allow managed types such as "interface" or "delegate" to be on an unsafe struct (a struct that gets pointed to or includes pointers). This means ClangSharp emits COM objects as structs instead of interfaces, so that a COM object can exist on an unsafe struct.

The winmd emitter takes the C#-compilable source created by ClangSharp and emits it into a .winmd. A .winmd can define an interface, have a struct use it as a field type, and have a function parameter point at the struct. However, the CLR will not be able to load it because it's invalid to the CLR.

The emitter also looks at SAL attributes that ClangSharp outputs for parameters and adds metadata attributes for const, in/out, COM out pointers, etc. It will also mark fields and parameters via attributes as null-terminated strings while preserving the original pointer type. It is up to consumers of the fields and parameters to interpret the metadata and turn them into language-appropriate types such as a string.

Contributing

See CONTRIBUTING.md.

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.