2011-02-05 47 views
5

开发人员!
我有很奇怪的问题。我的项目在C++中编写了DLL,并在C#中编写了一个GUI。我已经实现了一些互操作性的回调。我计划在某些情况下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 
      }    
     } 
    } 
} 

的问题是,它崩溃在第六千八百六十迭代!我认为这个问题缺乏我对这个问题的了解。 Sombody能帮助我吗?

回答

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 2013-12-16 13:19:08