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

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

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

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

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

}

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