RCode

a software development blog by Bojan Resnik

Passing C++/CLI delegate to native code

Posted by Bojan Resnik on May 18, 2009

Recently I had to interface a C++/CLI assembly with a native DLL written in C. This is mostly straightforward, but the C DLL could raise an internal event and provided a way to have the application notified of this event. In order to be informed, the application has to register a callback function that will be invoked by the DLL when the event is raised. The registration function is declared like this:

typedef void (__stdcall* EventCallback)();
void RegisterCallback(EventCallback callback);

Using an ordinary function for the callback would be easy, but I wanted to use a .NET delegate so that I could convert the native event into a .NET event. This scenario also turns out to be supported by .NET. All you need to take care of is to prevent the delegate from being moved or collected by the garbage collector.

public delegate void EventDelegate();

ref class NativeInterface
{
public:
    NativeInterface()
    {
        // Create the delegate from a member function
        nativeCallback_ = gcnew EventDelegate(this, &NativeInterface::Callback);

        // As long as this handle is alive, the GC will not move or collect the delegate
        // This is important, because moving or collecting invalidate the pointer
        // that is passed to the native function below
        delegateHandle_ = GCHandle::Alloc(nativeCallback_);

        // This line will actually get the pointer that can be passed to
        // native code
        IntPtr ptr = Marshal::GetFunctionPointerForDelegate(nativeCallback_);

        // Convert the pointer to the type required by the native code
        RegisterCallback( static_cast<EventCallback>(ptr.ToPointer()) );
    }

    !NativeInterface()
    {
        // Free the handle to the delegate, allowing GC to collect
        // the delegate
        if (delegateHandle_.IsAllocated)
            delegateHandle_.Free();
    }

private:
    GCHandle delegateHandle_;
    EventDelegate^ nativeCallback_;

    void Callback()
    {
        Console::WriteLine("Native event raised");
    }
};

kick it on DotNetKicks.comShout it

About these ads

8 Responses to “Passing C++/CLI delegate to native code”

  1. Firstly, thank you for this post. It was exactly the example that I needed. But I do have a lingering question.

    You mention that the GCHandle is needed to keep the garbage collector from moving or collecting the delegate, but according to the MSDN documentation for GCHandle, the handle you have allocated (GCHandleType::Normal) will allow the delegate to be moved but not collected. It would seem, however, that moving the delegate isn’t important, as the MSDN documentation for the GetFunctionPointerForDelegate function suggests that you only need to keep the garbage collector from collecting the delegate. So my question is do you even need the GCHandle at all, since the delegate is a member variable of the NativeInterface class, and therefore won’t be collected? In a simple example based on your code above, this is what I discovered, but I’m not sure whether more complicated code would lead to a problem. Do you have any thoughts on the matter?

    • Bojan Resnik said

      Upon reflection, I think you are absolutely right – as long as the instance that created the delegate is alive, the delegate will not be collected. It was my mistake to declare the delegate as member variable, as it should have been local to the constructor. I am sure I had it there at some point when writing some of the experiment code, though :)

      Thanks for the feedback,
      Bojan

  2. crvkumar said

    Great article.. Thanks for sharing the knowledge…

  3. Hey, this is a great article/sample.
    I had this issue running my C++/CLI assembly under LabView, and I’ve read that NUnit causes the same AppDomain issue.

    There’s not a LOT of info out there on this issue, but I started with this post:
    http://social.msdn.microsoft.com/Forums/eu/clr/thread/e281f160-d1e5-4c7b-b186-4e727ecacd55
    but couldn’t get it to work, and it’s not very clear.

    Your post was VERY clear and I got it working in no time, so THANKS!

  4. [...] 参考: http://resnikb.wordpress.com/2009/05/18/passing-ccli-delegate-to-native-code/ [...]

  5. [...] 참조사이트1(Managed String을 Unmanaged String으로 변환하기) 참조사이트2(델리게이트를 함수포인터로 변환하기) [...]

  6. Mah said

    Hi this is what I really need to do for my current project. But could you be a little more specific about implementing RegisterCallback function?
    I don’t know how this function should be related to the native event.

    Thanks in Advance

    • Bojan Resnik said

      The issue I had was with an older C library which required callbacks – RegisterCallback wasn’t implemented by me, I just had to use it. This could happen, e.g. with Win32 API callbacks, or more generally when integrating with third-party systems. The idea is that the native code calls a function through pointer, but that the function somehow invokes managed code. How that function is actually implemented is irrelevant.

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

 
Follow

Get every new post delivered to your Inbox.

%d bloggers like this: