Как завершить работу программы при ее сбое? (который должен просто терпеть неудачу unit test вместо того, чтобы застрять навсегда)

Наши модульные тесты запускают дочерние процессы, и иногда эти дочерние процессы вылетают. Когда это происходит, появляется диалоговое окно "Отчет об ошибках Windows", и процесс остается живым до тех пор, пока это не будет отменено вручную. Это, конечно, предотвращает завершение модульных тестов.

Как этого можно избежать?


Вот пример диалога в Win7 с обычными настройками:

alt text

Если я отключу раздел реестра AeDebug, опция отладки JIT исчезнет:

alt text

Если я отключу проверку решений (единственное, что я могу контролировать с помощью панели управления), это выглядит так, но все равно появляется и все еще останавливает программу от умирания, пока пользователь не нажмет что-то. WerAddExcludedApplication документируется так же, как и этот эффект.

alt text

Ответ 1

Резюме из ответов jdehaan и Eric Brown, а также этот вопрос (см. также этот вопрос):

N.B. Эти решения могут влиять и на другие сообщения об ошибках, например. невозможность загрузить DLL или открыть файл.

Вариант 1: отключить глобально

Работает глобально на всей учетной записи пользователя или машине, что может быть как преимуществом, так и недостатком.

Установите [HKLM|HKCU]\Software\Microsoft\Windows\Windows Error Reporting\DontShowUI в 1. Дополнительная информация: Настройки WER.

Вариант 2: Отключить для приложения

Требуется модификация программы сбоя, описанная в документации как лучшая практика, непригодная для библиотечной функции.

Вызвать SetErrorMode: SetErrorMode(SetErrorMode(0) | SEM_NOGPFAULTERRORBOX); (или с помощью SEM_FAILCRITICALERRORS). Дополнительная информация: Отключение диалогового окна сбоя программы (объясняет нечетное расположение вызовов).

Вариант 2a: отключить функцию:

Требуется модификация программы сбоя, требуется Windows 7/2008 R2 (только для настольных приложений) или выше, описанная в документации, предпочтительнее SetErrorMode, подходящая для потоковой библиотеки.

Вызов и reset SetThreadErrorMode:

DWORD OldThreadErrorMode = 0;
SetThreadErrorMode(SEM_FAILCRITICALERRORS,& OldThreadErrorMode);
    …
SetThreadErrorMode (z_OldThreadErrorMode, NULL);

Дополнительная информация: не так много доступных?

Вариант 3: укажите обработчик

Требуется модификация программы сбоя.

Используйте SetUnhandledExceptionFilter, чтобы установить собственный структурированный обработчик исключений, который просто выходит, возможно, с отчетами и, возможно, попыткой очистки.

Вариант 4: Поймать как исключение

Требуется модификация программы сбоя. Только для приложений .NET.

Оберните весь код в глобальный блок try/catch. Укажите HandleProcessCorruptedStateExceptionsAttribute и, возможно, также SecurityCriticalAttribute по методу, улавливающему исключения. Дополнительная информация: Обработка поврежденных исключений состояния

Примечание: это может не вызвать сбои, вызванные "Управляемые помощники отладки" ; Если это так, они также должны быть отключены в приложении.

Вариант 5: Остановить процесс отчетности

Работает глобально на всей учетной записи пользователя, но только на контролируемую продолжительность.

Убейте процесс отчетов об ошибках Windows каждый раз, когда он появляется:

var werKiller = new Thread(() =>
{
    while (true)
    {
        foreach (var proc in Process.GetProcessesByName("WerFault"))
            proc.Kill();
        Thread.Sleep(3000);
    }
});
werKiller.IsBackground = true;
werKiller.Start();

Это все еще не полностью пуленепротивление, потому что консольное приложение может сбой с помощью другого сообщения об ошибке, видимо, отображаемого внутренней функцией NtRaiseHardError:

alt text

Ответ 2

Единственное решение - поймать все исключения на очень высоком уровне (для каждого потока) и закончить приложение должным образом (или выполнить другое действие).

Это единственный способ предотвратить исключение исключения из вашего приложения и активацию WER.

Дополнение:

Если исключение - это то, чего вы не исключаете, вы можете использовать AssertNoThrow (NUnit) или аналогично в другой структуре Unit Test, чтобы заключить код, запускающий дочерние процессы. Таким образом, вы также получите его в своем отчете Unit Test. Это, на мой взгляд, самое чистое возможное решение, о котором я могу думать.

Addition2: Как видно из приведенных ниже комментариев, я ошибался: вы не всегда можете поймать асинхронные исключения, это зависит от того, что позволяет среда. В .NET некоторые исключения не могут быть пойманы, что делает мою идею бесполезной в этом случае...

Для .NET. Существуют сложные способы обхода решения, связанные с использованием AppDomains, что приводит к выгрузке AppDomain вместо сбоя всего приложения. Слишком плохо...

http://www.bluebytesoftware.com/blog/PermaLink,guid,223970c3-e1cc-4b09-9d61-99e8c5fae470.aspx

http://www.develop.com/media/pdfs/developments_archive/AppDomains.pdf


EDIT:

Я, наконец, понял. С .NET 4.0 Вы можете добавить атрибут HandleProcessCorruptedStateExceptions из System.Runtime.ExceptionServices в метод, содержащий блок try/catch. Это действительно сработало! Может быть, не рекомендуется, но работает.

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.ExceptionServices;

namespace ExceptionCatching
{
    public class Test
    {
        public void StackOverflow()
        {
            StackOverflow();
        }

        public void CustomException()
        {
            throw new Exception();
        }

        public unsafe void AccessViolation()
        {
            byte b = *(byte*)(8762765876);
        }
    }

    class Program
    {
        [HandleProcessCorruptedStateExceptions]
        static void Main(string[] args)
        {
            Test test = new Test();
            try {
                //test.StackOverflow();
                test.AccessViolation();
                //test.CustomException();
            }
            catch
            {
                Console.WriteLine("Caught.");
            }

            Console.WriteLine("End of program");

        }

    }      
}

Ответ 3

Попробуйте установить

HKCU\Software\Microsoft\Windows\Отчеты об ошибках Windows\DontShowUI

до 1. (Вы также можете установить тот же ключ в HKLM, но для этого вам нужны admin privs.)

Это должно помешать WER отображать любой пользовательский интерфейс.