在没有用户提示的情况下使用 RegisterApplicationRestart 重新启动崩溃的程序
Posted
技术标签:
【中文标题】在没有用户提示的情况下使用 RegisterApplicationRestart 重新启动崩溃的程序【英文标题】:Restart a crashed program with RegisterApplicationRestart without user prompt 【发布时间】:2015-12-07 19:29:51 【问题描述】:我正在使用 Windows 错误报告 API 调用 RegisterApplicationRestart 注册一个应用程序,以便在应用程序崩溃或 PC 重新启动时由 WER 自动重新启动。
但是,当应用程序崩溃时,会弹出默认的 WER 对话框(“xyz 已停止响应”/“是否要发送有关该问题的更多信息”),并且只有在关闭此对话框后程序才会重新启动。
有没有办法抑制这个对话框?
如果我调用SetErrorMode(SEM_NOGPFAULTERRORBOX),则对话框会按预期被抑制,但重启本身也会停止工作。
如果我通过更改注册表键 HKEY_CURRENT_USER\Software\ Microsoft\Windows\Windows Error Reporting\DontShowUI
来全局禁止对话框,我会得到相同的结果:对话框被禁止,但应用程序也不会重新启动。
我知道像第二个看门狗程序这样的变通方法,但我真的很想用 Windows 错误报告 API 的工具尽可能简单地解决这个问题。
【问题讨论】:
RegisterApplicationRestart 的官方文档明确指出:“未经用户同意,应用程序不会自动重启” 【参考方案1】:您可以改用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 异常,我们应该通过在 App config 中添加以下行来启用 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();
【讨论】:
以上是关于在没有用户提示的情况下使用 RegisterApplicationRestart 重新启动崩溃的程序的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有提交事件的情况下显示 setCustomValidity 消息/工具提示
如何在没有用户交互的情况下使用 GMail API 创建草稿
在没有 Windows 访问权限的情况下使用凭据信息访问共享文件夹