microsoft / CsWin32

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.
MIT License
1.99k stars 84 forks source link

C#/Win32 P/Invoke Source Generator

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.

NuGet (prerelease) NuGet (daily)

Build Status

Features

Animation demonstrating p/invoke code generation

Prerequisites

The .NET 5 SDK or Visual Studio 2019 Update 8 (16.8) for the C# compiler that added support for Source Generators. The experience with source generators in Visual Studio is still improving, and is noticeably better in VS 16.9. WPF projects have additional requirements.

In addition, some generated code may require use of the C# 9 language version (<LangVersion>9</LangVersion>) in your project file. See issue #4 for more on this.

See dotnet/pinvoke for precompiled NuGet packages with Win32 P/Invokes.

Usage

Install the Microsoft.Windows.CsWin32 package:

dotnet add package Microsoft.Windows.CsWin32 --prerelease

You should also install the System.Memory and System.Runtime.CompilerServices.Unsafe packages when targeting .NET Framework 4.5+ or .NET Standard 2.0, as these add APIs that significantly improve much of the code generated by CsWin32:

dotnet add package System.Memory
dotnet add package System.Runtime.CompilerServices.Unsafe

Projects targeting .NET Core 2.1+ or .NET 5+ do not need to add these package references, although it is harmless to do so.

Note that while the System.Memory package depends on the System.Runtime.CompilerServices.Unsafe package, referencing the latter directly is still important to get the latest version of the APIs it provides.

Your project must allow unsafe code to support the generated code that will likely use pointers. This does not automatically make all your code unsafe. Use of the unsafe keyword is required anywhere you use pointers. The source generator NuGet package sets the default value of the AllowUnsafeBlocks property for your project to true, but if you explicitly set it to false in your project file, generated code may produce compiler errors.

Create a NativeMethods.txt file in your project directory that lists the APIs to generate code for. Each line may consist of one of the following:

When generating any type or member, all supporting types will also be generated.

Generated code is added directly in the compiler. An IDE may make this generated code available to view through code navigation commands (e.g. Go to Definition) or a tree view of source files that include generated source files.

Assuming default settings and a NativeMethods.txt file with content that includes CreateFile, the P/Invoke methods can be found on the Windows.Win32.PInvoke class, like this:

using Windows.Win32;

PInvoke.CreateFile(/*args*/);

Constants are defined on the same class as the p/invoke methods (by default, the Windows.Win32.PInvoke class).

Other supporting types are defined within or under the Windows.Win32 namespace. Discovery of the namespace for a given type can be done with the Go To All feature (Ctrl+T) in Visual Studio with the type name as the search query.

A project may include many NativeMethods.txt files (each one necessarily in its own directory). CsWin32 will read them all to generate APIs, provided these files are included as AdditionalFiles in the project. A NativeMethods.txt file directly in the project directory is added automatically to AdditionalFiles. Files in other directories must be added to the project file manually.

Whether API requests are all in a single NativeMethods.txt file or split across many makes no difference to the generated result. We recommend using just one NativeMethods.txt file and keeping it sorted for easy bookkeeping. Multiple files perhaps makes the most sense in a Shared Project scenario where several API requests will be common across many projects, so sharing a NativeMethods.txt file with those same projects that contain all the necessary APIs for the set of shared source files make maintenance easier.

Some APIs require targeting a specific architecture and are not available when your C# project compiles as "Any CPU". Learn more about how this manifests and what your options are.

Customizing generated code

Several aspects of the generated code can be customized, including:

To configure these settings, create a NativeMethods.json file in your project directory. Specifying the $schema property that points to the schema adds completions, descriptions and validation in many JSON editors, and in fact is where all the documentation for the available settings is found.

{
  "$schema": "https://aka.ms/CsWin32.schema.json",
  "emitSingleFile": false
}

Most generated types include the partial modifier so you can add your own members to that type within your code.

When you need to replace a generated type, simply copy and paste it from generated code into your own source files and remove the partial modifier. Be sure to keep the name and namespace exactly the same. CsWin32 will notice that your project already declares the type and skip generating it, but generate everything else. Note that if that type is the only thing that references some other generated type, CsWin32 will stop generating that type too. To keep CsWin32 generating the referred types you need, add them explicitly to NativeMethods.txt.

Newer metadata

To update the metadata used as the source for code generation, you may install a newer Microsoft.Windows.SDK.Win32Metadata package:

dotnet add package Microsoft.Windows.SDK.Win32Metadata --prerelease

CsWin32 also consumes the WDK from a similarly named package: Microsoft.Windows.WDK.Win32Metadata.

Consuming daily builds

Can't wait for the next release to try out a bug fix? Follow these steps to consume directly from our daily build.

Just add this package feed to your nuget.config file:

   <add key="winsdk" value="https://pkgs.dev.azure.com/azure-public/winsdk/_packaging/CI/nuget/v3/index.json" />