c/c++语言里MiniDump是一个重要的调试手段,他们没有C#/java这样语言有很多异常输出信息(

JVM异常导出bug日志功能,通常在jdk目录,文件格式hs_err_%pid%.log,pid是进程id)。

我们通常在项目中都会把可预见性进行异常处理。常见的处理方法如下

try{

   ...


catch(Exception ex)
{
   HandleExeption(ex);
}

项目部署到客户机中运行在程序员无法评估的情况下,如(堆栈溢出、访问冲突)则无法处理

或者很难重现这种异常,这给程序调试带来一定程度上的障碍,而这个时候内存及当前机器环境的快

照信息对程序排错则至关重要。幸好我们.NET提供了应用程序域未捕获异常(不是所有异常)事件处理

接口AppDomain.UnhandledException先贴出Minidump封装类:

public sealed class MiniDump
    {
        [Flags]
        public enum DumpType : uint
        {
            // From dbghelp.h:
            MiniDumpNormal = 0x00000000,
            MiniDumpWithDataSegs = 0x00000001,
            MiniDumpWithFullMemory = 0x00000002,
            MiniDumpWithHandleData = 0x00000004,
            MiniDumpFilterMemory = 0x00000008,
            MiniDumpScanMemory = 0x00000010,
            MiniDumpWithUnloadedModules = 0x00000020,
            MiniDumpWithIndirectlyReferencedMemory = 0x00000040,
            MiniDumpFilterModulePaths = 0x00000080,
            MiniDumpWithProcessThreadData = 0x00000100,
            MiniDumpWithPrivateReadWriteMemory = 0x00000200,
            MiniDumpWithoutOptionalData = 0x00000400,
            MiniDumpWithFullMemoryInfo = 0x00000800,
            MiniDumpWithThreadInfo = 0x00001000,
            MiniDumpWithCodeSegs = 0x00002000,
            MiniDumpWithoutAuxiliaryState = 0x00004000,
            MiniDumpWithFullAuxiliaryState = 0x00008000,
            MiniDumpWithPrivateWriteCopyMemory = 0x00010000,
            MiniDumpIgnoreInaccessibleMemory = 0x00020000,
            MiniDumpValidTypeFlags = 0x0003ffff,
        };

        //typedef struct _MINIDUMP_EXCEPTION_INFORMATION {
        //    DWORD ThreadId;
        //    PEXCEPTION_POINTERS ExceptionPointers;
        //    BOOL ClientPointers;
        //} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;
        [StructLayout(LayoutKind.Sequential, Pack = 4)]  // Pack=4 is important! So it works also for x64!
        struct MiniDumpExceptionInformation
        {
            public uint ThreadId;
            public IntPtr ExceptioonPointers;
            [MarshalAs(UnmanagedType.Bool)]
            public bool ClientPointers;
        }

        //BOOL
        //WINAPI
        //MiniDumpWriteDump(
        //    __in HANDLE hProcess,
        //    __in DWORD ProcessId,
        //    __in HANDLE hFile,
        //    __in MINIDUMP_TYPE DumpType,
        //    __in_opt PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
        //    __in_opt PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
        //    __in_opt PMINIDUMP_CALLBACK_INFORMATION CallbackParam
        //    );
        [DllImport("dbghelp.dll",
          EntryPoint = "MiniDumpWriteDump",
          CallingConvention = CallingConvention.StdCall,
          CharSet = CharSet.Unicode,
          ExactSpelling = true, SetLastError = true)]
        static extern bool MiniDumpWriteDump(
          IntPtr hProcess,
          uint processId,
          IntPtr hFile,
          uint dumpType,
          ref MiniDumpExceptionInformation expParam,
          IntPtr userStreamParam,
          IntPtr callbackParam);

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentThreadId", ExactSpelling = true)]
        static extern uint GetCurrentThreadId();

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", ExactSpelling = true)]
        static extern IntPtr GetCurrentProcess();

        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcessId", ExactSpelling = true)]
        static extern uint GetCurrentProcessId();

        public static bool Write(string fileName)
        {
            return Write(fileName, DumpType.MiniDumpWithFullMemory);
        }
        public static bool Write(string fileName, DumpType dumpType)
        {
            using (var fs = new System.IO.FileStream(fileName, System.IO.FileMode.Create, System.IO.FileAccess.Write, System.IO.FileShare.None))
            {
                MiniDumpExceptionInformation exp;
                exp.ThreadId = GetCurrentThreadId();
                exp.ClientPointers = false;
                exp.ExceptioonPointers = System.Runtime.InteropServices.Marshal.GetExceptionPointers();
                bool bRet = MiniDumpWriteDump(
                  GetCurrentProcess(),
                  GetCurrentProcessId(),
                  fs.SafeFileHandle.DangerousGetHandle(),
                  (uint)dumpType,
                  ref exp,
                  IntPtr.Zero,
                  IntPtr.Zero);
                return bRet;
            }
        }

 

以下以Winform演示这个事件的使用方法

先拖一个界面如下

 

class Program
    {
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        static void Main(string[] args)
        {                      
            //exception handler
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;

            Application.ThreadException += Application_ThreadException;



            bool isRunWinService = args.Length > 0 && args[0].ToLower().Equals("-service");
            //用户手工启动
            if (!isRunWinService)
            {
                Application.Run(new frmSetup());
            }
            else
            {                             
                ServiceBase.Run(new ServiceBase[] {
                    new Daemon()
                });
            }
        }

        private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
        {
            string dumpFile = System.IO.Path.Combine(System.Environment.CurrentDirectory, string.Format("crash-dump-{0}.dmp", DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss")));
            MiniDump.Write(dumpFile);
        }

        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            string dumpFile = System.IO.Path.Combine(System.Environment.CurrentDirectory, string.Format("thread-dump-{0}.dmp", DateTime.Now.ToString("yyyy-MM-dd HH-mm-ss")));
            MiniDump.Write(dumpFile);
        }
    }

 两个按钮事件代码分别如下:

private void button1_Click(object sender, EventArgs e)
        {
            new Thread(() => { throw new Exception("Other thread"); }).Start();
        }

        private void button2_Click(object sender, EventArgs e)
        {

            string a = null;
            a.PadLeft(10);
        }

随便点击一个按钮都能触发异常处理,生成如下dump.dmp文件

拖到visual studio里面打开如下

 

“使用 仅托管进行调试”以下是打开dump文件后的效果,直接定位到异常处:

 

最近比较忙,时间紧张,文章写得比较粗糙,大家应该能明白什么意思了,有疑问欢迎留言。

参考:

AppDomain.CurrentDomain.UnhandledException not firing without debugging

Writing MiniDumps in C#

application level global exception handler didn't get hit

How to create minidump of a .NET process when a certain first chance exception occurs

 Should use both AppDomain.UnhandledException and Application.DispatcherUnhandledException?

 The simplest way to generate minidump for mixed managed & unmanaged stack?

CLRDUMP

How to Use the Debug Diagnostic Tool v1.1 (DebugDiag) to Debug User Mode Processes

posted on 2018-05-28 08:12  你不知道的浪漫  阅读(3730)  评论(0编辑  收藏  举报