-
Notifications
You must be signed in to change notification settings - Fork 130
Create an Any CPU Wrapper with PInvoke
It is common to use a .NET Wrapper library that handles all the interop code for using a native library from a .NET Application. This way we can remove the complexity out of the .NET Application that comes by calling native libraries. Additionally, it would even be possible to replace the native implementation in future with a .NET implementation.
I like to keep the .NET Applications in the Any CPU build configuration and let the CLR decide in which mode the application should run. But native code depends on the CPU architecture it was compiled for. Therefore, I need a .NET Wrapper library that loads the correct native library (x86 or x64) depending on the runtime process.
The following wrapper class defines the PInvoke calls for x86 and x64 separately. This creates redundant code but allows us to keep the .NET Wrapper independent of the CPU architecture.
[Export(typeof(INativeWrapper))]
internal class NativeWrapper : INativeWrapper
{
public int Foo(string baz)
{
if (Environment.Is64BitProcess)
{
return NativeDll64.Foo(baz);
}
else
{
return NativeDll86.Foo(baz);
}
}
private static class NativeDll64
{
[DllImport(@"x64\NativeLibrary.dll",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern int Foo(string baz);
}
private static class NativeDll86
{
[DllImport(@"x86\NativeLibrary.dll",
CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
internal static extern int Foo(string baz);
}
}
The members of the wrapper are exposed via an interface. This allows us to replace the concrete wrapper implementation with a mock implementation during unit testing. The wrapper class comes with the Export attribute that enables dependency injection with MEF (Managed Extensibility Framework).
public interface INativeWrapper
{
int Foo(string baz);
}
In unit tests it is sometimes overkill to configure MEF just to get the concrete wrapper implementation. As an alternative approach the concrete instance can be get via a Factory. The following class shows a singleton factory.
public static class NativeWrapperFactory
{
private static readonly INativeWrapper instance = new NativeWrapper();
public static INativeWrapper Instance
{
get { return instance; }
}
}