tags:

views:

493

answers:

3

In Delphi 7, I have a TMainForm.FormClose procedure that is intended to write out some status whenever the program exits. This works fine when manually closing the program. However, I discovered that if the program is "forced" to exit by Windows (for example after Windows Update that requires a reboot), the FormClose procedure is not called.

Edit - I'm new here, and it looks like I can't delete my own post. After some more searching, I found a solution.

+8  A: 

This one's real difficult. If you kill a process externally, that's instant termination. No chance to clean up, period. Windows Update's reboots often close a program "right" by sending it a WM_QUERYENDSESSION message, but it doesn't always work for whatever reason. Particularly, if your program delays for very long, (asking the user if he wants to save before quitting, for example,) the shutdown code will kill the process.

So if you want to guarantee that it'll always call that event handler, you'll have to guarantee that you never use a modal dialog box or anything else that would block the program from immediately bailing out when it receives WM_QUERYENDSESSION. This is probably more trouble than it's worth.

One alternative would be to do something similar to what Firefox does: Write the status data out to a temporary file while it's still running, and then when it restarts, check to see if that file's there and if the data indicates it's still in an "open" state. If so, your program can know that its last incarnation got killed somehow and take whatever action is appropriate, such as updating the status log (or whatever you're using) with the last available data.

Mason Wheeler
+4  A: 

A comment above suggested I summarize the answer I found (link). Basically, it says to provide your own handler for WM_QUERYENDSESSION. This is the recommended code:

procedure TForm1.WMQueryEndSession(var Message: TWMQueryEndSession);
begin
  inherited; { let the inherited message handler respond first }
  {--------------------------------------------------------------------}
  { at this point, you can either prevent windows from closing... }
  { Message.Result:=0; }
  {---------------------------or---------------------------------------}
  { just call the same cleanup procedure that you call in FormClose... }
  MyCleanUpProcedure;
  {--------------------------------------------------------------------}
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyCleanUpProcedure;
end;

I am not sure that calling Inherited before calling MyCleanUpProcedure is completely correct. If the Inherited procedure responds back to Windows first, Windows could still shut down the application before MyCleanUpProcedure had completed. I am not sure what Inherited does for the WM_QUERYENDSESSION message - I assume it defaults to allowing the shutdown immediately. In my application, the MyCleanUpProcedure runs very fast, so it would not cause Windows to display the "Not Responding" dialog due to no response to the WM_QUERYENDSESSION message.

To be sure my procedure runs to completion, maybe the procedure should look like this:

procedure TForm1.WMQueryEndSession(var Message: TWMQueryEndSession);
begin
  MyCleanUpProcedure;
  inherited;
end;

Or possibly this?

procedure TForm1.WMQueryEndSession(var Message: TWMQueryEndSession);
begin
  MyCleanUpProcedure;
  Message.Result:=1;    // tell Windows it is OK to shut down
end;
tim11g
+1  A: 

Follow up info: I didn't know if the inherited procedure did anything other than return a "1", so I tried it with both return and inherited. First run my shutdown procedure, then return a 1, then inherited.

procedure TForm1.WMQueryEndSession(var Message: TWMQueryEndSession);
begin
  MyCleanUpProcedure;
  Message.Result:=1;  {Signal that it is OK to shut down}
  inherited; { let the inherited message handler respond }
end;

I have tested this code for the case where the system is shutting down (either manually or because of Windows Update), and my application is still running. The following code has been verified to work correctly in this case - MyCleanUpProcedure does run before my application is shut down.

I have not tested this for the case where my application is killed by task manager - that is not a relevant use case for my application.

tim11g