尝试关闭阻止原因 API。 ShutdownBlockReasonCreate
API 文档以 CD 刻录为例,但同样适用于您的“关键任务”流程。
应用程序应在开始执行不可中断的操作时调用此函数,例如刻录 CD 或 DVD。操作完成后,调用ShutdownBlockReasonDestroy函数表示可以关闭系统。
请注意,文档专门提到了用户关闭,但我不明白为什么它也不应该适用于更新重启。
注意:记得检查功能是否成功;并在进程完成时销毁 Shutdown Reason。
根据您的评论,您似乎需要使用 Windows API 例程的帮助。我建议您在适当的库中声明外部函数。 (但您可以毫无顾虑地在同一个单元中进行测试。)
function ShutdownBlockReasonCreate(hWnd: HWND; Reason: LPCWSTR): BOOL; stdcall; external user32;
function ShutdownBlockReasonDestroy(hWnd: HWND): BOOL; stdcall; external user32;
以下演示如何使用 API。注意:注意错误检查。我已经演示了如何获取错误信息。你用它做什么取决于你。
要指出的另一件重要的事情(在 cmets 中重复)是您不应该阻塞主线程。有关详细信息,请参阅这些更改首次在 Vista 中引入时的 Microsoft 文档here。
procedure TForm1.JobStartClick(Sender: TObject);
var
LErr: Cardinal;
begin
ListBox1.Items.Add('Attempting to block shutdown:');
if (not ShutdownBlockReasonCreate(Application.MainForm.Handle,
'Super Critical Job')) then
begin
LErr := GetLastError;
ListBox1.Items.Add('... failed: ' + SysErrorMessage(LErr));
//Probably not safe to start your job in this case, but perhaps you
//choose to give it a shot anyway.
Exit;
end;
ListBox1.Items.Add('... success');
FJobRunning := True;
//Start the job.
//However, NB do not run the job here.
//If it takes a long time and is not asynchronous, you should probably
//run your job on a separate thread. ***Do not block the main thread
// otherwise Windows will still kill your app for not responding***
end;
procedure TForm1.JobEndClick(Sender: TObject);
var
LErr: Cardinal;
begin
if (not FJobRunning) then Exit;
//End the job.
//Again, do not block the main thread, so perhaps this is rather something
//to do after you already know the job is done.
FJobRunning := False;
ListBox1.Items.Add('Allow shutdown');
if (not ShutdownBlockReasonDestroy(Application.MainForm.Handle)) then
begin
LErr := GetLastError;
ListBox1.Items.Add('... failed: ' + SysErrorMessage(LErr));
end;
end;
//Declare the handler for the WM_QUERYENDSESSION message as follows.
//procedure WMQueryEndSession(var AMsg : TWMQueryEndSession); message WM_QUERYENDSESSION;
procedure TForm1.WMQueryEndSession(var AMsg: TWMQueryEndSession);
begin
ListBox1.Items.Add('WMQueryEndSession');
if (FJobRunning) then
//NB: This is very important.
//You still need to confirm that your application wants to block
//shutdown whenever you receive this message.
AMsg.Result := 0
else
inherited;
end;