【问题标题】:Using Marshal::GetFunctionPointerForDelegate and calling a callback from unmanaged code causes a buffer overrun when quitting application退出应用程序时,使用 Marshal::GetFunctionPointerForDelegate 并从非托管代码调用回调会导致缓冲区溢出
【发布时间】:2025-12-27 19:35:06
【问题描述】:

我正在使用带有 .NET Framework 3.5 的 Visual Studio 2008 并制作 C++/CLI/.NET Forms 应用程序;游戏的编辑器。除了编辑器之外的所有游戏代码都是纯C++。我已经到了需要从非托管代码调用表单代码中的回调的地步。

我正在使用 GetFunctionPointerForDelegate。

首先,我的 Form 类中有这些:

public: delegate void ADelegate(Int32, float, float, float, Int32);
public: static ADelegate^ delegateInstance;

然后我在 Form 构造函数中设置回调:

        // Set up World Objects callback
        typedef void (__stdcall *WorldLocationUpdateCallback)(Int32, float, float, float, Int32);
        WorldLocationUpdateCallback _callback;

        // Create an instance of a delegate, using GetFunctionPointerForDelegate
        delegateInstance = gcnew ADelegate( this, &MainForm::InitWorldObjectPane );

        // Convert the delegate to a pointer
        IntPtr anIntPtr = Marshal::GetFunctionPointerForDelegate( delegateInstance );

        // Cast the pointer into a function pointer
        _callback = static_cast<WorldLocationUpdateCallback>(anIntPtr.ToPointer());

        CEditorUI::UI()->setWorldLocationUpdateCallback( (void*)_callback );

所以我存储指针并在非托管代码中,在我的应用程序初始化的早期从不同的线程调用回调:

    // Set up World Object pane in editor to use this map/maptile/etc
    {
        typedef void (*WorldLocCallback)(int, float, float, float, int);
        void* _callback = CEditorUI::UI()->getWorldLocationUpdateCallback();
        int _mapId = charGfxObj->getMapId();
        float _posX = charGfxObj->getPos().x;
        float _posY = charGfxObj->getPos().y;
        float _posZ = charGfxObj->getPos().z;
        int _tileRadius = TILE_RADIUS;
        ((WorldLocCallback)(_callback))( _mapId, _posX, _posY, _posZ, _tileRadius );
    }

一切都在我的应用程序中正常运行,直到我关闭应用程序。它总是在调用线程中因缓冲区溢出错误而死,调用堆栈相当无用:

editor.exe!__crt_debugger_hook()    Unknown
>editor.exe!__report_gsfailure()  Line 298 + 0x7 bytes  C
editor.exe!__CorExeMain@0()  + 0xe3 bytes   C++

这里的关键是即使回调实际上什么也没做,它也会崩溃。要导致崩溃,我所要做的就是启用调用回调的最后一行代码。禁用该行,没有问题。请记住,崩溃仅在退出时发生,而不是在实际使用回调时发生。

有什么可以尝试的推荐吗?

【问题讨论】:

    标签: c++-cli unmanaged buffer-overrun


    【解决方案1】:

    在搞砸了一整天并搜索其他相关内容之后,我稍微解决了这个问题,以防其他人遇到这个问题。

    将委托声明移到类外并用:

    [UnmanagedFunctionPointerAttribute(CallingConvention::Cdecl)] 
    

    ...为我解决了。显然cdecl的堆栈清除效果解决了溢出问题。

    【讨论】:

    • 非常感谢您,我已经花了 3 多天的时间试图确定这一点。当 GC 在我的托管代码中运行时,我会遇到堆栈溢出。我有一个托管 C++ 类包装了一个设置回调的非托管类。按照您的描述装饰回调解决了问题