Convert DllImport methods to runtime loading.
This is an add-in for Fody
Install the LazyImport.Fody NuGet package and update the Fody NuGet package:
PM> Install-Package Fody
PM> Install-Package LazyImport.FodyAdd <LazyImport/> to FodyWeavers.xml.
Example configuration:
<Weavers>
<LazyImport>
<Library Name="mylib" InitMethod="Initialize" />
</LazyImport>
</Weavers>Here’s a typical P/Invoke class:
using System;
using System.Runtime.InteropServices;
public struct MyPoint
{
public int X;
public int Y;
public MyPoint(int x, int y)
{
X = x;
Y = y;
}
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogCallback([MarshalAs(UnmanagedType.LPStr)] string message);
public partial class NaticveMethods
{
[DllImport("mylib",
EntryPoint = "complex_function",
CallingConvention = CallingConvention.Cdecl,
SetLastError = true,
ExactSpelling = false,
PreserveSig = true,
BestFitMapping = false,
ThrowOnUnmappableChar = true)]
public static extern void text_complex_all_attributes(
[MarshalAs(UnmanagedType.LPWStr)] string input,
[In, Out][MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] array,
int arraySize,
ref MyPoint point,
out int status);
[DllImport("mylib")]
public static extern void test_callback_set_logger(LogCallback callback);
}After installing LazyImport.Fody and setting up FodyWeavers.xml,
just build your project — the library will automatically rewrite P/Invoke methods to load at runtime.
You can add InitMethod method stub to your P/Invoke class.
The actual implementation is automatically generated by Fody at compile time.
/// Other code...
public partial class NativeMethods
{
public static void Initialize(IntPtr handle)
=> throw new NotImplementedException("This method is implemented by Fody at compile time.");
public static void Initialize(string handle)
=> throw new NotImplementedException("This method is implemented by Fody at compile time.");
/// Other code...
}static void Main(string[] args)
{
// Load your native DLL
NativeMethods.Initialize("path/to/mylib.dll");
// Use P/Invoke methods normally
var p = new MyPoint();
NativeMethods.test_complex_all_attributes("111", new int[] { 1, 2, 3 }, 3, ref p, out int status);
}LazyImport supports two modes.
- Dynamic mode (default): Rewrites P/Invoke methods to use runtime library loading and function pointer lookup.
- Static mode: Only replaces the DLL name in
DllImport— keeps normal P/Invoke behavior.
<Weavers>
<LazyImport>
<Library Name="mylib" />
</LazyImport>
</Weavers>Equivalent explicit dynamic configuration:
<Weavers>
<LazyImport>
<Library Name="mylib" InitMethod="Initialize" />
</LazyImport>
</Weavers><Weavers>
<LazyImport>
<Library
Name="mylib"
InitMethod="Initialize"
Include="test_basic_*;test_string_*"
Exclude="test_basic_legacy_*" />
</LazyImport>
</Weavers><Weavers>
<LazyImport>
<Library Name="mylib" ReplaceName="__Internal" />
</LazyImport>
</Weavers><Weavers>
<LazyImport>
<Library Name="mylib" InitMethod="InitializeMyLib" Include="api_*" />
<Library Name="mylib2" ReplaceName="__Internal" />
</LazyImport>
</Weavers>In Dynamic Mode, LazyImport adds these to your class:
- A library handle field
- A function pointer field for each converted method
- Two
Initializemethods:
Initialize(string dllPath)
Initialize(IntPtr libraryHandle)
If you define the stub method, Fody will replace its body during build.
Nameis required for every<Library>.InitMethodandReplaceNamecannot be used together.- If neither is set, mode defaults to Dynamic.
InitMethodnames must be unique across all libraries.Include/Excludesupport wildcards:*and?.- If no libraries are configured, LazyImport will auto-detect DLLs from P/Invoke methods.
- Auto-detect only works if exactly one native library is used.
You can implement your own loader by adding this class:
static class NativeLibraryLoader
{
public static IntPtr Load(string path)
{
// Your custom logic to load the DLL
}
public static IntPtr GetExport(IntPtr handle, string symbolName)
{
// Your custom logic to get function pointer
}
}