您可以在C#代码中捕获本地异常吗?


76

在C#代码中,您可以捕获某个非托管库中从深处抛出的本机异常吗?如果是这样,您需要采取其他措施来捕获它还是使用标准try ... catch来获取它?

Answers:


35

您可以使用Win32Exception并使用其NativeErrorCode属性适当地处理它。

// http://support.microsoft.com/kb/186550
const int ERROR_FILE_NOT_FOUND = 2;
const int ERROR_ACCESS_DENIED = 5;
const int ERROR_NO_APP_ASSOCIATED = 1155; 

void OpenFile(string filePath)
{
    Process process = new Process();

    try
    {
        // Calls native application registered for the file type
        // This may throw native exception
        process.StartInfo.FileName = filePath;
        process.StartInfo.Verb = "Open";
        process.StartInfo.CreateNoWindow = true;
        process.Start();
    }
    catch (Win32Exception e)
    {
        if (e.NativeErrorCode == ERROR_FILE_NOT_FOUND || 
            e.NativeErrorCode == ERROR_ACCESS_DENIED ||
            e.NativeErrorCode == ERROR_NO_APP_ASSOCIATED)
        {
            MessageBox.Show(this, e.Message, "Error", 
                    MessageBoxButtons.OK, 
                    MessageBoxIcon.Exclamation);
        }
    }
}

7
我相信这不是自动的,只有在使用指定它的P / Invoke签名时才会抛出。它是基于Win32错误代码而不是Win32异常引发的。
Curt Hagenlocher

15

不带()的捕获将捕获不符合CLS要求的异常,包括本机异常。

try
{

}
catch
{

}

有关更多信息,请参见以下FxCop规则: http://msdn.microsoft.com/zh-cn/bb264489.aspx


11
是的,这是真的。在比较日志文件和代码并以为我失去了时间之后,我特别烦恼了几个小时,才发现了这一点。但是,显而易见的问题是,您可以从空的catch块中确定本机异常的类型吗?您可以记录任何有用的信息来识别异常源吗?
泰森(Tyson)2012年

11

C#和本机代码之间的互操作层会将异常转换为托管形式,从而使其可以被您的C#代码捕获。从.NET 2.0开始,catch (Exception)应该捕获除不可恢复的错误以外的任何内容。


3
在.NET 1.x中,有可能引发不是源自Exception类的异常,但是默认情况下此功能在2.0中处于关闭状态
Curt Hagenlocher 08/09/29

4
在C ++中,您可以抛出任何对象,因此对于互操作性问题,能够捕获不是从Exception派生的对象很有用。常见的情况是C ++程序将仅抛出String(错误消息)。
MattDavey 2011年

5

使用.NET Reflector的某个地方,我看过以下代码:

try {
  ...
} catch(Exception e) {
  ...
} catch {
  ...
}

嗯,C#不允许抛出不是从System.Exception类派生的异常。据我所知,interop编组器的任何异常处理都是由继承System.Exception的异常类包装的。

所以我的问题是是否有可能捕获不是System.Exception的异常。


1
可以发出或创建抛出任意对象的IL。C#编译器不允许您这样做,但是其他编译器可以,或者就像我说的那样,您可以直接发出IL。没有类型的catch语句将捕获任意对象以及继承Exception的对象。
technophile

5

这取决于您正在谈论的本机异常类型。如果您指的是SEH异常,则CLR将执行以下两项操作之一。

  1. 如果是已知的SEH错误代码,它将把它映射到相应的.Net异常(即OutOfMemoryException)
  2. 在不可映射(E_FAIL)或未知代码的情况下,它将仅引发SEHException实例。

这两个都将通过一个简单的“ catch(Exception)”块来捕获。

可以跨越本机/托管边界的另一种本机异常是C ++异常。我不确定它们是如何映射/处理的。我的猜测是,由于Windows在SEH之上实现C ++异常,因此它们以相同的方式映射。


Windows implements C++ exceptions on top of SEH-这似乎仅对VC是正确的吗?

3

差不多,但是不完全是。您将通过捕获异常

try 
{
  ...
}
catch (Exception e)
{
  ...
}

但是您仍然会遇到潜在的问题。根据MSDN的说明,为了确保调用异常析构函数,您必须像这样捕获:

try
{
  ...
}
catch
{
  ...
}

这是确保调用异常析构函数的唯一方法(尽管我不确定为什么)。但是,这需要您在蛮力与可能的内存泄漏之间进行权衡。

顺便说一句,如果使用(Exception e)方法,您应该知道可能遇到的不同类型的异常。RuntimeWrappedException是任何托管的非异常类型都将映射到的类型(对于可以抛出字符串的语言),其他类型将被映射,例如OutOfMemoryException和AccessViolationException。COM Interop HRESULTS或E___FAIL以外的异常将映射到COMException,最后在最后,您具有E_FAIL的SEHException或任何其他未映射的异常。

那你该怎么办?最好的选择是不要在非托管代码中抛出异常!哈哈 确实,尽管您可以选择,但会设置障碍并失败,这会使得选择更加糟糕,在异常处理过程中可能会发生内存泄漏,或者不知道您的异常类型。


-1

我相信,标准的尝试捕获应该可以解决问题。

我遇到了类似的问题,System.data异常抛出了一个未捕获的sqlClient异常,在我的代码中添加了一个try..catch在实例中起到了作用


-2

如果您使用

try
{

}
catch(Exception ex)
{

}

它会捕获所有异常,具体取决于您调用外部库的方式,您可能会获得与com相关的异常,该异常封装了错误,但可以捕获该错误。


但是,无论如何,您都无法捕获StackOverflow或OutOfMemoryException,对吗?
核心

这些正在结束的错误,使应用程序停止,所以是的,您无法使用它们。
米切尔卖家

7
这实际上不是很正确,它将捕获所有符合CLS的异常。C ++ / CLI和MC ++都是能够引发非CLS兼容异常的语言。
Peter Oehlert

1
我同意Peter的观点,我正在使用非托管DLL,并且我无法捕获某些异常。
Tilendor 2011年

20
截至我撰写本评论时,这个问题有17个赞成票和7个最喜欢的票,因此,这显然是一个正在发生的问题,我认为人们会在诉诸SO之前尝试使用简单的捕获程序。我亲眼看到异常来自非托管依赖项时,异常会通过您提出的构造爆炸。我认为这个答案不正确。
JohnFx 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.