매우 간단한 C# 대리자가 C에서 작성된 관리되지 않는 .DLL에 콜백으로 전달되어 몇 백 번의 반복 (콜백) 후에 불가피하게 충돌합니다. C#은 먼저 콜백을 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 콜백 후, 실패 윈도우 프로그램을 닫고 여부를 통지합니다. 해결책이 가능합니다. "
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 함수를 호출 할 수 없습니다.
정적 CallBackType 콜백 = 새로운 CallBackType (콜백)에서
;
대상 개체는 관리되지 않는 C 코드에서 사용되는지 여부를 모르기 때문에 GC로 이동됩니다. 결국,이 객체에 C 코드 만 복사 참조 ...
감사, 제 2 용액은 미세 (30000> 대한 콜백)를 작동하고 난 후 첫번째 시험 할 것이다. C# (C가 아닌)이 스택을 정리해야하는지/이해해야하는지 정확히 이해합니까? 실제로, C# 코드에서 extern C 함수는 CallingConvention = CallingConvention.Cdecl과 함께 추가로 장식되어야합니다 ([DllImport]). 실제로,이 경우 (Cdecl) 스택 정리는 호출자 (즉, C#)에 의해 수행되고 콜백 함수 (C에서)는 __stdcall로 꾸며진다. (스택 정리는 호출 된 함수에 의해 수행된다. C#의 콜백). – user2770141
이 관리 형/관리되지 않는 전화 컨벤션 사업에 대한 참고 자료를 제안 해주십시오. 나는 그것을 설명하는 ~ 100 웹 페이지를 읽었지만, 그것은 모두 연금술로 보인다. – user2770141
일치하는 항목 만 있으면됩니다. C# pinvoke의 기본값은 __stdcall이며 C의 기본값은 __cdecl이므로 누군가가 선언을 변경해야합니다. 어느 것을 선택할지는 당신에게 달려 있습니다. [이 답변]에서 더 많은 연금술을 찾을 수 있습니다 (http : // stackoverflow.com/a/15664100/17034). –