2011-02-05 9 views
5

sviluppatori!
Ho un problema molto strano. Il mio progetto ha una scrittura DLL in C++ e una GUI scritta in C#. E ho implementato la callback per una certa interoperabilità. Ho progettato che C++ dll chiamerà il codice C# in alcune circostanze. Funziona ... ma non molto e non capisco perché. Il problema segnato in commento in C# parte
Ecco il codice completo di campione semplificata:NullReferenceException durante la richiamata C++ alla funzione C#

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# parte:

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 
      }    
     } 
    } 
} 

Il problema è che si blocca in 6860i iterazione! Credo che il problema sia la mancanza della mia conoscenza in materia. Potrebbe essere utile il sombody?

risposta

10
 SetDelegate(new WriteToConsoleCallback(PrintSymbol)); 

Sì, questo non funziona correttamente. Il codice nativo sta memorizzando un puntatore a funzione per l'oggetto delegato ma il garbage collector non può vedere questo riferimento. Per quanto lo riguarda, ci sono no riferimenti all'oggetto. E la prossima collezione lo distrugge. Kaboom.

È necessario memorizzare un riferimento all'oggetto. Aggiungere un campo nella classe di conservarlo:

private static WriteToConsoleCallback callback; 

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

La regola è che la classe che memorizza l'oggetto deve avere una durata di almeno finchè opportunità di codice nativo per rendere il callback. Deve essere statico in questo caso particolare, è solido.

+0

Nota: ecco a cosa serve 'GC.KeepAlive'. – Yogu

+0

Potrebbe funzionare in questo caso molto specifico. –