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?

+3


source to share


2 answers


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 of Application.MainForm.HandleAllocated

    how True

    . 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.

+3


source


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

0


source







All Articles