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
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
Attila Kovacs Where is this settings in Project Options, don't really want to hack it via the actual DPR file
ReplyDeleteAttila Kovacs NOPE, SORRY : I was really hopeful for that being the solution, as when I open a new project I get the line :
ReplyDeleteApplication.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.
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/sub
ReplyDelete/sub me too
ReplyDeleteOut of interest, how are the forms being created and are you setting ownership to the main form or the application?
ReplyDeleteI 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?
Martyn Spencer All of our forms descend from an Ancestor ( TfrmecxForm ) and we use our own CreateWindow method to set everything up, so :
ReplyDeleteconstructor 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
Martyn Spencer, Attila Kovacs I stand corrected on further inspection of the code I am finding a lot of calls are of the nature:
ReplyDeletewith 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;
Out of interest, does the behaviour change if the application is set as the form owner?
ReplyDeleteWhenever 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.
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
ReplyDeleteIn 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.
ReplyDeleteIs 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.
I was wondering if that HWND_TOP should be HWND_TOPMOST for a starter ?
ReplyDeleteIt 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.
ReplyDeleteMartyn 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
ReplyDeleteMaybe 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/sub
ReplyDelete/sub
ReplyDeleteMartyn 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
ReplyDeleteHmmm. 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...?)
ReplyDeleteI am not at my office now but will dig it out tomorrow morning.
ReplyDeleteMartyn Spencer Thank you
ReplyDeleteCouldn't you step around slow window construction by showing the window without the data, deploy a loading cursor, and then establish the contents?
ReplyDeletePaul 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
ReplyDeletePaul McGee That's what I always do when a form has slow initialization.
ReplyDeleteWhen 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.
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.
ReplyDeleteI 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 :)
Arthur Hoornweg Could you give an example please
ReplyDeleteTony Danby
ReplyDeleteJust 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;
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.
ReplyDeleteGert 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
ReplyDeleteIt took us a few months of unhappy customers till we handled the problem.
ReplyDeleteGert Scholten Try a year
ReplyDelete