Analyze Crash
DataDog/dd-trace-dotnet
Stack Trace Crash Analysis for dd-trace-dotnet. An agent skill from DataDog/dd-trace-dotnet.
Helps write, review and debug .NET declarations that call native C and C++ libraries, covering signatures, string marshalling, memory lifetime and SafeHandle.
$ npx skills add dotnet/skills --skill dotnet-pinvoke -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install dotnet/skills dotnet-pinvoke --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/dotnet-advanced/skills/dotnet-pinvoke .claude/skills/dotnet-pinvoke && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "dotnet-pinvoke" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvoke into .claude/skills/dotnet-pinvoke/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dotnet-pinvoke", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvokeType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add dotnet/skills --skill dotnet-pinvoke -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install dotnet/skills dotnet-pinvoke --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/dotnet-advanced/skills/dotnet-pinvoke .agents/skills/dotnet-pinvoke && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "dotnet-pinvoke" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvoke into .agents/skills/dotnet-pinvoke/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dotnet-pinvoke", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dotnet/skills --skill dotnet-pinvoke -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install dotnet/skills dotnet-pinvoke --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/dotnet-advanced/skills/dotnet-pinvoke .cursor/skills/dotnet-pinvoke && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "dotnet-pinvoke" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvoke into .cursor/skills/dotnet-pinvoke/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dotnet-pinvoke", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/dotnet/skills.git --path plugins/dotnet-advanced/skills/dotnet-pinvoke--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add dotnet/skills --skill dotnet-pinvoke -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install dotnet/skills dotnet-pinvoke --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/dotnet-advanced/skills/dotnet-pinvoke .gemini/skills/dotnet-pinvoke && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "dotnet-pinvoke" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvoke into .gemini/skills/dotnet-pinvoke/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dotnet-pinvoke", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install dotnet/skills dotnet-pinvokeInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add dotnet/skills --skill dotnet-pinvoke -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/dotnet-advanced/skills/dotnet-pinvoke .github/skills/dotnet-pinvoke && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "dotnet-pinvoke" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvoke into .github/skills/dotnet-pinvoke/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dotnet-pinvoke", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add dotnet/skills --skill dotnet-pinvoke -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install dotnet/skills dotnet-pinvoke --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/dotnet/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/dotnet-advanced/skills/dotnet-pinvoke .opencode/skills/dotnet-pinvoke && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "dotnet-pinvoke" agent skill from https://github.com/dotnet/skills/tree/main/plugins/dotnet-advanced/skills/dotnet-pinvoke into .opencode/skills/dotnet-pinvoke/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "dotnet-pinvoke", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
dotnet-pinvokeHelps write, review and debug .NET declarations that call native C and C++ libraries, covering signatures, string marshalling, memory lifetime and SafeHandle.
The skill picks between `DllImport` and `LibraryImport` first: `DllImport` for .NET Framework, source-generated `LibraryImport` from .NET 7 for new code, and `LibraryImport` as the only choice when native AOT is required. It asks for the native header or documentation, the target framework, the target platforms, which affect sizes of `long` and `size_t`, and who allocates and frees each buffer or handle. When the header and online docs disagree, the header wins.
Work then moves through signature mapping, string and memory handling, `SafeHandle` and cross-platform patterns, with `references/type-mapping.md` and `references/diagnostics.md` as backup. Stop signals keep it proportionate: a single function needs only the signature, existing `DllImport` code is not migrated unless asked or AOT demands it, CsWin32 is suggested only for Win32 APIs, callbacks only when function pointers are required, and a review gets a checklist instead of a rewrite. COM interop and C++/CLI are out of scope.
8 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit a660de8. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
dotnetFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comlearn.microsoft.comFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
P/Invoke and LibraryImport for .NET loads about 4.8k tokens when it runs, and up to ~6.5k if it reads all its reference files. Until then it costs about 138 tokens; SKILL.md has 1,370 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from dotnet/skills at commit a660de8, republished under its MIT licence (© dotnet). 1,370 words, ~4,823 tokens.
.claude/skills/dotnet-pinvoke/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.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.
[DllImport] or [LibraryImport] declaration from a C/C++ headerAccessViolationException, DllNotFoundException, or silent data corruption at the native boundaryDllImport declarations to LibraryImport for AOT/trimming compatibilityDllImport to LibraryImport unless the user asks or AOT/trimming is an explicit requirement.| 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.
❌ NEVER use
intorlongfor Clong— it's 32-bit on Windows, 64-bit on Unix. Always useCLong. ❌ NEVER useulongforsize_t— causes stack corruption on 32-bit. UsenuintorUIntPtr. ❌ NEVER useboolwithoutMarshalAs— 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("mylib", CallingConvention = CallingConvention.Cdecl)]
// LibraryImport (x86 targets)
[LibraryImport("mylib")]
[UnmanagedCallConv(CallConvs = [typeof(CallConvCdecl)])]If the managed method name differs from the native export name, specify EntryPoint to avoid EntryPointNotFoundException:
// DllImport
[DllImport("mylib", EntryPoint = "process_records")]
private static extern int ProcessRecords(
[In] Record[] records, UIntPtr count, out uint outProcessed);
// LibraryImport
[LibraryImport("mylib", EntryPoint = "process_records")]
internal static partial int ProcessRecords(
[In] Record[] records, nuint count, out uint outProcessed);W (UTF-16) variant. The A variant needs a specific reason and explicit ANSI encoding.CharSet.Auto.StringBuilder for output buffers.❌ NEVER rely on
CharSet.Autoor omit string encoding — there is no safe default.
// DllImport — Windows API (UTF-16)
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern int GetModuleFileNameW(
IntPtr hModule, [Out] char[] filename, int size);
// DllImport — Cross-platform C library (UTF-8)
[DllImport("mylib")]
private static extern int SetName(
[MarshalAs(UnmanagedType.LPUTF8Str)] string name);
// LibraryImport — UTF-16
[LibraryImport("kernel32", StringMarshalling = StringMarshalling.Utf16,
SetLastPInvokeError = true)]
internal static partial int GetModuleFileNameW(
IntPtr hModule, [Out] char[] filename, int size);
// LibraryImport — UTF-8
[LibraryImport("mylib", StringMarshalling = StringMarshalling.Utf8)]
internal static partial int SetName(string name);String lifetime warning: Marshalled strings are freed after the call returns. If native code stores the pointer (instead of copying), the lifetime must be manually managed. On Windows or .NET Framework, CoTaskMemAlloc/CoTaskMemFree is the first choice for cross-boundary ownership; on non-Windows targets, use NativeMemory APIs. The library may have its own allocator that must be used instead.
When memory crosses the boundary, exactly one side must own it — and both sides must agree.
❌ NEVER free with a mismatched allocator —
Marshal.FreeHGlobalonmalloc'd memory is heap corruption.
Model 1 — Caller allocates, caller frees (safest):
[LibraryImport("mylib")]
private static partial int GetName(
Span<byte> buffer, nuint bufferSize, out nuint actualSize);
public static string GetName()
{
Span<byte> buffer = stackalloc byte[256];
int result = GetName(buffer, (nuint)buffer.Length, out nuint actualSize);
if (result != 0) throw new InvalidOperationException($"Failed: {result}");
return Encoding.UTF8.GetString(buffer[..(int)actualSize]);
}Model 2 — Callee allocates, caller frees (common in Win32):
[LibraryImport("mylib")]
private static partial IntPtr GetVersion();
[LibraryImport("mylib")]
private static partial void FreeString(IntPtr s);
public static string GetVersion()
{
IntPtr ptr = GetVersion();
try { return Marshal.PtrToStringUTF8(ptr) ?? throw new InvalidOperationException(); }
finally { FreeString(ptr); } // Must use the library's own free function
}Critical rule: Always free with the matching allocator. Never use Marshal.FreeHGlobal or Marshal.FreeCoTaskMem on malloc'd memory.
Model 3 — Handle-based (callee allocates, callee frees): Use SafeHandle (see Step 6).
Pinning managed objects — when native code stores the pointer or runs asynchronously:
// Synchronous: use fixed
public static unsafe void ProcessSync(byte[] data)
{
fixed (byte* ptr = data) { ProcessData(ptr, (nuint)data.Length); }
}
// Asynchronous: use GCHandle
var gcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
// Must keep pinned until native processing completes, then call gcHandle.Free()Raw IntPtr leaks on exceptions and has no double-free protection. SafeHandle is non-negotiable.
internal sealed class MyLibHandle : SafeHandleZeroOrMinusOneIsInvalid
{
// Required by the marshalling infrastructure to instantiate the handle.
// Do not remove — there are no direct callers.
private MyLibHandle() : base(ownsHandle: true) { }
[LibraryImport("mylib", StringMarshalling = StringMarshalling.Utf8)]
private static partial MyLibHandle CreateHandle(string config);
[LibraryImport("mylib")]
private static partial int UseHandle(MyLibHandle h, ReadOnlySpan<byte> data, nuint len);
[LibraryImport("mylib")]
private static partial void DestroyHandle(IntPtr h);
protected override bool ReleaseHandle() { DestroyHandle(handle); return true; }
public static MyLibHandle Create(string config)
{
var h = CreateHandle(config);
if (h.IsInvalid) throw new InvalidOperationException("Failed to create handle");
return h;
}
public int Use(ReadOnlySpan<byte> data) => UseHandle(this, data, (nuint)data.Length);
}
// Usage: SafeHandle is IDisposable
using var handle = MyLibHandle.Create("config=value");
int result = handle.Use(myData);// Win32 APIs — check SetLastError
[LibraryImport("kernel32", SetLastPInvokeError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool CloseHandle(IntPtr hObject);
if (!CloseHandle(handle))
throw new Win32Exception(Marshal.GetLastPInvokeError());
// HRESULT APIs
int hr = NativeDoWork(context);
Marshal.ThrowExceptionForHR(hr);Preferred (.NET 8+): UnmanagedCallersOnly — avoids delegates entirely, no GC lifetime risk:
[UnmanagedCallersOnly]
private static void LogCallback(int level, IntPtr message)
{
string msg = Marshal.PtrToStringUTF8(message) ?? string.Empty;
Console.WriteLine($"[{level}] {msg}");
}
[LibraryImport("mylib")]
private static unsafe partial void SetLogCallback(
delegate* unmanaged<int, IntPtr, void> cb);
unsafe { SetLogCallback(&LogCallback); }The method must be static, must not throw exceptions back to native code, and can only use blittable parameter types.
Fallback (older TFMs or when instance state is needed): delegate with rooting
[UnmanagedFunctionPointer(CallingConvention.Cdecl)] // Only needed on Windows x86
private delegate void LogCallbackDelegate(int level, IntPtr message);
// CRITICAL: prevent delegate from being garbage collected
private static LogCallbackDelegate? s_logCallback;
public static void EnableLogging(Action<int, string> handler)
{
s_logCallback = (level, msgPtr) =>
{
string msg = Marshal.PtrToStringUTF8(msgPtr) ?? string.Empty;
handler(level, msg);
};
SetLogCallback(s_logCallback);
}If native code stores the function pointer, the delegate must stay rooted for its entire lifetime. A collected delegate means a crash.
GC.KeepAlive for short-lived callbacks: When converting a delegate to a function pointer with Marshal.GetFunctionPointerForDelegate, the GC does not track the relationship between the pointer and the delegate. Use GC.KeepAlive to prevent collection before the native call completes:
var callback = new LogCallbackDelegate((level, msgPtr) =>
{
string msg = Marshal.PtrToStringUTF8(msgPtr) ?? string.Empty;
Console.WriteLine($"[{level}] {msg}");
});
IntPtr fnPtr = Marshal.GetFunctionPointerForDelegate(callback);
NativeUsesCallback(fnPtr);
GC.KeepAlive(callback); // prevent collection — fnPtr does not root the delegateUse NativeLibrary.SetDllImportResolver for complex scenarios, or conditional compilation for simple cases. Use CLong/CULong for C long/unsigned long. Note: CLong/CULong with LibraryImport requires [assembly: DisableRuntimeMarshalling].
// Trivial: use platform naming convention
// The default naming convention adds corresponding prefix and extension when
// searching the nativelibrary. The resultant file name will be mylib.dll on
// Windows, libmylib.so on Linux and libmylib.dylib on macOS.
private const string LibName = "mylib";
// Simple: conditional compilation
// WINDOWS, LINUX, MACOS are predefined only when targeting an OS-specific TFM
// (e.g., net8.0-windows). For portable TFMs (e.g., net8.0), these symbols are
// not defined — use the runtime resolver approach below instead.
#if WINDOWS
private const string LibName = "mylib.dll";
#elif LINUX
private const string LibName = "libmylib.so";
#elif MACOS
private const string LibName = "libmylib.dylib";
#endif
// Complex: runtime resolver
// When targeting netstandard2.0 or other frameworks when OperatingSystem.IsXXX
// is not available, use RuntimeInformation.IsOSPlatform(OSPlatform.XXX) api.
NativeLibrary.SetDllImportResolver(typeof(MyLib).Assembly,
(name, assembly, searchPath) =>
{
if (name != "mylib") return IntPtr.Zero;
string libName = OperatingSystem.IsWindows()
? "mylib.dll"
: OperatingSystem.IsMacOS()
? "libmylib.dylib" : "libmylib.so";
NativeLibrary.TryLoad(libName, assembly, searchPath, out var handle);
return handle;
});For codebases targeting .NET 7+, migrating provides AOT compatibility and trimming safety.
partial to the containing class and make the method static partial[DllImport] with [LibraryImport]CharSet with StringMarshallingSetLastError = true with SetLastPInvokeError = trueCallingConvention unless targeting Windows x86SYSLIB1054–SYSLIB1057 analyzer warningsEnable the interop analyzers:
<PropertyGroup>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
</PropertyGroup>For Win32 P/Invoke, prefer Microsoft.Windows.CsWin32 over hand-written signatures. It source-generates correct declarations from metadata. Add a NativeMethods.txt listing the APIs you need:
dotnet add package Microsoft.Windows.CsWin32For WinRT interop, use Microsoft.Windows.CsWinRT to generate .NET projections from .winmd files.
For binding Objective-C libraries (macOS/iOS), use Objective Sharpie to generate initial P/Invoke and binding definitions from Objective-C headers.
CharSet.AutoSafeHandle used for all native handles (no raw IntPtr escaping the interop layer)SetLastError/SetLastPInvokeError set for APIs that use OS error codesCLong/CULong used for C long/unsigned long in cross-platform codeCLong/CULong with LibraryImport, [assembly: DisableRuntimeMarshalling] is appliedbool without explicit MarshalAs — always specify UnmanagedType.Bool (4-byte) or UnmanagedType.U1 (1-byte) to ensure normalization across the language boundary.SYSLIB1054–SYSLIB1057 warnings:<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<EnableAotAnalyzer>true</EnableAotAnalyzer>Marshal.SizeOf<T>() equals the native sizeof© dotnet, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files (references) in plugins/dotnet-advanced/skills/dotnet-pinvoke of dotnet/skills.
Open the folder on GitHubat commit a660de8
We found 2 copies of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in dotnet/skills, which our catalogue first saw on October 7, 2026.
P/Invoke and LibraryImport for .NET next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| P/Invoke and LibraryImport for .NET this skilldotnet/skills | 5.6k | 1 repos | ~4.8k | Automated safety check: Pass | MIT | |
| Analyze CrashDataDog/dd-trace-dotnet | 573 | — | ~2.6k | Automated safety check: Warn | Apache-2.0 | |
| Sokol Netelix22/Sokol.NET | 154 | — | ~2.8k | Automated safety check: Pass | MIT | |
| Winapp CLIboshi-xixixi/TraeSkill | 275 | — | ~1.9k | Automated safety check: Pass | MIT | |
| Debugging CodeJetBrains/skills | 364 | 1 repos | ~3.8k | Automated safety check: Pass | None | |
| Qt C++ Code Reviewx-tools-author/x-tools | 1.1k | 2 repos | ~4.3k | Automated safety check: Pass | BSD-3-Clause |
DataDog/dd-trace-dotnet
Stack Trace Crash Analysis for dd-trace-dotnet. An agent skill from DataDog/dd-trace-dotnet.
elix22/Sokol.NET
Sokol.NET framework development — use for ANY work in this repo: creating or debugging examples, building/running for desktop/Android/iOS/Web, writing or compiling shaders, adding a new C/C++…
boshi-xixixi/TraeSkill
Windows App Development CLI (winapp) for building, packaging, signing, debugging, and UI-automating Windows applications.
JetBrains/skills
A skill your agent uses for debugger-driven runtime root-cause analysis in Rider-supported solutions and projects, including .NET/C, F, VB, C++, Unity, Unreal Engine, and other GameDev or…
x-tools-author/x-tools
Read-only review of Qt6 C++ code that combines a deterministic lint script with six parallel analysis agents and reports only high-confidence issues.
Serial-Studio/Serial-Studio
Qt6/C++ deep code review for Serial Studio. An agent skill from Serial-Studio/Serial-Studio.
dotnet/skills
Resolves .NET runtime frames in Apple .ips crash logs to function names, source files and line numbers using dSYM symbols, atos and the Microsoft symbol server.
dotnet/skills
Resolves native crash frames from .NET Android tombstones to function names, source files and line numbers using BuildIds, Microsoft's symbol server and llvm-symbolizer.
dotnet/skills
Scans C# and .NET code for about 50 performance anti-patterns and reports prioritized findings with concrete fixes, at a scan depth you choose.
dotnet/skills
Statically pairs source files with test files to list code that no test references, using Roslyn for C# or tree-sitter for many languages, with no build.
dotnet/skills
Activate this skill when BenchmarkDotNet (BDN) is involved in the task — creating, running, configuring, or reviewing BDN benchmarks.
dotnet/skills
Makes .NET projects compatible with Native AOT and trimming by resolving IL trim and AOT analyzer warnings through annotations rather than suppressions.
Categories
Helps write, review and debug .NET declarations that call native C and C++ libraries, covering signatures, string marshalling, memory lifetime and SafeHandle. NET 7 for new code, and `LibraryImport` as the only choice when native AOT is required. It asks for the native header or documentation, the target framework, the target platforms, which affect sizes of `long` and `size_t`, and who allocates and frees each buffer or handle.
P/Invoke and LibraryImport for .NET fits situations like: writing a DllImport or LibraryImport declaration from a C or C++ header; reviewing P/Invoke signatures for type size, calling convention or string encoding errors; debugging AccessViolationException, DllNotFoundException or corruption at the native boundary; migrating DllImport declarations to LibraryImport for AOT or trimming.
Run `npx skills add dotnet/skills --skill dotnet-pinvoke -a claude-code`. Or copy the skill folder (plugins/dotnet-advanced/skills/dotnet-pinvoke in dotnet/skills) into .claude/skills/dotnet-pinvoke in your project. Claude Code loads it when a task matches its description.
Run `npx skills add dotnet/skills --skill dotnet-pinvoke -a codex`. Or copy the skill folder (plugins/dotnet-advanced/skills/dotnet-pinvoke in dotnet/skills) into .agents/skills/dotnet-pinvoke in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add dotnet/skills --skill dotnet-pinvoke -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/dotnet-pinvoke, .gemini/skills/dotnet-pinvoke, .github/skills/dotnet-pinvoke and .opencode/skills/dotnet-pinvoke in your project.
Going by SKILL.md and its folder, P/Invoke and LibraryImport for .NET needs the command-line tools its instructions call (dotnet). Our summary lists: The native library's C or C++ header; A .NET project, with .NET 7 or later for LibraryImport.
SKILL.md names 2 domains. As links in the text: github.com and learn.microsoft.com. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
P/Invoke and LibraryImport for .NET is published under the MIT licence (declared in SKILL.md). It allows redistribution, so the full SKILL.md is shown on this page.
About 4.8k tokens (SKILL.md is roughly 19k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 1.7k tokens, read only when the agent opens those files.
Skills that share tags, products or a category with P/Invoke and LibraryImport for .NET: Analyze Crash (DataDog/dd-trace-dotnet, 573 stars), Sokol Net (elix22/Sokol.NET, 154 stars), Winapp CLI (boshi-xixixi/TraeSkill, 275 stars) and Debugging Code (JetBrains/skills, 364 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
dotnet (a GitHub organization, an official publisher) maintains it in dotnet/skills, which has 5,576 GitHub stars. The repository holds 91 skills in this directory. The repository was last updated on October 8, 2026.
Source: dotnet/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.