VS2010 не показывает необработанное сообщение об исключении в приложении WinForms в 64-разрядной версии Windows

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

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

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

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

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

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

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

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

Ответы

Ответ 1

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

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

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

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

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

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

Ответ 2

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

С введением 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

Как упоминает Ханс, скомпилируйте приложение и запустите exe без прикрепленного отладчика.

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

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

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

Ответ 4

Я использую 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...

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

Ответ 5

Простым Form_Shown может быть, если вы можете перенести код инициализации на другое событие, например Form_Shown которое 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!

}