2

我有一个使用一些非托管 GDI+ 功能的 C# 应用程序,我得到一个(非托管?)异常:

Log Name: Application
Source: .NET Runtime
Date: 7/1/2013 9:14:58 AM
Event ID: 1026
Task Category: None
Level: Error
Keywords: Classic
User: N/A
Computer: sth
Description:
Application: sth
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.AccessViolationException

堆栈跟踪是:

at System.Drawing.SafeNativeMethods+Gdip.IntGdipDisposeImage(System.Runtime.InteropServices.HandleRef)
at System.Drawing.SafeNativeMethods+Gdip.IntGdipDisposeImage(System.Runtime.InteropServices.HandleRef)
at System.Drawing.Image.Dispose(Boolean)
at System.Drawing.Image.Dispose()
at “..()
at Aspose.Cells.Charts.Chart.ToImage(System.IO.Stream, Aspose.Cells.Rendering.ImageOrPrintOptions)

我可以防止它完全崩溃吗?我曾尝试使用不带参数的 catch 语句,但 sth 告诉我这还不够。我用这些错误的代码制作了自己的 C++ DLL,但似乎没有任何效果,除非 DLL 抛出某事:

__declspec(dllexport) void __cdecl Function1(void) // This doesn't get caught.
{
    char *chrs = new char[1000];
    delete []chrs;
    delete []chrs;
}

__declspec(dllexport) void __cdecl Function3(void) // This doesn't get caught.
{
    try
    {
        MessageBox(NULL, L"AAA", L"Caption", MB_OK);
        char *chrs = NULL;
        chrs[0] = 'a';
    }
    catch(...)
    {
        MessageBox(NULL, L"BBB", L"Caption", MB_OK); // I get no messagebox here... (probably it doesn't get caught)
    }
}

__declspec(dllexport) void __cdecl Function2(void) // This gets caught as a .NET Exception.
{
    throw "Oh My!";
}

那么,我可以做些什么来防止我的EXE崩溃吗?

编辑

另外,这是否System.AccessViolationException意味着这是一个托管异常?如果,是的,我想知道为什么我没有抓住它...我的代码包含在 try{}catch(){} 中...

编辑2

我的猜测是 GDI+ 破坏了堆,CLR 稍后检测到它并抛出异常。但它不能被捕获,因为它不是从 try 块内抛出的。在这种情况下我可以做某事吗?

4

1 回答 1

6

一些例外是令人讨厌的,它们表明您的程序中发生了非常重大的事故。有些人是如此讨厌,以至于完全不可能从这种情况中恢复过来。该站点的名称属于该类别。ExecutionEngineException 也是如此。这些太糟糕了,以至于 catch 块仍然可以正确执行的几率为零。他们会立即终止您的程序。

System.AccessViolationException 只是超讨厌的头发,我们称它为super-nasty。这是处理器在发现无法再执行代码时生成的异常。对程序来说当然是致命的。它有几个可能的原因,最典型的原因是无效的机器代码指令或对不可读写的内存位置的访问。堆损坏是内存访问问题的一个非常典型的原因。

捕获异常在技术上是可能的,因为异常处理使处理器远离无法再执行的代码,并迫使它在其他地方继续。因此,CLR 不会被迫终止程序。然而,这是一个非常强烈的迹象,表明您的程序状态已严重损坏。这种腐败很少是孤立的。

当您处理位图时看到它炸弹严重指向这种损坏。很有可能它是一个损坏的非托管堆,GDI+ 使用它来存储位图的像素数据。这种损坏通常不是由 GDI+ 本身引起的,它可能是在您的程序中运行的其他非托管代码,当它摸索指针时将垃圾喷射到堆中。

您可以旋转命运之轮并抓住异常。但是,当您的程序访问该损坏堆上的数据时,您的程序很有可能会继续轰炸 AV。轰炸 OutOfMemoryException 也是非常可能的。毕竟,您打算释放非托管内存,但这并没有发生。这种情况稍后会发生,它可以造成更多的损害并且提供更少的方法来诊断导致它的原因。就像终结器一样,它也会爆炸。这是致命的,当终结器抛出未处理的异常时,CLR 会终止程序。

抓住它真的只是一个创可贴,你真的需要解决根本原因。然而这很难,代码中堆损坏触发 AV 的位置永远不会靠近导致损坏的代码。换句话说,怀疑 GDI+ 是原因并不能帮助您找到原因。事实上,这是不可能的原因,GDI+ 是一段经过大量测试的代码,每天运行数十亿次。

如果您不怀疑自己的进程中存在非托管代码,请通过在另一台机器上测试您的程序来隔离问题。例如,当您使用 OpenFileDialog 时,可能就像加载到您的进程中的错误 shell 扩展一样简单。如果您这样做,则通常需要为您在程序中使用的任何非托管代码编写单元测试。祝你好运。

于 2013-07-01T13:12:45.250 回答