NullReferenceException during C ++ callback for C # function

developers!
I have a very strange problem. My project has a DLL file in C ++ and a graphical interface in C #. And I applied the callback for some compatibility. I planned for a C ++ dll to call C # code in some cases. It works ... but not for long, and I cannot understand why. Problem noted in comment in C # part
Here is the complete simplified code:

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]); } } } }; 

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

The problem is that it crashes on the 6860th iteration! I believe that the problem is the lack of my knowledge in this matter. Can anyone help me out?

+5
source share
1 answer
  SetDelegate(new WriteToConsoleCallback(PrintSymbol)); 

Yes, that will not work. The native code stores a function pointer for the delegate object, but the garbage collector does not see this link. How important this is, there are no object references. And the next collection destroys it. Kaboom.

You need to save the link to the object yourself. Add a field to the class to save it:

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

The rule is that the class that stores the object must have a lifetime, at least until the native code can call back. It must be static in this particular case, which is solid.

+10
source

Source: https://habr.com/ru/post/913003/


All Articles