2011-02-05 20 views
5

desarrolladores!
Tengo un problema muy extraño. Mi proyecto tiene DLL escrito en C++ y una GUI escrita en C#. Y he implementado devolución de llamada para cierta interoperabilidad. Planeé que C++ dll llamará al código C# en algunas circunstancias. Funciona ... pero no mucho y no puedo entender por qué. El problema marcado en comentario en C# parte
Aquí el código completo de la muestra simplificada:NullReferenceException durante la devolución de llamada de C++ a la función 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 
      }    
     } 
    } 
} 

El problema es que se estrella en 6860a ¡iteración! Creo que el problema es la falta de mi conocimiento del tema. ¿Podría Sombody ayudarme?

Respuesta

10
 SetDelegate(new WriteToConsoleCallback(PrintSymbol)); 

Sí, esto no puede funcionar correctamente. El código nativo almacena un puntero de función para el objeto delegado pero el recolector de elementos no utilizados no puede ver esta referencia. En lo que a él respecta, hay referencias no al objeto. Y la próxima colección lo destruye. Kaboom.

Debe almacenar una referencia al objeto usted mismo. Añadir un campo en la clase para almacenarla:

private static WriteToConsoleCallback callback; 

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

La regla es que la clase que almacena el objeto debe tener una vida útil de al menos tanto tiempo como oportunidad de código nativo para hacer la devolución de llamada. Debe ser estático en este caso particular, eso es sólido.

+0

Tenga en cuenta que es para lo que 'GC.KeepAlive' es. – Yogu

+0

Podría funcionar en este caso muy específico. –

Cuestiones relacionadas