Delphi - How to prevent Forms/MsgBoxes to move under prior form?
Asked Answered
C

4

17

Many times after the Windows 98 era we have experienced that some dialogs lose their Z-Order and move back to the prior form.

For example:

Dialog1.ShowModal;

Dialog1.OnClickButton() : ShowMessage('anything');

When MessageBox appears, it sometimes doesn't have focus and is moved under Dialog1. The users are confused about it, they say: My application froze!!! But if they use Alt+Tab to move to another app and back, the focus returns to the MessageBox and it will be the foreground window.

We have experienced this with ShowMessage, MessageBox, normal forms, and also QuickReport forms.

Does anyone know about this? Is it a Windows bug? How can you prevent it? How to catch this?

Thanks for your help: dd


I really said that AFTER Win98, so all OSs (Win7 also) are affected by this problem. We used Delphi 6 Prof, so the properties are not working with Default forms.

Somebody said that message dialogs are controllable with MessageBox + MB_APPLMODAL. This is good news, but we have many old forms and components, third party tools.

So it is hard work to make a completely new application with substitution of the forms.

But we will try doing this.

I think the answer is this is a half application problem and half a Windows problem. If Windows sometimes handles this, and sometimes doesn't - that seems to be a Windows bug. But if we can force good modal window making then it is a programming bug.

Can somebody explain to me what is the meaning of the WS_POPUP flag? Does it have some side effect or not?

Thanks: dd

Claxton answered 8/6, 2010 at 12:6 Comment(4)
i've often have this issue too, i haven't found a answer either. Looking forward to what people have come up with.Longsuffering
With what version this is happening?Croteau
you're Windows 98? Wow. As Sertac asks, the version of Delphi is critical. Delphi 2007 and later have a workaround.Limekiln
Below, he says he is using DELPHI 6, and is NOT talking about Windows 98, but later versions.Limekiln
R
16

That's what the PopupMode and PopupParent properties are for.

E.g., you can do:

Dialog1.PopupMode := pmExplicit;
Dialog1.PopupParent := self;
Dialog1.ShowModal;

This tells Windows the correct Z-order.

Ramey answered 8/6, 2010 at 13:9 Comment(9)
Wow, the documentation on this is not helpful at all! But try it; it works!Ramey
@Craig - wouldn't help with ShowMessage, MessageBox and exception messages though..Croteau
@Sertac, those are convenience functions around regular windows. You can do custom exception messages by handling Application.OnException. Likewise, if you need a customized ShowMessage, you can write one.Ramey
@Craig - Sure you can - simulate a form with a system message box, find the top window, popupparent it, normalize top mosts, disable task windows, and then don't use the convenient procedures in forms.pas while doing these because probably the problem lies there to begin with... And instead of forms, you can also do CreateWindowEx, message loops etc.. but forms are convenient. ;) All of this is beyond my point, which was to point out what I said.Croteau
@Sertac, do you actually find it challenging to call CreateMessageDialog? Really?Ramey
@Craig - See qc.embarcadero.com/wc/qcmain.aspx?d=67134 for instance, to see why it will "not" help (at least for versions before D2009). Sorry if I'm sounding a bit sour on this, but I've struggled with the VCL.., I've struggled with a sysop.., worked around some, could not work around some... I can't duplicate the remaining issues but customers report "glitches".. If you excuse me, I don't want to struggle yet with a TeamB'er... All I said was that popupparent would not work with MessageBox etc..Croteau
If the original poster is using Delphi 7 or earlier, this won't help. And he's on Windows 98. So not exactly up with the times.Limekiln
@Sertac, that's an entirely different issue. You're using a stay on top form (nearly always a mistake IMHO, but that's a separate issue). He is not. Regarding the WinAPI MessageBox, if you use it, you can do the same thing that PopupParent does.Ramey
Using fsStayOnTop, as Craig says, is considered a poor solution.Limekiln
L
7

For old versions of delphi (prior to Delphi 2007), on forms OTHER than your main form:

interface
  TMyForm = Class(TForm)
  protected
    procedure CreateParams(var Para: TCreateParams); override;
  end;
...
implementation
...
procedure TMyForm.CreateParams(var Para: TCreateParams);
begin
  inherited;
  Para.Style := Para.Style or WS_POPUP;
  { WinXP Window manager requires this for proper Z-Ordering }
  // Para.WndParent:=GetActiveWindow;
  Para.WndParent := Application.MainForm.Handle;
end;

For message boxes include MB_TOPMOST in your flags:

Application.MessageBox(PChar(amessage), PChar(atitle),    otherflags or MB_TOPMOST);
Limekiln answered 8/6, 2010 at 17:29 Comment(1)
I also have a workaround for Dialogs created with the VCL Dialogs unit, prior to Delphi 2007. If anybody needs that (for versions prior to 2007), ask a new question and I'll post that code.Limekiln
O
1

I looked at this page and the FAQ for half an hour and still can't find how to post a comment, so forgive me for this breach of protocol.

First of all I'd like to make clear that the poster, IMHO, is not using Windows 98. He writes "after Windows 98 era" which I understand means he is having this problem with Windows versions after 98.

As I am having this problem too (CB2009), I'd like to emphasize the poster's question "Is it Windows bug?", which I have not seen answered. If it's a Delphi/Builder bug, maybe there is a way to avoid it? I can't see how intercepting all potential dialogs is a workable solution, nor avoid using fsStayOnTop. I have a settings form that needs to stay on top of my main form, but the settings form can and will popup dialogs that under certain conditions will disappear under the settings form.

It would be very helpful if I would understand where the support of z-order goes wrong, as it may offer a clue on how to avoid it.

Orleanist answered 9/6, 2010 at 10:49 Comment(5)
It is a windows bug, in the sense, that the Z-order of your forms is not preserved by the architecture of the Windows internal "window manager" in Windows XP, after a certain point. It is NOT a windows bug, in the sense that the Windows API itself does not guarantee that it will remember some kind of particular Z-Order that exists, unless you have set your parent-window links correctly using the CreateParams (as I have shown in my answer).Limekiln
Incidentally can you really not see any text (possibly gray) or a button labelled "Add Comment"?Limekiln
Yes, but only for my own post. Maybe one is only allowed to comment on other posts if one has enough points, but then I can post a reply.. So I do not see the logic in this. Nor why this bug exists btw, seems easy enough to get it right.Orleanist
Me too. It seems a fundamental error in the Windows platform, that Microsoft WILL NOT FIX. Thus, it falls to us, poor developers to provide what they ask for: A way for their Window Manager to determine Z-order after it 'forgets it', by checking the Win32 Window memory records (calling them objects seems a bit of a stretch). It seems that the window manager is especially prone to forgetting your window order, when your main window application message loop has points of non-responsiveness lasting > 500 msec.Limekiln
This is a Delphi VCL issue, not a Windows bug. The Delphi dialogs do not properly expose themselves to the OS.Compote
E
1

A trick I've used recently was to apply these two lines of code during the creation of each form:

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or 
  WS_EX_APPWINDOW or WS_EX_TOPMOST);
SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopWindow);

Handle is the handle of the form (Form1.Handle). The WS_EX_APPWINDOW part makes each window appear on the task bar, remove it if you don't want that additional effect.

For my main form I use this line:

SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or
  WS_EX_TOPMOST);

I also use this function to help build my custom dialogs (I created a new function for each style of dialog - error, confirmation, etc.):

function CustomDlg(const AMessage : string; const ADlgType: TMsgDlgType;
  const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn) : TForm;
begin
  Result := CreateMessageDialog(AMessage, ADlgType, AButtons, ADefaultButton);
  with Result do
    begin
      SetWindowLong(Handle, GWL_EXSTYLE, GetWindowLong(Handle, GWL_EXSTYLE) or 
        WS_EX_APPWINDOW or WS_EX_TOPMOST);
      SetWindowLong(Handle, GWL_HWNDPARENT, GetDesktopwindow);
      FormStyle := fsStayOnTop;
      BringToFront;
    end;
end;

The FormStyle := fsStayOnTop; part is optional, of course, but I use it to make sure my confirmation and error dialogs are always visible to the user.

It seems like a bit of work but the net effect is that I no longer have to worry about forms accidentally hiding behind other forms.

Endemic answered 31/8, 2011 at 11:24 Comment(1)
I should add that with the SetWindowLong() function you do not need to create your own custom forms and can do this to any existing forms you have. If you are already creating custom forms then overriding CreateParams() is the way to go.Endemic

© 2022 - 2024 — McMap. All rights reserved.