PInvoke-Reverse PInvoke and __stdcall - __cdecl
I came across this issue yesterday and thought that I will blog about the same. The example below demonstrates a simple PInvoke and Reverse PInvoke through delegates. The initial call was defined to use the __cdecl convention. As you can notice when executing the code the stack gets corrupted and the for loop ends prematurely.
Copy code to: nat.cpp
Compile: cl /LD nat.cpp
#include <stdio.h>
#include <string.h>
typedef void ( *callback)(wchar_t * str);
extern "C" __declspec(dllexport) void caller(wchar_t * input, int count, callback call)
{
for(int i = 0; i < count; i++)
{
call(input);
}
}
Copy code to: man.cs
Compile: csc man.cs
using System.Runtime.InteropServices;
public class foo
{
public delegate void callback(string str);
public static void callee(string str)
{
System.Console.WriteLine("Managed: " +str);
}
public static int Main()
{
caller("Hello World!", 10, new callback(foo.callee));
return 0;
}
[DllImport("nat.dll",CallingConvention=CallingConvention.StdCall)]
public static extern void caller(string str, int count, callback call);
}
The above code prints the “Managed: Hello World!” twice and exits. To fix this let us change the calling convention to __stdcall.
Copy code to: nat.cpp
Compile: cl /LD nat.cpp
#include <stdio.h>
#include <string.h>
typedef void (__stdcall *callback)(wchar_t * str);
extern "C" __declspec(dllexport) void __stdcall caller(wchar_t * input, int count, callback call)
{
for(int i = 0; i < count; i++)
{
call(input);
}
}
Now if you run the code it will print the message “Managed: Hello World!” 10 times. Marshalling and getting the signatures can be extremely subtle.