VS2010 не показывает необработанное сообщение об исключении в приложении WinForms в 64-разрядной версии Windows
Когда я создаю новый проект, я получаю странное поведение для необработанных исключений. Вот как я могу воспроизвести проблему:
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 ответов
Это неприятная проблема, вызванная уровнем эмуляции 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, чтобы заставить отладчик остановиться на строке кода, которая вызывает исключение.
- Напишите попробовать / поймать в
Load
обработчик событий и аварийный вызов в блоке catch. - использование
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException)
вMain()
метод, чтобы ловушка исключений в цикле сообщений не отключалась в режиме отладки. Это, однако, делает все необработанные исключения трудными для отладки,ThreadException
Событие довольно бесполезно. - Подумайте, действительно ли ваш код принадлежит
Load
обработчик события. Это очень редко нужно, однако оно очень популярно в VB.NET и в лебединой песне, потому что это событие по умолчанию, и двойной щелчок тривиально добавляет обработчик события. Вы только когда-либо действительно нуждаетесьLoad
когда вас интересует фактический размер окна после применения пользовательских настроек и автоматического масштабирования. Все остальное принадлежит конструктору. - Обновление до Windows 8 или более поздней версии позволяет решить эту проблему с wow64.
По моему опыту, я вижу эту проблему, только когда я работаю с подключенным отладчиком. Приложение работает так же, когда работает автономно: исключение не проглатывается.
С введением 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.
}
Итак, я не понимаю, почему ранее отладчику было невозможно обрабатывать пересечение стековых кадров режима ядра, но с помощью этого исправления они как-то поняли это.
Как упоминает Ганс, скомпилируйте приложение и запустите исполняемый файл без отладчика.
Для меня проблема заключалась в изменении имени свойства Class, с которым был связан элемент управления BindingSource. При запуске без IDE я смог увидеть ошибку:
Невозможно привязать свойство или столбец SendWithoutProofReading в источнике данных. Имя параметра: dataMember
Исправление элемента управления BindingSource для привязки к обновленному имени свойства решило проблему:
Я использую 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...
Таким образом, студия сломалась бы там, где произошло необъяснимое исключение.
Простой обходной путь может быть, если вы можете переместить свой код инициализации в другое событие, например, как 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!
}