2

I'm trying to PInvoke into this C++ library function:

int Initialize(Callback* callback);

struct Callback
{
    //.......
    void virtual StateChanged(int state) = 0;
};

I have tried this naive C# code, but it doesn't work.

[DllImport("CPlusPlusLibrary.dll", SetLastError = true, EntryPoint = "?Initialize@CPlusPlusLibrary@@YAHPAUCallback@1@@Z")]
public static extern int Initialize(Callback callback);

[StructLayout(LayoutKind.Sequential)]
public class Callback
{
    //....
    public delegate void IntDelegate(int state);
    public IntDelegate StateChanged(int state);
}
var callback = new Callback();
var result = Initialize(callback);
Alex Burtsev
  • 12,418
  • 8
  • 60
  • 87
  • You need to make a C++/CLI wrapper. – David Heffernan Dec 16 '11 at 12:29
  • @Alex Burtsev, your definition of Callback in C# is incorrect. You were trying to pass an instance of a C# class while it takes a pointer in the C++ API. The struct of Callback in C++ is basically a (pure) abstract class although you skipped the rest of the code. The struct only contains a pointer pointing to the virtual function table, so the size is only 4 bytes for 32 bits process and 8 bytes for 64 bits process. If you point the address in the virtual function table to pointer of StateChanged in C#. Your code shall work. – xInterop Dec 13 '15 at 04:46

3 Answers3

3

It is impossible to do it that way as far as I know. Methods are not "in there" as fields would be, beside this, creating a struct with virtual method will create a vtable pointer in your objects, that you are not taking into account in c# mirrored class.

What you can do is to PInvoke to method that takes a functionPointer (in C++) and pass a delegate (C#) there. You can then use this function pointer to call it from native code and your delegate will launch.

You could then change your stateChange method definition to take a Callback* as a first parameter, so when you call it from native code you can pass an object pointer which is responsible of that change and marshal it back to Callback in c#.

//Edit without having source of native dll, building a bridge between c# and c++ is what comes to my mind. This can be done with c++/cli or c++, sticking to native my idea would be something like this:

//c++ <--> new c++ dll <--> c#
struct CallbackBridge : public Callback
{
    void (*_stateChanged)(int);

    virtual void stateChanged(int state)
    {
        if (_stateChanged)
            _stateChanged(this, state);
    }
};

void* CreateCallback() { return new CallbackBridge(); }
void DeleteCallback(void* callback); { delete callback; }
void setStateChanged(void* callback, void (*ptr)(void*, int))
{
    CallbackBridge* bridge = (CallbackBridge*)callback;
    bridge->stateChanged = ptr;
}
///... other helper methods

The idea here is to treat your object as a black box (hence void* everywhere - it can be any pointer, but from c# you will just see this a a SafeHandle / IntPtr and write helper methods that you can PInvoke to to create / delete and modify objects. You can mock those virtual calls by giving your object a delegate through such method.

From c# usage could look like this: (IntPtr for simplicity, SafeHandle could be used):

IntPtr callback = CreateCallback();
SetStateChanged(callback, myCallback);
//somewhere later:
DeleteCallback(callback);

void MyCallback(IntrPtr callback, int state)
{
    int someData = SomeOtherHelperMethod(callback);
    ConsoleWrite("SomeData of callback is {0} and it has changed it's state to {1}", someData, state);
}

I know, it's a bit clumsy for bigger objects, but without c++/cli wrapper I have never found any better way to be able to handle all those tricky cases like virtual calls etc.

Marcin Deptuła
  • 11,789
  • 2
  • 33
  • 41
2

Your C# class is no substitute for the C++ struct, it has an entirely different internal organization. The C++ struct has a v-table because of the virtual method. It is not a POD type anymore. It has a hidden field that contains a pointer to the v-table. And a compiler-generated constructor and destructor. The v-table is an array of pointers to the virtual methods. A C# class has something similar, called "method table", but it organized completely differently. It will have the System.Object methods for example and contains pointers to all methods, not just the virtual ones. The class object layout is completely different as well. The struct keyword in the C++ language in this context is just a substitute for the class keyword with all members public by default.

A wrapper class written in the C++/CLI language is required. There's a bit of a learning curve to the language but it is not steep if you've got experience with .NET and C++. Boilerplate code for such a wrapper is in this answer. You can call back from native code into managed code through a function pointer returned by Marshal::GetFunctionPointerForDelegate().

Community
  • 1
  • 1
Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
0

I have heard that you don't have the source code of C++ library. Then you might have a look at this.

But exporting classes from unmanaged dll to managed one without source sounds dangerous to me.

ali_bahoo
  • 4,732
  • 6
  • 41
  • 63