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

时间:2013-11-23 23:56:14

标签: c# c dll callback unmanaged

我有一个非常简单的C#委托作为回调传递给用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);

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

1 个答案:

答案 0 :(得分:3)

  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);