System.Runtime.InteropServices.GetHRForException的陷阱

从字面上看,GetHRForException函数的作用很简单:得到Exception所对应的HRESULT的值。但是,GetHRForException还会做一件事情:设置当前线程的IErrorInfo使之指向该Exception(严格来说是获得Exception的CCW中的IErrorInfo接口指针)。如果对IErrorInfo不熟悉的朋友们可以把IErrorInfo看成COM版本的GetLastError或者errno。设置IErrorInfo会导致之后的代码如果使用GetErrorInfo查询IErrorInfo的值,会获得一个非0的结果,那么有些代码可能会认为程序出错而拒绝继续执行。更糟糕的是,如果之后的代码在做COM Interop或者PInvoke,CLR会检查IErrorInfo,如果IErrorInfo非0则认为该调用失败,并抛出异常或者返回错误值(视乎PreserveSigAttribute是否存在)。直接的结果是可能函数调用成功,但是因为IErrorInfo已经被设置而导致该调用最终失败。很有意思的是在.NET中有一段处理资源的代码正好有这个问题,结果间接导致了巴西葡萄牙语版本(是的,你没看错J)的.NET的RegAsm挂掉。其实我个人认为这个函数应该被命名为GetHRForExceptionAndSetIErrorInfo(Exception ex);虽然有些长,但是很清晰,总比简短而错误的名字要来的好。

在正常情况下,如果你需要获得Exception的HRESULT值,应该直接使用Exception.HResult属性。那么什么时候才应该使用GetHRForException呢?当你写了一个.NET的函数准备让非托管代码调用(最好是通过COM),并且返回一个HRESULT,这个时候你有必要把托管函数内部抛出的异常转换为HR,并且把Exception本身的信息设置到当前线程的IErrorInfo中去,这才是最符合COM规范的。

你可能感兴趣的:(.net,J#)