如何正确地将方法从C#9.0发送到C/C++;库使用C#9.0新委托*?

如何正确地将方法从C#9.0发送到C/C++;库使用C#9.0新委托*?,c#,c++,c,glfw,c#-9.0,C#,C++,C,Glfw,C# 9.0,注意:使用dotnet版本5.0.100-rc.2.20479.15 因此,正如标题所说,我试图找出如何正确地将函数从C#9.0发送到C/C++代码库(例如glfw),并将该函数用作回调方法(即用于错误回调) 随着C#9.0中delegate*语法的引入,我一直在尝试将一个委托从C#作为IntPtr发送到C/C++中的一个函数,该函数在C#中作为委托可见* 对于这个例子,我准备了一个测试项目,它模拟我在代码中得到的行为和我试图完成的事情。我的C/C++库中有两个函数(在本例中代替GLFW),Se

注意:使用dotnet版本5.0.100-rc.2.20479.15

因此,正如标题所说,我试图找出如何正确地将函数从C#9.0发送到C/C++代码库(例如glfw),并将该函数用作回调方法(即用于错误回调)

随着C#9.0中delegate*语法的引入,我一直在尝试将一个委托从C#作为IntPtr发送到C/C++中的一个函数,该函数在C#中作为委托可见*

对于这个例子,我准备了一个测试项目,它模拟我在代码中得到的行为和我试图完成的事情。我的C/C++库中有两个函数(在本例中代替GLFW),
SetErrorCallbackFunction
InvokeError

  • SetErrorCallbackFunction
    :通过定义为
    typedef void(*ERRORcallback)的函数指针设置错误回调方法(int error_code,const char*description)

  • InvokeError
    :模拟从
    SetErrorCallbackFunction
    设置的错误回调函数由于代码中其他地方的错误而被调用

  • 在C代码中,我有一个名为
    Util
    的库类,它将C/C++库中的函数指针作为
    委托*
    提供给
    SetErrorCallbackFunction

    在main方法中,我调用SetErrorCallback,它将
    操作
    作为参数(调用errorcallback时我希望运行的方法)。此方法将委托
    \u ErrorCallback
    设置为lambda函数,该函数将字节*描述转换为字符串,并使用
    error\u code
    和新生成的字符串调用
    Action ErrorCallback
    参数。然后,该lambda/delegate将从
    Marshal.GetFunctionPointerForDelegate(\u ErrorCallback)
    转换为IntPtr,并最终发送到C/C++库

    但在运行代码时,我会得到以下输出:

    Set errorCallback
    Invoking error
    Fatal error. Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code.
    
    InvokeError
    方法尝试调用
    errorCallback
    时,会产生致命错误行。 这是我唯一能看到的用我想要的行为做这件事的方式。我不想在代码的最高级别处理byte*(在main方法中,我宁愿给函数一个带有int和字符串的lambda,而不是int和byte*)

    代码中的第二个示例是另一种方法,但仍然不理想,因为我必须使用字节*,就像我之前说过的,我不想在“更高级别”的代码本身中使用字节*)

    第二个示例
    代码输出如下:

    Set errorCallback
    Invoking error
    Error 4 - Custom Description
    
    我还为委托*尝试了以下不同的参数类型:

    private static delegate*<Delegate, void> SetErrorCallbackFunction = (delegate*<Delegate, void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");
    
    使用系统;
    使用系统文本;
    名称空间测试
    {
    公共不安全类GlfwWrapper
    {
    //GLFW示例
    公共静态委托*glfwSetErrorCallback=(委托*)GlfwUtil.StaticProcAddressPointer(“glfwSetErrorCallback”);
    公共静态委托*glfwCreateWindow=(委托*)GlfwUtil.StaticProcAddressPointer(“glfwCreateWindow”);
    //与C/C++库中的函数签名匹配的委托
    公共不安全委托void error callbackdelegate(int error_代码,字节*说明);
    //代理已存储,因此它不会被垃圾回收。
    公共静态ErrorCallbackDelegate\u ErrorCallback;
    公共静态void SetErrorCallback(IntPtr errorCallback){
    _ErrorCallback=(错误代码,描述)=>
    {
    字节*walk=描述;
    而(*walk!=0)walk++;
    errorCallback(错误代码,Encoding.UTF8.GetString(description,(int)(walk-description));
    };
    glfwSetErrorCallback(Marshal.GetFunctionPointerForDelegate(_ErrorCallback));
    }
    公共静态IntPtr CreateWindow(int宽度、int高度、字符串标题、IntPtr监视器、IntPtr共享)
    {
    固定(byte*ptr=Encoding.UTF8.GetBytes(title))
    {
    返回glfwCreateWindow(宽度、高度、ptr、监视器、共享);
    }
    }
    }
    }
    
    代码: C#9.0代码:

    使用系统;
    使用系统文本;
    名称空间测试
    {
    公共静态不安全类程序
    {
    静态void Main(字符串[]参数)
    {
    Wrapper.SetErrorCallback((错误代码,描述)=>
    {
    WriteLine($“Error{Error_code}-{description}”);
    });
    //我想避免这种情况(1/2)
    //Wrapper.SetErrorCallback((错误代码,描述)=>
    // {
    //字节*walk=描述;
    //而(*walk!=0)walk++;
    //WriteLine($“Error{Error_code}-{Encoding.UTF8.GetString(description,(int)(walk-description))}”);
    // });
    Wrapper.InvokeError();
    //第二个例子-----------------------------------------
    //这将起作用,但每次都必须用字节*编写方法的问题并不理想。
    SetErrorCallbackFunctionSECOND_示例(&ErrorCallback);
    Wrapper.InvokeError();
    //第二个例子-----------------------------------------
    }
    //第二个例子
    公共静态void ErrorCallback(int error_代码,字节*说明)
    {
    字节*walk=描述;
    而(*walk!=0)walk++;
    WriteLine($“Error{Error_code}-{Encoding.UTF8.GetString(description,(int)(walk-description))}”);
    }
    }
    }
    
    使用系统;
    使用System.Runtime.InteropServices;
    使用系统文本;
    名称空间测试
    {
    公共不安全类包装器
    {
    //委托*调用错误C/C++函数
    公共静态委托*InvokeError=(委托*)Util.StaticProcAddressPointer(“InvokeError”);
    
    private static delegate*<ErrorCallbackDelegate, void> SetErrorCallbackFunction = (delegate*<ErrorCallbackDelegate, void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");
    
    using System;
    using System.Text;
    using System.Runtime.InteropServices;
    public unsafe class Wrapper
    {
        public unsafe delegate void dg(int x, byte* y);
        [DllImport("glfw3.dll")]
        public static extern IntPtr glfwCreateWindow(int width, int height, byte* title, IntPtr monitor, IntPtr share);
        [DllImport("glfw3.dll")]
        public static extern void glfwSetErrorCallback(System.IntPtr f);
    
        // Method to set the error callback.
        public static dg _ErrorCallback;
        public static void SetErrorCallback(Action<int, string> errorCallback)
        {
            _ErrorCallback = (error_code, description) =>
            {
                byte* walk = description;
                while (*walk != 0) walk++;
                errorCallback(error_code, Encoding.UTF8.GetString(description, (int)(walk - description)));
            };
    
            glfwSetErrorCallback(Marshal.GetFunctionPointerForDelegate(_ErrorCallback));
        }
        public static IntPtr CreateWindow(int width, int height, string title, IntPtr monitor, IntPtr share)
        {
            fixed (byte* ptr = Encoding.UTF8.GetBytes(title))
            {
                return glfwCreateWindow(width, height, ptr, monitor, share);
            }
        }
    }
    public static unsafe class Program
    {
        static void Main(string[] args)
        {
            Wrapper.SetErrorCallback((error_code, description) =>
            {
                Console.WriteLine($"Error {error_code} - {description}");
            });
    
            Wrapper.CreateWindow(1280, 720, "Title", IntPtr.Zero, IntPtr.Zero);
            Console.ReadLine();
        }
    }