I am using a DLL provided for me, written in C and I want to call its functions from C#. I have explained in a previous post how to dynamically load a function from a DLL and map it onto a C# delegate so it can be called from C#.
However, this is only part of the problem. The other part is how to map the C types of the function parameters and return value onto C# types so that data can be passed backwards and forwards between the two languages.
The Solution – Marshalling
The solution that Microsoft provide is called Marshalling and is provided by classes in the System.Runtime.InteropServices namespace. This article is in part inspired by a Microsoft article on Marshalling and another on the Platform Invoke interfaces.
The marshalling mechanism allows the calling conventions and the parameter types of the C function to be specified and, furthermore, to automatically be type converted. So, for example, here’s a C function:
char* ErrorMessage(int id);
In order to dynamically load and call this function from C#, I first declare a delegate with marshalling attributes applied:
[UnmanagedFunctionPointer(CallingConvention.Winapi)] [return: MarshalAs(UnmanagedType.LPStr)] private delegate string ErrorMessage_t([MarshalAs(UnmanagedType.I4)] int id);
As explained in the earlier article, this delegate declaration effectively declares a function type. The actual function can then be loaded and converted to this type and then called.
The attributes (i.e. the code in square brackets ) need further explanation.
First, the function itself is declared as an UnmanagedFunctionPointer – this is letting the system know that the function is not managed, so for example it will not be garbage collected. It also says that parameters are to by passed using the WinAPI parameter-passing convention.
The next attribute marshals the return type as unmanaged type “LPStr”. This is a pointer to a C string and corresponds to the char* type in C. The unmanaged part of the marshalling states that the memory associated with the return value will not be garbage collected. In this case the function returns a pointer to an internal buffer and so doesn’t need to be collected anyway.
Note that the return type of the C# delegate is “string”, i.e. a C# string type. The return value from the function will be automatically converted from char* (a byte-wide null-terminated character buffer) to string (a 16-bit wide Unicode C# string).
The function parameter itself is of C type int and is marshalled as unmanaged type “I4”. This is unnecessary since the C type int is the same as the C# type int, but I have included it for completeness. Note how the marshalling attribute prefixes the parameter declaration.
Once you have such a delegate declaration, you can now dynamically load a function from a DLL and map it onto the delegate so it can be called from C# as a previous post explained.