NativeInvoke is a modern, zero-overhead, generics-capable P/Invoke generator for .NET.
It uses Roslyn source generation to enforce blittable, function-pointer based, lazy-loaded native bindings - without the runtime overhead of DllImport.
You write clean interfaces.
NativeInvoke generates the unsafe bits for you at compile-time.
- Cross-platform and AOT/JIT-friendly.
- No
DllImport. - No delegate allocation (no pinning).
- No runtime dependencies.
- No marshalling.
- No reflection.
- No dynamic codegen (dynamic IL).
- Just pure compile-time generation glue.
Install the NuGet package:
dotnet add package NativeInvokeOr edit your .csproj to always stay up-to-date (followed by dotnet restore --no-cache):
<ItemGroup>
<PackageReference Include="NativeInvoke" Version="*"/>
</ItemGroup>How floating versions work:
*-*: Latest version including pre-releases (e.g.,1.1.0-beta.1,2.0.0-alpha.2)*: Latest stable version only1.*: Latest stable version with major version 11.2.*: Latest stable version with major.minor 1.2
| Feature | Benefit |
|---|---|
| Source-generated | Zero runtime overhead |
| Function pointers | Faster than DllImport |
| Lazy-loading support | Load symbols/functions only when needed |
| Interface-based | Fully mockable for testing |
| Generics support | Use generics in P/Invoke |
| No static pollution | Clean public API surface |
.NET 9 Lock support |
Modern, allocation-free synchronization |
- C# 14 / .NET 9 or later
- Unsafe code enabled (
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>) - Roslyn source generators enabled (default in SDK-style projects)
π See Attribute docs.
Checkout the full Example project for more!
Click here to toggle an example for playing a beep sound on Windows platform (a.k.a. System.Console.Beep)
global using NativeInvoke; // Import our attributes in your project
global using NIMA = NativeInvoke.NativeImportMethodAttribute;
using BOOL = int; // Win32 BOOL is 4-bytes (0=false, 1=true)
using DWORD = uint; // double-word
#if NET6_0_OR_GREATER
[System.Runtime.Versioning.SupportedOSPlatform("windows")] // Optional (for clarity)
#endif
public interface IKernel32<TBool> // Generics are supported!
where TBool : unmanaged
{
[NIMA("Beep")] // Optional; Use this attribute if you want to load a different name/ordinal,
// or override a calling convention per function (defaults to platform-specific).
TBool Boop(DWORD frequency, DWORD duration);
[NIMA(null)] // Use null or empty string to skip generation; could also omit this and set ExplicitOnly=true
void IgnoreMe();
}The property can be nested anywhere you want (class/struct/interface/record), and you can use any accessibility level you need - the generator will match your declaration.
public static partial class Win32
{
private const string kernel32 = "kernel32";
[NativeImport(
kernel32 // Specify native library name
, EnforceBlittable = true // Whether to enforce blittable type validation (applies to all methods, can be overriden per-method)
, ExplicitOnly = false // Whether only methods explicitly marked with NIMA should be considered
, Inherited = true // Whether to consider inherited interface methods
, Lazy = false // Whether to use lazy or eager module loading
, CallingConvention = CallingConvention.StdCall // Define the default calling convention (default is platform-specific, applies to all methods, can be overriden per-method)
, SuppressGCTransition = false // Whether to suppress the GC transition (applies to all methods, can be overriden per-method)
, SymbolPrefix = "" // Define common prefix (prepended to method name unless using explicit entry point)
, SymbolSuffix = "" // Define common suffix (appended to method name unless using explicit entry point)
)]
public static partial IKernel32<BOOL> Kernel32 { get; }
}Win32.Kernel32.Boop(600u, 300u);Under the hood, NativeInvoke generates:
- A nested sealed
__Implclass implementing your (generic) interface - Static (readonly) function pointer fields (
delegate* unmanaged) - Lazy or eager symbol resolution (
NativeLibrary) - A clean property implementation using the
fieldkeyword - Thread-safe lazy initialization using .NET 9
Locktype
All without touching your container type.
- Support C# 9 / .NET 5 and later via
#if; current source generator is relying on C# 14 features and .NET 9 API - Add support for loading symbol from numeric ordinal
-
Implement default symbol name prefix and suffix -
AddEnforceBlittableandExplicitOnlyflags - Switch to
[UnmanagedCallConv]/typeof(CallConv*)for future-proofed calling conventions (MemberFunction, Swift, etc.) - Use
IndentedTextWriterfor source-code generation -
AppendGuidto generated fields (to prevent name collisions for overloaded functions) - Make unit tests
- Auto-generate proper page for docs and examples (maybe use GitHub io page or wiki)
- Explore micro-optimization: IL weaver via
Fody, replace interface dispatch andDllImportcalls withcalli
PRs, issues, and ideas are welcome.
NativeInvoke is built for developers who want maximum performance without sacrificing clean API design.
If you like this or you are using this in your project, consider:
- Becoming a β π€©
- Spreading the word
MIT - do whatever you want, just don't blame me if you calli into oblivion.

