使用RegisterApplicationRestart重新启动崩溃的程序,不需要用户提示

本文关键字:不需要 用户 提示 程序 RegisterApplicationRestart 重新启动 崩溃 使用 | 更新日期: 2023-09-27 18:12:12

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

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

是否有办法抑制这个对话框?

如果我调用SetErrorMode(SEM_NOGPFAULTERRORBOX),那么对话框将被抑制,正如预期的那样,但重启本身也停止工作。

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

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

使用RegisterApplicationRestart重新启动崩溃的程序,不需要用户提示

您可以使用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>

然而,它不会捕获所有(例如:环境。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();
        }
    }
}