你能在 C # 代码中捕捉到一个本机异常吗?

在 C # 代码中,是否可以捕获从非托管库深处抛出的本机异常?如果是这样,你需要做什么不同的捕捉它或做一个标准的尝试... 捕捉它?

53572 次浏览

如果使用

try
{


}
catch(Exception ex)
{


}

它将捕获所有异常,这取决于您如何调用外部库,您可能会得到一个 com 相关的异常,该异常封装了错误,但它将捕获错误。

C # 和本机代码之间的互操作层将异常转换为托管表单,允许 C # 代码捕获它。从现在开始。NET 2.0,catch (Exception)应该捕获任何不可恢复的错误。

一个标准的尝试接球应该可以做到这一点,我相信。

我遇到了类似的问题,System.data 异常抛出了一个 sqlClient 异常,但是没有捕获,因此添加了一个 try。.捕捉到我的代码在实例中起了作用

您可以使用 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);
}
}
}

在使用.NET 反射器的某个地方,我看到了以下代码:

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

嗯,C # 不允许引发非系统派生的异常。异常类。据我所知,互操作封送拆收器捕获的任何异常都由继承 System 的异常类包装。例外。

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

Catch without ()将捕获包括本机异常在内的非 CLS 兼容异常。

try
{


}
catch
{


}

有关更多信息,请参见下面的 FxCop 规则 Http://msdn.microsoft.com/en-gb/bb264489.aspx

这取决于您所说的本机异常的类型。如果您指的是 SEH 异常,那么 CLR 将执行以下两种操作之一。

  1. 在已知 SEH 错误代码的情况下,它会将其映射到适当的.Net 异常(即 OutOfMemory yException)
  2. 在无法映射(E _ FAIL)或未知代码的情况下,它只会抛出一个 例外实例。

这两者都将被一个简单的“ catch (Exception)”块捕获。

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

差不多,但不完全是

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

但你还是会有潜在的问题。根据 MSDN,为了确保调用异常析构函数,您必须捕获以下内容:

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

这是确保调用异常析构函数的唯一方法(尽管我不确定为什么)。但是这样一来,你就只能在暴力和可能的内存泄漏之间进行权衡了。

顺便说一下,如果使用(Exception e)方法,您应该知道可能遇到的不同类型的异常。RuntimeWrappedException 是任何托管非异常类型将被映射到的类型(对于可以抛出字符串的语言) ,其他类型也将被映射,例如 OutOfMemorial yException 和 Access赋值 Exception。除了 E _ _ FAIL 之外的 COM 互操作 HResults 或异常将映射到 COMException,最后您将得到 E _ FAIL 或任何其他未映射异常的 SEHException。

那你该怎么办?最好的选择是不要从非托管代码中抛出异常!哈。实际上,如果您有一个选择,那么设置障碍并失败,使得选择哪个更糟糕,在异常处理期间有可能出现内存泄漏,或者不知道异常的类型。

和。Net Framework 4.8 如果本机代码中的异常是 处理得很好,然后您可以使用标准 try catch 捕获它。

try
{
//call native code method
}
catch (Exception ex)
{
//do stuff
}

然而 ,如果本机代码位于您无法控制的第三方 dll 中,您可能会发现开发人员无意中抛出了未处理的异常。我发现除了一个全局错误处理程序之外,没有任何东西可以捕捉到这些错误。

private static void Main()
{
AppDomain.CurrentDomain.UnhandledException += OnUnhandledException;
try
{
//call native code method
}
catch (Exception ex)
{
//unhandled exception from native code WILL NOT BE CAUGHT HERE
}
}


private static void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
var exception = e.ExceptionObject as Exception;
//do stuff
}

这是有原因的。未处理的本机异常可能指示无法恢复的损坏状态(例如堆栈溢出或访问冲突)。但是,有些情况下,您仍然希望在终止进程之前执行一些操作,比如记录刚刚试图使您的 Windows 服务崩溃的错误! ! !

一点历史

不再需要下列任何一项。
从.Net 2.0-3.5,你可以使用一个空的 catch:

try
{
//call native code method
}
catch (Exception ex)
{
//do stuff
}
catch
{
//do same stuff but without any exception detail
}

从。Net 4它们启用了默认情况下的 关机了本机异常,并且您需要通过使用属性装饰您的方法来显式地重新打开它。

[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
private static void Main()
{
try
{
//call native code method
}
catch (Exception ex)
{
//do stuff
}
}

还需要 app.config 文件的 改变:

<configuration>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>