Delphi 防止应用程序关闭

ehxuflar  于 2022-11-04  发布在  其他
关注(0)|答案(3)|浏览(321)

我试图阻止我的应用程序被关闭的窗口。该应用程序是运行在windows 8和写在XE6。我尝试了以下代码,但它似乎完全被忽略。为了测试它,我只是发送“结束任务”通过任务管理器。我需要的是一种方法,让我的应用程序完成它所做的事情时,应用程序被关闭的用户,通过任务管理器或窗口关闭。正常关闭不是问题,这是由FormCloseQuery事件处理的。但其他2个方法我无法工作。在windows XP之前,这很容易通过捕获wm_endsession和wm_queryendsession来实现,从vista开始,您需要使用ShutDownBlockReasonCreate,该函数返回true,但似乎无论如何都不起作用。

procedure WMQueryEndSession(var Msg : TWMQueryEndSession); message WM_QUERYENDSESSION;
procedure WMEndSession(var Msg: TWMEndSession); message WM_ENDSESSION;

function ShutdownBlockReasonCreate(hWnd: HWND; Reason: LPCWSTR): Bool; stdcall; external user32;
function ShutdownBlockReasonDestroy(hWnd: HWND): Bool; stdcall; external user32;

procedure TForm1.WMEndSession(var Msg: TWMEndSession);
begin
  inherited;

  Msg.Result := lresult(False);
  ShutdownBlockReasonCreate(Handle, 'please wait while muting...');
  Sleep(45000); // do your work here
  ShutdownBlockReasonDestroy(Handle);
end;

procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  inherited;
  Msg.Result := lresult(False);
  ShutdownBlockReasonCreate(Handle, 'please wait while muting...');
  Sleep(45000); // do your work here
  ShutdownBlockReasonDestroy(Handle);
end;

更新

将消息结果更改为true并删除休眠不会更改任何内容。

procedure TForm1.WMEndSession(var Msg: TWMEndSession);
begin
  inherited;
  Msg.Result := lresult(True);
  ShutdownBlockReasonDestroy(Application.MainForm.Handle);
  ShutdownBlockReasonCreate(Application.MainForm.Handle, 'please wait while muting...');
end;

procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  inherited;
  Msg.Result := lresult(True);
  ShutdownBlockReasonDestroy(Application.MainForm.Handle);
  ShutdownBlockReasonCreate(Application.MainForm.Handle, 'please wait while muting...');
end;
yptwkmov

yptwkmov1#

documentation要阻塞关机则需要返回FALSE以响应WM_QUERYENDSESSION
此外,您不能在此消息处理程序中进行工作。工作必须在其他地方进行。如果您不及时回复此消息,系统将不会等待您。

  • 开始工作前请致电ShutdownBlockReasonCreate
  • 工作时从WM_QUERYENDSESSION返回FALSE。处理此消息时不要工作。立即返回。
  • 当工作完成时,调用ShutdownBlockReasonDestroy

WM_QUERYENDSESSION的行程常式可能如下所示:

procedure TMainForm.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  if Working then
    Msg.Result := 0
  else
    inherited;
end;

然后,执行工作的代码需要在工作开始前调用ShutdownBlockReasonCreate,在工作结束时调用ShutdownBlockReasonDestroy,并确保上面使用的Working属性在工作期间计算为True
如果你的工作阻塞了主线程,那么你就有麻烦了。主线程必须有响应,否则系统不会等待你。把工作放在线程中通常是前进的方向。如果你的主窗口不可见,那么你就没有机会阻塞关机。细节解释如下:http://msdn.microsoft.com/en-us/library/ms700677.aspx
如果你收到了WM_ENDSESSION,那就太晚了。系统会崩溃的。
为了测试它,我简单地通过任务管理器向它发送“结束任务”。
这与关闭阻塞无关。测试关闭阻塞的方法是注销。如果用户坚持要杀死你的进程,你几乎无能为力。Sertac的答案详细介绍了这一点。
最后,忽略API调用的返回值也是非常糟糕的。不要这样做。

yh2wf1be

yh2wf1be2#

下面是在 Delphi 11.1 Alexandria中测试的一些不同案例的一些解决方案:
当用户或系统关闭应用程序时,将调用表单OnCloseQuery,以了解触发了这两个事件中的哪一个,请调用GetSystemMetrics并将SM_SHUTTINGDOWN作为参数传递。
当存在暂挂的系统关闭时,设置系统度量SM_SHUTTINGDOWN,否则清除。
如果您只想在系统关闭时向用户显示suppress exit confirmation message,则只需执行以下操作即可:

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if GetSystemMetrics (SM_SHUTTINGDOWN) > 0 then
    Exit;  // app is being closed by a system shutdown

   // app being closed by user, ask user to confirm exit
  if not ConfirmAppExit then
    CanClose := False;
end;

请注意,如果系统正在关闭,则将调用窗体OnCloseQuery,但不会调用窗体OnClose,您放在OnClose中的任何代码都不会执行。因此,如果希望代码在系统关闭时执行,请不要将代码放在OnClose中,而是使用下面描述的WM_EndSession处理程序。
如果您需要更多的功能并能够阻止关闭,请首先为消息WM_QueryEndSession编写一个处理程序:

procedure WMQueryEndSession(var Msg: TWMQueryEndSession); message WM_QueryEndSession;

在此处理程序中,除了返回消息结果外,不执行任何操作,此消息将发送到您的应用程序以检查其是否同意关闭系统,但这并不意味着系统正在关闭,因为所有应用程序必须首先同意此消息,但如果只有一个应用程序拒绝此消息(返回False),则不会发生关机(当确实发生关机时,您将收到WM_EndSession消息)。
在WM_QueryEndSession处理程序中,检查是否存在任何必须一次性完成的关键运行任务,或者如果中断将导致数据丢失,如果存在正在运行的关键任务,则返回False以拒绝系统关闭,例如:

procedure TForm1.WMQueryEndSession(var Msg: TWMQueryEndSession);
begin
  if CriticalTaskRunning then
    Msg.Result := 0              // Deny system shutdown
  else
    inherited;                       // Agree to system shutdown, Same as Msg.Result := 1
end;

不要返回False,如果你的任务不是关键的,可以中断,不要中断你的任务在这一点上,只是返回True,并保持你的任务运行,因为一些其他的应用程序可能会拒绝关机,你只是白白中断了你的任务!,中断你的任务只有当你收到WM_EndSession消息,这意味着所有的应用程序同意关机,系统真的关机。
通过返回False,关闭现在被拒绝,此时使用ShutdownBlockReasonCreat是多余的,但您可以使用它向用户解释(在关闭屏幕中)您的应用阻止关闭的原因。如果您使用此选项,请确保在任务完成后调用ShutdownBlockReasonDestroy。
当收到WM_EndSession时,您现在知道系统确实正在关闭。如果您需要执行清理、中止任何正在运行的任务、保存更改关闭DB/文件...等等,那么您可以使用ShutdownBlockReasonCreate来阻止关闭,直到您完成清理,然后在完成清理后解除阻止关闭,例如:

procedure WMEndSession (var Msg: TWMEndSession);
begin
  if CleanUpRequired then
    begin
      ShutdownBlockReasonCreate (Handle, 'My app is preparing to close, just a sec...');
      try
        DoCleanUp;
      finally
        ShutdownBlockReasonDestroy (Handle);
      end;
    end;
end;

其他一些关闭块方法建议您在每次启动任务时创建一个关闭块,并在任务完成后销毁关闭块,即使系统没有关闭!我在这里的方法是只在必要时创建关闭块,并且只在系统真正关闭时创建。
我希望它对某人有用!

相关问题