Ошибка в FormClosingEventArgs.CloseReason? - PullRequest
6 голосов
/ 03 сентября 2010

Требования, с которыми я сталкиваюсь

Около 12 человек используют это приложение, но мы только хотим разрешить 4 закрывать приложение традиционными методами (Alt + F4, Файл> Выход, Закрыть)

Если используется какой-либо другой метод (TaskManager, WindowsShutdown) или один из разрешенных пользователей закрывает приложение, нам необходимо выполнить некоторую очистку (закрытие некоторых каналов подключения)

Код I 'Мы использовали для удовлетворения указанных требований

private void formClosing(object sender, FormClosingEventArgs e)
{
    // If a user is allowed to close the application, an empty file (filename)
    // will be in the root directory of the application.
    if(e.CloseReason == CloseReason.UserClosing && !File.Exists("filename"))
    {
        e.Cancel = true;
        return;
    }

    // Cleanup
}

Проблема

Если пользователь (не разрешено закрывать) пытается закрыть приложение с помощью традиционных методов, то пытается закрыть с помощью диспетчера задач CloseReason Перечисление enum не сбрасывается само по себе, в результате чего диспетчер задач выдает запрос на принудительное закрытие, предотвращая очистку приложения.

Вопрос

Этоошибка, или я что-то упустил, то, что сбрасывает CloseReason после отмены события FormClosing .

Ответы [ 2 ]

6 голосов
/ 03 сентября 2010

.NET Reflector - ваш друг, когда узнаете, как работает WinForms.

Класс Form имеет внутреннее поле с именем closeReason , и оно используется при создании параметра события, который вы проверяете в событии Closing . Это внутреннее поле установлено в четырех разных местах, которые я могу найти. Это ...

1, Метод Form.Close () устанавливает значение closeReason = UserClosing.

Это имеет смысл, поскольку выполнение ручного вызова метода Form.Close () обычно является результатом некоторых действий пользователя, таких как опция меню File-> Exit , являющаяся выбранный пользователем. Очевидно, это действие пользователя.

2, WM_SYSCOMMAND (SC_CLOSE) устанавливает closeReason = UserClosing.

WndProc формы обрабатывает системную команду SC_CLOSE , устанавливая closeReason в UserClosing и позволяет стандартному окну proc выполнить и закрыть приложение. Это имеет смысл, поскольку эта SC_CLOSE отправляется, когда пользователь нажимает кнопку закрытия окна Chrome или выбирает параметр закрытия из щелчка правой кнопкой мыши на строке заголовка. Оба являются действиями пользователя, поэтому установка closeReason на UserClosing выглядит правильно.

3, WndProc обрабатывает сообщение WM_CLOSE (0x10) с closeReadon = TaskManagerClosing

WM_CLOSE отправляется диспетчером задач и другими приложениями для закрытия окна, и если closeReason в настоящее время равно Нет , он обновляет его до TaskManagerClosing, Обратите внимание, что эта проблема обновляется, только если она Нет , так как я думаю, что это проблема для вас.

4, WndProc обрабатывает сообщения 0x11 и 0x16 с closeReason = WindowsShutDown

Это не очень интересно, так как вы не заботитесь об этом сценарии, но это просто стандартная обработка завершенных сообщений.

Итак, основная проблема, с которой вы столкнулись, заключается в том, что closeReason ни при каких условиях не сбрасывается обратно на Нет при отмене события Closing . Поэтому пункт 3 выше никогда не будет корректно обновлять значение до TaskManagerClosing , если это произойдет после вашей отмены. Поскольку closeReasson является внутренним полем, вы не можете обновить его напрямую. Но вы можете обмануть, и это подход, который я использовал сам в прошлом. Вам нужно использовать отражение, чтобы получить доступ к внутреннему полю, а затем сбросить его на Нет , когда вы установите Cancel = true в обработчике событий.

Я не тестировал этот код, но вам нужно что-то вроде ...

PropertyInfo pi = typeof(Form).GetProperty("CloseReason",
                                           BindingFlags.Instance |
                                           BindingFlags.SetProperty |
                                           BindingFlags.NonPublic);

pi.SetValue(this, CloseReason.None, null);
0 голосов
/ 06 сентября 2010

Я думаю, что вы не можете предотвратить закрытие вашего процесса, если он инициирован диспетчером задач (то есть ОС ... он «большой босс», нет смысла отрицать это, например, закрытие программы).).

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

Кроме того, если пользователь нажимает в TaskManager «перейти к процессу» в списке приложений, и оттуда процесс заканчивается, я не думаюВы будете получать любое событие вообще ...

Возможно, было бы лучше, если бы у вас была служба Windows, которая работает за кулисами и отслеживает, что экземпляр работает.Таким образом, пользователи, вероятно, не будут знать, что такой процесс существует, поскольку это не их приложение, и вы можете использовать его, чтобы отслеживать завершение работы приложения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...