VS2010 не показывает необработанное сообщение об исключении в приложении WinForms в 64-разрядной версии Windows - PullRequest
76 голосов
/ 08 февраля 2011

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

1) создайте новое приложение Windows Forms (C #, .NET Framework 4, VS2010)

2) добавьте следующий код в Form1_Loadобработчик:

int vara = 5, varb = 0;
int varc = vara / varb;
int vard = 7;

Я ожидаю, что VS сломается и покажет необработанное сообщение об исключении во второй строке.Однако происходит то, что третья строка просто пропускается без каких-либо сообщений, и приложение продолжает работать.

У меня нет этой проблемы с моими существующими проектами на C #.Поэтому я предполагаю, что мои новые проекты создаются с некоторыми странными настройками по умолчанию.

Кто-нибудь имеет представление, что не так с моим проектом ???

Я пытался установить флажки в Debug-> Exceptions,Но тогда выполнение прерывается, даже если я обработаю исключение в блоке try-catch;что тоже не то, что я хочу.Если я правильно помню, в этом диалоговом окне был столбец с именем «необработанные исключения» или что-то в этом роде, что делало бы именно то, что я хочу.Но в моих проектах есть только один столбец («Брошенный»).

Ответы [ 5 ]

121 голосов
/ 08 февраля 2011

Это неприятная проблема, вызванная уровнем эмуляции wow64, который позволяет запускать 32-разрядный код в 64-разрядной версии Windows 7. Он проглатывает исключения в коде, который запускается в ответ на уведомление, генерируемое 64-оконный менеджер битов, как событие Load.Не позволяя отладчику увидеть его и вмешаться. Эту проблему трудно решить, группы Windows и DevDiv в Microsoft указывают пальцем вперед и назад.DevDiv ничего не может с этим поделать, Windows думает, что это правильное и задокументированное поведение, как бы странно это ни звучало.

Это, безусловно, задокументировано , но никто не понимает последствий и не думает об этомразумное поведение.Особенно, когда оконная процедура скрыта от глаз, конечно, как в любом проекте, который использует классы-обертки, чтобы скрыть оконную сантехнику.Как и в любом приложении Winforms, WPF или MFC.Основная проблема заключается в том, что Microsoft не смогла выяснить, как передать исключения из 32-разрядного кода обратно в 64-разрядный код, который вызвал уведомление, обратно в 32-разрядный код, который пытается обработать или отладить исключение.

Этопроблема только с подключенным отладчиком, ваш код будет бомбить как обычно без такового.

Проект> Свойства> вкладка «Сборка»> Цель платформы = AnyCPU и снимите флажок Предпочитать 32-разрядный.Ваше приложение теперь будет работать как 64-битный процесс, исключая режим сбоя wow64.Некоторые последствия: он отключает Edit + Continue для версий VS до VS2013 и может не всегда быть возможным, если у вас есть зависимость от 32-битного кода.

Другие возможные обходные пути:

  • Отладка> Исключения> установите флажок «Брошено» для исключений CLR, чтобы заставить отладчик останавливаться на строке кода, которая вызывает исключение.
  • Записать try / catch в обработчике события Load и выполнить аварийный вызов в блоке catch.
  • Используйте Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException) в методе Main(), чтобы ловушка исключений в цикле сообщений не отключалась в режиме отладки.Однако это затрудняет отладку всех необработанных исключений, событие ThreadException довольно бесполезно.
  • Подумайте, действительно ли ваш код принадлежит обработчику события Load.Это очень редко нужно, однако оно очень популярно в VB.NET и в лебединой песне, потому что это событие по умолчанию, и двойной щелчок тривиально добавляет обработчик события.Вам действительно нужно Load только когда вы заинтересованы в фактическом размере окна после применения пользовательских настроек и автомасштабирования.Все остальное принадлежит конструктору.
  • Обновление до Windows 8 или более поздней версии, они решают эту проблему wow64.
10 голосов
/ 17 августа 2012

По своему опыту, я вижу эту проблему, только когда я работаю с подключенным отладчиком.Приложение работает так же, когда работает автономно: исключение не проглатывается.

С введением KB976038 вы можете сделать эту работу, как вы ожидаете снова.Я никогда не устанавливал исправление, поэтому я предполагаю, что оно входит в состав Win7 SP1.

Это было упомянуто в этом сообщении:

Вот некоторый код, который включает исправление:

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()
    {
        Kernel32.DisableUMCallbackFilter();

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

Я подтвердил (с помощью простого примера, показанного ниже), что это работает, как и следовало ожидать.

protected override void OnLoad(EventArgs e) {
    throw new Exception("BOOM");   // This will now get caught.
}

Итак, я не понимаю, чтопочему ранее отладчику было невозможно обрабатывать пересечение стековых кадров режима ядра, но с помощью этого исправления они как-то поняли это.

3 голосов
/ 26 марта 2015

Как упоминает Ганс, скомпилируйте приложение и запустите исполняемый файл без отладчика.

Для меня проблема заключалась в изменении имени свойства Class, с которым был связан элемент управления BindingSource.При запуске без IDE я смог увидеть ошибку:

Невозможно привязать свойство или столбец SendWithoutProofReading в источнике данных.Имя параметра: dataMember

Исправление элемента управления BindingSource для привязки к обновленному имени свойства решило проблему: enter image description here

1 голос
/ 28 февраля 2013

Я использую WPF и столкнулся с этой же проблемой.Я уже попробовал предложения Hans 1-3, но они мне не понравились, потому что студия не остановилась на том, где была ошибка (поэтому я не мог просмотреть свои переменные и увидеть, в чем проблема).

Итак, я попробовал 4-е предложение Ганса.Я был удивлен тем, сколько моего кода может быть перемещено в конструктор MainWindow без каких-либо проблем.Не уверен, почему я привык вкладывать так много логики в событие Load, но, видимо, многое из этого можно сделать в ctor.

Однако эта проблема была такой же, как 1-3.Ошибки, возникающие во время ctor для WPF, включаются в общее исключение Xaml.(внутреннее исключение имеет настоящую ошибку, но опять же я хотел, чтобы студия просто сломалась на месте фактической проблемы).

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

    void Window_Loaded(object sender, RoutedEventArgs e)
    {
        new Thread(() =>
        {
            Thread.Sleep(50);
            CrossThread(() => { OnWindowLoaded(); });
        }).Start();
    }
    void CrossThread(Action a)
    {
        this.Dispatcher.BeginInvoke(a);
    }
    void OnWindowLoaded()
    {
        ...do my thing...

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

0 голосов
/ 07 января 2018

Простой обходной путь может быть, если вы можете переместить свой код инициализации в другое событие, например Form_Shown, которое вызывается позже Form_Load, и использовать флаг для запуска кода запуска в первой показанной форме:

bool firstLoad = true; //flag to detect first form_shown

private void Form1_Load(object sender, EventArgs e)
{
    //firstLoad = true;
    //dowork(); //not execute initialization code here (postpone it to form_shown)
}

private void Form1_Shown(object sender, EventArgs e)
{
    if (firstLoad) //simulate Form-Load
    {
        firstLoad = false;

        dowork();
    }
}

void dowork()
{
    var f = File.OpenRead(@"D:\NoSuchFile756.123"); //this cause an exception!

}
...