2011-02-05 4 views
5

разработчиков!
У меня очень странная проблема. Мой проект имеет DLL-файл в C++ и графический интерфейс на C#. И я применил обратный вызов для некоторой совместимости. Я планировал, что C++ dll вызовет код C# в некоторых случаях. Это работает ... но не долго, и я не могу понять, почему. Проблема отмечается в комментарии в C# часть
Здесь приведен полный код упрощенного образца:NullReferenceException при обращении C++ к функции 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# часть:

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

Проблема заключается в том, что он выходит из строя в 6860th итерация! Я считаю, что проблема заключается в недостатке моих знаний в этом вопросе. Может ли кто-нибудь помочь мне?

ответ

10
 SetDelegate(new WriteToConsoleCallback(PrintSymbol)); 

Да, это не может работать должным образом. Нативный код хранит указатель на функцию для объекта делегата, но сборщик мусора не видит эту ссылку. Насколько это касается, есть no ссылки на объект. И следующая коллекция уничтожает его. Kaboom.

Вы должны сохранить ссылку на объект самостоятельно. Добавление поля в классе, чтобы сохранить его:

private static WriteToConsoleCallback callback; 

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

правило состоит в том, что класс, который хранит объект должен иметь срок службы, по крайней мере до тех пор, как возможность нативный код, чтобы сделать обратный вызов. Он должен быть статичным в этом конкретном случае, это твердо.

+0

Обратите внимание, что для этого используется 'GC.KeepAlive'. – Yogu

+0

Он может работать в этом конкретном случае. –

Смежные вопросы