多次迭代后,来自 C 的 C# 回调莫名其妙地失败

标签 c# c dll callback unmanaged

我有一个非常简单的 C# 委托(delegate)作为回调传递给用 C 编写的非托管 .DLL,它在几百次迭代(回调)后莫名其妙地崩溃了。 C# 首先将回调传递给 C,然后调用一个无限 C 循环,它每秒调用一次回调。

using System;
using System.Runtime.InteropServices;

class tst {
    public const String DLL_NAME = @"dll-tst3.dll";
    public delegate void CallBackType(Int32 fst, Int32 snd);

    [DllImport(DLL_NAME)]
    public static extern void SetCallback(CallBackType cb);

    [DllImport(DLL_NAME)]
    public static extern void UnmanagedInfiniteLoop();

    static UInt32 nCallbackCalls = 0;

    public static void CallBack(Int32 fst, Int32 snd) {
        Console.WriteLine("nCallbacks={0}, fst={1}, snd={2}",
               ++nCallbackCalls, fst, snd);
        DateTime dt = DateTime.Now;
        String logLine = String.Format("{0}: {1}, {2}", 
               dt.ToString("yyyy-MM-dd HH:mm:ss.fff"), fst, snd);
        Console.WriteLine("{0}", logLine);
        GC.KeepAlive(callback); // prevent garbage collection
    }

    static CallBackType callback = new CallBackType(CallBack);

    static void Main(string[] args) {
        SetCallback(callback); // register callback in C
        UnmanagedInfiniteLoop(); // C code calling callback indefinitely
    }
}

我的 C 代码是这样的:

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>

typedef void(__cdecl *callback_t) (int fst, int snd);

callback_t callback;

extern "C" //Every method defined in this block is not mangled 
{
__declspec(dllexport) void __cdecl SetCallback (callback_t cb) {
    callback = cb;
}

__declspec(dllexport) void __cdecl UnmanagedInfiniteLoop () {
    int a = 0;
    int b = 1;
    for (;;) {
        Sleep(1000);
        callback(a++, b++); // C# callback
    }
}
} //End 'extern "C"' to prevent name mangling

它失败了,总是在 550 次回调之后,“出现问题导致程序停止正常工作。Windows 将关闭程序并在有可用解决方案时通知您。”

nCallbacks=550, fst=549, snd=550
2013-11-24 00:12:34.509: 549, 550
nCallbacks=551, fst=550, snd=551
2013-11-24 00:12:35.510: 550, 551
nCallbacks=552, fst=551, snd=552
2013-11-24 00:12:36.511: 551, 552

此时弹出上面的“A problem...”错误信息。

如果 C# CallBack 做了更实质性的事情,比如将 logLine 写入(附加)到流中,它会在较少的回调后崩溃。

注意额外的装饰

[DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]

不帮忙。

愚蠢的假设:

  1. 不能从 C# 回调中调用 I/O 函数。

  2. static CallBackType callback = new CallBackType(CallBack);

目标对象正被 GC 移动,因为它不知道它是否被非托管 C 代码使用。毕竟,C 代码只是复制对该对象的引用...

最佳答案

  typedef void(__cdecl *callback_t) (int fst, int snd);

回调 的调用约定不匹配。这将使堆栈不平衡,并在下溢时迅速使程序崩溃。修复:

  [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
  public delegate void CallBackType(Int32 fst, Int32 snd);

或者在C端:

  typedef void (__stdcall *callback_t)(int fst, int snd);

关于多次迭代后,来自 C 的 C# 回调莫名其妙地失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20169447/

相关文章:

c++ - c99 : 63 characters of an internal name are significant?

c++ - 使用带有 C++ 对象和标准库容器的插件系统

c# - 使用第三方 DLL 对 PInvokeStackImbalance 进行故障排除

.net - 该类型存在于两个 DLL 中

c# - 在 ASP.NET 中删除记录后防止后退按钮或显示错误

c# - 如何将列表 <AudioTrack> 从类传递到类 AudioPlayer

c# - ASP.NET Web Api 作为一个解决方案中的独立项目

c++ - C/C++ Reflection and JNI - 一种调用尚未编写的本地代码的方法

java - 判断一个列表是否是另一个具有正确顺序的列表的有序子集的函数 c# 或 java

c++ - types.h 中的内容——编译器在哪里定义了 int、signed int 等的宽度?