C++回调到C#函数期间的NullReferenceException

Ant*_*nov 7 c# c++ callback marshalling nullreferenceexception

开发商!
我有一个非常奇怪的问题.我的项目有用C++编写的DLL和用C#编写的GUI.我已经实现了一些互操作性的回调.我计划在某些情况下C++ dll会调用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]);
            }
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

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
            }            
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题是它在6860次迭代中崩溃了!我认为问题在于我在这个问题上缺乏知识.sombody可以帮帮我吗?

Han*_*ant 12

       SetDelegate(new WriteToConsoleCallback(PrintSymbol));
Run Code Online (Sandbox Code Playgroud)

是的,这不能正常工作.本机代码存储委托对象的函数指针,但垃圾收集器无法看到此引用.就其而言,没有对该对象的引用.下一个系列会破坏它.KABOOM.

您必须自己存储对象的引用.在类中添加一个字段来存储它:

    private static WriteToConsoleCallback callback;

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

规则是存储对象的类必须具有至少与本机代码进行回调的机会一样长的生命周期.在这种特殊情况下它必须是静态的,这是可靠的.