Reputation: 2572
Given this scenario:
procedure TForm2.Button1Click(Sender: TObject);
var
Form: TForm;
begin
Form := TForm.Create(nil);
Form.OnClose := FormClosed;
Form.Show;
Sleep(200);
TThread.CreateAnonymousThread(
procedure
begin
TThread.Synchronize( nil,
procedure
begin
Form.Close;
MessageDlg('Testing', mtInformation, [mbok], 0);
end);
end).Start;
end;
procedure TForm2.FormClosed(Sender: TObject; var Action: TCloseAction);
begin
Action := TCloseAction.caFree;
end;
My MessageDlg call is not displayed (the result of this call is always mrCancel
(2)).
After digging around, it's related to the OnClose event and setting the Action to caFree.
Changing Form.Close
to Form.Free
and removing the OnClose event entirely displays MessageDlg ok. Placing MessageDlg before the call to Form.Close works ok. Originally I thought scope of my Form variable might have caused the problem but declaring Form
as a private field in TForm2 instance doesn't solve the problem.
My aim was to display a splash form, execute my thread, then through call backs of said thread, close the Splash form and display dialog where appropriate to the user.
For clarity, why does this occur?
Upvotes: 4
Views: 587
Reputation: 613451
What is happening is that the dialog's owning window is the form that is being closed. When the dialog starts its modal message loop, the form is released and takes down its owned windows with it. Including the dialog.
Test this out, to give you more confidence that what I state above is correct, by replacing the call to show the dialog first with
MessageBox(0, ...);
and then with
MessageBox(Form.Handle, ...);
That is, be explicit about the dialog's owner.
The first version, with no owner, will show the dialog. The second won't because it replicates the scenario in your code.
Upvotes: 7
Reputation: 23056
The Windows runtime requires that the messages for a visual window be processed by a message loop running in the same thread that creates that window.
The Windows API's also enforce rules about what operations can be performed on a window from a thread other than that in which the window was created. i.e. very little indeed, other than sending or posting messages to it.
With that information in mind, we can explain what is occurring in your case.
The Form.Close method ultimately closes a form by posting it a message (CM_RELEASE
). But in your implementation the message loop responsible for responding to that message - the application main message loop - is blocked as a result of the fact that the message is posted from within a Synchronize() method.
i.e. your Synchronize()d method posts the message to close the form, but that message cannot and will not be processed by that forms window until your Synchronize()d method has completed, and that will not occur until the user has responded to the message box you are presenting in that method.
I hope that helps you understand what is going on in your code.
Upvotes: 3