Recent

Author Topic: Automatical initialisation  (Read 603 times)

T3mp0

  • Newbie
  • Posts: 1
Automatical initialisation
« on: September 09, 2025, 04:30:17 pm »
It's my first post, so hello to everyone.
I'm completely new to Lazarus (literally started today).
I created a project and can't make it do two simple things:
  • Start my server automatically.
  • Give access to the procedure Log() to my second unit.
If I put the code in a button and press it after the form is shown, everything works fine. But I want it to start automatically with my program without the need to press a button.

The project has two units: Unit1 and Commands.
The first unit has a form, the second had code only.

Here is a part of both of them:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   IdBaseComponent, IdComponent, IdCustomTCPServer, IdTCPServer, IdContext,
  10.   Commands, IdGlobal;
  11.  
  12. type
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class(TForm)
  17.     btnStart: TButton;
  18.     btnStop: TButton;
  19.     MemoLog: TMemo;
  20.     TcpServer: TIdTCPServer;
  21.     procedure btnStartClick(Sender: TObject);
  22.     procedure btnStopClick(Sender: TObject);
  23.     procedure FormCreate(Sender: TObject);
  24.     procedure TcpServerConnect(AContext: TIdContext);
  25.     procedure TcpServerDisconnect(AContext: TIdContext);
  26.     procedure TcpServerExecute(AContext: TIdContext);
  27.   private
  28.     procedure Log(const S: string);
  29.   public
  30.  
  31.   end;
  32.  
  33. var
  34.   Form1: TForm1;
  35.  
  36. implementation
  37.  
  38. {$R *.lfm}
  39.  
  40. { TForm1 }
  41.  
  42. procedure TForm1.FormCreate(Sender: TObject);
  43. begin
  44.   // This doesn't work. Nothing is initiated.
  45.   TcpServer.DefaultPort := 5000;
  46.   TcpServer.Active := True;
  47.   Log('Server started on port ' + IntToStr(TcpServer.DefaultPort));
  48.   Commands.LogProc := @Log;
  49.   Log('Commands.LogProc created');
  50. end;
  51.  
  52. procedure TForm1.btnStartClick(Sender: TObject);
  53. begin
  54.   // This works perfectly fine.
  55.   TcpServer.DefaultPort := 5000;
  56.   TcpServer.Active := True;
  57.   Log('Server started on port ' + IntToStr(TcpServer.DefaultPort));
  58.   Commands.LogProc := @Log;
  59.   Log('Commands.LogProc created');
  60.  
  61. end;                                          
  62.  
  63. procedure TForm1.Log(const S: string);
  64. begin
  65.   MemoLog.Lines.Add(FormatDateTime('hh:nn:ss', Now) + ' ' + S);
  66. end;
  67.  

Second unit:
Code: Pascal  [Select][+][-]
  1. unit Commands;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, IdContext, IdGlobal;
  9.  
  10. type
  11.   TLogProc = procedure(const S: string) of object;
  12.  
  13. var
  14.   LogProc: TLogProc; // <-- set from Unit1
  15.  
  16. procedure HandleCommand(AContext: TIdContext; const Cmd: string; const Data: TBytes);
  17.  
  18. implementation
  19.  
  20. procedure Test(AContext: TIdContext; const Data: TBytes);
  21. var
  22.   ClientVersion, UserID: LongInt;
  23.   DataToSend: TIdBytes;
  24. begin
  25.     LogProc('We are in the Test procedure');
  26. end;    
  27.  

Why do neither the server nor the Log procedure work without pressing the button?
What's more, if I try to connect from the client, the program crashes. But not after I press the Start button.
The exact same code that’s in btnStart is also in FormCreate (I also tried FormShow with the same result), but it seems like the program is ignoring what’s in FormCreate.
I'll be grateful for an explanation and a solution.
Cheers

I forgot to mention. Calling Log from Unit1 works fine, no need to press the Start button.
« Last Edit: September 09, 2025, 04:35:37 pm by T3mp0 »

Thaddy

  • Hero Member
  • *****
  • Posts: 18363
  • Here stood a man who saw the Elbe and jumped it.
Re: Automatical initialisation
« Reply #1 on: September 11, 2025, 06:18:22 am »
You can bypass the messaging/event based code by overriding the form's afterconstruction procedure:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.AfterConstruction;// also in the form's interface section as protected override
  2. begin
  3.   inherited AfterConstruction; // <--- don't forget!
  4.   TcpServer.DefaultPort := 5000;
  5.   TcpServer.Active := True;
  6.   Log('Server started on port ' + IntToStr(TcpServer.DefaultPort));
  7.   Commands.LogProc := @Log;
  8.   Log('Commands.LogProc created');
  9. end;
The afterconstruction is called after the form and all its children are created and initialized but before the message loop starts.
The difference with FormCreate is that it completely bypasses the messaging/event system.
That means it is not interfered by the message loop.

Why this should work? Especially with complex forms the message queue can be clogged with messages at start.
This bypasses the message queue altogether.

I have not tested the code, because I do not have Indy components installed, but I have used it in similar scenario's.
« Last Edit: September 11, 2025, 07:19:01 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Lutz Mändle

  • Jr. Member
  • **
  • Posts: 83
Re: Automatical initialisation
« Reply #2 on: September 11, 2025, 06:48:31 am »
What is your operating system and what is in the lpr-file (I guess you have one)?
« Last Edit: September 11, 2025, 06:55:02 am by Lutz Mändle »

Thaddy

  • Hero Member
  • *****
  • Posts: 18363
  • Here stood a man who saw the Elbe and jumped it.
Re: Automatical initialisation
« Reply #3 on: September 11, 2025, 07:22:11 am »
I have tested your code with my afterconstruction suggestion and that works.
Just added missing execute code and renamed test() to HandleCommand proc implementation in command.pas.
(also works)
Removed the buttons and unnecessary stuff altogether for the benefit of this demo.
Used the Indy TidTCPServer as you did.
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, fphttpserver, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   IdTCPServer, IdCustomTCPServer, IdContext,commands;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     tcpserver: TIdTCPServer;
  17.     Memo1: TMemo;
  18.     procedure tcpserverExecute(AContext: TIdContext);
  19.   private
  20.      procedure AfterConstruction;override;
  21.      procedure Log(const s:string);
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30. {$R *.lfm}
  31.  
  32. { TForm1 }
  33.  
  34. procedure TForm1.tcpserverExecute(AContext: TIdContext);
  35. begin
  36.   //
  37. end;
  38.  
  39. procedure TForm1.AfterConstruction;
  40. begin
  41.   inherited AfterConstruction;
  42.   TcpServer.DefaultPort := 5000;
  43.   TcpServer.Active := True;
  44.   Log('Server started on port ' + IntToStr(TcpServer.DefaultPort));
  45.   Commands.LogProc := @Log;
  46.   Log('Commands.LogProc created');
  47. end;
  48.  
  49. procedure TForm1.Log(const s: string);
  50. begin
  51.   memo1.lines.add(s);
  52. end;
  53.  
  54. end.
Code: Pascal  [Select][+][-]
  1. unit Commands;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, IdContext, IdGlobal;
  9.  
  10. type
  11.   TLogProc = procedure(const S: string) of object;
  12.  
  13. var
  14.   LogProc: TLogProc; // <-- set from Unit1
  15.  
  16. procedure HandleCommand(AContext: TIdContext; const Cmd: string; const Data: TBytes);
  17.  
  18. implementation
  19.  
  20. procedure HandleCommand(AContext: TIdContext;const Cmd: string; const Data: TBytes);
  21. var
  22.   ClientVersion, UserID: LongInt;
  23.   DataToSend: TIdBytes;
  24. begin
  25.     LogProc('We are in the Test procedure');
  26. end;
  27.  
  28. end.
If it still does not work the only place where you could make a mistake is in tcpserverExecute but you did not show code for that.
« Last Edit: September 11, 2025, 07:45:43 am by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

 

TinyPortal © 2005-2018