gpt4 book ai didi

c# - 如何使用 C# 9.0 新委托(delegate)*将方法从 C# 9.0 正确发送到 C/C++ 库?

转载 作者:行者123 更新时间:2023-12-03 07:15:36 45 4
gpt4 key购买 nike

通知 : 使用 dotnet 版本 5.0.100-rc.2.20479.15
所以,正如标题所说,我试图弄清楚如何正确地将函数从 C# 9.0 发送到 C/C++ 代码库(例如 glfw)并将该函数用作回调方法(即用于错误回调) .
随着在 C# 9.0 中引入委托(delegate)*<> 语法,我一直在尝试将 C# 中的委托(delegate)作为 IntPtr 发送到 C/C++ 中的函数,该函数在 C# 中作为委托(delegate)*<> 可见。
对于这个例子,我准备了一个测试项目来模拟我得到的行为以及我试图在代码中完成的事情。我的 C/C++ 库中有两个函数(在这种情况下代替 GLFW),SetErrorCallbackFunctionInvokeError .

  • SetErrorCallbackFunction :通过定义为typedef void (*ERRORcallback)(int error_code, const char* description);的函数指针设置错误回调方法
  • InvokeError :模拟从SetErrorCallbackFunction设置的错误回调函数由于代码中其他地方的错误而被调用。

  • 在 C# 代码中,我有一个名为 Util 的库类。这给了我来自 C/C++ 库的函数指针作为 delegate*<IntPtr, void>对于 SetErrorCallbackFunction .
    在 main 方法中,我调用 SetErrorCallback 接收 Action<int, string>作为参数(调用errorcallback时我想运行的方法)。这个方法设置一个委托(delegate), _ErrorCallback等于 lambda 函数,它将 byte* 描述转换为字符串并调用 Action<int, string> errorCallback error_code 的参数和新生产的字符串。然后将该 lambda/delegate 从 Marshal.GetFunctionPointerForDelegate(_ErrorCallback) 转换为 IntPtr最终发送到 C/C++ 库。
    但是,在运行代码时,我得到了输出:
    Set errorCallback
    Invoking error
    Fatal error. Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code.
    InvokeError 时产生 fatal error 行方法尝试调用 errorCallback .
    这是我可以看到以我想要的行为执行此操作的唯一方法。我不想在代码的最高级别处理 byte*(在 main 方法中,我宁愿给函数一个带有 int 和 string 而不是 int 和 byte* 的 lambda)。 SECOND EXAMPLE在代码中是一种替代方式,但仍然不理想,因为我必须使用 byte* 就像我之前所说的那样,我不想在代码本身的“更高级别”上做)。 SECOND EXAMPLE代码输出如下:
    Set errorCallback
    Invoking error
    Error 4 - Custom Description
    我还为委托(delegate)*尝试了这些不同的参数类型:
    private static delegate*<Delegate, void> SetErrorCallbackFunction = (delegate*<Delegate, void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");
    private static delegate*<ErrorCallbackDelegate, void> SetErrorCallbackFunction = (delegate*<ErrorCallbackDelegate, void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");
    但他们都有同样的问题。
    如果有人对我可能做错了什么有任何想法,或者如果 fatal error 只是 C# 9.0 与委托(delegate)*一起工作的方式,请告诉我。如果您不理解我冗长的解释或需要澄清/更多细节,请告诉我,我会更新帖子以提供更多信息。
    谢谢!
    编辑:
    Edit1:在 C# 中包含一个 Wrapper 类,以更清楚地表明我希望将 byte* 转换为 string 以从主代码中抽象出来。
    Edit2:包括我正在使用的 dotnet 版本。
    编辑 3:作为 madreflection提到,我确实错过了 unmanaged InvokeError 声明中的关键字方法。然而,这是故意的。添加 unmanaged关键字将导致代码按预期工作,例如,使用没有 InvokeError 的库公开的方法, unmanaged关键字不能在任何地方使用。因此,为了使示例与 GLFW 之类的库相匹配,其中 errorCallback 方法完全由非托管代码管理, InvokeError方法声明不包括 unmanaged关键词。
    Edit4:下面的 GLFW 示例:
    输出:
    fatal error 。无效程序:试图从托管代码调用 UnmanagedCallersOnly 方法。
    GLFW 示例:
    using System;
    using System.Text;

    namespace Test
    {
    public static unsafe class Program
    {
    static void Main(string[] args)
    {
    // GLFW Example
    {
    GlfwWrapper.SetErrorCallback((error_code, description) =>
    {
    Console.WriteLine($"Error {error_code} - {description}");
    });
    GlfwWrapper.CreateWindow(1280, 720, "Title", IntPtr.Zero, IntPtr.Zero);
    }

    }
    }
    }
    using System;
    using System.Text;

    namespace Test
    {
    public unsafe class GlfwWrapper
    {
    // GLFW example
    public static delegate*<IntPtr, void> glfwSetErrorCallback = (delegate*<IntPtr, void>)GlfwUtil.StaticProcAddressPointer("glfwSetErrorCallback");
    public static delegate*<int, int, byte*, IntPtr, IntPtr, IntPtr> glfwCreateWindow = (delegate*<int, int, byte*, IntPtr, IntPtr, IntPtr>)GlfwUtil.StaticProcAddressPointer("glfwCreateWindow");

    // Delegate that matches the function signature in C/C++ library
    public unsafe delegate void ErrorCallbackDelegate(int error_code, byte* description);
    // Delegate stored so it doesn't get garbage collected.
    public static ErrorCallbackDelegate _ErrorCallback;

    public static void SetErrorCallback(IntPtr 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);
    }
    }
    }
    }
    代码:
    C# 9.0 代码:
    using System;
    using System.Text;

    namespace Test
    {
    public static unsafe class Program
    {
    static void Main(string[] args)
    {
    Wrapper.SetErrorCallback((error_code, description) =>
    {
    Console.WriteLine($"Error {error_code} - {description}");
    });
    // I want to avoid this (1/2)
    // Wrapper.SetErrorCallback((error_code, description) =>
    // {
    // byte* walk = description;
    // while (*walk != 0) walk++;
    // Console.WriteLine($"Error {error_code} - {Encoding.UTF8.GetString(description, (int)(walk - description))}");
    // });
    Wrapper.InvokeError();

    // SECOND EXAMPLE -----------------------------------------
    // This will work but the issue with having to write a method each time with a byte* is not ideal.
    Wrapper.SetErrorCallbackFunctionSECOND_EXAMPLE(&ErrorCallback);
    Wrapper.InvokeError();
    // SECOND EXAMPLE -----------------------------------------
    }

    // SECOND EXAMPLE
    public static void ErrorCallback(int error_code, byte* description)
    {
    byte* walk = description;
    while (*walk != 0) walk++;

    Console.WriteLine($"Error {error_code} - {Encoding.UTF8.GetString(description, (int)(walk - description))}");
    }
    }
    }
    using System;
    using System.Runtime.InteropServices;
    using System.Text;

    namespace Test
    {
    public unsafe class Wrapper
    {
    // delegate* to InvokeError C/C++ function
    public static delegate*<void> InvokeError = (delegate*<void>)Util.StaticProcAddressPointer("InvokeError");
    // delegate* to SetErrorCallbackFunction C/C++ function
    public static delegate*<IntPtr, void> SetErrorCallbackFunction = (delegate*<IntPtr, void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");

    // SECOND EXAMPLE --------------------------------------------------
    public static delegate*<delegate*<int, byte*, void>, void> SetErrorCallbackFunctionSECOND_EXAMPLE = (delegate*<delegate*<int, byte*, void>, void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");
    // SECOND EXAMPLE --------------------------------------------------

    // Delegate that matches the function signature in C/C++ library
    public unsafe delegate void ErrorCallbackDelegate(int error_code, byte* description);
    // Delegate stored so it doesn't get garbage collected.
    public static ErrorCallbackDelegate _ErrorCallback;

    // Method to set the error callback.
    public static void SetErrorCallback(Action<int, string> errorCallback)
    {
    // By doing it here (2/2)
    _ErrorCallback = (error_code, description) =>
    {
    byte* walk = description;
    while (*walk != 0) walk++;

    errorCallback(error_code, Encoding.UTF8.GetString(description, (int)(walk - description)));
    };
    SetErrorCallbackFunction(Marshal.GetFunctionPointerForDelegate(_ErrorCallback));
    }
    }
    }

    C 库:
    #include "library.h"

    ERRORcallback errorCallback;

    void SetErrorCallbackFunction(ERRORcallback callback)
    {
    errorCallback = callback;
    std::cout << "Set errorCallback" << std::endl;
    }

    void InvokeError()
    {
    std::cout << "Invoking error" << std::endl;
    errorCallback(4, "Custom Description");
    }
    #pragma once
    #include <iostream>

    typedef void (*ERRORcallback)(int error_code, const char* description);
    extern "C"
    {
    __attribute__((visibility("default"))) void SetErrorCallbackFunction(ERRORcallback callback);
    __attribute__((visibility("default"))) void InvokeError();
    }

    最佳答案

    非常感谢@madreflection。添加 [UnmanagedFunctionPointer(CallingConvention.Cdecl)]给代表并添加unmanaged[Cdecl]全部 委托(delegate)* 函数解决了这个问题。我不知道默认值是 __stdcall .

    关于c# - 如何使用 C# 9.0 新委托(delegate)*将方法从 C# 9.0 正确发送到 C/C++ 库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64543091/

    45 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com