如何正确地将方法从C#9.0发送到C/C++;库使用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),如何正确地将方法从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
SetErrorCallbackFunction
和InvokeError
SetErrorCallbackFunction
:通过定义为typedef void(*ERRORcallback)的函数指针设置错误回调方法(int error_code,const char*description)代码>
InvokeError
:模拟从SetErrorCallbackFunction
设置的错误回调函数由于代码中其他地方的错误而被调用
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();
}
}