欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-09 16:37:04
第一次写博客,如有错误,请大家及时告知,本人立即改之。 如果您有好的想法或者建议,我随时与我联系。 如果发现代码有误导时,请与我联系,我立即改之。 好了不多说,直接贴代码。 一般的错误,使用下面三个就可以了。我不太赞同项目里面大量使用try{}catch{}(释放资源除外) 特殊情况下,此函数可检测 ......

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

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

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

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

一般的错误,使用下面三个就可以了。我不太赞同项目里面大量使用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);
    }

}

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