gioma 19 Posted July 6, 2022 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
Uwe Raabe 2057 Posted July 6, 2022 Could it be that you have PrimaryForm.Visible = True in the Object Inspector? 1 Share this post Link to post
gioma 19 Posted July 6, 2022 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
Uwe Raabe 2057 Posted July 6, 2022 Sorry for sounding blatant, but it seems you are doing something you shouldn't do. Unfortunately we can't see what it is. 1 Share this post Link to post
gioma 19 Posted July 6, 2022 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
gioma 19 Posted July 6, 2022 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: Share this post Link to post
gioma 19 Posted July 6, 2022 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
gioma 19 Posted July 6, 2022 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
Lajos Juhász 293 Posted July 6, 2022 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
PeterBelow 238 Posted July 6, 2022 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
Tom Chamberlain 47 Posted July 6, 2022 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
Remy Lebeau 1396 Posted July 6, 2022 (edited) 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 July 6, 2022 by Remy Lebeau 1 Share this post Link to post
Remy Lebeau 1396 Posted July 6, 2022 (edited) 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 July 6, 2022 by Remy Lebeau 2 Share this post Link to post
gioma 19 Posted July 7, 2022 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
Tom Chamberlain 47 Posted July 11, 2022 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