使用 P/Invoke 在托管和非托管回调链上引发异常
Posted
技术标签:
【中文标题】使用 P/Invoke 在托管和非托管回调链上引发异常【英文标题】:Raising exception on managed and unmanaged callback chain with P/Invoke 【发布时间】:2017-02-17 12:35:02 【问题描述】:我正在使用 P/Invoke 调用来包装原生 API。对于错误处理,我使用以下方法:
从托管代码中获取回调函数。 从非托管代码调用此回调函数以指示错误。 在回调中抛出异常。换句话说,流程是这样的:
Managed Method => (P/Invoke) Unmanaged Function => Managed Callback => Throw Exception.
当我测试这个方法时,我可以在第一个托管方法中成功捕获异常。但是,我不能 100% 确定这不会对堆栈或内存泄漏造成任何副作用。
使用这种方法安全吗?如果没有,是否有任何其他方法来指示错误(可能包括堆栈跟踪)而不会使使用 P/Invoke 的 API 混乱?
附:我可以访问本机代码。
【问题讨论】:
托管回调的作用是什么?如果存在的唯一原因是引发托管异常,您可能应该考虑编写一个 C++/CLI 互操作程序集,无需托管回调即可。 是的,我知道 C++/CLI,但是该项目是使用 P/Invoke 方法开始的,并且需要一些工作来转换现有功能。 您可以继续使用您拥有的 P/Invoke,并为新代码使用混合模式互操作程序集。无论如何,这并没有完全回答我提出的问题。 它会起作用,异常处理在 Windows 上是统一的,堆栈展开是操作系统的职责。然而,本机代码是否期望您抛出异常是一个悬而未决的问题。不太可能。如果是 C++ 代码,则必须使用 /Eha 进行编译,而不是默认设置。如果它是 C 代码,那么它不知道到底发生了什么 :) 内存泄漏和状态损坏是相当可能的结果,因为它们不会导致即时故障,所以很棘手。彻底测试这一点至关重要,请与此代码的作者交谈以感觉更好。期待一个“不!” 谢谢。您的评论和this link 澄清一切。 【参考方案1】:简短回答:是的,它是安全的。
在特定条件下:
非托管函数可以处理 SEH 异常(C++ 可以,但 C 不能) 非托管函数会将异常传递给其调用者,因此其调用者必须能够理解和处理托管异常(或向上传递)。例如,你不能启动一个新线程并在新线程中调用托管回调,回调抛出的异常肯定会终止你的应用程序。
【讨论】:
新线程是指本地线程、托管线程还是两者兼而有之? 看来第一个条件还应该包括只有Visual C++支持这个。 C++ 和 C 都可以处理 SEH 异常。两者都没有提供标准语言功能来做到这一点。除非你编译 C++ 代码并指示编译器去非标准,并允许catch(...)
也处理 SEH 异常(编译器开关 /EHa
)。以上是关于使用 P/Invoke 在托管和非托管回调链上引发异常的主要内容,如果未能解决你的问题,请参考以下文章
.Net调用非托管代码(P/Invoke与C++InterOP)