Может ли C# WinForm статическая пустота Main НЕ перехватить исключение?

У меня есть WinForm заявление написано в C# где я положил try-catch блок в Program.csв записи программы static void Main Метод, прямо в начале приложения, как это:

using System;
using System.IO;
using System.Windows.Forms;

namespace T5ShortestTime {
    static class Program {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
            try {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new T5ShortestTimeForm());
            } catch (Exception e) {
                string errordir = Path.Combine(Application.StartupPath, "errorlog");
                string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
                if (!Directory.Exists(errordir))
                    Directory.CreateDirectory(errordir);                
                File.WriteAllText(errorlog, e.ToString());              
            }
        }
    }
}

Как видите, Application помещается в try-catch блок и в catch Блок, единственное, что он делает, это создает файл журнала ошибок.

Теперь все хорошо. Мое приложение работает нормально, и в случае сбоя последний Exception должны быть захвачены try-catch заблокировать и сохранить в файле журнала ошибок.

Однако, когда я запускаю свою программу некоторое время, я получаю необработанное исключение (null ссылка). Что меня удивляет, так это то, что исключение не создает файл журнала ошибок.

Теперь этот пост показывает, что это может быть вызвано ThreadException или же HandleProcessCorruptedStateExceptions (два наиболее одобренных ответа), но мой случай показывает простой null ссылочное исключение:

Problem signature:
  Problem Event Name:   CLR20r3
  Problem Signature 01: T5ShortestTime.exe
  Problem Signature 02: 2.8.3.1
  Problem Signature 03: 5743e646
  Problem Signature 04: T5ShortestTime
  Problem Signature 05: 2.8.3.1
  Problem Signature 06: 5743e646
  Problem Signature 07: 182
  Problem Signature 08: 1b
  Problem Signature 09: System.NullReferenceException
  OS Version:   6.3.9600.2.0.0.272.7
  Locale ID:    1033
  Additional Information 1: bb91
  Additional Information 2: bb91a371df830534902ec94577ebb4a3
  Additional Information 3: aba1
  Additional Information 4: aba1ed7202d796d19b974eec93d89ec2

Read our privacy statement online:
  http://go.microsoft.com/fwlink/?linkid=280262

If the online privacy statement is not available, please read our privacy statement offline:
  C:\Windows\system32\en-US\erofflps.txt

С чего бы это?

3 ответа

Решение

последнее исключение должно быть зафиксировано блоком try-catch

Этого не произойдет. За исключением одного случая, когда вы запускаете вашу программу с подключенным отладчиком. Таким образом, вы наверняка успокоились, полагая, что это сработает, каждый всегда начинает какое-то время запускать свою программу с F5.

Application.Run() имеет в своем коде back-stop, который вызывает события, try/catch-em-all, который вызывает событие Application.ThreadException, когда обработчик события генерирует необработанное исключение. Такая задержка действительно необходима, особенно в 64-разрядной версии Windows 7. Очень плохие вещи случаются, когда нет обработчика исключений. Однако при запуске с отладчиком эта обратная остановка отсутствует, что делает слишком сложными для отладки необработанные исключения.

Таким образом, когда вы отлаживаете, то ваше предложение catch будет работать. Создание необработанных исключений слишком сложно для отладки. Когда вы запускаете без отладчика, ваше предложение catch не будет работать, и ваша программа аварийно завершит работу, как вы описали. Создание необработанного исключения слишком сложно для отладки.

Так что не делай так. То, как Application.Run() обрабатывает необработанные исключения, настраивается с помощью метода Application.SetUnhandledExceptionMode(). Вам больше понравится эта версия:

    [STAThread]
    static void Main() {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        if (!System.Diagnostics.Debugger.IsAttached) {
            Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
            AppDomain.CurrentDomain.UnhandledException += LogException;
        }
        Application.Run(new Form1());
    }

    private static void LogException(object sender, UnhandledExceptionEventArgs e) {
        string errordir = Path.Combine(Application.StartupPath, "errorlog");
        string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
        if (!Directory.Exists(errordir))
            Directory.CreateDirectory(errordir);
        File.WriteAllText(errorlog, e.ToString());
        AppDomain.CurrentDomain.UnhandledException -= LogException;
        MessageBox.Show("Error details recorded in " + errorlog, "Unexpected error");
        Environment.Exit(1);
    }

С этим кодом вы можете без проблем отлаживать необработанные исключения. Тест Debugger.IsAttached гарантирует, что отладчик всегда будет останавливаться при падении обработчика события. Без отладчика он затем отключает событие Application.ThreadException (оно совершенно бесполезно) и способствует прослушиванию всех исключений. В том числе поднятые в рабочих потоках.

Вы должны дать предупреждение пользователю, чтобы окно не просто исчезло без следа. Я собирался порекомендовать MessageBox, но заметил, что эта ошибка снова возвращается в Windows 10. Вздох.

ThreadException не является типом исключения типа (NullReferenceException). Это то, что:

Это событие позволяет приложению Windows Forms обрабатывать необработанные исключения, возникающие в потоках Windows Forms.

Это означает, что он обрабатывает исключения в потоках, отличных от основного потока.

Итак, вам необходимо подписаться на: AppDomain.CurrentDomain.UnhandledException также для обработки исключений в вашем основном потоке (независимо от типа исключения, например NullReference, IndexOutOfRange, так далее..).

Хорошо, в конце концов, я реализую Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException) например, Hans Passant для VB.Net в этом посте. Здесь я положил свой собственный код + журнал ошибок для C#:

static void Main() {
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    if (!System.Diagnostics.Debugger.IsAttached) {
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.ThrowException);
        AppDomain.CurrentDomain.UnhandledException += LogUnhandledExceptions;
    }
    Application.Run(new T5ShortestTimeForm());
}

private static void LogUnhandledExceptions(object sender, UnhandledExceptionEventArgs e) {
    Exception ex = (Exception)e.ExceptionObject;
    string errordir = Path.Combine(Application.StartupPath, "errorlog");
    string errorlog = Path.Combine(errordir, DateTime.Now.ToString("yyyyMMdd_HHmmss_fff") + ".txt");
    if (!Directory.Exists(errordir))
        Directory.CreateDirectory(errordir);
    File.WriteAllText(errorlog, ex.ToString());
    Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(ex));
}

Кроме того, кажется, что источником путаницы здесь является то, что на самом деле Exceptions происходят:

Первым было какое-то исключение из самого приложения:

System.Exception: Exception of type 'System.Exception' was thrown.
   at T5ShortestTime.T5ShortestTimeForm..ctor() in C:\Test.cs:line 45
   at T5ShortestTime.Program.Main() in C:\Test.cs:line 19
   at ...

И второй происходит во время Dispose из Form компоненты, что создает еще одно исключение, и это null ссылочное исключение:

System.NullReferenceException: Object reference not set to an instance of an object.
   at T5ShortestTime.T5ShortestTimeForm.Dispose(Boolean disposing)
   at System.ComponentModel.Component.Finalize()

Поэтому, когда я проверяю исключение в моем приложении, NullReferenceException приходит последний, в Dispose,

введите описание изображения здесь

Мне удается захватить это только после того, как я установил UnhandledExceptionMode в ThrowException выше.

Другие вопросы по тегам