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.