6

Если в обработчике события Load происходит исключение, то при запуске из VS оно не отображается - просто прерывается выполнение метода и отображается форма. Это происходит как при запуске, так и при пошаговой отладке.

В предыдущих версиях VS эту проблему решало создание новой конфигурации билда под x64. Хотя, приходилось выставлять Empty в качестве базовой, что само по себе неудобно, т. к. приходится заново настраивать уровни предупреждений.

Но в VS2015 что-то ничего хорошего не получается. Что делать?

Скринвидео проблемы (27MB).

PS: Нашёл несколько таких вопросов на enSO, но там либо жуткие костыли, либо описание того, что это связано с 32-битностью приложения.

Qwertiy
  • 123,725
  • Вот интересная статья на эту тему https://support.microsoft.com/ru-ru/kb/976038 – Dmitry Jan 07 '16 at 02:49
  • @Dmitry, --------------------------- Автономный установщик обновлений Windows

    Обновление не применимо к этому комьютеру.


    ОК

    Или надо руками создавать? А поможет?

    – Qwertiy Jan 07 '16 at 11:38
  • Думаю стоит поискать подобное решение именно для вашей системы. Список систем обновляемых данным обновлением не очень широк к сожаленю – Dmitry Jan 07 '16 at 11:40
  • @Dmitry, у меня Win7 x64 - там в перечисленных была. – Qwertiy Jan 07 '16 at 11:41
  • Да, действительно, есть такая. А еще какие-то ошибки есть или только эта? – Dmitry Jan 07 '16 at 11:42
  • реестр правили? – Dmitry Jan 07 '16 at 11:43
  • @Dmitry, нет. Я ожидал, что обновление именно это и делает. – Qwertiy Jan 07 '16 at 11:46
  • попробуйте руками по реестру пройтись, а потом еще раз попытайтесь установить обновление – Dmitry Jan 07 '16 at 11:49
  • А это случайно не связано с политикой обработки unobserved exceptions? http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5.aspx – VladD Jan 15 '16 at 21:45
  • @VladD, в моём коде Task же вообще нет. Кстати, с тасками как раз исключения работают, хотя я читал, что не должны. Ты скринвидео смотрел? – Qwertiy Jan 15 '16 at 21:47
  • @Qwertiy: Ага, смотрел. У меня есть подозрение, что в базовом OnLoad стоит catch { /*ignore*/ }. – VladD Jan 15 '16 at 21:58
  • @Qwertiy: С тасками можно конфигурировать. И на мой вкус это ужасно, т. к. поведение должно быть жёстко определено языком: имею я право тупо Cancel'ить ненужные таски, или после этого мне нужно ещё ловить TaskCanceledException? Поменял конфигурацию, и код не валиден — ыыыыы! – VladD Jan 15 '16 at 22:00
  • Есть радикальное решение данной проблемы: используйте явный перехват всех исключений try catch в "точках входа" в вашем приложении. – Zverev Evgeniy Aug 17 '16 at 18:07
  • @ZverevEugene, зачем он мне? – Qwertiy Aug 17 '16 at 18:55
  • @Qwertiy "Он" вам затем, чтобы ваше приложение штатно вело себя при возникновении исключений. Штатно закрывалось, а не через исключение. Даже в "маленьких" и "внутренних" приложениях, которые кроме вас самого никто не увидит, это нужно для самодисциплины. Воспитание в себе дисциплины это субъективный вопрос, я не спорю, но решение работает совершенно объективно. – Zverev Evgeniy Aug 18 '16 at 11:20
  • @ZverevEugene, мне нужно, чтобы VS остановилась на исключении и я мог дебажить код в месте возникновения исключения. – Qwertiy Aug 18 '16 at 11:23
  • @Qwertiy Прошу прощения, не врубился с ходу в суть вопроса. Предлагаю два решения проблемы ниже. – Zverev Evgeniy Aug 18 '16 at 12:46

1 Answers1

3

1. Первый вариант из англоязычного SO. Кратенько копирую:

Добавляем класс для вызова потребных функций:

public static class Kernel32
{
    public const uint PROCESS_CALLBACK_FILTER_ENABLED = 0x1;

    [DllImport("Kernel32.dll")]
    public static extern bool SetProcessUserModeExceptionPolicy(UInt32 dwFlags);

    [DllImport("Kernel32.dll")]
    public static extern bool GetProcessUserModeExceptionPolicy(out UInt32 lpFlags);


    public static void DisableUMCallbackFilter() {
        uint flags;
        GetProcessUserModeExceptionPolicy(out flags);

        flags &= ~PROCESS_CALLBACK_FILTER_ENABLED;
        SetProcessUserModeExceptionPolicy(flags);
    }
}

Вызываем это всё на старте приложения:

[STAThread]
static void Main()
{
    if (System.Diagnostics.Debugger.IsAttached)
    {
        Kernel32.DisableUMCallbackFilter();
    }

    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

2. Второй вариант - разорвать негодный контекст вызова Form_Load:

    private void Form1_Load(object sender, EventArgs e)
    {
        if (System.Diagnostics.Debugger.IsAttached)
        {
            //Форме рано представать перед пользователем.
            this.WindowState = FormWindowState.Minimized;
            this.ShowInTaskbar = false;

            Task.Run(
                delegate
                {
                    //Это нужно, чтобы успеть посмотреть, как процесс
                    //загрузки будет выглядеть для пользователя.
                    //Thread.Sleep(3000);

                    this.Invoke(
                        new Action(delegate
                        {
                            FormLoadLogic();

                            //Теперь можно форму показать.
                            this.WindowState = FormWindowState.Normal;
                            this.ShowInTaskbar = true;
                        }));
                });
        }
        else
        {
            FormLoadLogic();
        }
    }

    FormLoadLogic()
    {
        this.Text = "step 1";
        throw new Exception("test");
        this.Text = "step 2";
    }
  • Но ведь эти костыли останутся в коде, хотя при запуске без VS они не нужны. Хотелось бы как-то отладчик заставить правильно работать без костылей в коде. – Qwertiy Aug 18 '16 at 13:11
  • @Qwertiy Костыли можно убрать через проверку режима работы. Сейчас впишу. – Zverev Evgeniy Aug 18 '16 at 15:29
  • #if DEBUG расставить? – Qwertiy Aug 18 '16 at 16:21
  • @Qwertiy Я не люблю #IF DEBUG поскольку сам часто использую сборки, откомпилированные в Debug, в продукционной среде. Я предпочитаю System.Diagnostics.Debugger.IsAttached. Я прописал это всё в коде, посмотрите. – Zverev Evgeniy Aug 18 '16 at 16:37
  • #if DEBUG будет выкинуто при компиляции, а проверка на дебаггер будет исполняться каждый раз и оставит код в приложении. – Qwertiy Aug 18 '16 at 20:47
  • @Qwertiy Ну если хотите - замените на #IF DEBUG, но это мнимая оптимизация. С точки зрения производительности, проверка эта не стоит ровным счётом ничего. А вот забыть поменять конфигурацию с Debug на Release перед перекомпиляцией очень легко. Признайтесь честно, есть у вас система, которая проверяет, что сборки, публикуемые в продукционную среду, откомпилированы в конфигурации Release? Я вам помогу. Нет у вас такой системы. У меня такая система есть. Например, подготовка выкладки у меня отнимает 2 часа. Получаю предупреждение, сборки - в Debug. Я переделываю всё? Ну конечно же нет! – Zverev Evgeniy Aug 19 '16 at 17:23
  • WinForms-приложение делаю только для себя, соответственно они достаточно мелкие, а системы автосборки нет. Но если бы была, она бы сама собирала в нужной конфигурации и от меня бы это не зависело. msbuild может принимать ключ командной строки для сборки в нужном режиме - его и надо использовать на CI-сервере. – Qwertiy Aug 20 '16 at 08:01
  • @Qwertiy Зайду с другой стороны. Если вы хотите изменить поведение обработки ошибок именно в процессе отладки - правильно использовать System.Diagnostics.Debugger.IsAttached. Если вам надо внедрить альтернативную ветку кода для конфигурации Debug - используйте IF #DEBUG. Я в коде оставляю первый вариант, так как вы объяснили задачу так: "чтобы VS остановилась на исключении и я мог дебажить код в месте возникновения исключения". – Zverev Evgeniy Aug 22 '16 at 13:16