Delphi 在运行时创建并运行TTimer

Delphi 在运行时创建并运行TTimer,delphi,delphi-7,Delphi,Delphi 7,我试图在Delphi中实现一种类似于Javascript的setTimeout()过程的行为:在延迟几秒钟后运行。为此,我在运行时创建了一个TTimer,运行它,然后释放它 这是我的密码: procedure createAndRunTimer(); procedure goTimer(Sender: TObject); begin (sender as ttimer).enabled := false; // do stuff here sender.free;

我试图在Delphi中实现一种类似于Javascript的
setTimeout()
过程的行为:在延迟几秒钟后运行。为此,我在运行时创建了一个
TTimer
,运行它,然后释放它

这是我的密码:

procedure createAndRunTimer();
  procedure goTimer(Sender: TObject);
  begin
    (sender as ttimer).enabled := false;
    // do stuff here
    sender.free;
  end;
var
  t : TTimer;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;
  t.OnTimer := goTimer(t);
end;
但我的代码无法编译,编译器返回以下错误:

[DCC错误]unit1.pas(2153):E2010不兼容类型:“TNotifyEvent”和“过程、非类型化指针或非类型化参数”


有任何提示吗?

TNotifyEvent
声明为:

TNotifyEvent = procedure(Sender: TObject) of object;
对象的
使其成为一个闭包,它是一种特殊类型的方法指针,带有两个指针-一个指向对象的指针,一个指向在对象上被调用的非静态类方法的指针。因此,您不能将独立函数,当然也不能将嵌套函数直接分配给
TNotifyEvent
。这就是编译器所抱怨的

因此,您需要声明一个类来包装
OnTimer
事件处理程序,例如:

type
  TTimerEvents = class
  public
    procedure goTimer(Sender: TObject);
  end;

procedure TTimerEvents.goTimer(Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // NOTE: you cannot destroy the Sender object from here, you must delay
  // the destruction until after this handler exits!  You can post a
  // custom window message via PostMessage() and have the message handler
  // call Sender.Free().  Or, you can use a worker thread to call
  // Sender.Free() via TThread.Synchronize() (or TThread.Queue() in Delphi
  // 8 and later).  Or, in Delphi 10.2 Tokyo and later, you can call
  // Sender.Free() via TThread.ForceQueue().  Or, use whatever other
  // mechanism you want to use to call Sender.Free(), as long as it works
  // asynchronously and calls Sender.Free() in the same thread that
  // constructed the TTimer object ...
end;

var
  events: TTimerEvents;

procedure createAndRunTimer();
var
  t : TTimer;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;
  t.OnTimer := events.goTimer;
  t.Enabled := True;
end;

initialization
  events := TTimerEvents.Create;
finalization
  events.Free;
或者,您可以使用
方法,这样就不需要包装类的实际实例:

type
  TTimerEvents = class
  public
    class procedure goTimer(Sender: TObject);
  end;

class procedure TTimerEvents.goTimer(Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // delay-destroy the Sender as needed ...
end;

procedure createAndRunTimer();
var
  t : TTimer;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;
  t.OnTimer := TTimerEvents.goTimer;
  t.Enabled := True;
end;
或者,在Delphi 2006及更高版本中,您可以使用:

也就是说,有一种方法可以在不使用任何类包装器的情况下使用独立函数:

procedure goTimer(Self: Pointer; Sender: TObject);
begin
  (Sender as TTimer).Enabled := false;

  // do stuff here

  // delay-destroy the Sender as needed ...
end;

procedure createAndRunTimer();
var
  t : TTimer;
  event : TNotifyEvent;
begin
  t := TTimer.Create(frmprinc);
  t.Interval := 5000;

  TMethod(event).Data := nil; // or whatever you want to pass to the Self parameter...
  TMethod(event).Code := @goTimer;
  t.OnTimer := event;

  t.Enabled := True;
end;

我使用了Torry的这个过程作为延迟,它在执行时不会锁定线程:

procedure Delay(dwMilliseconds: Longint);
var
  iStart, iStop: DWORD;
begin
  iStart := GetTickCount;
  repeat
    iStop := GetTickCount;
    Application.ProcessMessages;
    Sleep(1);
  until (iStop - iStart) >= dwMilliseconds;
end;

错误消息的意思是它所说的。请参阅OLH以了解TNotifyEvent的定义。您不能将嵌套过程用作嵌套过程。谢谢,我将该过程移到了主过程之外,但错误remainsTNotifyEvent是object的
过程
,换句话说,它需要是类的方法,而不是备用过程,这是我想你已经把它变成了什么。不要“发件人.免费”!请参阅文档中的“免费”“。请参阅问题的答案。q被标记为
delphi-7
,那么为什么
CreateAnonymousThread
适用呢?@MartynA I只是提供了多个选项,因为其他读者可能正在使用更现代的delphi版本。我已经为您重新编写了它。在一个紧密循环中调用
ProcessMessages()
总是一个坏主意。我以前也写过类似的循环,但我至少会使用
GetQueueStatus()
MsgWaitForMultipleObjects()
来知道何时实际需要调用
ProcessMessages()
以及何时可以跳过它。但是,在这种情况下,我建议使用with
WaitForSingleObject()
/
MsgWaitForMultipleObjects()
RegisterWaitForSingleObject()
,而不是使用
GetTickCount()
Sleep()
procedure Delay(dwMilliseconds: Longint);
var
  iStart, iStop: DWORD;
begin
  iStart := GetTickCount;
  repeat
    iStop := GetTickCount;
    Application.ProcessMessages;
    Sleep(1);
  until (iStop - iStart) >= dwMilliseconds;
end;