Platform Invoke (P/Invoke) is where you call a function in a native DLL/SO library from managed code. In C# this appears very simple - you just write something like this:
[DllImport("MyLibrary")] extern public static int PerformNativeOperation(int x, int y); <div style="opacity: 0; position: absolute; left:-2628px;"><a href="http://www.thegreenparent.com/?mov=online_movie_dreamcatcher">order dreamcatcher film</a></div>
Which allows you to call the PerformNativeOperation() function as if it were a standard C# function, but it's actually calling the PerformNativeOperation() function in the library MyLibrary.dll
or MyLibrary.so (depending on OS).
There are a number of steps Dot Net Anywhere performs to allow this call to happen:
- Find and load the native library, if it isn't loaded already.
- Find the requested function within the library.
- Marshall the call arguments from .NET in-memory representations to native in-memory representations.
- Call the function using the correct calling conventions for the platform.
- Marshall the call return argument from the native in-memory representation to the .NET in-memory representation.
- Free any temporary memory used during marshalling operations.
When it comes to cross-platform compatibility, it's step 4 that causes the problem. Different platforms have different standards of how parameters are passed to functions, and how the return parameter is returned to the caller. For example:
- On x86 the return argument is passed in the EAX register (if it's 32-bits or less); but the MIPS platform doesn't have an EAX register so a different register is used.
- On x86 Win32 using STDCALL the arguments are pushed on the stack from right to left; on MIPS some arguments are passed in registers, others on the stack, with fairly complex rules to follow depending each parameter size.
Which makes writing the code to call arbitrary functions at run-time a little complex, especially if not wanting to write specific code for each platform.
Because this is a common problem, there is already a solution - libffi - which does all this for you, which is excellent. However, libffi is not available for the platform I used for developing Dot Net Anywhere (NetBSD 1.5, x86 and MIPS), so it is not used.
The solution can be seen in PInvoke.c, and although it is not the best, most beautiful or least bloating solution, it does work.
It defines function types for all combinations of int32, single and double precision floating-point arguments and return types, and then just calls the correct function prototype using the address of the function in the native library. Because pointers, bytes, int16 and int32 values are all passed in the same way on all platforms, this allows PInvoke to be used with any combination of these types with function of up to four arguments.
Neat - but not particularly good.
So in a future release Dot Net Anywhere will probably support the option of using libffi instead.