C#/C++ 回调类(非函数)互操作 - 如何?

Posted

技术标签:

【中文标题】C#/C++ 回调类(非函数)互操作 - 如何?【英文标题】:C# / C++ Callback Class (not function) Interop - howto? 【发布时间】:2015-08-07 08:55:19 【问题描述】:

抱歉,如果有重复 - 我一直在努力寻找答案(我发现了一些关于使用函数回调的 C++ 函数的问题,以及从 C/C++ 调用时使用类作为回调的一些答案,但是..

我在 C# 中。 我正在调用 C++ 函数 我无法更改 C++ 函数的签名。 另外,我使用的是 p/invoke 的动态方法,而不是静态绑定(我的示例如下);

我可以处理函数采用简单值或包含简单值的结构并返回简单值的情况,但在这种情况下,我有一个 C 函数,它采用回调对象。

根据网上的一些想法,我尝试创建一个具有相同签名的类,然后固定该类,并将其传递进去。但是我得到了“对象是非 Blittable”的 C# 错误(它没有'里面没有任何变量!)。

头文件:

再次抱歉,如果我的示例中有任何错误,我已尝试删除所有不相关的代码并分解宏,但我希望您了解所发生事情的本质 p>

    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);

我的 C# 文件:

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);
    

欢迎提出建议

【问题讨论】:

你不能在这里传递指向托管类的指针,因为你的托管类 Callbacker 没有实现 FOO::ICallback 接口 【参考方案1】:

您可以为实现ICallback 接口的托管Callbacker 类创建一些非托管包装器。 像这样的:

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);

然后在 C# 部分,您可以创建一个委托来编组为PointerToManagedFunctionToInvoke,将其传递给CreateUnmanagedDelegate 接收ICallback 的非托管实现,并使用它传递给您的process

请注意managedCallback 应在UnmanagedDelegate 类对象处于活动状态时保持分配在C# 端。并且您应该在不再使用 UnmanagedDelegate 对象时将其删除。

或者你可以使用瘦 C++/CLI 来实现这个包装器。

【讨论】:

非常感谢!我花了一些时间来完成并实现它——我所做的是制作了第三个库,我直接绑定到 C# 项目而不通过 pInvoke。当我编译您的代码时,尽管它反对在课程末尾缺少分号(我不是世界上最好的 C++ 程序员,但对吗!?)我现在怀疑我在 C 中有这个包装层我可以重新定义我的结构如何从一个常见的 .h 文件中包含......但它有效。非常感谢! 是的,在类声明之后应该有分号——我的错——没有编译代码来检查语法错误。不客气,但是 - 请非常小心内存管理 - 一个简单的错误可能会导致内存泄漏和错误。这不是通常的 C# 垃圾收集代码。

以上是关于C#/C++ 回调类(非函数)互操作 - 如何?的主要内容,如果未能解决你的问题,请参考以下文章

将 C# 函数指针传递到 C++/CLI 互操作 dll

Python与C/C++互操作

将 C# 与 C 函数互操作的性能损失 [关闭]

C-Fortran 字符串互操作性

C-Fortran 字符串互操作性

好厉害的库edge js 实现C 与node js互操作