views:

415

answers:

3

I need a Timer in a 'no form' Delphi unit (there's still a main unit with a form), so I do this:

unit ...

interface

type
  TMyTimer = Class(TTimer)
  public
    procedure OnMyTimer(Sender: TObject);
  end;

var
  MyTimer: TMyTimer;

implementation

procedure TMyTimer.OnMyTimer(Sender: TObject);
begin
  ...
end;

initialization

MyTimer := TMyTimer.Create(nil);
with MyTimer do
begin
  Interval := 1000;
  Enabled := True;
  OnTimer := OnMyTimer;
end;

finalization

FreeAndNil(MyTimer);

The problem is that the OnMyTimer procedure is never run. I'll truly appreciate any ideas as to why :-)

+6  A: 

Apart from the fact you created a MyTimer and freed a MouseTimer, I don't see anything wrong with your code (I do assume you use your code in a GUI application or at least are having a message loop)

This code sample works with Delphi 5. The Hello Worldget's written to the eventlog every second.

unit Unit2;

interface

uses
  extctrls;

type
  TMyTimer = Class(TTimer)
  public
    procedure OnMyTimer(Sender: TObject);
  end;

var
  MyTimer: TMyTimer;

implementation

uses
  windows, sysutils, classes;

procedure TMyTimer.OnMyTimer(Sender: TObject);
begin
  OutputDebugString(PChar('Hello World'));
end;

initialization

MyTimer := TMyTimer.Create(nil);
with MyTimer do
begin
  Interval := 1000;
  Enabled := True;
  OnTimer := OnMyTimer;
end;

finalization
  FreeAndNil(MyTimer);

end.
Lieven
I don't know why, but I made it work by rearranging units in the *uses* sections (ExtCtrls in interface, SysUtils and Windows in implementation). Go figure :-)
Mikhail
Mikhail, I assure you the units' usage order had no effect on your program's success. The problem was elsewhere.
Rob Kennedy
I Have to second that.
Lieven
+7  A: 

In order for a timer to work, your program must process messages. In a GUI program, that part is automatic; the TApplication class provides that for you. But you say you have a "no form" program, so I suppose you're probably not calling Application.Run in your DPR file.

To use a timer, you need to process messages. The typical starting point for a message pump is code like this:

while Integer(GetMessage(Msg, 0, 0, 0)) > 0 do begin
  TranslateMessage(Msg);
  DispatchMessage(Msg);
end;

When a timer's period has elapsed, the OS effectively places a wm_Timer message in your program's message queue. The GetMessage call fetches messages from the queue, and DispatchMessage calls the destination window's window procedure. TTimer creates a hidden window for itself to serve as the target for those messages, and DispatchMessage makes sure they get there.

Rob Kennedy
Often creating a message loop just to use a timer seems overkill to me. You can use other mechanism tha work better in threads and similar objects. WaitForSingle object for instance etc...
Runner
Absolutely. I could have gone on at length about alternatives to TTimer, but I decided to limit my answer to the question at hand.
Rob Kennedy
I meant no disrespect. I just wanted, if somebody reads it, that they are aware of this.
Runner
+1  A: 

Is your unit used by other units or not? If this unit isn't used by others it won't even get in the initialization section. Or maybe the unit is finalized sooner then you think.

Put a breakpoint at the MyTimer := TMyTimer.Create(nil); line and at the FreeAndNil(MyTimer) line and run your application. Make sure that the timer is created when you want it to and not destroyed too early.

The_Fox