我有一段代码,在非托管函数调用的垃圾收集委托方面抛出了异常。这是代码:
// 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;
}
它解决了这个问题。为什么?我不知道。也许只是个巧合。我有一种直觉,认为在最终块中使用变量会导致变量的对象没有提前处理(因为它在最终块中使用)。这是真的吗?无论如何,提供带有托管回调的dllimported函数是一种正确的方法吗?
谢谢你,帕维尔
发布于 2011-06-24 16:47:27
是的,你这样做是正确的。不完全是。垃圾收集器无法知道本机代码对委托的引用。它被掩埋在由Marshal.GetFunctionPointerForDelegate()生成的一次雷击中,这是GC无法触及的。因此,您必须有另一个对委托的引用,一个GC可以看到的引用。
通过使用局部变量,GC还可以遍历堆栈和CPU寄存器,并可以看到委托引用。但是,当您在没有调试器的情况下以发行模式运行代码时,这会出错。通过附加调试器,抖动报告局部变量的生存期,直到方法结束为止。这样调试就容易多了。如果没有调试器,它将不再这样做。即使在最后块中将变量设置为null时,抖动优化器也会删除该赋值。在发布版本中启用了优化器。
最好的方法是将委托引用存储在类的一个字段中。并确保类对象的生存期足以超过回调。其次,最好在局部变量上使用GC.KeepAlive()。
发布于 2011-06-24 15:54:58
您的文章并不包含理解所发生的事情所需的所有信息:非托管函数原型、PInvoke声明。无论如何,查看问题标题,我发现您需要使用Marshal.GetFunctionPointerForDelegate方法--这是将托管回调传递给非托管代码的方法。
https://stackoverflow.com/questions/6468727
复制相似问题