Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi application main form moving behind other windows on modal close

I'm starting to have issues with my main form disappearing behind other application windows on closing modal forms and I was hoping someone would have come across (and solved!) this issue previously or have suggestions on where to locate breakpoints to debug the problem.

My issues originally started with the classic 'shy dialog' problem with modal dialogs appearing under the main form which occurred intermittently. To try to sort this I changed all my modal forms' popupmode to pmAuto and also added Application.ModalPopupMode := pmAuto; and Application.MainFormOnTaskBar := true; to my application dpr.

Now I'm getting the main form disappearing behind other windows on closing the modal pop-ups. I have suspicion is that the behaviour is mainly caused when a modal form opens a second window (I've problems with both a MessageDlg and a straight Form.create(Application); Form.show;), though there's no obvious problems with the show/free code (ShowModal forms are created owner = nil, modeless with owner = application). In both cases the form disappears on closing the first original modal form, but manipulating the modal form without triggering a new form/dialog to appear seems to work as expected.

There are other nasties going on in the background on the main form with a refresh timer that activates a background thread, but usually this hasn't fired in the time it takes to see it not working. Other than that we are firing off calls to a remote server via a third-party DLL (the application is effectively a client-side GUI).

Annoyingly I can't get a mini program to mimic the behaviour and running in the IDE makes seeing the behaviour difficult, as the IDE itself contains a lot of windows that muddy the Z-ordering.

Edit - After writing my answer below, it appears I'm getting a deactivate event sent to the application (I can catch it through Application.OnDeactivate) - it seems similar to WPF App loses completely focus on window close Delphi doesn't have the Activate method that the c# solutions have, but I'll play with some windows messaging to see if I get anywhere

like image 677
Matt Allwood Avatar asked Mar 17 '16 15:03

Matt Allwood


1 Answers

Following David's advice in comments I created a little logging form to be created on startup containing memo, timer and the following OnTimer event:

procedure TForm1.Timer1Timer(Sender: TObject);
  function logtomemo(aHandle: HWND): TWinControl;
  var
    form: TWinControl;
  begin
    form := findControl(ahandle);
    if form <> nil then
      memo1.Lines.Add(format('handle %d - form %s', [ahandle, form.Name]));
    result := form;
  end;
var
  handle: HWND;
  form: TWinControl;

begin
  memo1.Clear;
  handle := application.ActiveFormHandle;
  repeat
    form := logtomemo(handle);
    handle := GetWindow(handle, GW_OWNER);
  until (handle = application.MainFormHandle) or (form = nil);
  logtomemo(handle);
end;

Clicking around I noticed that as soon as I clicked outside of my application, our splash form appeared as the only form in the list. (Historically our splash screen used to only be freed after Application.Run, as they used to keep some other references on it for some reason - before my time and wasn't really needed anymore).

Changing the lifetime of the splashscreen to be destroyed before Application.Run appears to have sorted the issue - something that I'd never have guessed would be the cause in a million years.

Need a final sign-off that it doesn't reappear once I get rid of this little debug form, but hopefully a problem that's been frustrating me for a few days is now sorted - thanks!

Edit

As I noted in my edit and the comments to this question, the above debug didn't work, as the presence of the new form 'fixed' the problem. Changing the code so the output was sent to the Event Log or a text file rather than requiring a form also didn't reveal anything - all forms in the Z order remained in place.

In the end, I was able to fix the symptom rather than the cause by attaching the following code to Application.OnModalEnd

if Application.ModalLevel = 0 then
  Windows.SetActiveWindow(Application.MainFormHandle);

This successfully sets the active window back to the main form after the last modal dialog has been closed.

This may have some side-effects if the user is expecting a non-modal form that isn't the main form to regain focus, but our application architecture doesn't really follow this structure and with Application.MainFormOnTaskbar, the main form won't hide the other forms (as long as they're not unparented)

like image 155
Matt Allwood Avatar answered Oct 15 '22 01:10

Matt Allwood