Delphi - How to prevent Forms/MsgBoxes to move und

2019-01-21 12:07发布

问题:

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

回答1:

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.



回答2:

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);


回答3:

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.



回答4:

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.