C++/CLI:捕获所有(.NET/Win32/CRT)异常

8

我知道这是不被赞同的,但现在我已经没有别的选择了。我正在开发一款C++/CLI应用程序,但有一个bug我无法追踪 - 主要是因为它绕过了我的当前崩溃处理程序:

AppDomain::CurrentDomain->UnhandledException += gcnew UnhandledExceptionEventHandler(&LogAndExit);
Application::ThreadException += gcnew ThreadExceptionEventHandler(&LogAndExit);
Application::SetUnhandledExceptionMode(UnhandledExceptionMode::CatchException);
try 
{ 
    Application::Run(gcnew frmMain()); 
} 
catch (Exception^ ex) 
{ 
    LogAndExit(ex); 
} 
catch (...) 
{ 
    LogAndExit(); 
}

我猜这是标准的.NET崩溃处理。MSDN报告称,某些CRT异常将超过托管堆栈,并静默地终止应用程序。

我一直在阅读_set_invalid_parameter_handler,但即使我遇到LNK2001错误,它似乎也不能与/clr:pure一起使用。我是对的,还是我错过了一个lib文件?


1
调试+异常,勾选抛出标志。 - Hans Passant
无法。目标机器上没有安装VS2010。 - hb.
难道不可能让 JIT 转储程序内置于 Windows 中,为您抓取转储文件吗? - Zipper
我实际上会在捕获异常时写入完整的内存转储,但并非总是如此。 :/ - hb.
5个回答

4

您能在/clr模式下运行吗?如果可以,请尝试以下操作:

#include <exception>

然后:
try
{
    try
    {
        Application::Run(gcnew frmMain()); 
    }
    catch(const exception& ex)
    {
        throw gcnew System::Exception(gcnew System::String(ex.what()));
    }
} 
catch (Exception^ ex) 
{ 
    LogAndExit(ex); 
} 
catch (...) 
{ 
    LogAndExit(); 
}

另外需要注意的一点是:如果您的应用程序是多线程的,那么您只能从运行frmMain()的线程中捕获异常。因此,在这种情况下,无法对整个应用程序进行全局异常捕获!


我曾经在/clr下运行,但我的其中一台机器(ATM机)无法工作。/clr:pure可以在所有机器上运行。 - hb.
@hb,好的,另一个问题仍然有效:你是否在运行多个线程? - Kiril
C++/CLI进程托管了一个net.pipe WCF服务,所以我猜是可以的。不过所有的方法都被try/catch包裹了(CLR)。 - hb.
@hb,他们都记录了异常吗?如果是的话,那么你可能只需要追踪失败原因...当然,你已经知道了这一点... - Kiril
哈哈...是的,确切地说。什么也不显示,但有时它就会死掉。 - hb.

1

我建议你尝试使用 __try / __except

__try 
{ 
  Application::Run(gcnew frmMain()); 
} 
__except(EXCEPTION_EXECUTE_HANDLER) 
{ 
  LogAndExit(new Exception("Some Unmanage exception")); 
}

MSDN文档在这里:

http://msdn.microsoft.com/en-us/library/s58ftw19(v=vs.80).aspx

如果你想变得更加巧妙,你可以尝试像这样双重包装它:

__try 
{ 
  try {
    Application::Run(gcnew frmMain()); 
  } 
  catch(SEHException^ e)
  {
    LogAndExit(new Exception("Some Unmanage exception")); 
  }
  catch(...) //Leave this out if you're /clr:pure
  {
    LogAndExit(new Exception("Some Unmanage exception")); 
  }
} 
__except(EXCEPTION_EXECUTE_HANDLER) 
{ 
  LogAndExit(new Exception("Some Unmanage exception")); 
}

结构化异常通常被包装成SEHException^。

您还需要考虑到,您可能会捕获异常,但是LogAndExit方法中的某些内容会导致抛出次级异常,实际上这才是导致程序结束的原因。尝试在LogAndExit函数中加桩,并查看是否可以使崩溃静默地发生,而不是带有标准的终止消息和/或将您的LogAndExit代码包装在另一个try/catch中,隐藏任何异常。

作为一位花费了大量时间学习C++/CLI的人,我可以理解您的困境。希望这个解决方案能够帮助您。

有关C++/CLI异常处理的其他MSDN文档:

如何:定义和安装全局异常处理程序 http://msdn.microsoft.com/en-us/library/171ezxzc.aspx

/clr下的异常处理 http://msdn.microsoft.com/en-us/library/633chdda.aspx


我在__except处理程序中使用完整的转储来尝试弄清楚到底发生了什么。你还有其他建议吗?如果我还没有秃头,我可能已经把它拔光了。 - hb.

1

首先,这不适用于表单。

在使用 Windows Forms 的应用程序中,主应用程序线程中未处理的异常会导致引发 Application.ThreadException 事件。如果处理了此事件,则默认行为是未处理的异常不会终止应用程序,尽管应用程序处于未知状态。在这种情况下,不会引发 UnhandledException 事件。可以通过使用应用程序配置文件或使用 Application.SetUnhandledExceptionMode 方法将模式更改为 UnhandledExceptionMode.ThrowException,在挂钩 ThreadException 事件处理程序之前更改此行为。这仅适用于主应用程序线程。对于在其他线程中抛出的未处理异常,将引发 UnhandledException 事件。

其次,可能会出现未经处理的异常(不属于类型System::Exception)。

try { Application::Run(gcnew frmMain()); } 
catch (Exception^ ex) { LogAndExit(ex); }
catch (...) { LogAndExit(new Exception("Some Unmanage exception"));

msdn- 如何在 Visual C++ 中捕获异常


嗯,是的。我忘记复制那个了。现在是实际的代码。 - hb.
你有没有读过关于如何处理CurrentDomain->UnhandledException对表单影响的MSDN文章? - Yochai Timmer
是的。问题是这些处理程序都没有被使用,只会默认显示Windows的“应用程序崩溃”对话框。 - hb.

0

有几种异常处理类型。你发布的将仅处理托管异常。

C++代码也可能抛出未经管理的异常(尤其是来自标准库)。此外,非托管代码可以抛出Win32异常。

从这里开始 阅读结构化异常处理(Windows异常)。C++异常和托管异常都是建立在SEH之上的,所以如果你在进程的每个线程的顶点处处理SEH,就可以覆盖到所有异常。


托管异常使用SEH,而托管处理程序处理SEH异常。如果C++代码使用/EHa,则它抛出的任何异常也将在底层使用SEH。托管代码无法捕获使用同步模型(/EHsc)编译的本机C++异常。 - Ben Voigt
那么,如果我遇到了C++异常,就只能依靠堆栈跟踪来查看它的来源了吗? - hb.
我相信在clr:pure模式下无法获得C++(或Win32)异常。几乎可以确定...此时,您最好的选择是获取一个minidump(例如,通过[ProcDump](http://technet.microsoft.com/en-us/sysinternals/dd996900.aspx)),然后将其加载到WinDbg或Visual Studio中。 - Stephen Cleary
除了处理程序中的异常之外,还有什么会在使用所有这些处理程序时显示那个可怕的Dr. Watson屏幕呢? - hb.
@hb:无论哪种版本,/clr 始终使用 /EHa,SEH 异常会被映射到 .NET 异常。只有当您将 C++/CLI 代码与标准 C++ 代码链接时,某些异常才可能会被忽略。而 Win32 SEH 异常将由 .NET 处理,无论 C++ 设置如何。 - Ben Voigt
显示剩余7条评论

0

并不是每次崩溃都是异常,至少不是立即发生的。很有可能一个野指针会破坏.NET内部数据结构,导致在抛出异常时,托管处理程序无法正确运行。

试试使用本地处理程序吧。Visual C++提供了__try/__except,但你需要确保处理程序在程序的每个线程的调用堆栈上,并且仍然有可能有未处理的异常(例如,CreateThread使用指向非法指令的指针)。为了处理所有边界情况,你应该使用SetUnhandledExceptionFilter

请注意,使用/clr:pure,所有代码都依赖于CLR,因此可能由于运行时损坏而导致失败。即使本地处理程序可能依赖于已损坏的状态,尽管它们比基于MSIL的处理程序更不易受损。为了健壮的错误处理,你真的需要在进程外运行代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接