调用带返回值的C#回调时触发Crash "at <unknown> <0xffffffff>"问题排查
带返回值的C#回调在Unity中调用崩溃问题
我用Go和C编写了原生库(C作为轻量封装层),并编写了C#互操作代码。目前可以成功将无返回值的C#方法注册为回调并从C中调用,但调用带返回值的回调时会触发崩溃。该库编译为Windows DLL,在Unity游戏中调用时生成的堆栈跟踪如下:
================================================================= Native Crash Reporting ================================================================= Got a UNKNOWN while executing native code. This usually indicates a fatal error in the mono runtime or one of the native libraries used by your application. ================================================================= ================================================================= Managed Stacktrace: ================================================================= at <unknown> <0xffffffff> at MyLib.Interop:TestCallGetIntCb <0x000cd> at MyLib.Test:TestCallGetIntCb <0x0007a> at Multiplayer:Update <0x000ea> at System.Object:runtime_invoke_void__this__ <0x00187> =================================================================
注释掉C代码中的回调调用即可消除错误。
触发崩溃的代码
原生库
callback.h
typedef int (*GetIntCallback)(); void SubscribeGetIntCallback(GetIntCallback cb); int GetInt();
callback.c
static GetIntCallback testGetIntCallback = 0; void SubscribeGetIntCallback(GetIntCallback cb) { testGetIntCallback = cb; } int GetInt() { if (testGetIntCallback != 0) { return testGetIntCallback(); } return 0; }
C#代码
Interop.cs
public class Interop { // 为简化已移除其他代码... public delegate int GetIntCallback(); [DllImport("mylibrary")] public static extern void SubscribeGetIntCallback(GetIntCallback intCb); }
Test.cs
public class Test { // 为简化已移除其他代码... public Test() { Interop.SubscribeGetIntCallback(TestIntCb); } [MonoPInvokeCallback(typeof(Interop.GetIntCallback))] private int TestIntCb() { return 100; } }
可正常运行的无返回值回调代码(对比用)
原生库
callback.h
typedef void (*StringCallback)(const char *message, int size); void SubscribeLogInfo(StringCallback cb); void LogInfo(const char *message);
callback.c
static StringCallback logInfoCallback = 0; void SubscribeLogInfo(StringCallback cb) { logInfoCallback = cb; } void LogInfo(const char *message) { if (logInfoCallback != 0) { logInfoCallback(message, (int)strlen(message)); } }
C#代码
Interop.cs
public class Interop { // 为简化已移除其他代码... public delegate void StringCallback(IntPtr ptr, int size); [DllImport("mylibrary")] public static extern void SubscribeLogInfo(StringCallback cb); }
Events.cs
public static class Events { // 为简化已移除其他代码... public delegate void MessageEventHandler(object sender, MessageEventArgs args); public static event MessageEventHandler LogInfoEvent; static Events() { Interop.SubscribeLogInfo(OnLogInfo); } [MonoPInvokeCallback(typeof(Interop.StringCallback))] private static void OnLogInfo(IntPtr messagePtr, int size) { string message = Marshal.PtrToStringAnsi(messagePtr, size); LogInfoEvent?.Invoke(typeof(Events), new MessageEventArgs(message)); } }
疑问点
- 是否存在方法签名问题?
- 是否因为Test类不是静态类?
- 是否因为TestIntCb()方法不是静态方法?
- 是否因为TestIntCb()的返回值位于托管内存,返回时未复制到非托管内存?
- 是否存在类型兼容性问题?int是 blittable 类型,难道不需要转换直接复制吗?
内容的提问来源于stack exchange,提问作者Daniel Vernall
相关产品推荐
相关产品推荐

