Jump to content
Tommi Prami

WinAPI to query if a form is ready to Rock.

Recommended Posts

let's say if I have window Handle (Delphi app form), found by some windows API like FindWindow etc.

 

Sometimes it seems  that Window is created and has an handle but not fully functional yet. Is there a way to query with windows API that Delphi form is run all OnCreate etc events amnd is fully visible and all components are ready for user.

 

Reason I as we use AutoIT to automatically test our Apps,, and sometimes Form is not ready yet. No fully visible or still runnin initialization events (OnCreate etc).

 

If not I've suggested to add out base form class an message handler which we could use to quety, then problem would be how the Form it self Knows everything is OK, up and running 🙂

 

-Tee-

Share this post


Link to post

There is no panacea for this kind of problem, too much depends on how the form in question has been set up. What you can do from "outside" the process is

  • check if the window is visible (IsWindowVisible)
  • check whether it is the current foreground window (GetForegroundWindow)
  • check whether its message loop is running (WaitForInputIdle, or use SendMessageTimeout to send a do-nothing message to the form, e.g. wm_null, the call will not return until the loop is running)

Nothing will help you if the form does some kind of delayed initialization, e.g. using a timer started when the form is created.

 

Also keep in mind that a Delphi form may recreate its window handle at the drop of a (virtual) hat during the initialization phase.

Edited by PeterBelow
  • Like 1

Share this post


Link to post
4 hours ago, Tommi Prami said:

to add out base form class an message handler which we could use to quety, then problem would be how the Form it self Knows everything is OK, up and running

Wouldn't that be the easiest? I don't see much of a "problem" of how to know when everything is ready. By default, it should be after the first time OnShow (or maybe even OnActivate) has been called. This can be done in your base class. If you need more elaborate logic in one form class, then you can override that behaviour.

  • Like 1

Share this post


Link to post
53 minutes ago, PeterBelow said:

Nothing will help you if the form does some kind of delayed initialization, e.g. using a timer started when the form is created.

 

Also keep in mind that a Delphi form may recreate its window handle at the drop of a (virtual) hat during the initialization phase.

 

Delayed initialization is an separate problem I think, and that can be handled Case by case, if needed.

 

How, why and when Delphi will recreate the Hvnd for a Form? Never heard of this, this might explain rare weirdness.

 

-Tee- 

Share this post


Link to post
17 minutes ago, Der schöne Günther said:

A TWinControl has a method RecreateWnd():

http://docwiki.embarcadero.com/Libraries/en/Vcl.Controls.TWinControl.RecreateWnd

 

Maybe you can have a look at the VCL sources where this gets called...

It seems this can happen (On Form) quite common places. Not too many but still. Did not know any of this, Always thought that handle would be totally permanent on it's whole life time...

 

-Tee-

Share this post


Link to post
2 hours ago, Tommi Prami said:

 

Delayed initialization is an separate problem I think, and that can be handled Case by case, if needed.

 

How, why and when Delphi will recreate the Hvnd for a Form? Never heard of this, this might explain rare weirdness.

 

-Tee- 

Some of the form properties (e.g. border and bordericons) are implemented via window styles on the API level, and Windows only honors some of the styles when the window is first created. Changes to these properties recreate the window handle for this reason. Another reason are modal forms with popupmode pmauto or pmexplicit; their window handle has to be recreated when they are shown to tie them to their popup parent in Z-order. Changing the formstyle also recreates the handle.

  • Thanks 1

Share this post


Link to post
8 hours ago, PeterBelow said:

check whether its message loop is running (WaitForInputIdle, or use SendMessageTimeout to send a do-nothing message to the form, e.g. wm_null, the call will not return until the loop is running)

You have to be careful with WaitForInputIdle(), though:

WaitForInputIdle should really be called WaitForProcessStartupComplete

WaitForInputIdle waits for any thread, which might not be the thread you care about

  • Like 1

Share this post


Link to post

While was walking to work had couple of ideas on this,

 

What if at FormActivate (For example) event of base form, I would do either.

 

1. Send message to window it self,

  If I've understood correctly window will not receive that message untill it has processed all the Form initializations of it self, and is able to recveive messages again.

 

2. Create and start timer and disable and free it on first tick. I think this is essentially same as the method 1. Some codes, me included, are not used to using windows messages too much. Delphi hides the need quite well.

 

When the message arrives or timer fires set the flag that can be queried with other messsage from outside of the app.

 

This should give pretty good starting point.

 

-Tee-

Share this post


Link to post

you can detour TForm's DoCreate, DoDestroy, DoShow, Activate etc... from a separate unit and set some flags if it helps

Share this post


Link to post
1 hour ago, Attila Kovacs said:

you can detour TForm's DoCreate, DoDestroy, DoShow, Activate etc... from a separate unit and set some flags if it helps

Could you elaborate bit more? What this means in practise

 

-Tee-

Share this post


Link to post

We had similar problem, except, that we wanted to know, when app finishes processing any long task, and is ready to accept new input. And we wasn't allowed to change the code of the app. Our first attempt was to use SetWindowsHookEx in WH_FOREGROUNDIDLE mode. That was working in most cases, but sometimes we had situations, when the last message in the message queue is actually the one, that causes processing of long task, so our hook was triggering at the beginning of the processing, not the end. Also, this hook will hang until some message will get in to message queue, so some sort of wake up message is required. Finally we decided, that since target app uses runtime packages, we can call VCL for help. Now our test app is using windows hooks only to inject our code (dll compiled with runtime packages) in to target process. Then we create event handler for Application.OnIdle. This approach is much more reliable, something like 95%.

If you can change the code of target app, then it will the best approach.

 

P.S. You could avoid all of that, if VCL supported UI Automation

Edited by Микола Петрівський

Share this post


Link to post
Guest
On 8/6/2019 at 12:09 PM, Микола Петрівський said:

P.S. You could avoid all of that, if VCL supported UI Automation

And if that could trickle down to 3rd party!!! Oh what a boon!

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×