我有一个非常简单的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)]
没有帮助。
愚蠢的假设:
无法通过C#回调调用I / O函数。
在
static CallBackType callback = new CallBackType(CallBack);
GC正在移动目标对象,因为它不知道它是否被非托管C代码使用。毕竟,C代码只是复制对该对象的引用...
答案 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);