C # / C ++ Callback class (not a function) Interop - howto?

Sorry if there is a duplicate - I struggled to find an answer (I found a few questions about C ++ functions that use function callbacks and some answers that use classes as callbacks when called from C / C ++, but ..

  • I am in C #.
  • I am calling a C ++ function
  • I cannot change the signature of a C ++ function.
  • Also, I am using a dynamic p / invoke method, not a static binding (my example is below);

I can handle a situation where a function takes a simple value or a structure that contains simple values ​​and returns simple values, but in this case I have a C function that takes a callback object.

Following some ideas on the internet, I tried to create a class with the same signature, then bind that class and pass it in. But I get a C # error: "Object is not-Blittable" (which doesn't matter) t there are variables in it!).

Header file:

again apologizes if there are any errors in my example, I tried to strip all non-matching codes and blow up macros, but I hope you get the gist of what is happening

    struct someData_t
    {
    int length; /**< JSON data length */
    char* pData; /*< JSON data */
    };

    namespace FOO {
    class ICallback
    {
    public: virtual ~ICallback() {}

        virtual void Callback(const someData_t &response) = 0;
    };
    }

    extern "C" __declspec(dllexport) void process(const someData_t *inData, FOO::ICallback *listener);

      

My C # file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace Scratchpad {
    class Program {
    static void Main(string[] args) {
        Console.Out.WriteLine("I'm in Managed C#...");

        IntPtr user32 = NativeMethods.LoadLibrary(@"somelongpath\my_c.dll");

        IntPtr pAddressOfFunctionToCall = NativeMethods.GetProcAddress(user32, "process");

        process proc = (process)Marshal.GetDelegateForFunctionPointer(pAddressOfFunctionToCall, typeof(process));

        String someJson = "{ \"type\":\"someTestJson\"}";
        byte[] rawdata = Encoding.UTF8.GetBytes(someJson);

        someData myData = new someData();
        int dataLength = rawdata.Length * Marshal.SizeOf(typeof(byte)); // I realise byte is size 1 but..

        myData.length = rawdata.Length;

        myData.pData = Marshal.AllocHGlobal(dataLength);
        Marshal.Copy(rawdata, 0, myData.pData, dataLength);

        Console.Out.WriteLine("Size of mydata: " + Marshal.SizeOf(myData));
        IntPtr unmanagedADdr = Marshal.AllocHGlobal(Marshal.SizeOf(myData));

        Marshal.StructureToPtr(myData, unmanagedADdr, true);

        // ################################################################
        // FIXME: This area still working
        Callbacker myCallback = new Callbacker();

        GCHandle gch = GCHandle.Alloc(myCallback, GCHandleType.Pinned);

        IntPtr mycallbackPtr = gch.AddrOfPinnedObject();

        // FIXME: close of working area.
        // ################################################################
        // CALL THE FUNCTION!
        proc(unmanagedADdr, mycallbackPtr);


        myData = (someData) Marshal.PtrToStructure(unmanagedADdr, typeof(someData));

        Marshal.FreeHGlobal(unmanagedADdr);
        Marshal.FreeHGlobal(myData.pData);
        gch.Free();
        unmanagedADdr = IntPtr.Zero;

        bool result = NativeMethods.FreeLibrary(user32);

        Console.Out.WriteLine("Fini!)");
    }

    private delegate void process(IntPtr data, IntPtr callback);

    [StructLayout(LayoutKind.Sequential)]
    private struct someData { 
        public int length; 
        public IntPtr pData; 
    }

    private class Callbacker {
        public void Callback(someData response) {
        Console.WriteLine("callback Worked!!!");
        }
    }

    }

    static class NativeMethods {
    [DllImport("kernel32.dll")]
    public static extern IntPtr LoadLibrary(string dllToLoad);

    [DllImport("kernel32.dll")]
    public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);

    [DllImport("kernel32.dll")]
    public static extern bool FreeLibrary(IntPtr hModule);
    }
}

      

Any suggestions are appreciated

+3


source to share


1 answer


You can create a non-managed wrapper for your managed Callbacker

class that implements the interface ICallback

. Something like that:

typedef void (*PointerToManagedFunctionToInvoke)(const someData_t&);

class UnmanagedDelegate : public FOO::ICallback {
    private:
        PointerToManagedFunctionToInvoke managedCallback;
    public:
        UnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
        : managedCallback(inManagedCallback) {}

        virtual void Callback(const someData_t &response)
        {
            managedCallback(response);
        }
};

// Export this to managed part
UnmanagedDelegate* CreateUnmanagedDelegate(PointerToManagedFunctionToInvoke inManagedCallback)
{
    return new UnmanagedDelegate(inManagedCallback);
}

      

Then in the C # part, you can create a delegate for the marshal like PointerToManagedFunctionToInvoke

, pass it to CreateUnmanagedDelegate

get unmanaged implementation, ICallback

and use it to navigate toprocess



Remember managedCallback

to stay on the C # side as long as the class object is UnmanagedDelegate

alive. And you have to remove the object UnmanagedDelegate

when it is no longer in use.

OR you can use thin C ++ / CLI to implement this wrapper.

+2


source







All Articles