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 для привязки к обновленному имени свойства устраняет проблему:
Ответ 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!
}