Jump to content
gioma

Application.CreateForm : Shows Main form before Application.run

Recommended Posts

Hello everyone,
I'm facing a problem with Delphi Alexandria.
In some cases I would like not to show the main form of the project.
To do this, simply use the following code:

 

  
  //... other preliminary operations

  Application.Initialize;
  Application.Title := GF_GetAppName;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TPrimaryForm, PrimaryForm);

  if pos('-NOSHOW', UpperCase(CmdLine)) > 0 then
  begin
    Application.ShowMainForm := False;
    PrimaryForm.visible:=false;
  end;

  Application.Run;

 

Obviously the project is quite complex, but these instructions should be enough to stop the Main Form from doing the "Show" event.

 

In debugging, on the other hand, I realized that when it executes the line 

 

Application.CreateForm(TPrimaryForm, PrimaryForm);

 

The show event of the form is executed, before:

 

  Application.Run;

 

 

Then the instructions are executed:

     Application.ShowMainForm: = False;
     PrimaryForm.visible: = false;

 

which hide the main form, but I didn't want it to be shown at all.

 

finally executes

 

Application.Run;

 

 

and at this point I expect that the form (if the -NOSHOW parameter is not passed) activates the onShow event and not that it is activated instead by the "Application.CreateForm" statement

 

Why does this happen? What am I missing?

 

Share this post


Link to post

Could it be that you have PrimaryForm.Visible = True in the Object Inspector?

  • Like 1

Share this post


Link to post
1 hour ago, Uwe Raabe said:

Could it be that you have PrimaryForm.Visible = True in the Object Inspector?

Yes, indeed it is true.
Trivially the problem is that.
But now I have another problem.
I create and modify objects of PrimaryForm at runtime (including frames) and it happens that if I set PrimaryForm.visible = false in the Object Inspector when I make the window visible again (show or visible=true) the program remains blocked, as if it could not redraw the interface.
In the code I have no errors, all the instructions are executed without problems, but when I view the window it remains frozen, as if it could not redraw it.

Share this post


Link to post

Sorry for sounding blatant, but it seems you are doing something you shouldn't do. Unfortunately we can't see what it is.

  • Like 1

Share this post


Link to post

It's a very big project, so it's hard for me post the code.
Basically the program connects to a socket server and performs some operations.
If started by another program, which is installed as a server, the program starts hidden, with an icon on the tray bar.
The operations it performs with the socket server update the interface (with messages, buttons/pannels that become visible / invisible).
If I start the program and the main form has the option visible=true in the object explorer everything works fine.
Otherwise, if the program is hidden, when the interface is shown it presents transparent pieces as if it had not been able to draw it and then freezes until it crashes.

Share this post


Link to post

Simple example:

 

main Form

unit MainFormDefault;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Buttons, Vcl.StdCtrls, Vcl.ExtCtrls;

type
  TFmainFormDef = class(TForm)
    Panel1: TPanel;
    Panel2: TPanel;
    ComboBox1: TComboBox;
    SpeedButton1: TSpeedButton;
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  FmainFormDef: TFmainFormDef;

implementation

{$R *.dfm}

procedure TFmainFormDef.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:=CaFree;
end;

procedure TFmainFormDef.FormCreate(Sender: TObject);
var
  T:TThread;
begin
  T:=TThread.CreateAnonymousThread(
  Procedure
  begin
    TThread.Sleep(2000);
    FmainFormDef.Visible:=true;
  end);
  T.FreeOnTerminate:=true;
  T.Start;
end;

end.

File .dpr

program TestHideMainForm;

uses
  Vcl.Forms,
  MainFormDefault in 'MainFormDefault.pas' {FmainFormDef},
  MainForm in 'MainForm.pas' {FmainForm};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TFmainFormDef, FmainFormDef);
  Application.ShowMainForm:=false;
  FmainFormDef.Visible:=false;

  Application.Run;
end.

 

Here I start a simple program in hidden mode, then with a simple thread I simulate an event that shows it.
In this case everything goes smoothly but when I close the program I get this error:

 

image.thumb.png.542077fe7fc1bf54ef310bdb9821d280.png

Share this post


Link to post

in the original project use of graphic components.
In that case I have problems with the visualization of these components (buttons, labels, etc.) ..
All this always if the form has the visible = false property.
It is probably a component problem, but the fact that with standard components I get such an error makes me think there is so much more.

Share this post


Link to post

Ok, I solved the problem with the example :

 

procedure TFmainFormDef.FormCreate(Sender: TObject);
var
  T:TThread;
begin
  T:=TThread.CreateAnonymousThread(
  Procedure
  begin
    TThread.Sleep(2000);
    TThread.Synchronize(nil,
      procedure
      begin
        FmainFormDef.Visible:=true;
      end);
  end);
  T.FreeOnTerminate:=true;
  T.Start;
end;

The statement that makes the form visible had to be synchronized with the main thread.
At this point I think the problem is related to the components.

Share this post


Link to post
24 minutes ago, gioma said:

The statement that makes the form visible had to be synchronized with the main thread.
At this point I think the problem is related to the components.

It's by design. Delphi form and VCL should be used only in the main thread.

Share this post


Link to post
4 hours ago, gioma said:

in the original project use of graphic components.
In that case I have problems with the visualization of these components (buttons, labels, etc.) ..
All this always if the form has the visible = false property.
It is probably a component problem, but the fact that with standard components I get such an error makes me think there is so much more.

Just to explain your problem: the VCL creates window handles on an as needed basis, which may delay this action until the window is shown and needs to be drawn.  In your example (without Synchronize) this causes the window handle to be created in the background thread, not the main thread, and that thread has no message loop...

Share this post


Link to post

Did you try it this way:

  Application.Initialize;
  Application.Title := GF_GetAppName;
  Application.MainFormOnTaskbar := True;
  Application.ShowMainForm := False; 
  Application.CreateForm(TPrimaryForm, PrimaryForm);

  if NOT (pos('-NOSHOW', UpperCase(CmdLine)) > 0) then
   Application.ShowMainForm := True; 

  Application.Run;

We have a class we inherit from TForm that we use to display our splash screen for EXE's and to prompt for user login, the only difference is we terminate the app if they can't pass the login.

 

Quote

begin
  Application.Initialize;
  Application.Title := 'Some App';
  Application.ShowMainForm := False;
  Application.CreateForm(TfrmMainForm, frmMainForm);
  if frmMainForm.DoLogonScreen then
   Application.ShowMainForm := True
  else
   Application.Terminate;
  Application.Run;
end.

This way the frmMainForm.Show is not called with Application.CreateForm but it is when the Application.Run is called.

 

Share this post


Link to post
11 hours ago, gioma said:

  if pos('-NOSHOW', UpperCase(CmdLine)) > 0 then

 

Just on a side note: the RTL has a FindCmdLineSwitch() function in the SyUtils unit:

if FindCmdLineSwitch('NOSHOW') then

 

Edited by Remy Lebeau
  • Like 1

Share this post


Link to post
2 hours ago, Tom Chamberlain said:

Did you try it this way:


Application.ShowMainForm := False;
if NOT (pos('-NOSHOW', UpperCase(CmdLine)) > 0) then
  Application.ShowMainForm := True;

 

That can be simplified:

Application.ShowMainForm := (Pos('-NOSHOW', UpperCase(CmdLine)) = 0);

Or:

Application.ShowMainForm := not FindCmdLineSwitch('NOSHOW');
Quote

We have a class we inherit from TForm that we use to display our splash screen for EXE's and to prompt for user login, the only difference is we terminate the app if they can't pass the login.

I wouldn't even bother calling Terminate() and Run() in that case, just exit immediately instead:

begin
  Application.Initialize;
  Application.Title := 'Some App';
  Application.CreateForm(TfrmMainForm, frmMainForm);
  if (not frmMainForm.DoLogonScreen) then Exit;
  Application.Run;
end. 

Or:

begin
  Application.Initialize;
  Application.Title := 'Some App';
  Application.CreateForm(TfrmMainForm, frmMainForm);
  if frmMainForm.DoLogonScreen then
    Application.Run;
end. 

Of course, it would be better to not even create the MainForm at all if you are not going to use it, eg:

begin
  Application.Initialize;
  Application.Title := 'Some App';
  if DoLogonScreen then
  begin
    Application.CreateForm(TfrmMainForm, frmMainForm);
    Application.Run;
  end;
end.

 

Edited by Remy Lebeau
  • Like 2

Share this post


Link to post

Eventually I got to the heart of the problem.
There is a thread in the program that uses the pipe to communicate with other programs.
In particular, the program receives a message to its pipe when it is started but an instance of it already exists in that session.
It does the same thing even when it's hidden and double-clicked on the TryIcon.
This message instructs the application to show itself and put itself in the foreground. However, if the program is hidden, this instruction blocks both the pipe thread and the main thread.
There was no synchronization with the main thread.

Share this post


Link to post
On 7/6/2022 at 4:58 PM, Remy Lebeau said:

That can be simplified:


Application.ShowMainForm := (Pos('-NOSHOW', UpperCase(CmdLine)) = 0);

Or:


Application.ShowMainForm := not FindCmdLineSwitch('NOSHOW');

I wouldn't even bother calling Terminate() and Run() in that case, just exit immediately instead:


begin
  Application.Initialize;
  Application.Title := 'Some App';
  Application.CreateForm(TfrmMainForm, frmMainForm);
  if (not frmMainForm.DoLogonScreen) then Exit;
  Application.Run;
end. 

Or:


begin
  Application.Initialize;
  Application.Title := 'Some App';
  Application.CreateForm(TfrmMainForm, frmMainForm);
  if frmMainForm.DoLogonScreen then
    Application.Run;
end. 

Of course, it would be better to not even create the MainForm at all if you are not going to use it, eg:


begin
  Application.Initialize;
  Application.Title := 'Some App';
  if DoLogonScreen then
  begin
    Application.CreateForm(TfrmMainForm, frmMainForm);
    Application.Run;
  end;
end.

 

 

It has been many years since implementing the method we use now but I remember there being some memory leaks or issues if we never let 'Application.Run' fire.  I think it had something to do with possible objects being created in the TfrmMainForm's on create and if you never call 'Application.Run' it never called free on the form or something strange like that.  We may have done it this way just to be safe and make sure things got cleaned up under every circumstance.

 

But it looks like the issues was thread related anyway, not related to when the main form was set to visible. 

 

 

 

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

×