从返回值的 C 调用 C# 方法时“在 <unknown> <0xffffffff>”崩溃

问题描述 投票:0回答:1

我正在用 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 的方法却不会崩溃?

我的想法:

  • 我的方法签名有问题吗?
  • 是因为我的Test类不是静态的吗?
  • 是因为我的GetIntCb()方法不是静态的吗?
  • 是不是因为GetIntCb()的返回值在托管内存中,返回时没有复制到非托管内存中?
  • 是否存在类型兼容性问题? int 是 blittable 不是吗,不应该不进行转换就复制吗?

感谢您的宝贵时间!

c# c unity-game-engine interop
1个回答
0
投票

我可以在这里看到你有两个问题:

  • 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;
    }
}

© www.soinside.com 2019 - 2024. All rights reserved.