Be careful with FormCloseQuery

help 64 Recently I had a very hard to find bug-problem with closing my application (made in Delphi).
It just refused to respond to Windows shutdown messages and prevented Windows to shutdown and restart..

I spent a lot of hours investigating this problem.
Initially the first part of the problem, because I was showing a dialog  for "Really close?" and preventing closing if some files are not saved, this was done in FormCloseQuery and it was stupid approach, because FormCloseQuery doesn’t know if windows is shutting down and thus preventing shutdown.

Then, I made a Message listeners for windows asking applications query for shutdown : WM_QueryEndSession and WM_EndSession:

// Shutdown messages

procedure WMEndSession(var Msg: TWMEndSession); message WM_ENDSESSION;

// Query to Shutdown

procedure WMQueryEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;


The problem remained: the messages were received only second time. The first time when Windows was shutting down the application was still preventing shutdown, and only for the second Shutdown call, the messages were received and Application quit.

So something else was corrupting even the messages queue.

After some time, I accidentally came under another Form in same application, which also had FormCloseQuery, this time it was really needed because this Form was closing with fading effect and Query was preventing close until fader finished. So that was the second part of the problem.

Again I needed to listen for Shutdown messages in this Form.

So to conclude: you need to listen for Windows shut down Messages and Queries in every form that has FormCloseQuery or better yet avoid it, if you want to let windows shutdown normally.

Also to note: Delphi made applications (on Windows shutdown) exit instantly without freeing objects, so be careful and it is a good practice to listen to Shutdown procedure and to call your own Closing and Freeing procedure manually:

//Windows' Query for Shutdown

procedure TMainForm.WMQueryEndSession(var Msg: TMessage);

begin

  //Windows is about to shut down - Alow or not

  bForceClose := True;

  Msg.Result := 1; // 1 for Signal that it is OK to shut down

  inherited; // let the inherited message handler respond

end;

 

//Windows is shutting down

procedure TMainForm.WMEndSession(var Msg: TWMEndSession);

begin

  // shutdowning

  if Msg.EndSession = True then

  begin

    //Windows is shutting down -- Closing'

    bForceClose := True;

    //Be CAREFULLL - MIGTH BE DOUBLE OR NO FREEING AT ALL

    CleanUp;   // My Closing procedure

    FinalDestroy; // Destructor sometimes never called on shutdown !!!!!!

 

    inherited; // let the inherited message handler respond

  end

  else

    inherited;

    // Msg.Result:=0;  //Signal that we got the message - not needed

end;

bForceClose here is a global boolean variable, signaling that we should force our application closing, and if you still need FormCloseQuery  check for bForceClose there (of course you need to set it false on application start):

procedure TMainForm.FormCloseQuery(Sender: TObject; var CanClose: Boolean);

begin

  CanClose := True;

  if not bForceClose then begin

        //no close 

        CanClose := False;

        //Do what you need here;

  end

  else CanClose := TRUE; //Forced close

end;

And you need this done in every form with FormCloseQuery.

Advertisements

2 responses to “Be careful with FormCloseQuery

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

%d bloggers like this: