Child Forms opening in the background

Child Forms opening in the background

Folks

We have a problem with our project and I wondered if anyone can resolve it or shed light on it. We have a large project and it is a database application written entirely in Delphi 10.2.3; although it started life in Delphi 5

It is a 85Mb (x86) or a 112Mb (x64) Windows executable. Our target OS is Windows 10.

We don't do anything clever with our forms, except that we will create them on the fly, not auto create them in the DPR. The only form auto created is the main form.

We have noticed over the last two years that occasionally a form will open in the background rather than the foreground. We think this may be because the form being opened is taking a long time to essentially "open", due to; in the most; queries needing to be run

However we are noticing that if we right lick on the executable in explorer and select Properties, Compatibility and check "Run this program in compatibility mode for:" and set it to either Windows 8 or Windows 7 then the forms no longer open in the background.

So the questions are:

1. What is causing this
2. How can we fix this in code/project, so that a user doesn't to fiddle with Compatibility settings

If anyone can shed any light on this we would be eternally grateful

Thanks

Comments

  1. Attila Kovacs Where is this settings in Project Options, don't really want to hack it via the actual DPR file

    ReplyDelete
  2. Attila Kovacs NOPE, SORRY : I was really hopeful for that being the solution, as when I open a new project I get the line :

    Application.MainFormOnTaskbar := True;

    and when I put this into our project it does appear to be working, but when I send it to be tested they say it fails.

    ReplyDelete
  3. Attila Kovacs It was a brand new executable with a brand new name and he will have tested it in a test folder as it was a test version. So the compat. settings should not have been there in the first place

    ReplyDelete
  4. Out of interest, how are the forms being created and are you setting ownership to the main form or the application?

    I did write some code to force a window to the front when it was running in a terminal services session and it works (mostly!). I can post it here if you think it may help? Not that it is really answering your question or fixing the problem but it may provide a temporary workaround.

    From what you say, it is intermittent but is there a way of repeating the behaviour consistently or is it truly random?

    ReplyDelete
  5. Martyn Spencer All of our forms descend from an Ancestor ( TfrmecxForm ) and we use our own CreateWindow method to set everything up, so :

    constructor TfrmecxForm.CreateWindow(AParent: TWinControl; AOwner: TComponent; AConnection: TADOConnection; ADDObjects: TucxDDObjects; AUserCode: String; ALogon: TecxLogon);
    begin
    if ADDObjects <> nil then DDObjects := ADDObjects;

    inherited Create(AOwner);

    if AParent <> nil then
    begin
    Parent := AParent;
    BorderStyle := bsNone;
    BorderIcons := [];
    BorderWidth := 0;
    Align := alClient;
    end;
    if ALogon <> nil then ecxLogon := ALogon;
    if AConnection <> nil then adoEvogenic := AConnection;
    UserCode := AUserCode;
    end;

    I believe Self is passed in as the AParent param for all calls to this.

    The failure only seems to occur when a form takes a long while to load at which point it is send to the back of the z-order

    However, the same form and the same code that fires that form off is shown in the foreground on success cases.

    Go figure

    ReplyDelete
  6. Martyn Spencer, Attila Kovacs I stand corrected on further inspection of the code I am finding a lot of calls are of the nature:

    with TfrmConsoleMenuMaint.CreateWindow(nil, Self, adoEvogenic, DDObjects, UserCode, ecxLogon, ADataModule, nil, ecxLogon.GetProcessID) do
    begin
    try
    Application.NormalizeTopMosts;
    SetWindowPos(Handle, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE + SWP_NOMOVE + SWP_NOSIZE);

    ShowModal;
    finally
    Free;
    end;

    ReplyDelete
  7. Out of interest, does the behaviour change if the application is set as the form owner?

    Whenever I have tried to debug these sorts of issue, it has always been a real pita and I have never found a workaround for it that I have been totally happy with. The TS client code that I put together based on several posts I found whilst reading about the issue certainly proved helpful and worked a significant percentage of the time, but again, never 100%.

    Is it possible to track whether the form is ever at the front of the z-order and that something else is happening in the app that results in it being sent to the back?

    Edit: This was posted before I read your latest comment.

    ReplyDelete
  8. Attila Kovacs I think it is down to the form taking to long to Show. I believe in most they are supposed to be non-modal as the user is ( for the majority of the forms ) meant to be able to navigate between each one

    ReplyDelete
  9. In my experience of earlier forms of Dephi, modal forms occasionally do end up behind the main form and it has been a source of constant irritation.

    Is this happening specifically with modal forms, non-modal forms or both? Is it only with forms that take some time to populate during the OnShow event?

    If it is, I would find a way of either forcing the window to the front after the long OnShow processing. Is this something that is possible? If not, I would Show the form and then either in a thread (ideally) or a TTimer event (not the cleanest way in my opinion) I would handle the actual data load.

    ReplyDelete
  10. I was wondering if that HWND_TOP should be HWND_TOPMOST for a starter ?

    ReplyDelete
  11. It would be HWND_TOP as HWND_TOPMOST would not move a window in front of other HWND_TOPMOST windows, if I am reading the docs correctly.

    ReplyDelete
  12. Martyn Spencer Not tried swapping the Owner; just investigating right now. Have tried some code to do with PopupMode and PopupParent but that did not seem overly succesful

    ReplyDelete
  13. Maybe you have to check your manifest. I suggest you have your own and don't depend over the one Delphi adds to your executable when you check the Use Themes option in the project options.

    ReplyDelete
  14. Martyn Spencer You said earlier "I did write some code to force a window to the front when it was running in a terminal services session and it works (mostly!)." Could you post that code please. Any ideas or code right now will help somehow and would be greatly appreciated. Thank you

    ReplyDelete
  15. Hmmm. I think I've seen something like this but can't remember where or when now. It would be really be best if you could come up with an MCVE. An idea: Possibly worth logging/examining the stream of Windows messages to see if something is either lost or somehow being effectively being handled out of order accidentally (perhaps due to some kind of re-entrancy problem, maybe due to the long loading/processing time of the show event...?)

    ReplyDelete
  16. I am not at my office now but will dig it out tomorrow morning.

    ReplyDelete
  17. Couldn't you step around slow window construction by showing the window without the data, deploy a loading cursor, and then establish the contents?

    ReplyDelete
  18. Paul McGee This is an option, yes, but it would be last resort as the system has 300+ forms. Rather find a solution for the base form that they all descend from. I might be able to work what you are suggesting into the base form, but I'd rather see if there is a better solution out there first

    ReplyDelete
  19. Paul McGee That's what I always do when a form has slow initialization.

    When the form becomes visible, I simply show an hourglass cursor and perform a postmessage(Handle, wm_DoSomethingDeferred...). As soon as the form has time to process its message queue, it will asynchronously start the dynamic method that does the initialization.

    ReplyDelete
  20. The code that I put together is as follows. It was written for an app that still uses Delphi 2007, so some of the code may need changing and may no longer be relevant, but you may find something that helps. It was the result of trying multiple suggestions in a number of different threads relating to the topic and I make no claims that this is the best (or even the correct) way to do it.

    I found that it worked for me. It was designed to force a window from either the current application or a different application to the front. It worked all the time for the current application but occasionally fails with a different application.

    function ForceForegroundWindow(lnHWND: HWND): Boolean;
    var
    ForeThread: Cardinal;
    AppThread: Cardinal;
    begin
    ForeThread := GetWindowThreadProcessId(GetForegroundWindow(), NIL);
    AppThread := GetCurrentThreadId();
    if ForeThread <> AppThread then begin
    if AttachThreadInput(ForeThread, AppThread, True) then begin
    if BringWindowToTop(lnHWND) then
    result := ShowWindow(lnHWND, 0)
    else
    result := False;
    AttachThreadInput(ForeThread, AppThread, False);
    end else
    result := False;
    end else begin
    if BringWindowToTop(lnHWND) then
    result := ShowWindow(lnHWND, 0)
    else
    result := False;
    end;
    end;

    Most of the functions called are from the Windows unit.

    I still think it would be better to defer the processing and perhaps try Arthur Hoornweg's suggestion.

    Incidentally, if someone knows of a better way than the code I posted above, I would be delighted to hear about it :)

    ReplyDelete
  21. Arthur Hoornweg Could you give an example please

    ReplyDelete
  22. Tony Danby

    Just a quick and dirty example here. I trigger the delayed initialization in formcreate() in this example but that is just one of the many possibilities.



    CONST wm_DelayedInitialization=wm_User+1234;

    type
    TMyForm = class(TForm)
    procedure FormCreate(Sender: TObject);
    public
    Procedure DelayedInitialization (var msg:tMessage); MESSAGE wm_DelayedInitialization;
    end;

    implementation

    {$R *.dfm}

    procedure TMyForm.DelayedInitialization(var msg: tMessage);
    begin
    Sleep(5000); //Simulate very slow initialization
    Caption:='Initialization is ready';
    cursor:=crDefault;
    end;

    procedure TMyForm.FormCreate(Sender: TObject);
    begin
    Cursor:=crHourglass;
    Caption:='Initializing...';
    Postmessage(Handle,wm_DelayedInitialization,0,0);
    end;

    ReplyDelete
  23. We had the same problem with modal forms. You need to set the popup parent of the created form to the owner form. All the suggestions with hwnd top etc don't work in all casus... Like rdp sessions. Vdi etc. Popup parent is the way to go.

    ReplyDelete
  24. Gert Scholten funny you should say that we have just coded the solution to this using popup parent and mode. We will be finished soon and I will publish a few bits of the code up here for the people who /sub'd

    ReplyDelete
  25. It took us a few months of unhappy customers till we handled the problem.

    ReplyDelete

Post a Comment