c# - 在没有用户提示的情况下使用 RegisterApplicationRestart 重新启动崩溃的程序

标签 c# windows crash restart windows-error-reporting

我正在使用 Windows 错误报告 API 调用 RegisterApplicationRestart注册应用程序以在应用程序崩溃或 PC 重新启动时由 WER 自动重启。

但是,当应用程序崩溃时,会弹出默认的 WER 对话框(“xyz 已停止响应”/“是否要发送有关该问题的更多信息”),只有在关闭此对话框后程序才会重新启动。

有没有办法抑制这个对话框?

如果我调用 SetErrorMode(SEM_NOGPFAULTERRORBOX) ,然后对话框会按预期被抑制,但重新启动本身也会停止工作。

如果我通过更改注册表项 HKEY_CURRENT_USER\Software\Microsoft\Windows\Windows Error Reporting\DontShowUI 全局禁止对话框,我得到相同的结果:对话框被禁止,但应用程序也不会重新启动。

我知道像第二个看门狗程序这样的解决方法,但我真的很想使用 Windows 错误报告 API 的工具尽可能简单地解决这个问题。

最佳答案

您可以改用 RegisterApplicationRecoveryCallback 并重新启动进程。它不会抑制错误报告对话框,但可以在没有用户交互的情况下重新启动应用程序。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;

namespace Test
{
    class Program
    {
        public delegate int RecoveryDelegate(IntPtr parameter);

        [DllImport("kernel32.dll")]
        private static extern int RegisterApplicationRecoveryCallback(
                RecoveryDelegate recoveryCallback,
                IntPtr parameter,
                uint pingInterval,
                uint flags);

        [DllImport("kernel32.dll")]
        private static extern void ApplicationRecoveryFinished(bool success);

        private static void RegisterForRecovery()
        {
            var callback = new RecoveryDelegate(p=>
            {
                Process.Start(Assembly.GetEntryAssembly().Location);
                ApplicationRecoveryFinished(true);
                return 0;
            });

            var interval = 100U;
            var flags = 0U;

            RegisterApplicationRecoveryCallback(callback,IntPtr.Zero,interval,flags);
        }

        static void Main(string[] args)
        {
            RegisterForRecovery();

            for (var i = 3; i > 0; i--)
            {
                Console.SetCursorPosition(0, Console.CursorTop);
                Console.Write("Crash in {0}", i);
                Thread.Sleep(1000);
            }
            Environment.FailFast("Crash.");
        }
    }
}

通过将 ErrorCode 设置为 SEM_NOGPFAULTERRORBOX,我们正在更改异常过滤行为并强制它传递异常 (EXCEPTION_CONTINUE_SEARCH) 而不是弹出错误报告对话框 (EXCEPTION_EXECUTE_HANDLER).

也许一个正确的方法(在大多数情况下实际上可以防止错误报告对话框弹出)是使用 SetUnhandledExceptionFilter 并在那里进行恢复,这在 .Net 中大致等同于使用 AppDomain.CurrentDomain.UnhandledException。如果我们需要捕获 Win32 异常,我们应该启用 LegacyCorruptedStatePolicy通过将以下行添加到应用程序配置。

<configuration>
   <runtime>
      <legacyCorruptedStateExceptionsPolicy enabled="true" />
   </runtime>
</configuration>

但是它不会捕获所有(例如 Environment.FastFail 或一些访问冲突),因此我建议同时使用两者。

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Threading;

namespace Test
{
    class Program
    {
        public delegate int RecoveryDelegate(IntPtr parameter);

        [DllImport("kernel32.dll")]
        private static extern int RegisterApplicationRecoveryCallback(
                RecoveryDelegate recoveryCallback,
                IntPtr parameter,
                uint pingInterval,
                uint flags);

        [DllImport("kernel32.dll")]
        private static extern void ApplicationRecoveryFinished(bool success);

        private static void RegisterForRecovery()
        {
            var callback = new RecoveryDelegate(p=>
            {
                Recover();
                ApplicationRecoveryFinished(true);
                return 0;
            });

            var interval = 100U;
            var flags = 0U;

            RegisterApplicationRecoveryCallback(callback,IntPtr.Zero,interval,flags);
        }

        private static void Recover()
        {
            //do the recovery and cleanup
            Process.Start(Assembly.GetEntryAssembly().Location);
        }

        private static unsafe void Crash1()
        {
            var p = (int*)0;
            p[0] = 0;
        }

        private static unsafe void Crash2()
        {
            var v = 1;
            var p =&v;
            p -= ulong.MaxValue;
            p[0] = 0;
        }

        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.UnhandledException +=
                new UnhandledExceptionEventHandler((s, e) =>
                {
                    Recover();
                    Environment.Exit(1);
                });

            RegisterForRecovery();

            for (var i = 3; i > 0; i--)
            {
                Console.SetCursorPosition(0, Console.CursorTop);
                Console.Write("Crash in {0}", i);
                Thread.Sleep(1000);
            }

            //different type of crash
            throw new Exception("Crash.");
            //Environment.FailFast("Crash.");
            //Crash1();
            //Crash2();
        }
    }
}

关于c# - 在没有用户提示的情况下使用 RegisterApplicationRestart 重新启动崩溃的程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32520036/

相关文章:

ios - 如果委托(delegate)不再存在会怎样?

javascript - JavaScript使iPad浏览器崩溃

c# - 从 Windows 服务启动 GUI 应用程序 - 窗口不出现

windows - 为什么 MSYS2 不解析 Windows 上的批处理文件?

c# - 为什么向结构体添加一个额外的字段会大大提高其性能?

c# - 简化 Excel 单元格列表以创建范围

c++ - 如何在不改变所有文本颜色的情况下设置单个单词的颜色?

android - SDK 升级到 30 后应用程序崩溃,DevSupportManager 类未找到异常

c# - 到 ToList() 还是不到 ToList()?

c# - 如何删除事件处理程序并将其重新附加到 C# 中的控件?