gpt4 book ai didi

c# - 将托管回调传递给 DllImport (ed) 函数

转载 作者:行者123 更新时间:2023-11-30 21:14:05 27 4
gpt4 key购买 nike

我有一段代码抛出了关于非托管函数调用的垃圾收集委托(delegate)的异常。这是代码:

// Setup Callback functions
errorCode = gsapi_set_stdio(ghostScriptPtr, new StdioMessageEventHandler(RaiseStdInCallbackMessageEvent), new StdioMessageEventHandler(RaiseStdOutCallbackMessageEvent), new StdioMessageEventHandler(RaiseStdErrCallbackMessageEvent));

if (errorCode >= 0)
{
try
{
//GC.SuppressFinalize(this);
// Init the GhostScript interpreter
errorCode = gsapi_init_with_args(ghostScriptPtr, commandParameters.Length, commandParameters);

// Stop the Ghostscript interpreter
gsapi_exit(ghostScriptPtr);
}
finally
{
// Release the Ghostscript instance handle
gsapi_delete_instance(ghostScriptPtr);
}
}

_Raise... 传递给函数的变量在被函数调用之前被释放。

我不知道我怎么了,但我把回调改成了变量:

var _RaiseStdInCallbackMessageEventHandler = new StdioMessageEventHandler(RaiseStdInCallbackMessageEvent);
var _RaiseStdOutCallbackMessageEventHandler = new StdioMessageEventHandler(RaiseStdOutCallbackMessageEvent);
var _RaiseStdErrCallbackMessageEventHandler = new StdioMessageEventHandler(RaiseStdErrCallbackMessageEvent);
// Setup Callback functions
errorCode = gsapi_set_stdio(ghostScriptPtr, _RaiseStdInCallbackMessageEventHandler, _RaiseStdOutCallbackMessageEventHandler, _RaiseStdErrCallbackMessageEventHandler);

最后阻止到:

    finally
{
// Release the Ghostscript instance handle
gsapi_delete_instance(ghostScriptPtr);
_RaiseStdInCallbackMessageEventHandler = _RaiseStdOutCallbackMessageEventHandler = _RaiseStdErrCallbackMessageEventHandler = null;
}

它解决了这个问题。为什么?我不知道。也许这只是巧合。我有一种直觉,在 finally block 中使用变量会导致不提前处理变量的对象(因为它在 finally block 中使用)。真的吗?无论如何,为 dllimported 函数提供托管回调是正确的方法吗?

谢谢,帕维尔

最佳答案

是的,您的方向是正确的。不完全的。垃圾收集器无法知道 native 代码具有对委托(delegate)的引用。它隐藏在由 Marshal.GetFunctionPointerForDelegate() 生成的 thunk 中,GC 无法访问。因此,您有必要对委托(delegate)有另一个引用,一个 GC 可以看到的引用。

您通过使用局部变量部分地完成了此操作,GC 还会遍历堆栈和 CPU 寄存器,并且可以看到委托(delegate)引用。但是,当您在没有调试器的情况下以 Release模式运行代码时,这会出错。附加调试器后,抖动会报告局部变量的生命周期,直到方法结束。这使得调试更容易。如果没有调试器,它就不再这样做了。即使在 finally block 中将变量设置为 null,抖动优化器也会删除该分配。发布版本中启用了优化器。

最好的办法是将委托(delegate)引用存储在类的一个字段中。并确保您的类对象存在足够长的时间以超过回调。下一个最好的办法是对局部变量使用 GC.KeepAlive()。

关于c# - 将托管回调传递给 DllImport (ed) 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6468727/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com