[Estimated Reading Time: 3 minutes]

In the beginning there was The Word a HWND. But not all gifts of creation can be relied upon, as I just learned. Or rather, remembered.

As regular readers will know, I am currently polishing up my Smoketest framework for release (don’t worry, this post won’t take long and then I can get back to that job).

I made a recent change that I was particularly pleased with. It was a trivial thing, but one of those small things that makes a big difference.

Previously the GUI console would indicate the currently executing test with a particular icon, replaced once the test was complete with another icon indicating the test result. This was fine, but on longer running tests was… well.. a bit boring. So I animated it.

Drawing on an experience from many years ago and combining that with message based thread synchronization, I opted to implement a motile thread *1 which would provide the beating heart of my animation. This thread would simply pump out messages at the rate of 12 per second, incrementing a frame counter.

*1my own term – and a specific class in my threading library – for a lightweight, fire-and-forget thread that just keeps churning away on it’s allocated, usually trivial task indefinitely. When I devised the class I purloined the name from a superb science fiction novel (Pandora’s Star) that I was reading at the time.

Upon receiving the message my console UI would update the icons of any currently running test(s) to the image for the frame indicated in the message.

And it all worked beautifully:

Animation working well
Animation working well

Things didn’t go so well when I rebuilt the test project (in this particular case for my Bonjour components – are you reading this Stefan ? :)) for Delphi 7.

Oh.  Dear.  Oh.  Dear.
Oh. Dear. Oh. Dear.

This problem has been bugging me for a couple of weeks now but I had a bit of a gap in my schedule to spend some time looking into it, and fortunately the problem and the solutions turned out to be very simple.

I was creating my animation beat thread in the FormCreate event of my main form and passing the form window handle at that point.

In Delphi 2010 and later this was fine. In Delphi 7 (and 2006 and possibly other versions though I didn’t check) the thread would happily run and pump out the messages as it should, but the Application.ProcessMessage() method would never receive them.

No doubt a fair few people out there are jumping up and down and pointing and shouting I KNOW WHAT’S WRONG I KNOW WHAT’S WRONG. And fortunately it didn’t take me long to arrive at the answer myself.

I remembered that over the years and various Delphi versions the order in which events occur during form creation has changed on different occasions. Also, the various conditions that result in window handles being created and subsequently re-created has also changed.

The reason that my animation messages were not being received was that the window handle to which they were being sent no longer existed because my console form window (the underlying HWND) had been destroyed and re-created at some point after providing the Handle to the thread in the FormCreate() event.

But only in these earlier versions of Delphi.

Having realised the problem, the solution was very simple: Move the code which assigned the HWND to the animation thread to an override of the form’s CreateHandle method. Then no matter if, when or how many times my form’s HWND was re-created, the animation thread would always be sure to be posting it’s messages to the right window:

  procedure TSmoketestConsole.CreateHandle;
  begin
    inherited;
    FPS.Handle := self.Handle;
  end;

And hey presto, my oh-so-pretty little animation works in all versions of Delphi from the venerable 7 onward.

All is once more right with the world.  Phew.
All is once more right with the world. Phew.

Apologies for the Screen-o-matic water-mark on these files by the way. I’ll be taking the time to look for a (Mac) tool for capturing such video vignettes but for now the free version of this did the job, and the watermark is the price I had to pay. Hey ho.

6 thoughts on “Creation Stories”

  1. I’m not convinced that’s the best solution. I’d personally create a non-VCL window that is not subject to re-creation. That gives you a window handle whose lifetime you are in charge of. Your way has a data race on FPS.Handle. Probably a benign race. But what if the window handle gets re-used? Again not, likely and Windows does take steps to avoid re-using window handles immediately. All the same, I’d still recommend using a non-VCL window and thus make this a non-issue. Since the window is to be created in the GUI thread you can use AllocateHWnd for the task.

    1. The “race” you see I don’t think is an issue since the FPS thread isn’t running during the create events – it only starts running when a test run is under way which can only happen after all create events have completed so as long as the last HWND it receives is the right one, all is good.

      Unless there are circumstances under which a TForm window might be re-created while a test run is running, then there might be an issue. In which case I can deploy a TMessageHandler to provide the reliable HWND to act as an intermediary between the FPS motile and the form. But I think I’ll keep that scaffolding in reserve unless/until I find it’s needed. 🙂

      1. I’d say your logic is backwards. You ought to be proving that there was no way a window could be re-created. I’m not sure why you’d reject a solution that is known to be correct, is trivial to implement, in favour of one that is very hard to prove to be correct and subject to variation across VCL versions.

        1. I’d say it’s your logic that’s backwards.

          I found an actual problem and an actual solution. Your solution is to a theoretical problem. If that problem doesn’t actually exist then the solution isn’t a solution at all, just needless over-engineering (albeit trivial in this case).

          I’m all for defensive programming but this level of paranoia is going a bit too far. After all, this is VCL code and as things look at the moment this isn’t likely to be subject to much – if any – evolution or change, unlike in the past. 🙂

Comments are closed.