Как завершить работу программы при ее сбое? (который должен просто терпеть неудачу 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 отображать любой пользовательский интерфейс.