Подтвердить что ты не робот

Перезапустите аварийную программу с помощью RegisterApplicationRestart без приглашения пользователя.

Я использую API-интерфейс отчетов об ошибках Windows RegisterApplicationRestart, чтобы зарегистрировать приложение, которое будет автоматически перезагружено WER, когда приложение выйдет из строя или перезагрузится компьютер.

Однако при сбое приложения появляется диалоговое окно WER по умолчанию ( "xyz перестает отвечать" / "Вы хотите отправить дополнительную информацию о проблеме" ), и только после закрытия этого диалогового окна перезагрузится программа.

Есть ли способ подавить это диалоговое окно?

Если я вызову SetErrorMode (SEM_NOGPFAULTERRORBOX), тогда диалоговое окно будет подавлено, как и ожидалось, но сам перезапуск также перестает работать.

Если я глобально отменяю диалоговое окно, изменяя раздел реестра HKEY_CURRENT_USER\Software\ Microsoft\Windows\Windows Error Reporting\DontShowUI, я получаю тот же результат: диалоговое окно подавляется, но приложение также не перезапускается.

Мне известны обходные пути, подобные второй сторожевой программе, но мне бы очень хотелось как можно проще решить эту проблему с помощью инструментов API отчетов об ошибках Windows.

4b9b3361

Ответ 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 Exceptions, мы должны включить LegacyCorruptedStatePolicy, добавив следующие строки в конфигурацию App.

<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();
        }
    }
}