Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
The recommended way to call Win32 APIs from C# is CsWin32, a source generator that produces type-safe P/Invoke wrappers at compile time. CsWin32 works with any C# project type—WinUI 3, WPF, WinForms, console, or class library—and eliminates the need to hand-write DllImport or LibraryImport declarations.
You list the Win32 function names you need in a text file, and CsWin32 generates the correct signatures, structs, constants, and COM interfaces automatically from Windows SDK metadata.
Choose an interop approach
| Approach | When to use | Pros | Cons |
|---|---|---|---|
| CsWin32 (recommended) | Any Win32/native API call from C# | Type-safe, generated from official Windows SDK metadata, handles marshaling and structs, AOT-friendly with configuration | Requires NuGet package; generated code is not visible by default |
| LibraryImport (.NET 7+) | One-off calls where you know the exact signature | Source-generated, AOT-compatible, no runtime marshaling | You write and maintain every signature manually |
| DllImport (legacy) | Existing code, or .NET Framework projects | Works everywhere, extensive community examples | Runtime marshaling, error-prone signatures |
| C#/WinRT | Windows Runtime APIs (Windows.* namespaces) |
Projected .NET types, natural C# experience | Only for WinRT APIs, not raw Win32 |
Note
CsWin32's default output uses the .NET runtime marshaller and is not automatically AOT-compatible. For NativeAOT or trimming, enable CsWin32RunAsBuildTask and DisableRuntimeMarshalling—see the CsWin32 AOT guidance.
Tip
If the API you need is in a Windows.* namespace (for example, Windows.Storage or Windows.Media), it's a Windows Runtime API. Use a WinRT projection instead of P/Invoke. See Call interop APIs from a .NET app.
Prerequisites
- Visual Studio 2022 (version 17.4 or later) or the .NET 8+ SDK
- An existing C# project (WinUI 3, WPF, WinForms, or console)
Note
Targeting .NET Framework or .NET Standard? Set <LangVersion>9</LangVersion> (or later) in your project file, and add the System.Memory and System.Runtime.CompilerServices.Unsafe NuGet packages.
Step 1: Install the CsWin32 NuGet package
In your project directory, run:
dotnet add package Microsoft.Windows.CsWin32
CsWin32 generates code that uses pointers and unsafe contexts. The NuGet package enables AllowUnsafeBlocks automatically. If your project explicitly sets <AllowUnsafeBlocks>false</AllowUnsafeBlocks>, remove that line or change it to true, otherwise the generated code won't compile.
Step 2: Request the APIs you need
Create a file named NativeMethods.txt in your project root (next to the .csproj file). Add one API name per line. For this walkthrough, start with a simple function:
GetTickCount
Save the file. CsWin32 reads it at compile time and generates the matching P/Invoke wrapper.
Step 3: Call the generated API
The generated code lives in the Windows.Win32 namespace under a static class called PInvoke. Call it like any other static method:
using Windows.Win32;
// Get the number of milliseconds since the system started.
uint uptime = PInvoke.GetTickCount();
Console.WriteLine($"System uptime: {uptime} ms");
Build your project. If the function name in NativeMethods.txt is valid, the call compiles and runs with no additional work.
Common gotchas
"I can't see the generated code"
CsWin32 is a source generator—its output doesn't appear as files in your project by default. To inspect the generated code:
- In Visual Studio, expand Dependencies > Analyzers > Microsoft.Windows.CsWin32 > Microsoft.Windows.CsWin32.SourceGenerator in Solution Explorer.
- Alternatively, set
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>in your project file to write the generated sources to theobj/folder.
AnyCPU platform target
CsWin32-generated code works with AnyCPU. You don't need to change your platform target for most Win32 calls.
Getting an HWND in WinUI 3
Many Win32 APIs require a window handle. In a WinUI 3 app, get the HWND from your Window instance:
using WinRT.Interop;
var hWnd = WindowNative.GetWindowHandle(this);
Then pass hWnd (as an HWND or nint) to the Win32 function. See Retrieve a window handle (HWND) for details.
Customizing CsWin32 behavior
Create a NativeMethods.json file next to your text file to control generation options such as wide-vs-narrow string marshaling or friendly overloads:
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"emitSingleFile": false,
"public": true
}
See the CsWin32 configuration reference for all options.
Next steps
- Choose your interop approach — decision guide for all Windows interop techniques
- Walkthrough: WinUI 3 app with Win32 interop — a deeper example that customizes a title bar using CsWin32
- CsWin32 on GitHub — source, samples, and issue tracker
- Platform Invoke (P/Invoke) — .NET documentation on P/Invoke fundamentals
- Call interop APIs from a .NET app — for WinRT COM-based interop scenarios (HWND passing, pickers, etc.)
Windows developer