2011-02-05 4 views
5

개발자!
나는 아주 이상한 문제가있다. 내 프로젝트에는 C++로 작성된 DLL과 C#으로 작성된 GUI가 있습니다. 일부 상호 운용성을 위해 콜백을 구현했습니다. 나는 C++ dll이 어떤 상황에서는 C# 코드를 호출 할 것을 계획했다. 그것은 작동하지만 ... 오래 걸리지 않고 이유를 이해할 수 없습니다. C 번호 부분에 주석으로 표시된 문제는 여기에
단순화 된 샘플의 전체 코드 :C#에서의 C++ 콜백 중 NullReferenceException

C++ DLL :

#include <SDKDDKVer.h> 
#define WIN32_LEAN_AND_MEAN 
#include <windows.h> 

BOOL APIENTRY DllMain(HMODULE hModule, 
        DWORD ul_reason_for_call, 
        LPVOID lpReserved 
            ) 
    { 
    switch (ul_reason_for_call) 
    { 
     case DLL_PROCESS_ATTACH: 
     case DLL_THREAD_ATTACH: 
     case DLL_THREAD_DETACH: 
     case DLL_PROCESS_DETACH: 
      break; 
    } 
    return TRUE; 
} 

extern "C" 
{  
    typedef void (*WriteSymbolCallback) (char Symbol); 
    WriteSymbolCallback Test; 

    _declspec(dllexport) void InitializeLib() 
    { 
     Test = NULL; 
    } 

    _declspec(dllexport) void SetDelegate(WriteSymbolCallback Callback) 
    { 
     Test = Callback; 
    } 

    _declspec(dllexport) void TestCall(const char* Text,int Length) 
    { 
     if(Test != NULL) 
     { 
      for(int i=0;i<Length;i++) 
      { 
       Test(Text[i]); 
      } 
     } 
    } 
}; 

C 번호 부분 :

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.InteropServices; 

namespace CallBackClient 
{ 
    class Program 
    { 
     [UnmanagedFunctionPointer(CallingConvention.Cdecl)] 
     private delegate void WriteToConsoleCallback(char Symbol); 

     [DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)] 
     private static extern void InitializeLib(); 

     [DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)] 
     private static extern void SetDelegate(WriteToConsoleCallback Callback); 

     [DllImport("CallbackSketch.dll",CharSet=CharSet.Ansi,CallingConvention=CallingConvention.Cdecl)] 
     private static extern void TestCall(string Text,int Length); 

     private static void PrintSymbol(char Symbol) 
     { 
      Console.Write(Symbol.ToString()); 
     } 

     static void Main(string[] args) 
     { 
      InitializeLib(); 
      SetDelegate(new WriteToConsoleCallback(PrintSymbol)); 

      string test = "Hello world!"; 


      for (int i = 0; i < 15000; i++) 
      { 
       TestCall(test, test.Length);// It crashes when i == 6860!!!! Debugger told me about System.NullReferenceException 
      }    
     } 
    } 
} 

문제는 6천8백60번째에 충돌이다 되풀이! 나는 그 문제가 그 주제에 대한 나의 지식의 부족이라고 믿는다. 누군가가 나를 도울 수 있었 니?

답변

10
 SetDelegate(new WriteToConsoleCallback(PrintSymbol)); 

예. 정상적으로 작동하지 않습니다. 네이티브 코드는 대리자 개체에 대한 함수 포인터를 저장하지만 가비지 수집기는이 참조를 볼 수 없습니다. 그것까지는 이 아니며 객체에 대한 참조는입니다. 그리고 다음 컬렉션은 그것을 파괴합니다. Kaboom.

직접 개체에 대한 참조를 저장해야합니다. 를 저장하는 클래스에서 필드를 추가

private static WriteToConsoleCallback callback; 

    static void Main(string[] args) 
    { 
     InitializeLib(); 
     callback = new WriteToConsoleCallback(PrintSymbol); 
     SetDelegate(callback); 
     // etc... 
    } 

규칙 것은 그 객체가 콜백을 네이티브 코드의 기회로 적어도 긴 수명을 가지고 있어야 저장하는 클래스입니다. 이 특별한 경우 정적이어야합니다. 그것은 견고합니다.

+0

'GC.KeepAlive'가 무엇인지 유의하십시오. – Yogu

+0

아주 특별한 경우에 작동 할 수 있습니다. –