【发布时间】:2009-07-15 21:00:11
【问题描述】:
我知道如何使用 ADPlus 或 DebugDiag 生成故障转储文件,但我想知道是否有办法在客户的计算机上执行此操作而无需安装这些工具...具体来说,我希望能够配置我的应用程序(例如使用注册表值)在发生严重故障时生成故障转储。更具体地说,我需要能够从 C# 应用程序中执行此操作,但如有必要,我不介意 P/Invoke'ing。谢谢!
【问题讨论】:
标签: c# .net crash-dumps
我知道如何使用 ADPlus 或 DebugDiag 生成故障转储文件,但我想知道是否有办法在客户的计算机上执行此操作而无需安装这些工具...具体来说,我希望能够配置我的应用程序(例如使用注册表值)在发生严重故障时生成故障转储。更具体地说,我需要能够从 C# 应用程序中执行此操作,但如有必要,我不介意 P/Invoke'ing。谢谢!
【问题讨论】:
标签: c# .net crash-dumps
请注意,从“失败”进程(甚至线程)内部创建小型转储本身并非易事或可能不准确(还有MiniDumpWriteDump 函数的备注)。
此外,如果您的进程非常愤怒以至于您可能需要编写故障转储,那么整个情况通常会非常紧张,即使尝试创建故障转储也可能导致另一次崩溃(诸如挂起之类的情况 - 但那些可能更难从当前流程中“捕获”)。
如果您无法在客户端系统上安装单独的应用程序,您可以做的“最好”的事情是启动一个外部进程(在紧急情况下也可能失败!)并让它从您当前的进程创建一个故障转储(见Superassert.NET from John Robbins)。您甚至可以将外部二进制文件放入您的应用程序资源,在启动时将其从那里提取(以尽量减少关键情况下的故障)到磁盘(如果您敢的话)。
【讨论】:
您可以配置 Windows 错误报告 (WER) 以使用以下注册表脚本在特定目录中创建故障转储:
Windows 注册表编辑器版本 5.00 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows 错误报告\LocalDumps] "DumpFolder"="C:\\Dumps" “转储计数”=dword:00000064 “转储类型”=dword:00000002 "CustomDumpFlags"=dword:00000000转储将进入 C:\Dumps,其名称反映了崩溃进程的名称。 DumpType=2 给出完整的内存转储。 DumpType=1 给出一个小型转储。在 64 位机器上,您不需要将它们放在 Wow32 节点下。 WER 仅使用上面指定的非 WOW 注册表项。
根据崩溃的类型,此方法可能不起作用。我还没有弄清楚为什么或哪些崩溃类型它没有捕捉到。有人吗?
【讨论】:
我认为,如果您的应用程序崩溃了,那么您不妨尝试创建一个小型转储文件,最糟糕的情况是什么,您的应用程序会崩溃?反正它是这样做的,所以你不妨试试。
MSDN forum mentioned by VoiDed 中的代码看起来很可靠。我需要一个 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
只要确保你可靠地处理对它的调用,你就应该相对安全。
【讨论】:
MiniDumpWriteDump 会使您的进程死锁,并阻止它终止。 MiniDumpWriteDump 做的第一件事就是暂停进程中的所有线程。如果你不走运,其中一个线程正在分配堆内存。而当MiniDumpWriteDump尝试分配堆内存时,它会无限期地等待锁被释放,但它被一个挂起的线程持有。您不能安全地在进程中执行此操作。
MINIDUMP_TYPE 中的所有值都是十六进制的,所以VB 应该有一个&H 前缀。
您可以在 AppDomain.UnhandledException 事件中 P/Invoke dbghelp.dll 的 MiniDumpWriteDump 函数。
在这种情况下,您可以转储 .NET 异常数据的日志并将 minidump 写入文件。
还有一个thread on the MSDN forums 描述了 P/Invoke 签名和正确用法。
【讨论】:
MiniDumpWriteDump 必须从单独的进程中调用。
您可以拨打Environment.FailFast,电话会:
FailFast 方法将消息字符串写入 Windows 应用程序事件日志,创建应用程序的转储,然后终止当前进程。消息字符串也包含在向 Microsoft 报告的错误中。
除其他外。
【讨论】:
根据您需要的信息类型,您可以为AppDomain.UnhandledException 事件添加处理程序吗? (我知道这不是您正在寻找的东西,但它绝对可以在客户端计算机上使用。)
【讨论】:
你使用像 log4net 这样的日志框架吗?通常,您关闭发布的调试级别消息。但是,您可以考虑编写一个特殊的附加程序,仅在某些情况下(如崩溃)记录到文件。这个附加程序首先写入一个仅内存的环形缓冲区,该缓冲区可以写入文件,稍后 - 例如由 280Z28 建议的异常处理程序触发。
【讨论】: