AdaskoTheBeAsT.Interop.Unmanaged
2.0.0
See the version list below for details.
dotnet add package AdaskoTheBeAsT.Interop.Unmanaged --version 2.0.0
NuGet\Install-Package AdaskoTheBeAsT.Interop.Unmanaged -Version 2.0.0
<PackageReference Include="AdaskoTheBeAsT.Interop.Unmanaged" Version="2.0.0" />
<PackageVersion Include="AdaskoTheBeAsT.Interop.Unmanaged" Version="2.0.0" />
<PackageReference Include="AdaskoTheBeAsT.Interop.Unmanaged" />
paket add AdaskoTheBeAsT.Interop.Unmanaged --version 2.0.0
#r "nuget: AdaskoTheBeAsT.Interop.Unmanaged, 2.0.0"
#:package AdaskoTheBeAsT.Interop.Unmanaged@2.0.0
#addin nuget:?package=AdaskoTheBeAsT.Interop.Unmanaged&version=2.0.0
#tool nuget:?package=AdaskoTheBeAsT.Interop.Unmanaged&version=2.0.0
AdaskoTheBeAsT.Interop.Unmanaged
๐ Typed, safer dynamic DLL loading for .NET when static
DllImportorLibraryImportis not enough.
AdaskoTheBeAsT.Interop.Unmanaged is a Windows-focused .NET library for loading native DLLs at runtime, resolving exports as strongly typed delegates, and releasing module handles safely with SafeHandle.
โจ Why developers will like it
- ๐ Safer lifetime management with
SafeLibraryHandle - ๐ฏ Strongly typed delegates instead of manual
IntPtrplumbing - ๐งฉ Runtime export lookup for optional or version-specific native APIs
- ๐ช Managed callback support when native code needs a function pointer
- โ๏ธ Low-level control through
LoadLibraryExflags - ๐งช Broad automated test coverage across classic .NET Framework and modern .NET
- ๐ Generated XML docs and nullable-enabled code
๐ค When this library shines
This package is especially useful when you need to:
- Load a native DLL by name or by full path at runtime
- Probe for exports that may or may not exist on a given machine
- Use different load flags for system DLLs, resource DLLs, or third-party binaries
- Pass managed delegates into unmanaged code as callbacks
- Support both older .NET consumers and the latest .NET runtimes from one package
๐ Why not just use DllImport?
If your native dependency is fixed at compile time and your exports are always present, DllImport or LibraryImport may be enough.
This library becomes valuable when you need dynamic loading, optional exports, runtime path selection, or callback pointer generation without hand-rolling the native interop plumbing every time.
๐ฅ๏ธ Platform and framework support
| Area | Support |
|---|---|
| Runtime | Windows only |
| Library target frameworks | netstandard2.0, net8.0, net9.0, net10.0 |
| Automated test matrix | net462, net47, net471, net472, net48, net481, net8.0, net9.0, net10.0 |
โน๏ธ
netstandard2.0support means broad API compatibility for consumers. The runtime behavior is still Windows-only because the library wrapskernel32APIs such asLoadLibraryEx,GetProcAddress, andFreeLibrary.
๐ฆ Installation
dotnet add package AdaskoTheBeAsT.Interop.Unmanaged
Or via Package Manager:
Install-Package AdaskoTheBeAsT.Interop.Unmanaged
๐ Quick start
1) Load a DLL and call an export
using System;
using System.Runtime.InteropServices;
using AdaskoTheBeAsT.Interop.Unmanaged;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate uint GetCurrentProcessIdDelegate();
using var library = new UnmanagedLibrary("kernel32.dll");
var getCurrentProcessId = library.GetUnmanagedFunction<GetCurrentProcessIdDelegate>("GetCurrentProcessId");
if (getCurrentProcessId is not null)
{
Console.WriteLine($"Current PID: {getCurrentProcessId()}");
}
2) Handle optional exports safely
GetUnmanagedFunction<TDelegate> returns null when the export is missing, which makes feature probing straightforward.
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate IntPtr OptionalExportDelegate();
using var library = new UnmanagedLibrary("SomeNativeSdk.dll");
var optionalExport = library.GetUnmanagedFunction<OptionalExportDelegate>("OptionalExport");
if (optionalExport is null)
{
Console.WriteLine("This version of the native SDK does not expose OptionalExport.");
}
3) Load from a specific path with explicit flags
Use a fully qualified path when you want deterministic loading behavior for a specific DLL.
var flags =
LoadLibraryFlags.LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR |
LoadLibraryFlags.LOAD_LIBRARY_SEARCH_SYSTEM32;
using var library = new UnmanagedLibrary(@"C:\Native\MyLibrary.dll", flags);
4) Use the static handle-based API
If you want to manage the handle yourself, the static helpers are available too.
using System;
using System.Runtime.InteropServices;
using AdaskoTheBeAsT.Interop.Unmanaged;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
delegate uint GetTickCountDelegate();
using var handle = UnmanagedLibrary.LoadLibrary("kernel32.dll");
var getTickCount = UnmanagedLibrary.GetUnmanagedFunction<GetTickCountDelegate>(handle, "GetTickCount");
if (getTickCount is not null)
{
Console.WriteLine($"Tick count: {getTickCount()}");
}
5) Pass a managed callback to native code
When you expose a managed delegate to unmanaged code, keep the returned binder alive for as long as native code may store or invoke the pointer.
using System;
using AdaskoTheBeAsT.Interop.Unmanaged;
Func<int, int, int> callback = (a, b) => a + b;
var callbackPointer = UnmanagedLibrary.GetFunctionPointerForDelegate(callback, out var binder);
// Pass callbackPointer to native code here.
GC.KeepAlive(binder);
๐ง API at a glance
UnmanagedLibrary
Main entry point for loading DLLs and resolving exports.
new UnmanagedLibrary(string fileName, LoadLibraryFlags flags = ...);
TDelegate? GetUnmanagedFunction<TDelegate>(string functionName);
static SafeLibraryHandle LoadLibrary(string fileName, LoadLibraryFlags flags = ...);
static void FreeLibrary(SafeLibraryHandle? safeLibraryHandle);
static TDelegate? GetUnmanagedFunction<TDelegate>(SafeLibraryHandle safeLibraryHandle, string functionName);
static IntPtr GetFunctionPointerForDelegate<T>(T delegateCallback, out object binder);
SafeLibraryHandle
Wraps the native module handle using the .NET SafeHandle pattern, which helps prevent leaks and double-free mistakes.
LoadLibraryFlags
Exposes Windows LoadLibraryEx flags so you can control how the loader locates and initializes modules.
๐ก๏ธ Safety and lifetime rules
These are the most important things to remember:
- โ
Keep the
UnmanagedLibraryinstance orSafeLibraryHandlealive while retrieved delegates are still in use - โ
Keep the callback
binderalive while native code may call the function pointer - โ Use the exact delegate signature and calling convention expected by the native export
- โ
Prefer explicit
LOAD_LIBRARY_SEARCH_*flags when loading third-party binaries - โ Do not unload the library and continue using delegates you obtained from it
- โ Do not load untrusted DLLs
โ ๏ธ Important behavior notes
- Export names are case-sensitive
- Invalid file names throw
ArgumentException - Failed loads throw
Win32Exception - Missing exports return
null FreeLibraryis safe to call withnullor an already closed handle- Flags such as
LOAD_LIBRARY_AS_DATAFILEchange loader behavior and are intended for special scenarios, not standard function invocation
๐ก Common use cases
- Loading Windows system DLLs such as
kernel32.dlloruser32.dll - Dynamically integrating with third-party native SDKs
- Supporting optional native features across multiple versions of the same DLL
- Registering managed callbacks with unmanaged code
- Choosing DLL resolution behavior explicitly to reduce surprises
๐งช Quality notes
The project is built with quality-oriented defaults, including:
- nullable reference types enabled
- generated XML documentation
- warnings treated as errors
- automated tests across .NET Framework 4.6.2-4.8.1 and .NET 8-10
โFAQ
Do I need to pass a full DLL path?
Not always. A bare module name such as kernel32.dll works when the selected flags can resolve it. Use a fully qualified path when you want deterministic loading from a specific location.
What happens if the export does not exist?
GetUnmanagedFunction<TDelegate> returns null, so you can probe for optional functionality without exceptions.
Do I need to use DelegatePin directly?
Usually no. Most consumers only need to keep the binder returned by GetFunctionPointerForDelegate rooted for the required lifetime.
Can I use this on Linux or macOS?
No. The package may target cross-platform TFMs, but its runtime implementation depends on Windows loader APIs.
๐ License
This project is licensed under the MIT License.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Memory (>= 4.6.3)
- System.Reflection.Emit (>= 4.7.0)
- System.Reflection.Emit.Lightweight (>= 4.7.0)
- System.Runtime.CompilerServices.Unsafe (>= 6.1.2)
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
-
net9.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on AdaskoTheBeAsT.Interop.Unmanaged:
| Package | Downloads |
|---|---|
|
AdaskoTheBeAsT.WkHtmlToX
AdaskoTheBeAsT.WkHtmlToX c# wrapper |
GitHub repositories
This package is not used by any popular GitHub repositories.