我试图阻止我的应用程序被关闭的窗口。该应用程序是运行在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;
3条答案
按热度按时间yptwkmov1#
按documentation要阻塞关机则需要返回
FALSE
以响应WM_QUERYENDSESSION
。此外,您不能在此消息处理程序中进行工作。工作必须在其他地方进行。如果您不及时回复此消息,系统将不会等待您。
ShutdownBlockReasonCreate
。WM_QUERYENDSESSION
返回FALSE
。处理此消息时不要工作。立即返回。ShutdownBlockReasonDestroy
。WM_QUERYENDSESSION
的行程常式可能如下所示:然后,执行工作的代码需要在工作开始前调用
ShutdownBlockReasonCreate
,在工作结束时调用ShutdownBlockReasonDestroy
,并确保上面使用的Working
属性在工作期间计算为True
。如果你的工作阻塞了主线程,那么你就有麻烦了。主线程必须有响应,否则系统不会等待你。把工作放在线程中通常是前进的方向。如果你的主窗口不可见,那么你就没有机会阻塞关机。细节解释如下:http://msdn.microsoft.com/en-us/library/ms700677.aspx
如果你收到了
WM_ENDSESSION
,那就太晚了。系统会崩溃的。为了测试它,我简单地通过任务管理器向它发送“结束任务”。
这与关闭阻塞无关。测试关闭阻塞的方法是注销。如果用户坚持要杀死你的进程,你几乎无能为力。Sertac的答案详细介绍了这一点。
最后,忽略API调用的返回值也是非常糟糕的。不要这样做。
yh2wf1be2#
下面是在 Delphi 11.1 Alexandria中测试的一些不同案例的一些解决方案:
当用户或系统关闭应用程序时,将调用表单OnCloseQuery,以了解触发了这两个事件中的哪一个,请调用GetSystemMetrics并将SM_SHUTTINGDOWN作为参数传递。
当存在暂挂的系统关闭时,设置系统度量SM_SHUTTINGDOWN,否则清除。
如果您只想在系统关闭时向用户显示suppress exit confirmation message,则只需执行以下操作即可:
请注意,如果系统正在关闭,则将调用窗体OnCloseQuery,但不会调用窗体OnClose,您放在OnClose中的任何代码都不会执行。因此,如果希望代码在系统关闭时执行,请不要将代码放在OnClose中,而是使用下面描述的WM_EndSession处理程序。
如果您需要更多的功能并能够阻止关闭,请首先为消息WM_QueryEndSession编写一个处理程序:
在此处理程序中,除了返回消息结果外,不执行任何操作,此消息将发送到您的应用程序以检查其是否同意关闭系统,但这并不意味着系统正在关闭,因为所有应用程序必须首先同意此消息,但如果只有一个应用程序拒绝此消息(返回False),则不会发生关机(当确实发生关机时,您将收到WM_EndSession消息)。
在WM_QueryEndSession处理程序中,检查是否存在任何必须一次性完成的关键运行任务,或者如果中断将导致数据丢失,如果存在正在运行的关键任务,则返回False以拒绝系统关闭,例如:
不要返回False,如果你的任务不是关键的,可以中断,不要中断你的任务在这一点上,只是返回True,并保持你的任务运行,因为一些其他的应用程序可能会拒绝关机,你只是白白中断了你的任务!,中断你的任务只有当你收到WM_EndSession消息,这意味着所有的应用程序同意关机,系统真的关机。
通过返回False,关闭现在被拒绝,此时使用ShutdownBlockReasonCreat是多余的,但您可以使用它向用户解释(在关闭屏幕中)您的应用阻止关闭的原因。如果您使用此选项,请确保在任务完成后调用ShutdownBlockReasonDestroy。
当收到WM_EndSession时,您现在知道系统确实正在关闭。如果您需要执行清理、中止任何正在运行的任务、保存更改关闭DB/文件...等等,那么您可以使用ShutdownBlockReasonCreate来阻止关闭,直到您完成清理,然后在完成清理后解除阻止关闭,例如:
其他一些关闭块方法建议您在每次启动任务时创建一个关闭块,并在任务完成后销毁关闭块,即使系统没有关闭!我在这里的方法是只在必要时创建关闭块,并且只在系统真正关闭时创建。
我希望它对某人有用!