VS2010 не відображає необроблене повідомлення про виключення у програмі WinForms у 64-розрядної версії Windows


77

Коли я створюю новий проект, я отримую дивну поведінку за необробленими винятками. Ось як я можу відтворити проблему:

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 #. Тож я здогадуюсь, що мої нові проекти створюються з якимись дивними налаштуваннями за замовчуванням.

Хтось уявляє, що не так з моїм проектом ???

Я спробував встановити прапорці у Налагодженні-> Винятки. Але тоді виконання виконується, навіть якщо я обробляю виняток у try-catchблоці; що теж не те, що я хочу. Якщо я добре пам’ятаю, у цьому діалоговому вікні був стовпець під назвою «не обробляються винятки» або щось подібне, яке могло б робити те, що я хочу. Але в моїх проектах є лише одна колонка ("Кинуто").


та сама проблема тут! Навантаження на форму вже вловлює очікування внутрішньо.
Pedro77

Відповіді:


124

Це неприємна проблема, спричинена шаром емуляції wow64, який дозволяє запускати 32-розрядний код у 64-розрядної версії Windows 7. Він ковтає винятки в коді, який запускається у відповідь на сповіщення, створене 64-розрядним диспетчером вікон. , як Loadподія. Запобігання відладчику бачити його і втручатися. Цю проблему важко виправити, групи Windows і DevDiv у Microsoft вказують пальцями вперед і назад. DevDiv з цим нічого не може зробити, Windows вважає, що це правильна та задокументована поведінка, як би загадково це не звучало.

Це, звичайно, задокументовано, але майже ніхто не розуміє наслідків або не вважає, що це розумна поведінка. Особливо не тоді, коли віконна процедура, звичайно, прихована від очей, як у будь-якому проекті, який використовує класи обгортки, щоб приховати сантехніку вікна. Як і будь-яка програма Winforms, WPF або MFC. Основна проблема полягає в тому, що корпорація Майкрософт не змогла зрозуміти, як повернути винятки з 32-розрядного коду назад у 64-розрядний код, який спричинив сповіщення, до 32-розрядного коду, який намагається обробити або налагодити виняток.

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

Проект> Властивості> Вкладка «Збірка»> Ціль платформи = AnyCPU та зніміть позначку «Віддати перевагу 32-розрядному». Тепер ваш додаток буде працювати як 64-розрядний процес, усуваючи режим відмови wow64. Деякі наслідки відключають редагування + продовження для версій VS до VS2013, і це може бути не завжди можливим, якщо у вас є залежність від 32-розрядного коду.

Інші можливі обхідні шляхи:

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

6
Так, для цього існує стаття Connect. Їх багато. Цей, мабуть, найкращий: connect.microsoft.com/VisualStudio/feedback/details/357311/…
Ганс Пасант,

5
І ось такий, який наводить на думку, що вони погано
уявляють,

1
Я перевірив, що ця сама помилка існує у програмі Winforms, у події OnFormClosed. І я не можу це вирішити, змінивши цільову програму на x86. Але принаймні я маю пояснення проблеми.
ferpega

1
Здається, є ще одне обхідне рішення Application.ThreadException. Налаштування, здається, допомагає - винятки запускаються в IDE та налагоджувачем.
tanascius

7
Windows 8 робити є ця проблема. Просто FYI.
ThunderGr

10

З мого досвіду, я бачу цю проблему лише тоді, коли я працюю з прикріпленим налагоджувачем. Додаток поводиться однаково, коли працює автономно: виняток не проковтується.

З введенням 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.
}

Отже, те, що я не розумію, - чому раніше налагоджувачу було неможливо обробляти перетинання фреймів стека в режимі ядра, але за допомогою цього виправлення вони якось це зрозуміли.


Set/GetProcessUserModeExceptionPolicyвсе ще не задокументовані на MSDN, і Kernel32.dll з Windows 8 не експортує їх.
Мартін

Чи немає способу зробити те саме, відредагувавши реєстр чи щось інше?
ThunderGr

1
Я їх читав. Я також прочитав коментар про те, що ядро ​​Windows 8 не експортує ці методи. Я випадково працюю на Windows 8. Я просто запитував, про всяк випадок.
ThunderGr

1
Якщо ви застосуєте виправлення, на яке ви вказуєте, ви можете вирішити проблему, відредагувавши реєстр, як вони описують, замість використання методів. Я пам’ятаю, що раніше вирішував цю проблему на машині Win7. На жаль, ви не можете цього зробити в Win 8.
ThunderGr

1
@ThunderGr Дякуємо, що вказали на це. Думаю, я не повинен коментувати минулий час сну :-)
Джонатан Рейнхарт,

3

Як згадує Ганс, скомпілюйте програму та запустіть exe без налагоджувача.

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

Неможливо прив’язати до властивості або стовпця SendWithoutProofReading у DataSource. Ім'я параметра: dataMember

Виправлення елемента керування BindingSource для прив’язки до оновленого імені властивості вирішило проблему: введіть тут опис зображення


1

Я використовую WPF і зіткнувся з цією ж проблемою. Я вже пробував пропозиції Ганса 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

Просте обхідне рішення може бути, якщо ви можете перемістити свій код ініціалізації до іншої події, наприклад, 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!

}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.