【发布时间】:2010-09-14 02:43:02
【问题描述】:
在 C# 代码中,您能否捕获从某个非托管库的深处抛出的本机异常?如果是这样,您需要做任何不同的事情来捕捉它还是标准的 try...catch 得到它?
【问题讨论】:
在 C# 代码中,您能否捕获从某个非托管库的深处抛出的本机异常?如果是这样,您需要做任何不同的事情来捕捉它还是标准的 try...catch 得到它?
【问题讨论】:
对于 .Net Framework 4.8 IF,本机代码中的异常是 handled nicely,然后您可以使用标准的 try catch 来捕获它。
try
{
//call native code method
}
catch (Exception ex)
{
//do stuff
}
但是,如果本机代码位于您无法控制的第 3 方 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 开始,它们 turned off 原生异常能够被默认捕获,您需要通过使用属性装饰您的方法来显式地将其重新打开。
[HandleProcessCorruptedStateExceptions]
[SecurityCritical]
private static void Main()
{
try
{
//call native code method
}
catch (Exception ex)
{
//do stuff
}
}
app.config 文件还需要change:
<configuration>
<runtime>
<legacyCorruptedStateExceptionsPolicy enabled="true" />
</runtime>
</configuration>
【讨论】:
这取决于您所谈论的本机异常类型。如果您指的是 SEH 异常,则 CLR 将执行以下两项操作之一。
这两个都将被一个简单的“catch (Exception)”块捕获。
另一种可以跨越本地/托管边界的本地异常是 C++ 异常。我不确定它们是如何映射/处理的。我的猜测是,由于 Windows 在 SEH 之上实现了 C++ 异常,它们只是以相同的方式映射。
【讨论】:
Windows implements C++ exceptions on top of SEH -- 这似乎只适用于 VC?
差不多,但不完全。您将使用
捕获异常try
{
...
}
catch (Exception e)
{
...
}
但您仍然会遇到潜在的问题。根据MSDN,为了确保调用异常析构函数,您必须像这样捕获:
try
{
...
}
catch
{
...
}
这是确保调用异常析构函数的唯一方法(尽管我不确定为什么)。但这会让您在蛮力与可能的内存泄漏之间进行权衡。
顺便说一句,如果您使用 (Exception e) 方法,您应该知道您可能遇到的不同类型的异常。 RuntimeWrappedException 是任何托管非异常类型将被映射到的对象(对于可以抛出字符串的语言),其他类型将被映射,例如 OutOfMemoryException 和 AccessViolationException。 COM 互操作 HRESULTS 或除 E___FAIL 之外的异常将映射到 COMException,最后您将获得 E_FAIL 的 SEHException 或任何其他未映射的异常。
那你该怎么办?最好的选择是不要从您的非托管代码中抛出异常!哈。确实,如果您有选择,设置障碍和失败会使选择更糟,在异常处理期间有可能发生内存泄漏,或者不知道您的异常是什么类型。
【讨论】:
不带 () 的捕获将捕获不符合 CLS 的异常,包括本机异常。
try
{
}
catch
{
}
有关详细信息,请参阅以下 FxCop 规则 http://msdn.microsoft.com/en-gb/bb264489.aspx
【讨论】:
您可以使用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 Reflector 我看到了以下代码:
try {
...
} catch(Exception e) {
...
} catch {
...
}
嗯,C# 不允许抛出不是从 System.Exception 类派生的异常。据我所知,interop marshaller 捕获的任何异常都由继承 System.Exception 的异常类包装。
所以我的问题是是否有可能捕获不是 System.Exception 的异常。
【讨论】:
我相信标准的 try catch 应该可以解决问题。
我遇到了一个类似的问题,System.data 异常抛出了一个未捕获的 sqlClient 异常,在我的代码中添加一个 try..catch 在实例中起到了作用
【讨论】:
C# 和本机代码之间的互操作层会将异常转换为托管形式,使其能够被您的 C# 代码捕获。从 .NET 2.0 开始,catch (Exception) 应该捕获除不可恢复错误之外的任何内容。
【讨论】:
如果你使用
try
{
}
catch(Exception ex)
{
}
它将捕获所有异常,具体取决于您调用外部库的方式,您可能会得到一个封装错误的 com 相关异常,但它会捕获错误。
【讨论】: