从非托管C到C#的回调可以正常工作,但仅限于调试器

从非托管C到C#的回调可以正常工作,但仅限于调试器,第1张

概述从非托管C回调C#很棘手. 我从中学到了大部分所需要的东西 MSDN article 还有这个      stackoverflow tip, 并且结果在调试器中工作正常. 但是在调试器之外它失败了“对象引用没有设置为对象的实例”. 这是(简化的)C#代码: class CSharpCode{ delegate void CallbackDelegate(); void DoC 从非托管C回调C#很棘手.
我从中学到了大部分所需要的东西
MSDN article
还有这个
stackoverflow tip,
并且结果在调试器中工作正常.
但是在调试器之外它失败了“对象引用没有设置为对象的实例”.

这是(简化的)C#代码:

class CSharpCode{    delegate voID CallbackDelegate();    voID DoCSharp()    {        CallbackDelegate callbackDelegate = TheCallback;        IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);        GCHandle gchCallbackDelegate = GCHandle.Alloc(callbackDelegatePointer);        GC.Collect(); // create max space for unmanaged allocations        CppClicode.DoCppCli(callbackDelegatePointer);    }    public static voID TheCallback()    {        MessageBox.Show("It worked");    }}

这是C代码:

#pragma managedpublic ref class CppClicode{    static voID DoCppCli(IntPtr^ callbackDelegatePointer)    {        callback theCallback = static_cast<callback>(callbackDelegatePointer->topointer());        DoCpp(theCallback);    }}#pragma unmanagedtypedef voID (__stdcall *callback)();voID DoCpp(callback theCallback){    theCallback();}

错误发生在调用theCallback()和到达TheCallback()之间.该错误表明某些不可见的托管对象已变为null.

如果我删除GC.Collect(),问题就会消失.但这只是意味着有一天当GC碰巧在错误的时刻发生时,它会再次出现作为间歇性的谜团.

GCHandle保护代表不被收集,但允许重新定位. MSDN文章说“如果一个委托由垃圾收集重新定位,它将不会影响底层托管回调,因此Alloc用于添加对委托的引用,允许重定位委托,但防止丢弃.使用GCHandle而不是pin_ptr减少了托管堆的碎片潜力.“

怎么了?

解决方法 您必须分配委托本身,而不是其IntPtr.完成CSharpCode实例后,您还必须释放GCHandle.
class CSharpCode : Idisposible{    delegate voID CallbackDelegate();    GCHandle gchCallbackDelegate;    voID DoCSharp()    {        CallbackDelegate callbackDelegate = TheCallback;        IntPtr callbackDelegatePointer = Marshal.GetFunctionPointerForDelegate(callbackDelegate);        gchCallbackDelegate = GCHandle.Alloc(callbackDelegate); // !!!!        GC.Collect(); // create max space for unmanaged allocations        CppClicode.DoCppCli(callbackDelegatePointer);    }    public voID dispose()    {        CleanUp();    }    ~CSharpCode()    {        CleanUp();    }     CleanUp()    {        if(gchCallbackDelegate.IsAllocated)            gchCallbackDelegate.Free();    }}

顺便说一下,我希望你有更强大的命名系统. DoCSharp,TheCallBack,theCallBack等名称让我很难理解这个问题.

总结

以上是内存溢出为你收集整理的从非托管C到C#的回调可以正常工作,但仅限于调试器全部内容,希望文章能够帮你解决从非托管C到C#的回调可以正常工作,但仅限于调试器所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1239137.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-06
下一篇 2022-06-06

发表评论

登录后才能评论

评论列表(0条)

保存