If you’ve ever worked with P/Invoke before, you’ll no doubt know that the typical usage pattern is something like this:
[DllImport("user32", CharSet=CharSet.Auto)]
int MessageBox(IntPtr hWnd, string lpText, string lpCaption, uint uType);
This is great if you’re wrapping system calls (like user32.dll, kernel32.dll, etc). But it’s not so good if you’re trying to wrap a custom native library. The reason is simple: the name of the DLL is hard-coded at compile-time, and you don’t get a choice as to where the system looks for it.
I use Firebird as an embedded database quite a bit, and this limitation is fairly obvious there. You need to give the embedded DLL a specific name, and it has to be in your application’s bin folder. The biggest problem is that the name of the DLL has changed a number of times over its lifetime, (gd32.dll, fbclient.dll, fbembed.dll, etc) so it’s hard to know which one to use without compiling the source for the provider yourself.
Anyway, I’ve submitted some code to the developers that will hopefully fix this in future versions (not sure if or when they’ll include it, but we’ll see...). Basically, I use Reflection.Emit to generate the “interop” class at runtime, which means I can put whatever I like as DLL name. This means you’ll be able to specify the name (and even the path) of the DLL to use in your Firebird connection string.
You can download the code at the bottom of the this post, but basically the way it works is, instead of having a static class that contains all your P/Invoke declarations, I’ve converted that class into an interface and replace all the [DllImport] attributes with my own custom [DynDllImport] attributes. The DynDllImportAttribute is just like DllImportAttribute, except it doesn’t take the DLL name in the constructor. All the other properties are basically just used to copy over to the generated DllImportAttribute at runtime.
The “meat” of my solution is the NativeLibraryFactory class. It takes the name of a DLL (for example, “fbembed”) and the Type of the interface, and it generates a class that implements the interface. Each interface method simply calls a P/Invoke declaration with the same signature and the passed-in DLL name for the DllImport.
This means that you can call something like this:
IFbClient fbClient = (IFbClient) NativeLibraryFactory.GetNativeLibrary(
typeof(IFbClient), "fbembed");
And the returned IFbClient has the same methods as the P/Invoke declarations.
Now, I’ll admit that for a lot of projects, this may be overkill. I also realise that you could use Marshal.GetDelegateForFunctionPointer along with LoadLibrary and GetProcAddress to do a similar thing. However, that is only available in .NET 2.0 and it’s also not cross-platform – you can’t use LoadLibrary/GetProcAddress on mono (though with some work, you might be able to write it cross-platform using dlopen, etc).
Anyway, you can download the code here: native-library-generator-1.0.zip