C# 将非托管CallersOnly函数指针传递到C+;时崩溃+;通过用SuppressGCTransition修饰的P/Invoke调用 假设我们有以下C++代码: typedef int (*getIntPtr)(void); extern "C" __declspec(dllexport) void InvokeFuncPtr(getIntPtr funcPtr) { std::wcout << funcPtr(); }

C# 将非托管CallersOnly函数指针传递到C+;时崩溃+;通过用SuppressGCTransition修饰的P/Invoke调用 假设我们有以下C++代码: typedef int (*getIntPtr)(void); extern "C" __declspec(dllexport) void InvokeFuncPtr(getIntPtr funcPtr) { std::wcout << funcPtr(); },c#,function-pointers,c#-9.0,unmanagedcallersonly,suppressgctransition,C#,Function Pointers,C# 9.0,Unmanagedcallersonly,Suppressgctransition,当InvokeFuncPtr标记为SuppressGCTransition时,这将导致程序崩溃,错误为“致命错误。无效程序:试图从托管代码调用非托管CallersOnly方法。”。如果我们删除SuppressGCTransition属性,它将按预期工作,并且123将打印到控制台 这是预期的行为吗?我认为这相当于运行时将ReturnInt()看作是从托管代码调用的,只需几个额外的间接步骤。如果是这样的话,有没有办法解决这个问题,或者我应该简单地关闭SuppressGCTransition属性?是的

InvokeFuncPtr
标记为
SuppressGCTransition
时,这将导致程序崩溃,错误为“致命错误。无效程序:试图从托管代码调用非托管CallersOnly方法。”。如果我们删除
SuppressGCTransition
属性,它将按预期工作,并且
123
将打印到控制台


这是预期的行为吗?我认为这相当于运行时将
ReturnInt()
看作是从托管代码调用的,只需几个额外的间接步骤。如果是这样的话,有没有办法解决这个问题,或者我应该简单地关闭
SuppressGCTransition
属性?

是的,您应该关闭
SuppressGCTransition
,因为堆栈是运行时用来标识调用方是托管的还是非托管的

如果堆栈中没有转换,则运行时无法判断堆栈是否已转换为非托管


或者,您可以关闭
UnmanagedCallersOnly
,而是使用
marshall来封送委托。GetFunctionPointerForDelegate

[UnmanagedCallersOnly]专用于调用C的本机代码,[SuppressGCTransition]专用于调用本机代码的C。
[DllImport("NativeLib.dll", CallingConvention = CallingConvention.Cdecl), SuppressGCTransition]
public static unsafe extern void InvokeFuncPtr(delegate* unmanaged[Cdecl]<int> funcPtr);
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
public static int ReturnInt() => 123;

// ... Elsewhere:
unsafe {
    InvokeFuncPtr(&ReturnInt);
}