Предотвращение закрытия приложения WinForm не работает - PullRequest
0 голосов
/ 30 октября 2019

Я использую следующий код на c #, чтобы временно заблокировать завершение работы приложения WinForm безуспешно, и я вижу, что система вообще не выключается, возможно, потому что работа, которую я должен выполнить при получении завершения работыуведомление делается в потоке пользовательского интерфейса. Windows не завершает работу приложения, если приложение не отвечает через 30 секунд, как задокументировано. См. Прикрепленное изображение. введите описание изображения здесь

public Form1()
{
    InitializeComponent();

    // Define the priority of the application (0x3FF = The higher priority)
    SetProcessShutdownParameters(0x3FF, SHUTDOWN_NORETRY);
}

[DllImport("user32.dll")]
public extern static bool ShutdownBlockReasonCreate(IntPtr hWnd, [MarshalAs(UnmanagedType.LPWStr)] string pwszReason);

[DllImport("user32.dll")]
public extern static bool ShutdownBlockReasonDestroy(IntPtr hWnd);

[DllImport("kernel32.dll")]
static extern bool SetProcessShutdownParameters(uint dwLevel, uint dwFlags);

private static int WM_QUERYENDSESSION = 0x11;
private static int WM_ENDSESSION = 0x16;
public const uint SHUTDOWN_NORETRY = 0x00000001;

private ManualResetEvent rEvent = new ManualResetEvent(false);
private bool blocked = false;
protected override void WndProc(ref System.Windows.Forms.Message m)
{
    if (m.Msg == WM_QUERYENDSESSION)
    {
        if (!blocked)
        {
            blocked = true;
            ShutdownBlockReasonCreate(this.Handle, "Closing in progress");

            this.BeginInvoke((Action)(() =>
            {
                // My clean-up work on UI thread
                Thread.Sleep(600000);

                // Allow Windows to shutdown
                ShutdownBlockReasonDestroy(this.Handle);
            }));

            m.Result = IntPtr.Zero;
        }
        else
        {
            m.Result = (IntPtr)1;
        }
    }

    if (m.Msg == WM_ENDSESSION)
    {
        if (blocked)
        {
            ShutdownBlockReasonDestroy(this.Handle);
            m.Result = (IntPtr)1;
        }
    }

    // If this is WM_QUERYENDSESSION, the closing event should be  
    // raised in the base WndProc.  
    base.WndProc(ref m);

} //WndProc

1 Ответ

0 голосов
/ 04 ноября 2019

Примечание : не проверять эту функциональность из среды IDE Visual Studio:
Создать исполняемый файл и запустить из него приложение.

Переключить private bool AllowEndSession true / false для отключения / включения блока перезагрузки системы.

Он имитирует загруженное приложение, которое все еще должно завершить свою работу при получении сообщения WM_QUERYENDSESSION. Конечно, ваше приложение должно быть в состоянии ответить на это сообщение: поток пользовательского интерфейса должен быть отзывчивым (т. Е. Приложение выполняет работу с потоком, отличным от потока пользовательского интерфейса).

Кроме того, вы должны оценить lParam, поскольку это может быть ENDSESSION_CRITICAL (сама система может быть вынуждена отключиться - нехватка питания и UPCработа на парах, как возможный крайний случай. Критическая ошибка системной службы как более общая причина).

Если приложение не занято, но требует выполнения операций очистки или других задач, которые могут занять больше времени, оно должно вернуть FALSE (IntPtr.Zero), когда WM_QUERYENDSESSION получено и инициировать процедуру, которая является причиной запроса на задержку при получении WM_ENDSESSION

Когда приложение возвращает TRUEдля этого сообщения он получает сообщение WM_ENDSESSION независимо от того, как другие приложения отвечают на сообщение WM_QUERYENDSESSION. Каждое приложение должно вернуть TRUE или FALSE сразу же после получения этого сообщения и отложить любые операции очистки до получения сообщения WM_ENDSESSION.

В качестве примечания, запросблок следует использовать только в том случае, если пользовательские данные могут быть скомпрометированы по определенным причинам или если какое-либо оборудование выполняет операцию (как запись на CD / DVD). Другие процедуры / задачи, выполняемые приложением, должны быть выполнены своевременно.


Когда AllowEndSession = false; и Приложение получает сообщение WM_QUERYENDSESSION, System.Windows.Forms.Timer запускается с тайм-аутом в 10 секунд (имитация занятого, но отзывчивого приложения потребует такого количества времени для завершения критической работы).

Система представит Пользователю классический экран блокировки перезапуска, сообщая, что приложение запросило задержку процесса перезапуска, показывая строку причины блокировки, предоставленную приложением (Cleaning Up/Doing stuff... Wait a sec передано ShutdownBlockReasonCreate, здесь).

Когда таймер истекает (тикает), работа завершается, и приложение закрывает свою основную форму, вызывает Application.Exit или что-либо еще, а также вызывает ShutdownBlockReasonDestroy().

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

private bool AllowEndSession = false;
private bool ShutbownBlockReasonCreated = false;
private System.Windows.Forms.Timer shutDownTimer = null;

protected override void WndProc(ref Message m)
{
    switch (m.Msg)
    {
        case WM_QUERYENDSESSION:
            if (!AllowEndSession) {
                bool result = ShutdownBlockReasonCreate(this.Handle, "Cleaning Up/Doing stuff... Wait a sec");

                shutDownTimer = new System.Windows.Forms.Timer();
                shutDownTimer.Tick += (s, evt) => {
                    ShutbownBlockReasonCreated = false;
                    ShutdownBlockReasonDestroy(this.Handle);
                    shutDownTimer.Enabled = false;
                    shutDownTimer.Dispose();
                    this.Close();
                };
                shutDownTimer.Interval = 10000;
                shutDownTimer.Enabled = true;

                ShutbownBlockReasonCreated = true;
                m.Result = IntPtr.Zero;
            }
            else {
                m.Result = (IntPtr)1;
            }
            break;
        case WM_ENDSESSION:
            if (ShutbownBlockReasonCreated) {
                ShutdownBlockReasonDestroy(this.Handle);
            }
            m.Result = (IntPtr)1;
            break;
        default:
            base.WndProc(ref m);
            break;
    }
}
...