Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi prevent application shutdown

I am trying to prevent my application from being shutdown by windows. The application is running on windows 8 and written in XE6. I tried following code but it seems to be completely ignored. To test it I simply send "end task" to it through the task manager. What I need is a way to let my application finish what its doing when the application is closed by the user, by the task manager of by a windows shutdown. Normal closing is not a problem, this is handled by the FormCloseQuery event. But the other 2 methods I can't get to work. Until windows XP this was easy by catching the wm_endsession and the wm_queryendsession, starting from vista you need the use ShutDownBlockReasonCreate, which returns true but does not seems to work anyway.

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;

Update

Changing the message result to true and removing the sleep changes nothing.

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;
like image 645
GuidoG Avatar asked Aug 27 '14 20:08

GuidoG


People also ask

How to keep data when app shuts down?

If possible it's better to save data in other place and next time you app is running again ask the user if he want to keep this data. Or even better, if you can, restore the app state exactly how it was at shutdown and you don't need to ask anything.

How to ask a user to keep data after shutdown?

"Applications should respect the user's intentions and return TRUE " If possible it's better to save data in other place and next time you app is running again ask the user if he want to keep this data. Or even better, if you can, restore the app state exactly how it was at shutdown and you don't need to ask anything.

Is it possible to block shutdown after saving data?

What you need to be able to do is block shutdown until you have finished saving any data. The way that is handled changed in Vista. The documentation you need starts here:


1 Answers

According to the documentation to block shutdown you need to return FALSE in response to WM_QUERYENDSESSION.

What's more, you must not do work in this message handler. The work must happen elsewhere. If you don't respond to this message in a timely fashion the system won't wait for you.

  • Call ShutdownBlockReasonCreate before you start working.
  • Whilst working return FALSE from WM_QUERYENDSESSION. Don't work whilst handling this message. Return immediately.
  • When the work is done call ShutdownBlockReasonDestroy.

The handler for WM_QUERYENDSESSION can look like this:

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

And then the code that performs the work needs to call ShutdownBlockReasonCreate before the work starts, ShutdownBlockReasonDestroy when the work ends, and make sure that the Working property used above evaluates to True during work.

If your work blocks the main thread then you are in trouble. The main thread must be responsive, otherwise the system won't wait for you. Putting the work in a thread is often the way forward. If your main window is not visible then you don't get a chance to block shutdown. The details are explained here: http://msdn.microsoft.com/en-us/library/ms700677.aspx

If you get as far as being sent WM_ENDSESSION then it's too late. The system is going down come what may.

To test it I simply send "end task" to it through the task manager.

That has nothing to do with shutdown blocking. The way you test shutdown blocking is to logoff. If the user insists on killing your process there is little that you can do about it. Sertac's answer covers this in detail.

Finally, ignoring the return values of API calls is also very poor form. Don't do that.

like image 73
David Heffernan Avatar answered Sep 30 '22 15:09

David Heffernan