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

如何解决如何使用C#9.0 new委托将方法从C#9.0正确发送到C / C ++库?

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

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

在C#9.0中引入了委托* 语法后,我一直试图将一个委托作为IntPtr从C#发送到C / C ++中的一个函数,该函数在C#中可以看作一个委托* 。

在此示例中,我准备了一个测试项目,该项目模拟了我所得到的行为以及我试图在代码中完成的工作。我的C / C ++库中有两个功能(在本例中为GLFW代替)SetErrorCallbackFunctionInvokeError

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

  2. InvokeError:模拟由于代码中其他地方的错误而从SetErrorCallbackFunction调用的错误回调函数集。

在C#代码中,我有一个名为Util的库类,该类为我提供了来自C / C ++库的函数指针,作为delegate*<IntPtr,void>的{​​{1}}。

在main方法中,我调用SetErrorCallback,该方法以SetErrorCallbackFunction作为参数(调用errorcallback时要运行的方法)。此方法将一个委托Action<int,string>设置为一个等于lambda函数的函数,该函数将byte *描述转换为字符串,并使用_ErrorCallback和新生成的字符串调用Action<int,string> errorCallback参数。然后,该lambda / delegate从error_code转换为IntPtr,最终将其发送到C / C ++库。

虽然在运行代码时,我得到了输出:

Marshal.GetFunctionPointerForDelegate(_ErrorCallback)

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

代码中的errorCallback是另一种实现方式,但仍然不理想,因为我必须像以前所说的那样使用字节*,我不想在“更高级别的代码”)。

SECOND EXAMPLE代码输出如下:

SECOND EXAMPLE

我还为委托人*尝试了这些不同的参数类型:

Set errorCallback
Invoking error
Error 4 - Custom Description
private static delegate*<Delegate,void> SetErrorCallbackFunction = (delegate*<Delegate,void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction");

但是他们都有相同的问题。

如果任何人对我可能做错的事情有任何想法,或者致命错误仅仅是C#9.0如何与委托*配合使用,请告诉我。如果您不理解我冗长的解释或需要澄清/更多详细信息,也请让我知道,我将用更多信息更新该帖子。

谢谢!

编辑:

Edit1:C#中包含一个Wrapper类,以更清楚地表明我希望从主代码中抽象出byte *到string的转换。

Edit2:包括我正在使用的dotnet版本。

Edit3:正如private static delegate*<ErrorCallbackDelegate,void> SetErrorCallbackFunction = (delegate*<ErrorCallbackDelegate,void>)Util.StaticProcAddressPointer("SetErrorCallbackFunction"); 所述,我确实在madreflection方法的声明中缺少了一个unmanaged关键字。但是,这是故意的。添加InvokeError关键字将使代码按预期工作,但是,例如,使用没有公开的unmanaged方法的库,InvokeError关键字将无法运行可以在任何地方使用。因此,为了使示例能够使用GLFW之类的库进行匹配,其中的errorCallback方法完全由非托管代码管理,unmanaged方法声明中不包含InvokeError关键字。

Edit4:GLFW示例如下:

输出: 致命错误。无效的程序:尝试从托管代码中调用UnmanagedCallersOnly方法。

GLFW示例:

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

代码:

C#9.0代码:

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> glfwCreateWindow = (delegate*<int,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);
            }
        }
    }
}
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))}");
        }
    }
}

C库:

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,void>,void> SetErrorCallbackFunctionSECOND_EXAMPLE = (delegate*<delegate*<int,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,(int)(walk - description)));
            };
            SetErrorCallbackFunction(Marshal.GetFunctionPointerForDelegate(_ErrorCallback));
        }
    }
}

#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");
}

解决方法

非常感谢@madreflection。将[UnmanagedFunctionPointer(CallingConvention.Cdecl)]添加到委托中并将unmanaged[Cdecl]添加到所有委托*函数可以解决此问题。我没有意识到默认值为__stdcall

,

您还可以使用DllImport:

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,string title,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();
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-