I’m working a lot with p/invoke, and know how it’s hard to produce correct signature for unmanaged method. So, today I decided to publish basic cheat sheet for methods, parameters and attributes you should use in order to invoke unmanaged methods from managed code without a lot of problems. We start with data type translations. Here the table to understand it.
Data type from unmanaged signature | Data type in managed signature |
int | int the same with all other simple types such as double, uint, etc or private objects |
void* | IntPtr |
int* | ref int the same with all other simple types such as double, uint, etc or private objects |
char** | ref IntPtr later, you should get ascii string by using System.Runtime.InteropServices.Marshal.PtrToStringAnsi() method |
wcar_t** | ref IntPtr later, you should get ascii string by using System.Runtime.InteropServices.Marshal.PtrToStringUni() method |
const int* | ref int |
const char* | [System.Runtime.InteropServices.In()] [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.LPWStr)] string |
… (variable argument) | [System.Runtime.InteropServices.In()] [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.AsAny)] object |
You can use either System.Runtime.InteropServices.In or System.Runtime.InteropServices.Out attribute to specify how arguments should be used.
Now we done with simple arguments, let’s see what can be done when argument is actually callback or delegate?
Unmanaged definition | Managed definition |
typedef void (*MyCallback)(int Arg) | [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.Cdecl)]delegate void MyCallback(int Arg) |
To call all those methods, we should know managed equivalents of unmanaged types. Here the table. The rule is simple – know how many bytes unmanaged type has and find managed type with the same number of bytes. Other words, you can marshal int into IntPtr too…
Unmanaged type | Managed equivalent |
bool | bool |
char | sbyte (signed), byte (unsigned) |
wchar_t | char |
double | double |
float | single |
int, long (signed) | Int32 |
int, long (unsigned) | UInt32 |
__int64 (signed) | Int64 |
__int64 | UInt64 |
short (signed) | Int16 |
short (unsigned) | UInt16 |
void | void |
But not only types are problem in managed/unmanaged transitions. Also structures are aligned differently. For this purpose we can use StructLayout attribute. Even if unmanaged classes are sequential and you used correct managed data types, you can find you with problems in Pack. What “pack” is? Pack is actually slot size in bytes for members of your structure. It can be 0, 1, 2, 4, 8, 16, 32, 64, or 128 and depends on the platform and application setting.
Now you can see, that it is not very complicated to create managed signatures when you have header of unmanaged assemblies. So go ahead and ask, if I missed something.
That’s all by now. Have a nice day and be good people.