Jacko
Jacko

Reputation: 13195

Call C# delegate from CLI class that wraps native C++ class

I have a CLI wrapper to a C++ class. The C++ class has a callback that passes a char* array, and the length of that array.

void (CPP_CALLBACK*)(char* data, unsigned int dataLength)

In C# land, I have this callback

  private delegate void AppPacketReceivedDelegate(byte[] data);

My CLI wrapper receives the CPP_CALLBACK callback, and must then somehow call the C# delegate.

Any ideas on how to do this ? I tried

System::Action<cli::array<char>^>

but I am not sure how to match the delegate to this Action.

Update:

I convert the delegate into a function pointer, and then I can call the function pointer from CLI using this syntax:

  typedef void(__stdcall * WRAPPER_APP_PACKET_CALLBACK) (cli::array<unsigned char>^);

But, when I call this, the array is always of size 1 !

Upvotes: 2

Views: 361

Answers (2)

MoNsTeR
MoNsTeR

Reputation: 63

Here a simple example, not exactly matching your situation, but it could be quickly adapted.

CppClass.h

namespace MyCppNamespace
{
    typedef void (__stdcall *callback_function)(int, const char*);

    class CppClass
    {
    private:
        callback_function _callbackFunc;

    public:
        void DoSomething(callback_function callbackFunc);
    }
}

DotNetClass.cs

namespace MyManagedNamespace
{
    public delegate void ManagedCallback(int size, string message);
}

CliWrapperClass.h

#include "CppClass.h"

using namespace MyManagedNamespace;

namespace MyCliNamespace
{
    public ref class CliWrapperClass
    {
    private:
        CppClass *_cppClass;

    public:
        void DoSomething(ManagedCallback ^ callback);
    }
}

CliWrapperClass.cpp

#include "CliWrapperClass.h"

namespace MyCliNamespace
{
    void CliWrapperClass::DoSomething(ManagedCallback ^ callback)
    {        
        System::IntPtr callbackPtr = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(callback);

        _cppClass->DoSomething(static_cast<callback_function>(callbackPtr.ToPointer())); 
    }
}

In the .Net class, you can create an instance of CliWrapperClass and call its DoSomething(...) function in this way:

namespace MyManagedNamespace
{
    public class ManagedClass
    {
        private ManagedCallback _callback = MyCallback;

        private void MyCallback(int size, string message)
        {
            // Do what you want ...
        }

        public void MyFunction()
        {
            CliWrapperClass wrapper = new CliWrapperClass();
            wrapper.DoSomething(_callback);
        }
    }
}

Upvotes: 1

Jacko
Jacko

Reputation: 13195

In the end, I marked the C# class and the corresponding delegate as unsafe, and then I was able to call the delegate from C++ and pass over the C-style array. On the C# side, I created a managed array and copied the bytes over using Marshal.Copy

Upvotes: 0

Related Questions