Windows 關機/登出/重啟時,會向所有視窗發送 WM_QUERYENDSESSION 訊息詢問「是否允許結束工作階段」。應用程式回傳 0 (False) 即可取消關機,回傳 1 (True) 表示同意。本文示範在 Delphi 中攔截此訊息實作「防關機」功能,適用於關鍵監控程式、不可中斷的資料處理任務等場景。
type
TForm1 = class(TForm)
private
procedure WMQueryEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;
public
{ Public declarations }
end;
procedure TForm1.WMQueryEndSession(var Msg: TMessage);
begin
// 回傳 0 (False) 告訴系統:不同意結束工作階段 → 取消關機/登出
Msg.Result := 0;
// 可選:顯示提示、記錄日誌、詢問使用者確認
// if MessageDlg('確定要取消關機嗎?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
// Msg.Result := 1
// else
// Msg.Result := 0;
end;
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
procedure WMQueryEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.WMQueryEndSession(var Msg: TMessage);
begin
// 直接阻止關機
Msg.Result := 0;
end;
end.
type
TForm1 = class(TForm)
private
FCriticalTaskRunning: Boolean;
procedure WMQueryEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;
public
property CriticalTaskRunning: Boolean read FCriticalTaskRunning write FCriticalTaskRunning;
end;
procedure TForm1.WMQueryEndSession(var Msg: TMessage);
begin
if FCriticalTaskRunning then
begin
// 關鍵任務執行中,阻止關機並提示
Msg.Result := 0;
// 可選:閃爍視窗、播放聲音、顯示非模態提示
FlashWindow(Handle, True);
// 記錄日誌
Log('關機被攔截: 關鍵任務執行中');
end
else
begin
// 無關鍵任務,允許關機
Msg.Result := 1;
end;
end;
| 訊息 | 階段 | 可否取消 | 用途 |
|---|---|---|---|
WM_QUERYENDSESSION | 詢問階段 | ✅ 可取消 (回傳 0) | 應用程式決定是否允許關機 |
WM_ENDSESSION | 通知階段 | ❌ 不可取消 | 系統已決定關機,執行清理儲存 |
WM_CLOSE | 視窗關閉 | ✅ 可取消 | 使用者點擊視窗關閉按鈕 |
type
TForm1 = class(TForm)
private
procedure WMEndSession(var Msg: TMessage); message WM_ENDSESSION;
end;
procedure TForm1.WMEndSession(var Msg: TMessage);
begin
if Msg.WParam = 1 then // TRUE: 系統確實要關機
begin
// 最後機會:儲存設定、關閉資料庫、釋放硬體資源
SaveSettings;
CloseDatabase;
ReleaseHardware;
end;
inherited;
end;
uses ShellApi;
ShutdownBlockReasonCreate(Handle, '正在處理關鍵資料,請稍候...');
// 任務完成後
ShutdownBlockReasonDestroy(Handle);
WM_QUERYENDSESSION 是 Delphi 程式參與 Windows 關機協商的標準介面。回傳 0 即可攔截關機,但這是一把雙面刃。現代應用應優先考慮 Windows Service 架構處理長期任務,或使用 ShutdownBlockReasonCreate 提供使用者可見的阻止原因。GUI 程式阻止關機應作為最後手段,且必須嚴格限制啟用條件與時間。