通过使用 SuppressGCTransition 修饰的 P/Invoke 调用将 UnmanagedCallersOnly 函数指针传递给 C++ 时发生崩溃

Crash when passing an UnmanagedCallersOnly function pointer to C++ via P/Invoke call decorated with SuppressGCTransition

假设我们有以下 C++ 代码:

typedef int (*getIntPtr)(void);

extern "C" __declspec(dllexport) void InvokeFuncPtr(getIntPtr funcPtr) {
    std::wcout << funcPtr();
}

我们可以在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);
}

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

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

是的,你应该放弃 SuppressGCTransition,因为堆栈是运行时用来识别调用者是托管还是非托管的。

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

或者,您可以停止 UnmanagedCallersOnly,而是用 Marshal.GetFunctionPointerForDelegate 编组委托。