自动生成.NET崩溃转储文件

45

我知道如何使用ADPlus或DebugDiag生成崩溃转储文件,但我想知道是否有一种方法可以在客户的计算机上进行此操作而无需安装这些工具... 具体来说,我希望能够通过配置我的应用程序(例如使用注册表值)在发生严重故障时生成崩溃转储。更具体地说,我需要能够从C#应用程序中实现此操作,但如果必要的话,我不介意使用P/Invoke。谢谢!

7个回答

17

需要注意的是,从“失败”的进程(甚至线程)内部创建minidump并不容易或可能不准确(另请参阅MiniDumpWriteDump函数的备注)。

此外,如果您的进程处于如此糟糕的状态,以至于您可能需要编写崩溃转储,那么整个情况通常已经非常混乱,即使尝试创建崩溃转储也可能会导致另一个崩溃(除了挂起之类的情况 - 但这些可能更难从当前进程内“捕获”)。

如果您无法在客户系统上安装单独的应用程序,则可以尝试启动外部进程(在关键情况下也可能失败!),并让该进程从当前进程创建一个crashdump(请参见John Robbins的Superassert.NET)。 您甚至可以将外部二进制文件放入应用程序资源中,在启动时从中提取(以尽量减少关键情况下的故障),并将其保存到磁盘上(如果您敢这样做)。


虽然在应用程序崩溃时小心行事是明智的,但也要考虑你的应用程序正在完成什么任务。如果你处于生产环境中,请非常小心——然而,如果你正在编写批处理应用程序,最好创建minidump以供以后调试,而不是进行耗时的重现。总体建议:在做出设计决策之前始终考虑你的情况,并谨慎对待过于宽泛的建议。 - jhclark
@jhclark 我完全不反对编写一个迷你/崩溃转储;事实上,一个_适当的_ 转储是我们可以要求的最好的死后分析工具。但关键不是试图从受害进程内部编写它。因为这通常是不可靠的。拥有某种外部看门狗进程是正确的方法(即使像Sysinternals procdump这样的东西在公司内部也可能会用到)。在我看来,无论应用程序的类型或者试图完成什么任务,这都是正确的。 - Christian.K

17
您可以使用以下注册表脚本配置Windows错误报告(WER)以在特定目录中创建崩溃转储:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting\LocalDumps]
"DumpFolder"="C:\\Dumps"
"DumpCount"=dword:00000064
"DumpType"=dword:00000002
"CustomDumpFlags"=dword:00000000
转储将放置在C:\Dumps文件夹中,并反映崩溃进程的名称。 DumpType = 2会给出完整的内存转储,DumpType = 1会给出迷你转储。在64位机器上,您不需要将其放在Wow32节点下。 WER仅使用上面指定的非WOW注册表键。
根据崩溃的类型,此方法可能无法正常工作。我尚未弄清楚为什么或哪些崩溃类型无法捕获。有人知道吗?

3
如此提及(http://msdn.microsoft.com/en-us/library/bb787181(VS.85).aspx),我认为这对托管代码不起作用。 - uli78
2
不支持自行进行定制崩溃报告的应用程序,包括.NET应用程序。 - Peter Ritchie
11
我已经尝试过这个方法,并且它适用于托管代码- 在我的测试中使用的是4.5框架。但我必须在我的.net应用程序中使用以下语句:Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException); - frank koch
2
MSDN文档不正确:这也适用于托管应用程序。在Windows 10 RS4上测试过,但我似乎记得在Windows 8上也可以工作。 - White hawk
也适用于非GUI .net应用程序(即无法使用Application.SetUnhandledExceptionMode()的情况)- 在Windows服务(通过TopShelf),.net 4.6,Windows 10 + Server 2008 R2上进行了测试。 - piers7
曾经为一个VB.NET Winforms应用程序工作,我不得不在项目属性中关闭应用程序框架,编写新的入口点并添加@frankkoch的建议,并确保DumpType设置为生成完整转储。这仍然无法处理引发的异常,但可以处理程序生成的异常。 - Burhan

8

我认为如果你的应用程序已经崩溃了,那么你不妨试着创建一个小型转储文件,最坏的情况是什么?你的应用程序已经崩溃了吗?反正它已经这样做了,所以你不妨试试。
VoiDed提到的MSDN论坛中的代码似乎非常可靠。我需要VB.Net版本,所以这里提供了VB版本,供有需要的人使用:

Friend Class MiniDump
    'Code converted from C# code found here: http://social.msdn.microsoft.com/Forums/en-US/clr/thread/6c8d3529-a493-49b9-93d7-07a3a2d715dc

    Private Enum MINIDUMP_TYPE
        MiniDumpNormal = 0 
        MiniDumpWithDataSegs = 1
        MiniDumpWithFullMemory = 2
        MiniDumpWithHandleData = 4
        MiniDumpFilterMemory = 8
        MiniDumpScanMemory = 10
        MiniDumpWithUnloadedModules = 20
        MiniDumpWithIndirectlyReferencedMemory = 40
        MiniDumpFilterModulePaths = 80
        MiniDumpWithProcessThreadData = 100
        MiniDumpWithPrivateReadWriteMemory = 200
        MiniDumpWithoutOptionalData = 400
        MiniDumpWithFullMemoryInfo = 800
        MiniDumpWithThreadInfo = 1000
        MiniDumpWithCodeSegs = 2000
    End Enum

    <Runtime.InteropServices.DllImport("dbghelp.dll")> _
    Private Shared Function MiniDumpWriteDump( _
         ByVal hProcess As IntPtr, _
         ByVal ProcessId As Int32, _
        ByVal hFile As IntPtr, _
         ByVal DumpType As MINIDUMP_TYPE, _
        ByVal ExceptionParam As IntPtr, _
         ByVal UserStreamParam As IntPtr, _
        ByVal CallackParam As IntPtr) As Boolean
    End Function

    Friend Shared Sub MiniDumpToFile(ByVal fileToDump As String)
        Dim fsToDump As IO.FileStream = Nothing

        If (IO.File.Exists(fileToDump)) Then
            fsToDump = IO.File.Open(fileToDump, IO.FileMode.Append)
        Else
            fsToDump = IO.File.Create(fileToDump)
        End If

        Dim thisProcess As Process = Process.GetCurrentProcess()
        MiniDumpWriteDump(thisProcess.Handle, _
                          thisProcess.Id, _
                          fsToDump.SafeFileHandle.DangerousGetHandle(), _
                          MINIDUMP_TYPE.MiniDumpNormal, _
                          IntPtr.Zero, _
                          IntPtr.Zero, _
                          IntPtr.Zero)
        fsToDump.Close()
    End Sub
End Class

只要你能够牢固地处理对它的调用,你就相对安全了。


3
“最糟糕的情况是您的应用程序崩溃吗?” - 那不是最糟糕的情况。更糟糕的情况是,调用 MiniDumpWriteDump 会导致您的进程死锁,并阻止其终止。 MiniDumpWriteDump 做的第一件事是挂起进程中的所有线程。如果您运气不好,其中一个线程正在分配堆内存。当 MiniDumpWriteDump 尝试分配堆内存时,它将无限期地等待锁被释放,但锁被挂起的线程所持有。在进程中进行这种操作是不安全的。 - IInspectable
1
你能否在这种状态下终止进程?如果这在99%的崩溃和死锁中起作用,在1%的情况下,用户只需要强制结束,我认为这是一个公平的权衡,取决于具体情况。从崩溃的进程启动一个新进程是否可行? - Joel McBeth
1
这个很好用,但我注意到在 MINIDUMP_TYPE 中所有的值都是十六进制的,所以在 VB 中应该加上 &H 前缀。 - PeterJ

5
你可以调用Environment.FailFast,它将会:

FailFast方法将消息字符串写入Windows应用程序事件日志,创建应用程序的转储,然后终止当前进程。消息字符串也包含在向Microsoft报告错误中。

除其他事项外。

5

您可以在AppDomain.UnhandledException事件中调用dbghelp.dllMiniDumpWriteDump函数。

在此事件中,您可以转储.NET异常数据并将minidump写入文件。

MSDN论坛上也有一个线程描述了P/Invoke签名和正确用法。


3
不行,你不能安全地这么做。必须从一个独立的进程中调用 MiniDumpWriteDump。 - IInspectable

0

根据您需要的信息类型,您可以为AppDomain.UnhandledException事件添加处理程序吗?(我知道这不完全是您要找的,但它绝对可用于客户端机器。)


-2
你是否使用像log4net这样的日志框架?通常情况下,你会在发布时关闭调试级别的消息。但是你可以考虑编写一个特殊的appender,在某些情况下(比如崩溃)仅将日志记录到文件中。该appender首先将日志写入一个仅存在于内存中的环形缓冲区中,稍后可以将其写入文件,例如通过280Z28建议的异常处理程序触发。

1
迷你转储比日志文件更有帮助。它捕获应用程序的完整状态,在未捕获异常抛出的那一刻。您可以在调试器中加载它,并获取完整的符号信息,方便地链接到源代码,所有线程的完整堆栈跟踪,精确的故障位置,已加载模块的信息,内存以及任何您想添加的自定义信息。 - IInspectable

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