【问题标题】:How to force an application crash when AccessViolationException is detected检测到 AccessViolationException 时如何强制应用程序崩溃
【发布时间】:2015-02-19 12:56:22
【问题描述】:

我们使用自动崩溃报告工具(即http://crashrpt.sourceforge.net)来生成崩溃报告。

因此,例如,如果一段非托管代码因访问 NULL 指针而失败,则应用程序崩溃,崩溃报告工具将激活,我们会获得可用的堆栈跟踪来诊断和分组问题。

问题在于 .NET 在某些情况下似乎会干扰崩溃处理。一个示例如下:

this.Dispatcher.BeginInvoke((ThreadStart)delegate
{
  // Send message to unmanaged control for performing a specific task.
  User32.SendMessage(...);
}, DispatcherPriority.Input);

如果非托管组件随后因访问冲突而失败,内部 .NET 方法会捕获实际上应该是崩溃的 AccessViolationException 并首先将其重新包装在 TargetInvocationException 中,然后崩溃(如果不使用方法,它将不会这样做调用)。

这非常不方便,因为本机堆栈信息完全丢失了。剩下的是以下堆栈,与非托管部分发生故障的确切位置无关:

kernelbase!RaiseException+0x6c
clr!RaiseTheExceptionInternalOnly+0x276
clr!RaiseTheException+0x86
clr!RaiseTheExceptionInternalOnly+0x30a
clr!RealCOMPlusThrow+0x2f
clr!ThrowInvokeMethodException+0xac
clr!RuntimeMethodHandle::InvokeMethod+0xa64
mscorlib_ni+0x2d37b1
mscorlib_ni+0x2cf92a
windowsbase_ni+0xd77b1
windowsbase_ni+0xd768a
windowsbase_ni+0xc2d5c
windowsbase_ni+0xc2c98
mscorlib_ni+0x302346
mscorlib_ni+0x302301
windowsbase_ni+0xc2b9b
windowsbase_ni+0xd640b
windowsbase_ni+0xd65ca
windowsbase_ni+0xd798b
windowsbase_ni+0xd78db
windowsbase_ni+0xd7756
windowsbase_ni+0xd768a
windowsbase_ni+0xd5cae
windowsbase_ni+0xd71e1
user32!InternalCallWinProc+0x23
user32!UserCallWinProcCheckWow+0x100
user32!DispatchMessageWorker+0x3ef
user32!DispatchMessageW+0x10
windowsbase_ni+0xddca8
windowsbase_ni+0xd5636
windowsbase_ni+0xd5325
windowsbase_ni+0xb27d3
presentationframework_ni+0x2721b7
presentationframework_ni+0x271e0f
presentationframework_ni+0x271baa
clr!CallDescrWorkerInternal+0x34
clr!CallDescrWorkerWithHandler+0x6b
clr!MethodDescCallSite::CallTargetWorker+0x152
clr!RunMain+0x1aa
clr!Assembly::ExecuteMainMethod+0x124
clr!SystemDomain::ExecuteMainMethod+0x614
clr!ExecuteEXE+0x4c
clr!_CorExeMainInternal+0xdc
clr!_CorExeMain+0x4d
mscoreei!_CorExeMain+0x10a
mscoree!ShellShim__CorExeMain+0x7d
mscoree!_CorExeMain_Exported+0x8
kernel32!BaseThreadInitThunk+0xe
ntdll!__RtlUserThreadStart+0x72
ntdll!_RtlUserThreadStart+0x1b

当非托管组件发生故障时,我们如何防止这种情况并强制应用程序立即崩溃?

【问题讨论】:

  • CrashRpt 是一个 C++ 崩溃报告库。它是如何连接到 .Net 代码中以捕获托管异常的?您能否举例说明何时从 .Net 调用非托管代码并且它不会干扰 AccessViolationException
  • @ChrisO'Neill “它是如何连接到 .Net 代码中以捕获托管异常的?”它实际上并没有钩住任何托管异常。它仅捕获非托管异常。每当不使用 BeginInvoke 时,它​​都不会干扰。因此,只需从上面的 sn-p 中删除第一行和最后一行,作为一个简单的示例。
  • 嗯,不,它也捕获托管异常。这就是您获得堆栈跟踪的方式。不好。这个库只是你想要完成的一个包 o' 字节。您需要一个调试器来可靠地检测具有支持处理程序的非托管异常。存在,您需要使用DebugDiag utility。也许ClrMD library 有用,我还没玩够。
  • 所以,主进程(CrashRpt 实际挂钩的那个)是非托管代码,大概是 C++。这个过程调用一个.Net库,然后使用BeginInvoke()调用一些非托管代码?
  • @ChrisO'Neill 主要进程是 .NET,它调用 C++ 库来安装崩溃处理程序。进程(或其他加载的 .NET 程序集)随后可以调用非托管代码,或者在这种情况下,通过向非托管控件发送消息来调用非托管函数。

标签: .net exception-handling crash crash-reports


【解决方案1】:

试试这个:

this.Dispatcher.BeginInvoke((Action) delegate 
{
    User32.SendMessage(...);
}, DispatcherPriority.Input);

应用程序应该按照您希望的方式崩溃。


我见过的大多数使用匿名委托调用Dispatcher.BeginInvoke() 的示例都使用Action


为什么会这样?

CLR 代码似乎在下面:

at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
at System.Windows.Threading.DispatcherOperation.InvokeImpl()

直接调用Action,其他大多数委托都是使用反射调用的。

反射机制会将异常包装在TargetInvocationException中。

this answer到另一个问题解释。


其他不会包装异常的特殊情况委托是: DispatcherOperationCallbackSendOrPostCallback,但要使用它们必须使用单个参数调用。

this.Dispatcher.BeginInvoke(DispatcherPriority.Input,
    (SendOrPostCallback)(delegate(object o)
    {
        throw new AccessViolationException(o.ToString());
    }), "test");

【讨论】:

  • 我没想到这会起作用。无论如何我做了改变,它确实似乎有所作为(不过,在我接受答案之前,我将继续验证这一点,需要确保它有效)。您介意解释一下为什么delegateAction 之间会有任何区别吗?我不明白为什么它的行为会有所不同。
  • 我已经编辑了答案来解释我的发现。请注意,建议的代码 sn-p 略有不同。
  • 我不认为这种解释是正确的,ThreadStart 变体实际上工作得很好(只要非托管部分不崩溃)。所以在内部处理ActionThreadStart肯定有一些不同,我觉得这很难相信......
  • ThreadStart 变体实际上工作得很好(只要非托管部分不崩溃)”。所以......它不能很好地工作?不要误以为ThreadStart 一定是调用BeginInvoke 的正确方式,因为它在大多数情况下都有效。我添加了一些调用BeginInvoke 的参考资料。随意在 CLR 代码中四处寻找,看看是什么导致了这种行为。我有而且我可以花很多时间找到在调用 System.RuntimeMethodHandle.InvokeMethod() 时实际运行的代码,以便为您提供“防水”的解释。
  • 添加了一个示例,表明 ThreadStart 不是唯一一个只有一半工作的代表。
【解决方案2】:

得到异常后使用下面的代码关闭应用程序。

Environment.Exit(1);    

Exit 需要一个名为 exitcode 的参数。如果 exitcode=0 表示没有错误。提供一个非零退出代码以反映错误。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-29
    • 1970-01-01
    • 2014-02-26
    相关资源
    最近更新 更多