Bug or design: Delphi doesn't process WM_ENDSESSION

In a previous mailed with the subject "[Delphi] Serios bug when closing

Quote
windows???" bim...@ibm.net(Per Bakkendorff) writes:
>When you have a delphi application running, and you are shutting down windows,
>(don't close your app first), NONE of your destructors are called!!!!
>I've developed two applications in delphi, and both customers reported back that
>data just entered were lost. (I use a desktop Paradox database). First I couldn't
>duplicate the error, but after some testing, I found that they were closing Windows
>without close my app first.
>The only solution I have found so far, are to close the tables in the event
>"QueryClose". This solved the problem with data loss, but this is _absolutely_ not
>the correct solution.

At first I thought the problem was programmer's code, but then I realized
that it really is a bug or at least a very lazy "feature".

The problem seems to be that when you close down Windows, it will first
send a WM_QueryEndSession message to all running top-level windows. This
is handled and processed correctly by the TForm object in VCL.

Then assuming that all applications indicated that it was ok to close down,
Windows will send WM_EndSession messages to all windows. This message is
not handled by VCL. The application is simply brought down with a bang.
No windows are closed, no destructor called and no exit procedures called.

The solution is to handle the WM_EndSession message yourself. There are
several ways of handling messages in Delphi, but the only reliable way
of handling the WM_ENDSESSION is to use the HookWindow method of
Application.

In the message handler, check to see if the message is a WM_ENDSESSION.
If so, we should close down the application. We could have called the
Close method of the main window, but the Windows API documentation states
that the system might go down anytime after return from the WM_ENDSESSION,
and a posted WM_QUIT message might never arrive to the application.

The solution is to simply call Halt instead. This will call all registred
exit procedures, including the ones in Controls and DB units. These will
free the application and screen objects and take the BDE down correctly.

A simple example follows:

unit Tst2u;

interface

uses
  SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls,
  Forms, Dialogs, Grids, DBGrids, DB, DBTables;

type
  TForm1 = class(TForm)
    DataSource1: TDataSource;
    Table1: TTable;
    DBGrid1: TDBGrid;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    function HookProc(var Message: TMessage): boolean;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const
  FlagFileName = 'C:\Flag.Fil';

procedure CreateFlagFile;
var
  F: System.Text;
begin
  System.Assign(F, FlagFileName);
  System.Rewrite(F);
  Writeln(F, 'This is a dummy flag file');
  System.Close(F);
end;

procedure KillFlagFile;
var
  F: File;
begin
  System.Assign(F, FlagFileName);
  System.Erase(F);
end;

procedure MyExitProc; far;
begin
  KillFlagFile;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.HookMainWindow(HookProc);
end;

function TForm1.HookProc(var Message: TMessage): boolean;
begin
  Result := false;
  if Message.Msg = WM_EndSession then
  begin
    if WordBool(Message.wParam) then
    begin
      { Windows is closing down - clean up!! }

      { This should execute all ExitProcs, close windows and call destructors... }
       Halt; { This works! }

      { This should close things down properly,
        but do we have enough time to handle any posted messages before Windows
        is already down??  This will result in a PostQuitMessage that might
        never arrive!}
{      Close;} { This doesn't always work - avoid it }
    end;
  end;
end;

initialization
  CreateFlagFile;
  AddExitProc(MyExitProc);
end.

This unit demonstrates that the exit procedures are called when closing
the app normally and when closing down Windows and using HookMainWindow.
Without the HookMainWindow call, the exit proc will not be called. This
is specially important for DB applications. Without the Halt, LCK files
will not be deleted, buffers might not be flushed, changes posted and
so on.

--
 Hallvard Vassbotn  | Falcon AS (a Reuters company) | Without programmers,
 hallv...@falcon.no | Stranden 1, 0250 OSLO, Norway | the world would stop!