csharp-scripts
Run file-based C# apps with the .NET CLI when the user explicitly wants C#/.NET code without creating a project. Use for C# language/API experiments, one-file…
Correctly call native (C/C++) libraries from .NET using P/Invoke and LibraryImport. Covers function signatures, string marshalling, memory lifetime, SafeHandle, and cross-platform patterns. USE FOR: writing new P/Invoke or LibraryImport declarations, reviewing or debugging
$ npx -y skills add dotnet/skills --skill dotnet-pinvoke --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/dotnet-pinvokeContext preview
The summary Claude sees to decide when to auto-load this skill.
Correctly call native (C/C++) libraries from .NET using P/Invoke and LibraryImport. Covers function signatures, string marshalling, memory lifetime, SafeHandle, and cross-platform patterns. USE FOR: writing new P/Invoke or LibraryImport declarations, reviewing or debugging
name: dotnet-pinvoke description: > Correctly call native (C/C++) libraries from .NET using P/Invoke and LibraryImport. Covers function signatures, string marshalling, memory lifetime, SafeHandle, and cross-platform patterns. USE FOR: writing new P/Invoke or LibraryImport declarations, reviewing or debugging existing native interop code, wrapping a C or C++ library for use in .NET, diagnosing crashes, memory leaks, or corruption at the managed/native boundary. DO NOT USE FOR: COM interop, C++/CLI mixed-mode assemblies, or pure managed code with no native dependencies. license: MIT
Calling native code from .NET is powerful but unforgiving. Incorrect signatures, garbled strings, and leaked or freed memory are the most common sources of bugs — all can manifest as intermittent crashes, silent data corruption, or access violations far from the actual defect.
This skill covers both `DllImport` (available since .NET Framework 1.0) and `LibraryImport` (source-generated, .NET 7+). When targeting .NET Framework, always use `DllImport`. When targeting .NET 7+, prefer `LibraryImport` for new code. When native AOT is a requirement, `LibraryImport` is the only option.
| Input | Required | Description | |-------|----------|-------------| | Native header or documentation | Yes | C/C++ function signatures, struct definitions, calling conventions | | Target framework | Yes | Determines whether to use `DllImport` or `LibraryImport` | | Target platforms | Recommended | Affects type sizes (`long`, `size_t`) and library naming | | Memory ownership contract | Yes | Who allocates and who frees each buffer or handle |
**Agent behavior:** When documentation and native headers diverge, always trust the header. Online documentation (including official Win32 API docs) frequently omits or simplifies details about types, calling conventions, and struct layout that are critical for correct P/Invoke signatures.
---
| Aspect | `DllImport` | `LibraryImport` (.NET 7+) | |--------|-------------|---------------------------| | **Mechanism** | Runtime marshalling | Source generator (compile-time) | | **AOT / Trim safe** | No | Yes | | **String marshalling** | `CharSet` enum | `StringMarshalling` enum | | **Error handling** | `SetLastError` | `SetLastPInvokeError` | | **Availability** | .NET Framework 1.0+ | .NET 7+ only |
The most dangerous mappings — these cause the majority of bugs:
| C / Win32 Type | .NET Type | Why | |----------------|-----------|-----| | `long` | **`CLong`** | 32-bit on Windows, 64-bit on 64-bit Unix. With `LibraryImport`, requires `[assembly: DisableRuntimeMarshalling]` | | `size_t` | `nuint` / `UIntPtr` | Pointer-sized. Use `nuint` on .NET 8+ and `UIntPtr` on earlier .NET. Never use `ulong` | | `BOOL` (Win32) | `int` | Not `bool` — Win32 `BOOL` is 4 bytes | | `bool` (C99) | `[MarshalAs(UnmanagedType.U1)] bool` | Must specify 1-byte marshal | | `HANDLE`, `HWND` | `SafeHandle` | Prefer over raw `IntPtr` | | `LPWSTR` / `wchar_t*` | `string` | UTF-16 on Windows (lowest cost for `in` strings). Avoid in cross-platform code — `wchar_t` width is compiler-defined (typically UTF-32 on non-Windows) | | `LPSTR` / `char*` | `string` | Must specify encoding (ANSI or UTF-8). Always requires marshalling cost for `in` parameters |
**For the complete type mapping table, struct layout, and blittable type rules**, see [references/type-mapping.md](references/type-mapping.md).
> ❌ **NEVER** use `int` or `long` for C `long` — it's 32-bit on Windows, 64-bit on Unix. Always use `CLong`. > ❌ **NEVER** use `ulong` for `size_t` — causes stack corruption on 32-bit. Use `nuint` or `UIntPtr`. > ❌ **NEVER** use `bool` without `MarshalAs` — the default marshal size is wrong.
Given a C header:
int32_t process_records(const Record* records, size_t count, uint32_t* out_processed);
**DllImport:**
[DllImport("mylib")]
private static extern int ProcessRecords(
[In] Record[] records, UIntPtr count, out uint outProcessed);**LibraryImport:**
[LibraryImport("mylib")]
internal static partial int ProcessRecords(
[In] Record[] records, nuint count, out uint outProcessed);Calling conventions only need to be specified when targeting Windows x86 (32-bit), where `Cdecl` and `StdCall` differ. On x64, ARM, and ARM64, there is a single calling convention and the attribute is unnecessary.
**Agent behavior:** If you detect that Windows x86 is a target — through project properties (e.g., `<PlatformTarget>x86</PlatformTarget>`), runtime identifiers (e.g., `win-x86`), build scripts, comments, or developer instructions — flag this to the developer and recommend explicit calling conventions on all P/Invoke declarations.
// DllImport (x86 targets)
[DllImport("mThis repository contains the .NET team's curated set of core skills and custom agents for coding agents. For information about the Agent Skills standard, see agentskills.io.
Repo: dotnet/skills
Run file-based C# apps with the .NET CLI when the user explicitly wants C#/.NET code without creating a project. Use for C# language/API experiments, one-file…
Set up NuGet trusted publishing (OIDC) on a GitHub Actions repo — replaces long-lived API keys with short-lived tokens. USE FOR: trusted publishing, NuGet…
Design, implement, optimize, and review SIMD code in .NET. USE FOR: vectorizing scalar loops with TensorPrimitives, Vector64/128/256/512, or platform hardware…
Guides technology selection and implementation of AI and ML features in .NET 8+ applications using ML.NET, Microsoft.Extensions.AI (MEAI), Microsoft Agent…
Configure OpenTelemetry distributed tracing, metrics, and logging in ASP.NET Core using the .NET OpenTelemetry SDK. Use when adding observability, setting up…
Guides conversion of a pre-.NET 8 Blazor Server app into a .NET 8+ Blazor Web App. USE FOR: migrating apps that use AddServerSideBlazor and MapBlazorHub to the…