.net 如何在检测到AccessViolationException时强制应用程序崩溃

.net 如何在检测到AccessViolationException时强制应用程序崩溃,.net,exception-handling,crash,crash-reports,.net,Exception Handling,Crash,Crash Reports,我们使用自动碰撞报告工具(即)生成碰撞报告 因此,如果一段非托管代码由于访问NULL指针而失败,例如,应用程序崩溃,崩溃报告工具激活,我们可以获得可用的堆栈跟踪,用于诊断和分组问题 问题是.NET在某些情况下似乎会干扰崩溃处理。一个样本如下: this.Dispatcher.BeginInvoke((ThreadStart)delegate { // Send message to unmanaged control for performing a specific task. Use

我们使用自动碰撞报告工具(即)生成碰撞报告

因此,如果一段非托管代码由于访问
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
当非托管组件出现故障时,我们如何防止这种情况并强制应用程序立即崩溃?

尝试以下方法:

this.Dispatcher.BeginInvoke((Action) delegate 
{
    User32.SendMessage(...);
}, DispatcherPriority.Input);
应用程序应该按您希望的方式崩溃


我看到的大多数示例都是使用匿名委托调用
Dispatcher.BeginInvoke()
时使用
操作


为什么会发生这种情况

似乎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()
直接调用
操作
,大多数其他委托都是使用反射调用的

反射机制将异常包装在
TargetInvocationException

请看另一个问题来解释


不会包装例外情况的其他特殊情况代表包括:
DispatcherOperationCallback
SendOrPostCallback
,但要工作,必须使用单个参数调用它们

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

获取异常后,使用以下代码关闭应用程序

Environment.Exit(1);    

Exit需要一个名为exitcode的参数。如果exitcode=0,则表示没有错误。提供一个非零退出代码以反映错误。< /P> CRASRPT是一个C++崩溃报告库。它是如何连接到.Net代码中以捕获托管异常的?你能举一个例子,说明当你从.Net调用非托管代码时,它不会干扰
AccessViolationException
?@ChrisO'Neill“它是如何连接到.Net代码以捕获托管异常的?”它实际上并没有连接到任何托管异常。它仅捕获非托管异常。只要不使用BeginInvoke,它就不会干扰。因此,只需删除上面代码段中的第一行和最后一行,就可以得到一个简单的示例。这就是你得到堆栈跟踪的原因。不好的。这个库对于您想要完成的任务来说只是一个包。您需要一个调试器来可靠地检测具有返回停止处理程序的非托管异常。这是存在的,你需要玩的。也许这是有用的,我还没有玩够。所以,主要过程(CrashRpt实际上是钩住的)是非托管代码,大概是C++。这个过程调用.NET库,然后使用<代码>开始调用()/<代码>?@ CaseOn.NeLet调用一些非托管代码。主进程是.NET,它调用C++库来安装崩溃处理程序。该进程(或其他加载的.NET程序集)随后可能会调用非托管代码,或者在本例中,通过向非托管控件发送消息来调用非托管函数。我没想到这会起作用。不管怎么说,我还是做了更改,而且它确实似乎起到了作用(在我接受答案之前,我将继续验证这一点,但我需要确保这是有效的)。请您解释一下为什么
代表
操作
之间会有任何区别,好吗?我不明白为什么会有不同的表现。我编辑了答案来解释我的发现。请注意,建议的代码片段略有不同。我认为这种解释不正确,
ThreadStart
变体实际上工作得很好(只要非托管部分不崩溃)。因此,在处理
Action
ThreadStart
的内部过程中肯定存在一些差异,我发现这很难相信……”“ThreadStart的
variant实际上工作得很好(只要非托管部分不崩溃)”。所以它不是很好用吗?不要误以为
ThreadStart
必须是调用
BeginInvoke
的正确方法,因为它大部分时间都能工作。我为调用
BeginInvoke
添加了一些参考资料。请随意查看CLR代码,看看是什么导致了这种行为。我已经并且可以花很多时间查找调用
System.RuntimeMethodHandle.InvokeMethod()
时实际运行的代码,以获得一个“防水”的解释。添加了一个示例,说明
ThreadStart
并不是唯一一个只起一半作用的委托。