我正在用 Go 和 C(瘦包装器)编写一个本机库,并用 C# 编写互操作代码。
我可以成功地将 C# 方法注册为库的回调,然后在它们不返回值时从 C 调用它们。但每当我尝试调用返回值的回调时,我就会崩溃。
该库被编译为 Windows DLL,下面是我尝试在 Unity 游戏中调用 C# 函数时生成的堆栈跟踪:
=================================================================
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
{
// Other code removed for clarity...
public delegate int GetIntCallback();
[DllImport("mylibrary")]
public static extern void SubscribeGetIntCallback(GetIntCallback intCb);
}
Test.cs
public class Test
{
// Other code removed for clarity...
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
{
// Other code removed for clarity...
public delegate void StringCallback(IntPtr ptr, int size);
[DllImport("mylibrary")]
public static extern void SubscribeLogInfo(StringCallback cb);
}
Events.cs
public static class Events
{
// Other code removed for clarity...
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));
}
}
为什么调用返回 int 的方法会崩溃,而调用返回 void 的方法却不会崩溃?
我的想法:
感谢您的宝贵时间!
我可以在这里看到你有两个问题:
DllImport
和委托声明上都缺少调用约定,我想应该是 CDecl
。public class Interop
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int GetIntCallback();
[DllImport("mylibrary", CallingConvention = CallingConvention.CDecl)]
public static extern void SubscribeGetIntCallback(GetIntCallback intCb);
}
public class Test
{
GetIntCallback _callback = TestIntCb;
public Test()
{
Interop.SubscribeGetIntCallback(_callback);
}
[MonoPInvokeCallback(typeof(Interop.GetIntCallback))]
private int TestIntCb()
{
return 100;
}
}