AdaskoTheBeAsT.Interop.Unmanaged 2.0.0

There is a newer version of this package available.
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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="AdaskoTheBeAsT.Interop.Unmanaged" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="AdaskoTheBeAsT.Interop.Unmanaged" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="AdaskoTheBeAsT.Interop.Unmanaged" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add AdaskoTheBeAsT.Interop.Unmanaged --version 2.0.0
                    
#r "nuget: AdaskoTheBeAsT.Interop.Unmanaged, 2.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package AdaskoTheBeAsT.Interop.Unmanaged@2.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=AdaskoTheBeAsT.Interop.Unmanaged&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=AdaskoTheBeAsT.Interop.Unmanaged&version=2.0.0
                    
Install as a Cake Tool

AdaskoTheBeAsT.Interop.Unmanaged

NuGet License: MIT

๐Ÿš€ Typed, safer dynamic DLL loading for .NET when static DllImport or LibraryImport is 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 IntPtr plumbing
  • ๐Ÿงฉ Runtime export lookup for optional or version-specific native APIs
  • ๐Ÿช Managed callback support when native code needs a function pointer
  • โš™๏ธ Low-level control through LoadLibraryEx flags
  • ๐Ÿงช 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.0 support means broad API compatibility for consumers. The runtime behavior is still Windows-only because the library wraps kernel32 APIs such as LoadLibraryEx, GetProcAddress, and FreeLibrary.

๐Ÿ“ฆ 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 UnmanagedLibrary instance or SafeLibraryHandle alive while retrieved delegates are still in use
  • โœ… Keep the callback binder alive 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
  • FreeLibrary is safe to call with null or an already closed handle
  • Flags such as LOAD_LIBRARY_AS_DATAFILE change loader behavior and are intended for special scenarios, not standard function invocation

๐Ÿ’ก Common use cases

  • Loading Windows system DLLs such as kernel32.dll or user32.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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.

Version Downloads Last Updated
3.0.0 233 4/20/2026
2.0.0 105 4/7/2026
1.0.0 204 11/23/2025