Tuesday 26 January 2010

C# Interop

I write image processing algorithms for a (so-called) living, which explains why my posts are so badly written and incomprehensible.  I write quite a few libraries in C/C++ for various industrial tasks and supply them as a trusty old windows dll.   Providing an interface for calling my libraries from a C# application front-end is something I have to do quite a bit of.  C++ delivers the performance needed for image processing and C# gives the quick and easy GUI. 



In order to call a regular 'C' dll from C#, you need to use something in .NET called P/Invoke.  This mechanism defines a function that is callable from C# which maps to a dll function call.  In the definition of the dll function, you can specify things like character sets for string passing, calling conversions etc.  As an example, if you wanted to import the windows kernel32 function Beep into C# using P/Invoke it looks something like:



[DllImport("kernel32.dll")]
public static extern bool Beep(int frequency, int duration);



In the beep example, the integer values passed from managed to unmanaged dll are so-called blittable types and will be passed directly.  Passing arrays is not quite as simple, since they have to be converted (marshalled) by the framework before they are passed to the unmanged dll.  Normally, you don't use unsafe code in your C# GUI, so you don't have pointers to data lying around handy.  Of course, most C libraries for image processing expect a pointer to some image data to be passed in somewhere, not a managed array object.  So somebody has to do some work to turn a managed array into a pointer, without totally screwing up the safe part of C# and all the other stuff going on like the garbage collector.  This is job of P/Invoke.


For example, if we have a C# array declared as:

 float[] CalTgtX = { 58,198,340, 58,198,340, 58,198,340};


which we want to pass into a C++ function that looks like this:

extern "C" __declspec(dllexport) void __stdcall CalibrateProjection(float *pTargetX)

Then we need to carefully define how C# should carry out this conversion.  Heres how to define the function in C# so that we can pass (marshal) that float array object from C# to the C dll function:


[DllImport("MyLib.dll", EntryPoint = "CalibrateProjection", CallingConvention = CallingConvention.StdCall)]
public static extern RETCODE CalibrateProjection([MarshalAs(UnmanagedType.LPArray, SizeConst = 9)] float[] pTargetX)

The first line tells the C# compiler to import a function.  DllImport tells c# we are importing a function from a Dll.  EntryPoint tells c# what the function stub is named. CallingConvention should match that used by the Dll - here it was __stdcall.  
The second line defines the function as it will appear to C#.  The key to converting the float array object to a pointer is in the MarshalAs attribute.  This will involve a copy to an unsafe array on the heap so can be slow for large arrays... very slow.


All the different flavours of managed types and structs can be marshalled this way.  More information on PInvoke can be found at http://msdn.microsoft.com/en-us/library/aa288468%28VS.71%29.aspx

 

Vision Experts

Friday 22 January 2010

LoaderLock MDA

This post isn't really about accelerated image processing, but the topic is related to deployment of DLLs of any type.  I hope this helps somebody save some time if they encounter this issue.  Whilst developing a C# demo app for one of my CUDA libraries, I encountered a strange error:

LoaderLock was detected
Message: DLL 'Cephalon.dll' is attempting managed execution inside OS Loader lock. Do not attempt to run managed code inside a DllMain or image initialization function since doing so can cause the application to hang.

It took me a while to figure out what was going on, and it was related to how I build CUDA libraries.  

When I make a CUDA enabled library, I wrap up the compiled kernel cubin files as a resource compiled into the DLL itself.  An alternative simpler method is to supply a cubin text file along with each dll and load it directly using the CUDA function:

cuModuleLoad(&cuModule, pszModulePath) 
 

but having two files can lead to version control and maintainance issues. Plus, I try and make a living doing this, and dont really want people reading my precious kernel code that took four months to write too easily. 



So I wrap up the compiled code string neatly inside the DLL as a resource, then get that resource string and compile it on-the-fly (or just-in-time) using the alternative CUDA function:


cuModuleLoadDataEx( &cuModule,pCubinStr,3,&jitOptions[0],&jitOptVals[0]);



This is great but in order to get the string resource from inside the DLL I need to call a varient of LoadResource. And I need to call FindResource to find that resource first.  And I need to call GetModuleHandle("LibraryName.dll") before any of those.  The problem is that GetModuleHandle is a prohibited function to call even indirectly from LoadLibrary when the DLL is first loaded and mapped into the process address space.  

The C# application was loading the DLL when it first encountered one of the functions, this then tried to initialise the CUDA module and load the resource automatically from the dll entry point.  Ultimately, the call to GetModuleHandle raised an alarm back in the managed code.  Not easy to spot.


More on the LoaderLock MDA can be found here





Vision Experts