procedure is not procedure of object lazarus / 64-FPC development ---------------------------------------------------------------------- hakelm · 2026-03-25 11:40 I need timers for non gui programs and since procedure is not procedure of object I had to introduce an otherwise unnecessary object that I can leave uninstantiated, see code below. I feel this is a bit clumsy. Is there a better way? Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  program Project1;{$mode objfpc}{$H+}uses  cthreads, Classes, sysutils, fptimer;type  tdummy=class (tobject)  procedure ontimer(Sender: TObject);end;var Timer: TFPTimer;  var dumb:tdummy; procedure tdummy.ontimer(Sender: TObject);begin   writeln('time is up');end; begin  Timer:= TFPTimer.Create(nil);  Timer.OnTimer:=@dumb.ontimer;  Timer.UseTimerThread:=true;  Timer.Interval:=1000;  Timer.Enabled:=true;  while true do begin    sleep(100);  end;end. ---------------------------------------------------------------------- Thaddy · 2026-03-25 12:03 Well, for one you need a message loop, not a dumb loop, if you want the timer events to be processed...... As your code stands it is never going to work. Try this (uses a little trick for the message loop):Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$mode objfpc}{$H+}uses  Classes, sysutils, fptimer; type  tdummy = class(TObject)    procedure ontimer(Sender: TObject);  end; var  Timer: TFPTimer;  dumb: tdummy; procedure tdummy.ontimer(Sender: TObject);begin  Writeln('time is up');end; begin  dumb := tdummy.Create;  Timer := TFPTimer.Create(nil);  Timer.OnTimer := @dumb.ontimer;  Timer.UseTimerThread := False;    // timer works on the main thread  Timer.Interval := 1000;  Timer.Enabled := True;   // Message loop: repeatedly check for timer events  while True do  begin    // For a TFPTimer, the timer event is triggered during    // CheckSynchronize (or any other "idle" processing).    CheckSynchronize;  // trick! CheckSynchronize has all the code we need    Sleep(10);   // small pause to avoid busy‑wait  end;end. ---------------------------------------------------------------------- Fibonacci · 2026-03-25 12:08 If you are using FPC trunk (3.3.1), you can use: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i {$modeswitch anonymousfunctions}{$modeswitch functionreferences} // optionally // ... Timer.OnTimer := procedure(Sender: TObject)begin  writeln('time is up');end; ---------------------------------------------------------------------- Thaddy · 2026-03-25 12:16 @Fibonacci With due respect, that ain't working without a message loop in a console application. I added a working example, with a neat little trick, to my answer. (btw: in mode delphi, you don't need the modeswitches: these are included) ---------------------------------------------------------------------- Xenno · 2026-03-25 12:23 We can use a thread; preferably in a reusable unit. For example: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit uTimeoutThread; ... type  TSimpleProc = procedure;   TTimeoutThread = class(TThread)  private    FProcedure: TSimpleProc;    FTimeoutMS: Integer;  protected    procedure Execute; override;  public    constructor Create(PrmMS: Integer; PrmPrc: TSimpleProc);    destructor Destory; override;  end;        implementation { TTimeoutThread } constructor TTimeoutThread.Create(PrmMS: Integer; PrmPrc: TSimpleProc);begin  inherited Create(True);  FreeOnTerminate := True;  FTimeoutMS := PrmMS;  FProcedure := PrmPrc;end;            destructor TTimeoutThread.Destory;begin  FProcedure();end;       procedure TTimeoutThread.Execute;begin  Sleep(FTimeoutMS);end;    end. Usage Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure PrcTimesUp;begin   writeln('time is up');end; var  MyTimeout: TTimeoutThread;begin  MyTimeout := TTimeoutThread.Create(1000, @PrcTimesUp);  MyTimeout.Start;  // or maybe:  TTimeoutThread.Create(1000, @PrcTimesUp).Start;end. ---------------------------------------------------------------------- Thaddy · 2026-03-25 12:28 @Xenno WHERE IS YOUR MESSAGE LOOP This about processing messages in a console application. Ofcourse you can use a thread, but the TTimer already provides an option for that and it is not relevant! That said, I have to explain my code a bit: - normally you would write your message loop with PeekMessage / DispatchMessage on Windows and would maybe inadvertently draw in the LCL for that reason. - CheckSynchronize is cross-platform, does the same and does not rely on the LCL Hence the code works in console applications, but is much more lightweight than pulling in the LCL. ---------------------------------------------------------------------- Fibonacci · 2026-03-25 12:28 Quote from: Thaddy on March 25, 2026, 01:16:07 pm@Fibonacci With due respect, that ain't working without a message loop in a console application. Weird, works for me Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program test; {$mode objfpc}{$H+}{$modeswitch anonymousfunctions} uses SysUtils, fpTimer; var  Timer: TFPTimer; begin  Timer := TFPTimer.Create(nil);  Timer.OnTimer := procedure(Sender: TObject)  begin    writeln('time is up');  end;  Timer.UseTimerThread := true;  Timer.Interval := 1000;  Timer.Enabled := true;   while true do begin    writeln('main loop');    sleep(1000);  end;end. ---------------------------------------------------------------------- hakelm · 2026-03-25 12:32 If you observe that there is a separate thread for the timer you may understand that the code works exactly as expected, at least with Free Pascal Compiler version 3.2.2 on Ubuntu 22: he@x2:/home/nisse/p/hep/test/ipc$ ./clitimer time is up time is up time is up time is up time is up time is up time is up ^C he@x2:/home/nisse/p/hep/test/ipc$ My question remains, is there a more elegant solution? H ---------------------------------------------------------------------- Thaddy · 2026-03-25 12:36 @Fibonacci Ah, I missed you had timerthread as true: that does a message loop indeed. My example was using the main thread only: Timer.UseTimerThread := False ---------------------------------------------------------------------- Xenno · 2026-03-25 12:41 @Thaddy It is an alternative using timeout. The caller may repeat when needed; works like js's setTimeout. Sorry, for not relevant. ---------------------------------------------------------------------- cdbc · 2026-03-25 13:02 Hi I dunno, but this works for me:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i  program test; uses  {$ifdef linux}  cthreads,  {$endif}  sysutils, fptimer; type  TEventHandlers = object    procedure TimerEvent(Sender: TObject);  end; var  Count  : integer = 0; procedure TEventHandlers.TimerEvent(Sender: TObject);begin  writeln('timer event fired');  inc(count);end;  var  Events : TEventHandlers;  Timer  : TFPTimer;  begin  Timer                := TFPTimer.Create(nil);  Timer.Enabled        := false;  Timer.UseTimerThread := true;  Timer.Interval       := 1000;  Timer.OnTimer        := @Events.TimerEvent;  Timer.Enabled        := true;   writeln('waiting... ');  while count < 5 do  begin    sleep(1);  end;   Timer.Free;end. Regards Benny ---------------------------------------------------------------------- Fred vS · 2026-03-25 13:02 Quote from: hakelm on March 25, 2026, 12:40:57 pmI need timers for non gui programs and since procedure is not procedure of object I had to introduce an otherwise unnecessary object that I can leave uninstantiated, see code below. Is there a better way? You may use TCustomApplication from CustApp: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$mode objfpc}{$H+}uses  cthreads, Classes, sysutils, CustApp, fptimer; type{ TCustConsole }   TcustConsole = class(TCustomApplication)    private       procedure ConsoleRun;    protected       procedure doRun;      override;    public       procedure ontimer(Sender: TObject);  // here procedure of object.    constructor Create(TheOwner: TComponent);      override;  end; var Timer: TFPTimer;       x : integer = 0;  { TCustConsole } procedure TCustConsole.ConsoleRun;begin  Timer:= TFPTimer.Create(nil);  Timer.OnTimer:=@ontimer;  Timer.UseTimerThread:=true;  Timer.Interval:=1000;  Timer.Enabled:=true;  while true and (x < 5) do begin    sleep(100);    end;end; procedure TCustConsole.ontimer(Sender: TObject);begin   writeln('time is up');   inc(x);end; procedure TCustConsole.doRun;begin   ConsoleRun;   writeln('Ciao...');   Timer.free;   Terminate;end; constructor TCustConsole.Create(TheOwner: TComponent);begin  inherited Create(TheOwner);  StopOnException := True;end; var   Application: TCustConsole;begin  Application       := TCustConsole.Create(Nil);  Application.Title := 'Custom Console';  Application.Run;  Application.Free;end. ---------------------------------------------------------------------- hakelm · 2026-03-25 13:32 For Thaddy I have the same problem but my new unusable tongue comes from my left arm. Cheers and just face it and sail on. ---------------------------------------------------------------------- hakelm · 2026-03-25 14:20 Thanks, My objective is simply to clean up some old fpc-programs. Introducing TCustomApplication would be to much work. H ---------------------------------------------------------------------- ccrause · 2026-03-25 16:52 Quote from: hakelm on March 25, 2026, 12:40:57 pmI need timers for non gui programs and since procedure is not procedure of object I had to introduce an otherwise unnecessary object that I can leave uninstantiated, see code below. There is a trick one can use: put the procedure pointer in the Code field of a TMethod record, set the Data pointer to nil (not necessary for this example, but try to be explicit anyway) then cast the record as a TNotifyEvent and assign to the event handler: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$mode objfpc}{$H+}uses  cthreads, Classes, SysUtils, fptimer; var  Timer: TFPTimer;  methodHack: TMethod; procedure onTimerProcedure;begin  writeln('time is up');end; begin  Timer := TFPTimer.Create(nil);  methodHack.Code := @onTimerProcedure;  methodHack.Data := nil;  Timer.OnTimer := TNotifyEvent(methodHack);  Timer.UseTimerThread := True;  Timer.Interval := 1000;  Timer.Enabled := True;  while True do  begin    sleep(100);  end;end. Whether this is good practice is debatable, but sometimes wrapping trivial contextless handlers in a class is overkill. Use with care. ---------------------------------------------------------------------- Thaddy · 2026-03-26 12:27 There is a convenience function for that somewhere. Forgot where, but is analogous to the KOL version. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function MakeMethod(code:pointer;data:pointer = nil):TNotifyEvent;inline;begin  TMethod(Result).Code := code;  TMethod(Result).Data := data;end;(Even if I wrote it, which I did, I forgot where it ended up. It is very old.) ---------------------------------------------------------------------- kagamma · 2026-03-26 14:06 You can declare it as class procedure. This way you don't have to instantiate an object. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i tdummy = class  class procedure ontimer(Sender: TObject);end;... Timer.OnTimer := @tdummy(nil).ontimer; ---------------------------------------------------------------------- PascalDragon · 2026-03-26 22:17 Quote from: ccrause on March 25, 2026, 05:52:09 pmWhether this is good practice is debatable, but sometimes wrapping trivial contextless handlers in a class is overkill. Please note that you should at least provide a procedure with a suitable signature, namely in case of TNotifyEvent with both Self and Sender parameters, cause depending on the platform and calling convention there might be issues with stack cleanup otherwise. Quote from: hakelm on March 25, 2026, 12:40:57 pmI need timers for non gui programs and since procedure is not procedure of object I had to introduce an otherwise unnecessary object that I can leave uninstantiated, see code below. I feel this is a bit clumsy. Is there a better way? Using a dummy class is in fact the usual way to go. Or put all your code inside a class and call that from the main function (e.g. something like TMyApplication.Run or so, but without having to inherit from something like TCustomApplication). ---------------------------------------------------------------------- hakelm · 2026-03-27 09:14 I didn't find any better way than to modify TFPTimer. Find the code attached. ---------------------------------------------------------------------- kupferstecher · 2026-03-27 10:28 Quote from: hakelm on March 25, 2026, 12:40:57 pm[...] for non gui programs [...] had to introduce an otherwise unnecessary object [...] OOP is also for non-GUI programs really helpful. I personally even use it on microcontrollers with static memory (using object instead of class). ---------------------------------------------------------------------- Thaddy · 2026-03-27 11:42 @hakelm The constructor is wrong: it needs to set the inherited parent. It may work by accident, but only because you want to initialize as nil. ---------------------------------------------------------------------- rvk · 2026-03-27 12:27 Quote from: Thaddy on March 27, 2026, 12:42:34 pmThe constructor is wrong: it needs to set the inherited parent. Parent? For a non-visual component? Do you mean owner? Even then, it can remain ownerless but you do need to free the instance yourself then. I would keep the Create the same as the base class. That way you can pass nil in the create code or assign an owner if you want. I'm also missing the " of object" after the tproc (which is for OnTimer). ---------------------------------------------------------------------- Thaddy · 2026-03-27 13:05 The IDE says so Anyway, do so. Also: the code flow is otherwise ok, since he sets the OnTimer (TNotifyEvent) from his own OnWhatevertimer, which is NOT a notify event, so that part is correct. Suggestion is:Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit noPoOtimer;{$mode delphi}{$H+}interface uses  Classes, sysutils, fptimer;type  TTimerProc=procedure(Sender: TObject); // NOT of object...taken care of  { tNoPoOTimer }  TNoPoOTimer=class(TFPTimer)  private    fOnNoPoOtimer:TTimerProc;    procedure setOnNoPoOtimer(AValue: TTimerProc);    procedure timeisup(Sender: TObject);  public    constructor create(aOwner:TComponent);override;    property OnNoPoOtimer:TTimerProc read fOnNoPoOtimer write setOnNoPoOtimer;  end;implementation procedure tNoPoOTimer.timeisup(Sender: TObject);begin  if assigned(OnNoPoOtimer) then    OnNoPoOtimer(sender);end; procedure tNoPoOTimer.setOnNoPoOtimer(AValue: TTimerProc);begin  self.OnTimer:=timeisup;  fOnNoPoOtimer:=AValue;end; constructor tNoPoOTimer.create(aOwner:TComponent);begin  inherited create(aOwner);  self.OnTimer:=timeisup; //<-----end;end.What he did is a redirection. ---------------------------------------------------------------------- hakelm · 2026-03-27 13:23 I seldom use gui-programs, but you are right a constructor with (AOwner: TComponent) is better. tproc is used when setting onnopootimer for a "normal" procedure ie not for a "method" procedure so it can't have  an "of object "specification. ---------------------------------------------------------------------- valdir.marcos · 2026-03-30 04:38 Interesting. ---------------------------------------------------------------------- hakelm · 2026-03-30 07:58 I should perhaps have pointed out that for methods or procedures of object you use the inherited tNoPoOTimer.OnTimer for "normal" procedures you use tNoPoOTimer.onnopootimer ---------------------------------------------------------------------- rvk · 2026-03-30 08:12 Quote from: Thaddy on March 27, 2026, 02:05:10 pmWhat he did is a redirection. Ah, yes, you are correct. I missed the point to assigning a normal procedure. BTW. I would have suggested a slightly adapted code. There would be no need for using the current FOnTimer. That would even break existing code. If you are inheriting the TFPTimer the 'proper' way to would be to override the caller of FOnTimer. In TFPTimer this would be the Timer procedure itself. That way, the original FOnTimer would still usable but you create an extra FOnProcTimer. At least this is how it's done in all classes throughout the VCL code in Delphi. Something like this: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TTimerProc = procedure(Sender: TObject);   TProcTimer = class(TFPTimer)  private    FOnProcTimer: TTimerProc;  protected    procedure Timer; override;  public    property OnProcTimer: TTimerProc read FOnProcTimer write FOnProcTimer;  end; procedure TProcTimer.Timer;begin  if Active and Assigned(FOnProcTimer) then    FOnProcTimer(Self);  inherited;end; But... there is a big problem. TFPTimer is really weirdly designed because it uses Assigned(FOnTime) inside StartTimer. So TFPTimer.Timer is create virtual but if you are overriding that in an inherited class, you don't get the expected functionality. They didn't have 'proper' object orientation and inheritance in mind when creating that class. I would have used TTimer but unfortunately TTimer is depended on the LCL app loop... so... for a quick hack, 'misusing' TFPTimer.OnTimer is fine, but when using this class in a much bigger project, you might also want to override StartTimer (luckely that's also virtual defined) and remove the "Assigned(FOnTimer)" from the if statement at the top. ---------------------------------------------------------------------- LeP · 2026-03-30 13:00 I use this, I had create it some times ago and used as standard in my project. It can be used as Component in Design mode (in Delphi), at runtime, in a thread or with a method. If used at runtime (and with a method or Thread procedure) it doesn't depend from VCL. Should be compatible with FPC (I tried it few month ago), but I don't know if in FPC needs LCL or not. ---------------------------------------------------------------------- Remy Lebeau · 2026-05-03 21:00 Quote from: ccrause on March 25, 2026, 05:52:09 pmThere is a trick one can use: put the procedure pointer in the Code field of a TMethod record, set the Data pointer to nil (not necessary for this example, but try to be explicit anyway) then cast the record as a TNotifyEvent and assign to the event handler Small nitpick - the procedure should have Self and Sender parameters declared explicitly, since that is how the compiler will actually be calling the procedure, even if the procedure doesn't look at them: Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure onTimerProcedure(Self: Pointer; Sender: TObject); Like you said, "try to be explicit". The Self will be the TMethod.Data value, and the Sender will be the timer object. Dang it. I didnt notice that there was a 2nd page to this discussion. PascalDragon had already pointed out that same issue: Quote from: PascalDragon on March 26, 2026, 11:17:08 pmPlease note that you should at least provide a procedure with a suitable signature, namely in case of TNotifyEvent with both Self and Sender parameters, cause depending on the platform and calling convention there might be issues with stack cleanup otherwise. ---------------------------------------------------------------------- Thaddy · 2026-05-05 14:40 Except that I already pointed out OP did nothing wrong, maybe this old code will help you? (Trunk only):Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program metref;{$mode delphi}type  TMeth = procedure(x: Integer) of object;  TMethRef = reference to procedure(x: Integer); procedure MethRefToMethPtr(const MethRef; var MethPtr);type  TVtable = array[0..3] of Pointer;  PVtable = ^TVtable;  PPVtable = ^PVtable;begin  // 3 is offset of Invoke, after QI, AddRef, Release  TMethod(MethPtr).Code := PPVtable(MethRef)^^[3];  TMethod(MethPtr).Data := Pointer(MethRef);end; function MakeMethRef: TMethRef;var  y: Integer;begin  y := 30;  Result := procedure(x: Integer)  begin    Writeln('x = ', x, ' and y = ', y);  end;end; procedure P;var  x: TMethRef;  m: TMeth;begin  x := MakeMethRef();  MethRefToMethPtr(x, m);  Writeln('Using x:');  x(10);  Writeln('Using m:');  m(10);end; begin  p();end.Courtesy of Barry Kelly.