【问题标题】:Pin a function pointer固定函数指针
【发布时间】:2012-07-09 17:58:51
【问题描述】:

我需要将托管回调传递给非托管 TCP 接收器。由于它是一个需要在应用程序的生命周期中存在的线程,我需要防止它被垃圾收集。我到处读到不需要固定函数指针,GCHandle.Alloc 将完成防止垃圾收集的工作。

但这是给定的吗?我已经看到托管此代码的 AppPool 因访问冲突而崩溃。为什么我不应该怀疑这个错误是因为函数指针被垃圾回收了?

post 支持这一事实。

更新: 这似乎大大减少了崩溃。这种方法有问题吗?

typedef void (__cdecl *ProcMessageFunc)(void* param, void* paramBuf, ULONG bufSize);
FuncDelegate^ fp = gcnew MessageFuncDelegate(this, &Handler); 
pin_ptr<MessageFuncDelegate^> pinnedFunctionPointer = &fp; 
ret = Receiver ((ProcMessageFunc)pinnedFunctionPointer);

【问题讨论】:

  • 将委托对象存储在静态变量中就足够了。由于许多其他原因,本机代码可能会因访问冲突而轰炸。
  • 我确实做到了。我倾向于怀疑垃圾收集是原因的原因是访问冲突发生不规律。更重要的是,崩溃转储中的调用堆栈我可以看到本机 dll,然后是 clr.dll,然后是堆栈顶部的 kernel32.dll。这个顺序是一致的。

标签: c# interop c++-cli


【解决方案1】:

我完全按照您的建议去做 - GCHandle.Alloc 在委托上,但没有固定 - 在许多不同平台和 .NET 版本 2.0 - 4 上广泛使用时没有任何问题。类似:

 DelegateHandle = GCHandle.Alloc(xlDelegate);
 FunctionPointer = Marshal.GetFunctionPointerForDelegate(xlDelegate);

FunctionPointer 然后传递给本机代码,DelegateHandle 保留以供以后清理。

这似乎是最好的参考:http://msdn.microsoft.com/en-us/library/367eeye0(v=vs.80).aspx

您指出的帖子中没有任何内容与此参考相矛盾 - 您确实需要保护委托免受垃圾收集,只是 不需要固定

【讨论】:

  • 函数的地址固定是否不必要,因为与堆对象不同,它们存在于 const 位置?顺便说一句,如果一个函数还没有经过 JIT 处理,它有什么地址?
  • 我认为对 GetFunctionPointerForDelegate 的调用构建了一个具有固定入口点的小编组存根,但如果委托被移动,存根仍然知道如何调用它。因此,导出的函数指针不是委托对象地址的直接导出(与固定数组或结构成员不同)。请记住,导出的“本机”函数还必须实现本机 -> 托管转换。
  • 这正是我所做的。不幸的是,访问冲突仍然发生。让我这样说吧。我怎样才能完全确定这不是访问冲突的原因?
  • 您是否有任何理由怀疑委托(您在其上调用了 GCHandle.Alloc)被收集为您的错误的原因?您可以通过调用 GC.Collect() 来检查 GC 案例,从而使集合更具确定性。本机代码很可能正在浪费一些内存。调用代理时会崩溃吗?参数正常吗?委托是否回调可能崩溃的本机代码?我只能分享我的经验,它可以在没有固定的情况下正常工作,正如文档所建议的那样。
  • @Govert 委托肯定被多次调用。它随机失败。正如我对上述问题的评论中提到的,我怀疑 GC 的原因是崩溃转储中的调用堆栈表明了这一点。
猜你喜欢
  • 2018-12-23
  • 2011-11-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-03
  • 1970-01-01
  • 2011-01-24
  • 2011-12-04
相关资源
最近更新 更多