C++ 期间出现 NullReferenceException回调 C# 函数

发布于 2024-10-16 09:20:30 字数 2655 浏览 2 评论 0原文

开发商!
我有很奇怪的问题。我的项目有用 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]);
            }
        }
    }
};

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

问题是它在第 6860 次迭代时崩溃了!我认为问题是我缺乏这方面的知识。有人可以帮助我吗?

developers!
I have very strange problem. My project has DLL writen in C++ and a GUI writen in C#. And I have implemented callback for some interoperability. I planed that C++ dll will call C# code in some circumstances. It works... but not long and I cant understand why. The problem marked in comment in C# part
Here the complete code of simplified sample:

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# part:

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 in 6860th iteration! I believe that the problem is lack of my knowlege in the subject. Could sombody help me?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

痴梦一场 2024-10-23 09:20:30
       SetDelegate(new WriteToConsoleCallback(PrintSymbol));

是的,这不能正常工作。本机代码正在存储委托对象的函数指针,但垃圾收集器无法看到该引用。就其而言,没有对该对象的引用。下一个集合会毁掉它。轰隆隆。

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

    private static WriteToConsoleCallback callback;

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

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

       SetDelegate(new WriteToConsoleCallback(PrintSymbol));

Yes, this cannot work properly. The native code is storing a function pointer for the delegate object but the garbage collector cannot see this reference. As far as it is concerned, there are no references to the object. And the next collection destroys it. Kaboom.

You have to store a reference to the object yourself. Add a field in the class to store 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 as long as native code's opportunity to make the callback. It must be static in this particular case, that's solid.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文