2013-11-23 1 views
0

매우 간단한 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)] 

이 도움이되지

참고.

바보 가설 :

  1. 하나는 C#을 콜백에서 I/O 함수를 호출 할 수 없습니다.

    정적 CallBackType 콜백 = 새로운 CallBackType (콜백)에서

  2. ;

대상 개체는 관리되지 않는 C 코드에서 사용되는지 여부를 모르기 때문에 GC로 이동됩니다. 결국,이 객체에 C 코드 만 복사 참조 ...

답변

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

감사, 제 2 용액은 미세 (30000> 대한 콜백)를 작동하고 난 후 첫번째 시험 할 것이다. C# (C가 아닌)이 스택을 정리해야하는지/이해해야하는지 정확히 이해합니까? 실제로, C# 코드에서 extern C 함수는 CallingConvention = CallingConvention.Cdecl과 함께 추가로 장식되어야합니다 ([DllImport]). 실제로,이 경우 (Cdecl) 스택 정리는 호출자 (즉, C#)에 의해 수행되고 콜백 함수 (C에서)는 __stdcall로 꾸며진다. (스택 정리는 호출 된 함수에 의해 수행된다. C#의 콜백). – user2770141

+0

이 관리 형/관리되지 않는 전화 컨벤션 사업에 대한 참고 자료를 제안 해주십시오. 나는 그것을 설명하는 ~ 100 웹 페이지를 읽었지만, 그것은 모두 연금술로 보인다. – user2770141

+0

일치하는 항목 만 있으면됩니다. C# pinvoke의 기본값은 __stdcall이며 C의 기본값은 __cdecl이므로 누군가가 선언을 변경해야합니다. 어느 것을 선택할지는 당신에게 달려 있습니다. [이 답변]에서 더 많은 연금술을 찾을 수 있습니다 (http : // stackoverflow.com/a/15664100/17034). –