Fork me on github

wpf 单例模式和异常处理 (原发布 csdn 2017-04-12 20:34:12)

第一次写博客,如有错误,请大家及时告知,本人立即改之。

如果您有好的想法或者建议,我随时与我联系。

如果发现代码有误导时,请与我联系,我立即改之。

好了不多说,直接贴代码。

一般的错误,使用下面三个就可以了。我不太赞同项目里面大量使用try{}catch{}(释放资源除外)

// 设置UI线程发生异常时处理函数
System.Windows.Application.Current.DispatcherUnhandledException += App_DispatcherUnhandledException;

// 设置非UI线程发生异常时处理函数
AppDomain.CurrentDomain.UnhandledException += App_CurrentDomainUnhandledException;

// 设置托管代码异步线程发生异常时处理函数
TaskScheduler.UnobservedTaskException += EventHandler_UnobservedTaskException;

特殊情况下,此函数可检测到c++封装的dll(不是百分之百可以检测到)

public delegate int CallBack(ref long a);
CallBack myCall;

[DllImport("kernel32")]
private static extern Int32 SetUnhandledExceptionFilter(CallBack cb);
    
// 设置非托管代码发生异常时处理函数
callBack = new CallBack(ExceptionFilter);
SetUnhandledExceptionFilter(callBack);

此函数可让程序"美化"结束。

完整代码:

public partial class App : Application
{
        [System.Runtime.InteropServices.DllImport("kernel32")]
        private static extern Int32 SetUnhandledExceptionFilter(CallBack cb);

        private delegate int CallBack(ref long a);

        private CallBack callBack;

        private System.Threading.Mutex mutex;

        public App()
        {
            Startup += new System.Windows.StartupEventHandler(App_Startup);
        }

        private void App_Startup(object sender, System.Windows.StartupEventArgs e)
        {
            mutex = new System.Threading.Mutex(true, $"{System.Reflection.Assembly.GetEntryAssembly().GetName().Name} - 8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F", out bool ret);
            if (!ret)
            {
                System.Windows.MessageBox.Show($"{System.Reflection.Assembly.GetEntryAssembly().GetName().Name} has already started up.",
                    "App_Startup",
                    System.Windows.MessageBoxButton.OK,
                    System.Windows.MessageBoxImage.Information);
                Environment.Exit(0);
                return;
            }

		   Log4net.Init(typeof(MainWindow));
		   
            // 设置UI线程发生异常时处理函数
            System.Windows.Application.Current.DispatcherUnhandledException += App_DispatcherUnhandledException;

            // 设置非UI线程发生异常时处理函数
            AppDomain.CurrentDomain.UnhandledException += App_CurrentDomainUnhandledException;

            // 设置非托管代码发生异常时处理函数
            callBack = new CallBack(ExceptionFilter);
            SetUnhandledExceptionFilter(callBack);

            // 设置托管代码异步线程发生异常时处理函数
            TaskScheduler.UnobservedTaskException += EventHandler_UnobservedTaskException;
        }

    void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
    {
        Log4net.gLogger.Error("--<App_DispatcherUnhandledException>--" + e.Exception.ToString());
        System.Windows.Forms.MessageBox.Show(e.Exception.ToString(),
            "Error",
            System.Windows.Forms.MessageBoxButtons.OK,
            System.Windows.Forms.MessageBoxIcon.Error);

        e.Handled = true;
    }

    void App_CurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Log4net.gLogger.Fatal("--<App_CurrentDomainUnhandledException>--" + e.ExceptionObject.ToString());
        if (System.Windows.Forms.DialogResult.Yes
            == System.Windows.Forms.MessageBox.Show(
            "软件出现不可恢复错误,即将关闭。是否选择生成Dump文件以供开发人员分析问题?",
            "Error", System.Windows.Forms.MessageBoxButtons.YesNo,
            System.Windows.Forms.MessageBoxIcon.Error, System.Windows.Forms.MessageBoxDefaultButton.Button1))
        {
            WriteDump();
        }
        Environment.Exit(0);
    }

    private int ExceptionFilter(ref long a)
    {
        Log4net.gLogger.Fatal("--<ExceptionFilter>--" + Environment.StackTrace);
        WriteDump();
        return 1;
    }

    private void WriteDump()
    {
        Dump.WriteDumpFile();
    }
}

对于一些问题,我们可以通过日志文件记录(我目前使用Log4net)。有时候日志不能完全帮助我们找到问题所在,这时dmp文件就可以帮助到我们。

internal class DumpWriter
{
    public enum MiniDumpType
    {
        None = 0x00010000,
        Normal = 0x00000000,
        WithDataSegs = 0x00000001,
        WithFullMemory = 0x00000002,
        WithHandleData = 0x00000004,
        FilterMemory = 0x00000008,
        ScanMemory = 0x00000010,
        WithUnloadedModules = 0x00000020,
        WithIndirectlyReferencedMemory = 0x00000040,
        FilterModulePaths = 0x00000080,
        WithProcessThreadData = 0x00000100,
        WithPrivateReadWriteMemory = 0x00000200,
        WithoutOptionalData = 0x00000400,
        WithFullMemoryInfo = 0x00000800,
        WithThreadInfo = 0x00001000,
        WithCodeSegs = 0x00002000
    }

    [DllImport("DbgHelp.dll")]
    private static extern bool MiniDumpWriteDump(
        IntPtr hProcess,
        Int32 processId,
        IntPtr fileHandle,
        MiniDumpType dumpType,
        ref MiniDumpExceptionInformation excepInfo,
        IntPtr userInfo,
        IntPtr extInfo);

    [DllImport("DbgHelp.dll")]
    private static extern bool MiniDumpWriteDump(
        IntPtr hProcess,
        Int32 processId,
        IntPtr fileHandle,
        MiniDumpType dumpType,
        IntPtr excepParam,
        IntPtr userInfo,
        IntPtr extInfo);

    [StructLayout(LayoutKind.Sequential, Pack = 4)]// Pack=4 is important! So it works also for x64!
    private struct MiniDumpExceptionInformation
    {
        public uint ThreadId;
        public IntPtr ExceptionPointers;
        [MarshalAs(UnmanagedType.Bool)]
        public bool ClientPointers;
    }

    [DllImport("kernel32.dll")]
    private static extern uint GetCurrentThreadId();

    private bool WriteDump(String dmpPath, MiniDumpType dmpType)
    {
        using (FileStream stream = new FileStream(dmpPath, FileMode.Create))
        {
            //取得进程信息
            Process process = Process.GetCurrentProcess();

            MiniDumpExceptionInformation mei = new MiniDumpExceptionInformation();
            mei.ThreadId = GetCurrentThreadId();
            mei.ExceptionPointers = Marshal.GetExceptionPointers();
            mei.ClientPointers = true;

            bool res = false;

            //如果不使用MiniDumpWriteDump重载函数
            //当mei.ExceptioonPointers == IntPtr.Zero => 无法保存dmp文件
            //且当mei.ClientPointers == false时程序直接崩溃(mei.ClientPointers == true程序不崩溃)
            //
            //以上测试信息硬件环境 cpu Pentium(R) Dual-Core CPU T4200 @ 2.00GHz
            //                 vs2013update5
            //在公司服务器上测试(64位系统、vs2013)不会出现上述情况
            /*res = MiniDumpWriteDump(
                process.Handle,
                process.Id,
                stream.SafeFileHandle.DangerousGetHandle(),
                dmpType,
                ref mei,
                IntPtr.Zero,
                IntPtr.Zero);*/

            if (mei.ExceptionPointers == IntPtr.Zero)
            {
                res = MiniDumpWriteDump(
                    process.Handle,
                    process.Id,
                    stream.SafeFileHandle.DangerousGetHandle(),
                    dmpType,
                    IntPtr.Zero,
                    IntPtr.Zero,
                    IntPtr.Zero);
            }
            else
            {
                res = MiniDumpWriteDump(
                    process.Handle,
                    process.Id,
                    stream.SafeFileHandle.DangerousGetHandle(),
                    dmpType,
                    ref mei,
                    IntPtr.Zero,
                    IntPtr.Zero);
            }
            return res;
        }
    }

    public DumpWriter()
    {
        FilePath = Environment.CurrentDirectory + @"\Dump";
        if (!Directory.Exists(FilePath))
            Directory.CreateDirectory(FilePath);
    }

    /// <summary>
    /// 保存dmp文件路径
    /// </summary>
    public string FilePath { get; protected set; }
    /// <summary>
    /// 保存dmp文件名称(包括路径)
    /// </summary>
    public string FileName { get; protected set; }
    /// <summary>
    /// 写dmp文件
    /// </summary>
    /// <param name="dmpType">参数,不同参数保存内容不一样</param>
    /// <returns></returns>
    public bool WriteDumpFile(MiniDumpType dmpType)
    {
        FileName = string.Format("{0}\\{1}_{2}.dmp",
            FilePath,
            DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss-fff"),
            Process.GetCurrentProcess().ProcessName);
        return WriteDump(FileName, dmpType);
    }

}

好了,就写这么多吧。一般情况下,这些应该可以帮助我们解决大部分问题了。

posted @ 2019-09-05 19:24  njit_77  阅读(454)  评论(0编辑  收藏  举报