KerckhoffsLabs.Runtime.InteropServices
1.4.0
Prefix Reserved
dotnet add package KerckhoffsLabs.Runtime.InteropServices --version 1.4.0
NuGet\Install-Package KerckhoffsLabs.Runtime.InteropServices -Version 1.4.0
<PackageReference Include="KerckhoffsLabs.Runtime.InteropServices" Version="1.4.0" />
<PackageVersion Include="KerckhoffsLabs.Runtime.InteropServices" Version="1.4.0" />
<PackageReference Include="KerckhoffsLabs.Runtime.InteropServices" />
paket add KerckhoffsLabs.Runtime.InteropServices --version 1.4.0
#r "nuget: KerckhoffsLabs.Runtime.InteropServices, 1.4.0"
#:package KerckhoffsLabs.Runtime.InteropServices@1.4.0
#addin nuget:?package=KerckhoffsLabs.Runtime.InteropServices&version=1.4.0
#tool nuget:?package=KerckhoffsLabs.Runtime.InteropServices&version=1.4.0
KerckhoffsLabs.Runtime.InteropServices
Platform-native interop types for the managed/unmanaged boundary.
Overview
C's unsigned long is 32-bit on Windows, 32-bit on 32-bit Unix, and 64-bit on 64-bit Unix. Get
that width wrong at a P/Invoke boundary and you don't get a compile error — you get silent stack
corruption, or a struct whose fields are all shifted by four bytes.
The .NET BCL ships CULong
to model this, but it is a thin wrapper: to do anything numeric with it you unwrap to Value, do
the arithmetic, and wrap back. NativeCULong honours the same platform contract while behaving like
a real integer.
- Correct width on every platform — 32-bit on Windows and 32-bit Unix, 64-bit on 64-bit Unix.
- Full generic math — implements
IBinaryInteger<T>,IUnsignedNumber<T>andIMinMaxValue<T>, so it drops straight intowhere T : IBinaryInteger<T>code with no unwrap step. - Formatting and parsing —
ISpanFormattable,IUtf8SpanFormattableandISpanParsable<T>. - Checked and unchecked conversions — every lossy cast ships as a pair, so
checkedcontexts throwOverflowExceptioninstead of silently truncating. - Blittable — embed it directly in
[StructLayout(LayoutKind.Sequential)]structs and let the runtime lay them out correctly per-OS. - One target framework — consumers reference plain
net10.0. No Windows-specific TFM, noRuntimeIdentifier. Validated on both the JIT and NativeAOT.
Installation
dotnet add package KerckhoffsLabs.Runtime.InteropServices
Requires .NET 10.0 or later.
Quick start
Use NativeCULong wherever a native API says unsigned long:
using System.Runtime.InteropServices;
using KerckhoffsLabs.Runtime.InteropServices;
internal static class Native
{
// C: unsigned long C_Initialize(void *pInitArgs);
[DllImport("pkcs11")]
internal static extern NativeCULong C_Initialize(nint pInitArgs);
// C: unsigned long C_GetMechanismInfo(unsigned long slotId, CK_MECHANISM_INFO *info);
[DllImport("pkcs11")]
internal static extern NativeCULong C_GetMechanismInfo(NativeCULong slotId, ref MechanismInfo info);
}
// C: typedef struct { unsigned long ulMinKeySize, ulMaxKeySize, flags; } CK_MECHANISM_INFO;
// Laid out correctly on every platform — 12 bytes on Windows, 24 bytes on 64-bit Unix.
[StructLayout(LayoutKind.Sequential)]
internal struct MechanismInfo
{
public NativeCULong MinKeySize;
public NativeCULong MaxKeySize;
public NativeCULong Flags;
}
NativeCULong rv = Native.C_Initialize(nint.Zero);
if (rv != default)
{
throw new InvalidOperationException($"C_Initialize failed: 0x{rv:X}");
}
Note. These samples use
DllImport, which needs no project-level opt-in.NativeCULongalso works with the source-generated[LibraryImport], but — as for any custom struct passed by value — that requires your project to set<AllowUnsafeBlocks>true</AllowUnsafeBlocks>and apply[assembly: DisableRuntimeMarshalling], or the generator reportsSYSLIB1062andSYSLIB1051.
Conversions are explicit, and the checked variants refuse to lose data:
NativeCULong count = (NativeCULong)16u; // always exact
nuint raw = count.Value; // the underlying storage
ulong wide = (ulong)count; // always exact
// A checked context — including the project-wide CheckForOverflowUnderflow
// setting — routes to the checked operator, which throws instead of wrapping.
NativeCULong bad = checked((NativeCULong)(-1)); // OverflowException
And because it is a real generic-math integer, it works in numeric code with no unwrap step:
static T Sum<T>(ReadOnlySpan<T> values) where T : IBinaryInteger<T>
{
T total = T.Zero;
foreach (T value in values)
{
total += value;
}
return total;
}
NativeCULong slots = Sum<NativeCULong>([(NativeCULong)3u, (NativeCULong)4u]);
Console.WriteLine(slots); // 7
Supported platforms
| Platform | C unsigned long |
NativeCULong storage |
Runtime asset |
|---|---|---|---|
| Windows x64 / arm64 (LLP64) | 32-bit | 32-bit | runtimes/win-*/lib/net10.0 |
| Windows 32-bit | 32-bit | 32-bit | lib/net10.0 |
| Unix 32-bit (ILP32) | 32-bit | 32-bit | lib/net10.0 |
| Unix 64-bit (LP64) | 64-bit | 64-bit | lib/net10.0 |
You reference plain net10.0, and the right asset is selected for you at runtime.
Documentation
- API reference — the full generated surface.
- How the per-platform storage is delivered — how a single-target package ships two builds of one assembly, and what it means that on 64-bit Windows you compile against an 8-byte type but run against a 4-byte one.
License
MIT — see LICENSE.
Support
Bug reports and feature requests belong in GitHub issues.
About
Built and maintained by KerckhoffsLabs and contributors.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.