2011-02-05 13 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 
      }    
     } 
    } 
} 

問題は、それが第六千八百六十にクラッシュするということです繰り返し!問題は私の知識が不足していると思う。誰かが私を助けることができますか?

答えて

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

これは非常に特殊なケースで動作する可能性があります。 –

関連する問題