Native Callback

May 29, 2007

tags: ,
6 comments

This is not new, but many people have hard time when combining native and managed code. One of the more challenging interop task is to call back from native code into managed code.


There are multiple ways of doing it:




  1. Use P/Invoke and pass a delegate as a pointer to the native code.
  2. Use COM interop and connection points (The COM wrapper produces .Net events)
  3. You may find other ways such as using Windows Messages or other inter-process comunication mechanism. For example to call to native code (not a callback) You can even use Code Generation and emit a “Calli” opcode that gets an IntPtr to a native function address (Adam Nathan demonstrates it in his .NET and COM book, pages 818-821. He P/Invoke to LoadLibrary and GetProcAddress and then he creates a method call using Reflection.Emit)
  4. Use C++/CLI and gcroot as a reverse reference to the managed code.

The first method is very easy, just define the delegate with the managed prototype that best matches the native function pointer, and pass it to the native register function. Be aware of two things:




  1. The delegate object should be kept a live as long as the native code has a pointer to it.


  2. The calling convention is __stdcall (great for Win32 API, but not so good for the default C/C++ __cdecl convention)

Use the COM way only if the native side is already based on COM. COM interop involved thread marshaling that can be nightmare since .NET is actually FTM while COM is usually STA or MTA.

The third ways are overkill to my opinion.

So, we have the C++/CLI way. C++/CLI based interop is best when you deal with classes and objects and not just plain C style function pointer.

In the following example, we have a native DLL that implements the basic command pattern:

 

 The native header file:

 

#ifdef NATIVE_EXPORTS
#define NATIVE_API __declspec(dllexport)
#else
#define NATIVE_API __declspec(dllimport)
#endif
#include
<stack>

class NATIVE_API CCommand
{
public:
    virtual void Do() = 0;
    virtual void Undo() = 0;
};

// This class is exported from the native.dll
class NATIVE_API CActivator
{
public:
    void Activate(CCommand *pCommand);
    CCommand *Undo();

private:
    std::stack<CCommand *> m_commands;
};


 

The native CPP file:

 

// native.cpp : Defines the exported functions for the DLL application.
//

#include “stdafx.h”
#include “native.h”

// This is the constructor of a class that has been exported.
// see native.h for the class definition
void CActivator::Activate(CCommand *pCommand)
{
    pCommand->Do();
    m_commands.push(pCommand);
}

CCommand *CActivator::Undo()
{
    if (m_commands.empty())
        return NULL;

    CCommand *pCommand = m_commands.top();
    m_commands.pop();
    pCommand->Undo();
    return pCommand;
}


 

As you can see the CActivator::Activate method takes a CCommand object, calls its Do() method and pushes it into a stack. The CActivator::Undo() method, takes a CCommand object from the stack, executes it and returns the command (this is later used to reclaim the command object native memory).

 

We would like to create a command object in C#, something like this:

 

namespace Commander
{
    class MyCommand : Activation.ICommand
    {
        #region ICommand Members

        public void Do()
        {
            Console.WriteLine(“Do”);
        }

        public void Undo()
        {
            Console.WriteLine(“Undo”);
        }

        #endregion
    }

    class MyCommand1 : Activation.ICommand
    {
        #region ICommand Members

        public void Do()
        {
            Console.WriteLine(“Do1”);
        }

        public void Undo()
        {
            Console.WriteLine(“Undo1”);
        }

        #endregion
    }

    class Program
    {
        static void Main(string[] args)
         {
           MyCommand cmd = new MyCommand();
            MyCommand1 cmd1 = new MyCommand1();
            using (Activation.Activator activator = new Activation.Activator())
            {
                activator.Activate(cmd);
                activator.Activate(cmd1);
                activator.Undo();
                activator.Undo();
            }

        }
    }
}


 

You may notice that I use a namespace Activation, and an Activator class that has Activate and Undo methods. This class is the glue, the C++/CLI wrapper of the native DLL that make the bridge between the managed and the native worlds. The wrapper is a C++/CLI class library CLR project that includes the native CActivator header file and link with the native lib file that represents the native DLL. Don’t forget to copy the native DLL to the C# debug or release directory so it will be loaded with the wrapper assembly. (If you don’t copy the native DLL you get a “file not found exception” from the .NET loader) 

 

Here are the wrapper files:

 

// Activation.h
#pragma once

#include “..\native\native.h”
#include <vcclr.h>

namespace Activation {

public interface class ICommand
{
void Do();
void Undo();
};

class CommandWrapper : public CCommand
{
public:
CommandWrapper(gcroot<ICommand ^> managedCommand)
: m_managedCommand(managedCommand)
{
}

virtual void Do()
{
m_managedCommand->Do();
}

virtual void Undo()
{
m_managedCommand->Undo();
}

private:
gcroot<ICommand ^> m_managedCommand;
};

public ref class Activator
{
public:
Activator();
~Activator();
void Activate(ICommand ^command);
void Undo();

private:
CActivator *m_pActivator;
};
}

// Activation.cpp

#include “stdafx.h”

#include “Activation.h”

namespace Activation
{
Activator::Activator() : m_pActivator(new CActivator())
{
}

Activator::~Activator()
{
delete m_pActivator;
}

void Activator::Activate(ICommand ^command)
{
CommandWrapper *pCommandWrapper = new CommandWrapper(command);
m_pActivator->Activate(pCommandWrapper);
}

void Activator::Undo()
{
delete m_pActivator->Undo();
}
}



When the C# code creates the Activation.Activator, the C++/CLI Activator class creates the native CActivator object and keeps it in a regular native pointer.

The C# code implements a command that implements the Activation.ICommand. When the C# calls the Activator.Activate(cmd), the C++/CLI Activator class allocates a CommandWrapper class that implements the CCommand base class from the native DLL. The magic is in the CommandWrapper class. To hold the managed ICommand object the CommandWrapper uses the gcroot template (gcroot is a C++ template that wraps the GCHandle class). gcroot keeps the managed object reference and allows us to call back to the C# command. 

The C++/CLI Activator class has a destructor. Destructors in C++/CLI ref classes implement the IDispose pattern. the C# using statement activates the Dispose method that delete the native CActivator class.

 

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

6 comments

  1. LuigiJune 26, 2008 ב 10:46

    thanks for this tutorial that I have to say just goes straight to the point! illuminating!

    Reply
  2. MarioMarch 25, 2009 ב 23:56

    Just what i need!!! Great Post!!!

    I get the big picture, but.. i’m having trouble with implementing it.. is it possible in any way to get my hands on your source code (solution)..

    you can email me @ onetomany@live.com

    Reply
  3. Elvis DukajAugust 8, 2009 ב 03:29

    My native dll is composed by an abastract class (not exported), two C factory function (extern “C” Interface* GetInterface();
    extern “C” void DestroyInterface(Interface*); and an implementation class (derived from the abstract one)
    How can i create a wrapper class in this situation? The native DLL doesn’t export class but just two C factory function, as in COM tecnology)

    Reply
  4. ElvisAugust 8, 2009 ב 03:34

    My native dll is composed by an abastract class (not exported), two C factory function (extern “C” Interface* GetInterface();
    extern “C” void DestroyInterface(Interface*); and an implementation class (derived from the abstract one)
    How can i create a wrapper class in this situation? The native DLL doesn’t export class but just two C factory function, as in COM tecnology)

    Reply
  5. BatulJuly 14, 2010 ב 17:39

    Just what i was looking for. Great article, thanks.

    Reply
  6. WillDecember 13, 2012 ב 15:02

    Dark blue on black? Otherwise, good stuff.

    Reply