How to check if Application.MainForm is valid?
How can I be sure that at some point in my VCL application lifecycle the value is Application.MainForm
valid, so I could send it a message (from MadExcept ExceptionHandler).
This can be anywhere (in the context of any thread) in my application (also initialization
, finalization
etc.)
I thought:
if Assigned(Application)
and (not Application.Terminated)
and Assigned(Application.MainForm)
and Application.MainForm.HandleAllocated then
begin
PostMessage(Application.MainForm.Handle, MyMessage, 0, 0);
end;
Is it correct?
source to share
How can I be sure that at some point in my application lifecycle the VCL Application.MainForm is valid, so I could send a message to it.
OK.
This can be at any point (in the context of any thread) in my application (also initialization, finalization, etc.)
Uh oh.
....
Is it correct?
No, of course not. Your code can never be thread safe because access to VCL objects from outside the main thread is not allowed.
In your particular case, consider the following sequence of events:
- You run your tests in
if
, culminating in an estimate ofApplication.MainForm.HandleAllocated
howTrue
. Ignore for a moment the fact that you are doing this outside the main thread. - Then you start preparing the call for
PostMessage
. But in this case, the main form is destroyed. - By the time your stream is available to be accessed
Application.MainForm
, it has disappeared.
You will need to do a little work here. You will need to do something like this:
// interface section of some unit
procedure RegisterMainFormHandle(Wnd: HWND);
procedure UnregisterMainFormHandle;
procedure PostMessageToMainForm(...);
// implementation section
var
MainFormHandleLock: TCriticalSection;
MainFormHandle: HWND;
procedure RegisterMainFormHandle(Wnd: HWND);
begin
MainFormHandleLock.Acquire;
try
MainFormHandle := Wnd;
finally
MainFormHandleLock.Release;
end;
end;
procedure UnregisterMainFormHandle;
begin
MainFormHandleLock.Acquire;
try
MainFormHandle := 0;
finally
MainFormHandleLock.Release;
end;
end;
procedure PostMessageToMainForm(...);
begin
MainFormHandleLock.Acquire;
try
if MainFormHandle <> 0 then
PostMessage(MainFormHandle, ...)
finally
MainFormHandleLock.Release;
end;
end;
You also need to create and destroy the critical section, but I assume you know how to do this.
In your main form, you override CreateWnd
and DestroyWnd
and agree that they call RegisterMainFormHandle
and UnregisterMainFormHandle
.
Then you can call PostMessageToMainForm
from any thread at any time.
Of course, if the main form window is recreated, you will lose some messages. It seems like this might be the problem. Using AllocateHwnd
to have a window whose lifetime you control is usually better than using the main form window.
source to share
Make the flag a global variable = false
at the beginning.
Make your basic shape by turning it into true
.
Check the box to make sure the main form has already been initialized or not yet set.
You can do it from places like mainform OnActivate
event or overridden methodTMainForm.Loaded
Likewise, when your application is finished and the main form is hidden (and later even destroyed) - you will return the reset flag to false
source to share