Recent

Author Topic: SetTimer in a second form  (Read 2219 times)

engine32

  • New Member
  • *
  • Posts: 10
SetTimer in a second form
« on: October 30, 2021, 02:30:25 am »
Hello,
I have the main form, with a button that when clicked, launches a second form with ShowModal.
On the second form, I set a timer with SetTimer and on WM_TIMER I add a new line to a memo. It compiles with no errors, but the timer message never arrives. In Delphi is working. Also, if I launch the second form with Show (not ShowModal) it is working as well. However, I would like to use ShowModal.
Here are the two units:
Code: Pascal  [Select][+][-]
  1. unit testpas;
  2.  
  3. {$ifdef fpc}
  4.   {$mode objfpc}{$H+}
  5. {$endif}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  11.   unit2;
  12.  
  13. type
  14.  
  15.   { TForm1 }
  16.  
  17.   TForm1 = class(TForm)
  18.     Button1: TButton;
  19.     procedure Button1Click(Sender: TObject);
  20.   private
  21.  
  22.   public
  23.  
  24.   end;
  25.  
  26. var
  27.   Form1: TForm1;
  28.  
  29. implementation
  30. {$R *.lfm}
  31.  
  32. { TForm1 }
  33.  
  34. procedure TForm1.Button1Click(Sender: TObject);
  35. var
  36.   a: Tfrm2;
  37. begin
  38.   a := Tfrm2.Create(Application);
  39.   try
  40.     a.ShowModal;
  41.   finally
  42.     a.Free;
  43.   end;
  44. end;
  45.  
  46. end.
  47.  

and

Code: Pascal  [Select][+][-]
  1. unit unit2;
  2.  
  3. {$ifdef fpc}
  4.   {$mode objfpc}{$H+}
  5. {$endif}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Windows;
  11.  
  12. type
  13.   { TFrm2 }
  14.   TFrm2 = class(TForm)
  15.     mTest: TMemo;
  16.     procedure FormCreate(Sender: TObject);
  17.   private
  18.   public
  19.     procedure WndProc(var Msg: TMessage); override;
  20.   end;
  21.  
  22. const
  23.   ctTimerId = 1;
  24.  
  25. var
  26.   intTimerId: integer;
  27.  
  28. implementation
  29. {$R *.lfm}
  30. { TFrm2 }
  31. procedure TFrm2.FormCreate(Sender: TObject);
  32. begin
  33.   intTimerId := SetTimer(Handle, ctTimerId, 1000, nil);
  34. end;
  35.  
  36. procedure TFrm2.WndProc(var Msg: TMessage);
  37. begin
  38.   if Msg.Msg = WM_TIMER then
  39.   begin
  40.     mTest.Lines.Add('timer');
  41.   end
  42.   else
  43.   begin
  44.     inherited WndProc(Msg);
  45.   end;
  46. end;
  47.  
  48. end.
  49.  

Thank you.

howardpc

  • Hero Member
  • *****
  • Posts: 3977
Re: SetTimer in a second form
« Reply #1 on: October 30, 2021, 12:14:38 pm »
Why not make it cross-platform?
Main unit:
Code: Pascal  [Select][+][-]
  1. unit testpas;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Forms, StdCtrls,
  9.   Unit2;
  10.  
  11. type
  12.   TForm1 = class(TForm)
  13.     Button1: TButton;
  14.     procedure Button1Click(Sender: TObject);
  15.   end;
  16.  
  17. var
  18.   Form1: TForm1;
  19.   Frm2: TFrm2;
  20.  
  21. implementation
  22.  
  23. {$R *.lfm}
  24.  
  25. procedure TForm1.Button1Click(Sender: TObject);
  26. var
  27.   a: TFrm2;
  28. begin
  29.   a := TFrm2.Create(Nil);
  30.   try
  31.     a.ShowModal;
  32.   finally
  33.     a.Free;
  34.   end;
  35. end;
  36.  
  37. end.
Memo unit:
Code: Pascal  [Select][+][-]
  1. unit Unit2;
  2.  
  3. {$mode ObjFPC}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Forms, StdCtrls, ExtCtrls;
  9.  
  10. type
  11.   TFrm2 = class(TForm)
  12.     mTest: TMemo;
  13.     Timer1: TTimer;
  14.     procedure FormCreate(Sender: TObject);
  15.     procedure Timer1Timer(Sender: TObject);
  16.   end;
  17.  
  18. implementation
  19.  
  20. {$R *.lfm}
  21.  
  22. procedure TFrm2.FormCreate(Sender: TObject);
  23. begin
  24.   Timer1.Interval := 1000;
  25.   Timer1.Enabled := True;
  26. end;
  27.  
  28. procedure TFrm2.Timer1Timer(Sender: TObject);
  29. begin
  30.   mTest.Lines.Add('timer');
  31. end;
  32.  
  33. end.

engine32

  • New Member
  • *
  • Posts: 10
Re: SetTimer in a second form
« Reply #2 on: October 30, 2021, 05:45:27 pm »
Thank you. I was / am aware about this solution, however, I am still looking for a solution for SetTimer. I see three outcomes:
1 I miss something and I would like to know what and to fix the problem
2 it's simply the way Lazarus works, in other words, suppressing timer messages when the form is shown in modal mode
3 there is a bug in Lazarus

My application also uses messages for serial communication (no component) and for Ethernet working with winsock directly. I am checking to see if those messages are passing through.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1099
    • Lebeau Software
Re: SetTimer in a second form
« Reply #3 on: October 30, 2021, 08:17:25 pm »
I was / am aware about this solution, however, I am still looking for a solution for SetTimer.

Assuming ShowModal() is processing Win32 messages correctly, then I would guess that the 2nd Form's HWND is likely being recreated by ShowModal() after the OnCreate event has exited, thus losing your timer.  In which case, you can work around that by overriding the virtual CreateWnd() method instead to call SetTimer().

Code: Pascal  [Select][+][-]
  1. unit unit2;
  2.  
  3. {$ifdef fpc}
  4.   {$mode objfpc}{$H+}
  5. {$endif}
  6.  
  7. interface
  8.  
  9. uses
  10.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, Windows;
  11.  
  12. type
  13.   { TFrm2 }
  14.   TFrm2 = class(TForm)
  15.     mTest: TMemo;
  16.   private
  17.     intTimerId: integer;
  18.   public
  19.     procedure WndProc(var Msg: TMessage); override;
  20.     procedure CreateWnd; override;
  21.   end;
  22.          
  23. implementation
  24.  
  25. {$R *.lfm}
  26.  
  27. const
  28.   ctTimerId = 1;
  29.  
  30. { TFrm2 }
  31.  
  32. procedure TFrm2.CreateWnd;
  33. begin
  34.   inherited;
  35.   intTimerId := SetTimer(Handle, ctTimerId, 1000, nil);
  36. end;
  37.      
  38. procedure TFrm2.WndProc(var Msg: TMessage);
  39. begin
  40.   if Msg.Msg = WM_TIMER then
  41.   begin
  42.     mTest.Lines.Add('timer');
  43.   end
  44.   else
  45.   begin
  46.     inherited WndProc(Msg);
  47.   end;
  48. end;
  49.      
  50. end.
     

Otherwise, you should create your own hidden HWND using the Win32 CreateWindow/Ex() function directly just for the timer to use (that is what TTimer does internally).  Then you are not tied to the lifetime of the Form's HWND.

Of course, it would be easier to just use TTimer instead.

My application also uses messages for serial communication (no component) and for Ethernet working with winsock directly. I am checking to see if those messages are passing through.

They should, if ShowModal() is running a proper Win32 message loop.  Unless you are tying those operations to the Form's HWND and it gets recreated after you setup the operations.  If there is one thing the VCL has taught me, and this probably applies to the LCL too, is that a Form's HWND is reliable for sending messages to at any given moment, but it is not reliable for persistent registrations over time, unless you override the CreateWnd()/DestroyWnd() methods to update those registrations every time the HWND is recreated by the framework, for whatever reason.
« Last Edit: October 30, 2021, 08:20:13 pm by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

engine32

  • New Member
  • *
  • Posts: 10
Re: SetTimer in a second form
« Reply #4 on: November 06, 2021, 06:11:18 pm »
  With CreateWnd both timer and winsock messages are working properly. Thank you.

 

TinyPortal © 2005-2018