An Adventure in Function Pointers, Delegates, and Calling Conventions

I’ve been interfacing a third-party library for a project at work. The third party library is a DLL with a C interface. Now most libraries for Windows usually have  a __stdcall calling conventions and for some reason I assumed that would be case with the C library. I guess it was because the demo application had  not done anything to give a hint otherwise. Since the client component was going to be in .net component, I wrote a nice little C++/CLI wrapper around the unmanaged native library, so that I could easily call it from C#. I decided against using P/Invoke because I don’t like using meta-information in source code to identify implementation of interfaces. In my opinion using P/Invoke client code too dependent on a specific library. That’s a topic for another post.

One of the functions of the library needed a function pointer to a callback function. The prototype of the function was something like this, wrapped with linkage.

extern "C"
{
    typedef void (*SomeCallbackType)(int, char*, float);
    void SetCallBackInNativeFunction(SomeCallbackType p){/*...*/}
}

Since I wanted the callback mechanism to hook up to .Net delegates I did something as follows,

using namespace System;
using namespace System::Runtime::InteropServices;

delegate void FooDelegate(int arg1, char* arg2, float arg3);

ref class AClass
{
    ///...
public:
    void FooCallback(int arg1, char *arg2, float arg3){/*...*/}
    ///..
};


static GCHandle globalGCHandle;

void SetCallback()
{
    AClass ^theObj = gcnew AClass();
    FooDelegate ^del = gcnew FooDelegate(theObj, &AClass::FooCallback);
    //We want to do this because the callback will be called many times. 
    //and may live through many garbage collection sprees.
    //This would be freed at the end of the program. 
    globalGCHandle = GCHandle::Alloc(del); 

    IntPtr ptr2Func = Marshal::GetFunctionPointerForDelegate(del);

    SetCallBackInNativeFunction((SomeCallbackType)ptr2Func.ToPointer());
}

 

Now every time time I ran my program, it would start up normally, but then mysteriously crash with really exceptions like 0xC00005, 0xC0000013, etc. I recognized these exceptions that indicated access violation and stack corruption exceptions. I turned on exception handlers in the debugger but the debugger refused to catch those exceptions. I was mystified. I thought that the exceptions were happening because the garbage collector was moving around my delegate, which is why  I used the globalGCHandle = GCHandle::Alloc(del). That didn’t help. Turned out the program was crashing because Marshal::GetFunctionPointerForDelegate returns a function pointer with __stdcall calling convention. Now as everyone knows __stdcall and __cdecl (the default in C compilers) are incompatible, and it causes really confusing errors because the somehow or the other the stack always ends up corrupted. The compiler (at least VC compiler) would have helped had I been passing a native function pointer. With managed code, the compiler happily let me shoot myself in the foot. The solution was to add the following attribute to delegate declaration

[UnmanagedFunctionPointer(CallingConvention::Cdecl)]
delegate void FooDelegate(int arg1, char* arg2, float arg3);

which I got from here

The program worked flawlessly after that (I mean it did what I wanted it to do).

 

Advertisements

3 Responses to An Adventure in Function Pointers, Delegates, and Calling Conventions

  1. Sam says:

    Thanks for the great post. This is exactly what I needed for my project.

  2. H says:

    Have no fear will this post will be formatted correctly and re-posted ASAP

  3. Tomato Blog says:

    Pass C++/CLI delegate as function pointer into native C++ class…

    To Pass C++/CLI delegate as function pointer into native C++ class, you can make use of this .net method: System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate, the key point is: define the delagate (MyDelegate^ m_myDelegate in my ex…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: