Handling native C/C++ libraries in .NET
I was reviewing some code and I saw this class I had no idea existed: NativeLibrary.
In my case, I had a demo using different libraries that were doing similar things: parsing PostgreSQL code. However, some of them were wrapping the native pg_query library and were using different versions of it. The result were weird errors depending on which library was overwriting the file last.
Solution: use NativeLibrary.SetDllImportResolver, which worked nicely.
But what is this strange class? Introduced in .NET Core 3, NativeLibrary has several methods:
- Load and TryLoad - loading native libraries
- GetMainProgramHandle - get a handle to resolve exports from the entry point module
- GetExport and TryGetExport - get the address of an exported symbol
- Free - free a loaded native library
- SetDllImportResolver - sets a callback for resolving native library imports from an assembly
In order to use a native library, you would do this in C#:
[DllImport("libraryName", ... some optional stuff..., EntryPoint = "myMethod_in_library"]
public static extern void MyMethod(); .NET is smart enough to look for libraryName in the format of the operating system: .dll for Windows, .dylib for MacOS, .so for Linux. However, the name is not enough, as DllImport expects a name that it resolves itself or a path.
You can provide a full or relative path. If you provide no path, the DLL must be in the current path at run time, unless the DLL is loaded by some other means. Be aware, however, that using a fully qualified path can introduce inaccuracy if the DLL is moved.
If you want it to work for various operating systems, though, things get complicated. You could use something like this:
internal static class NativeMethods
{
private const string LIB_NAME =
#if WINDOWS
"somePath/mylib.dll";
#elif LINUX
"someOtherPath/libmylib.so";
#elif OSX
"bahMacOs/libmylib.dylib";
#endif
[DllImport(LIB_NAME)]
internal static extern int MyFunction();
}
But it's brittle and ugly. Moreover, this is something that only works for the code you control. If it's in a library you load from a NuGet or something, you're out of luck. Enter SetDllImportResolver in the NativeLibrary class.
static IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
{
var platformDependentName = GetLibraryName(libraryName);
IntPtr handle;
NativeLibrary.TryLoad(platformDependentName, assembly, searchPath, out handle);
return handle;
}
// then later
NativeLibrary.SetDllImportResolver(Assembly.GetExecutingAssembly(), DllImportResolver); This works for platform dependent paths, but also for paths in the individual NuGet packages that I had in my project. It all depends on your implementation of the GetLibraryName method.
Note that this applies to all dll resolving, so you could conceivably use it to log things.
Comments
Be the first to post a comment