在 TidHTTPServer.OnCommandGet 中创建对象(带有计时器)失败

Creating an object (with a timer) inside a TidHTTPServer.OnCommandGet fails

TidHTTPServer.OnCommandGet 我创建了一个新对象。这个对象有一个计时器,应该立即启动但没有。 TimerEVent 永远不会触发! 当我在其他地方创建对象时它可以工作...

一些代码

  TVolumeFader=Class(TObject)

...
constructor TVolumeFader.Create(...);
begin
  inherited Create;
  ...
  
    VolTimer:=TTimer.Create(NIL);
    VolTimer.Enabled:=FALSE;
    VolTimer.Interval:=100;
    VolTimer.OnTimer:=DoTimerTick;
end;

procedure TVolumeFader.DoTimerTick(Sender:TObject);
begin
  LogWrite('TimerTick in VolumeFader',Debug);
  If Assigned(VolTimer)then Begin;
    VolTimer.Enabled:=FALSE;
  End;
  try
    LogWrite('Executing VolumeFade in VolumeFader',Debug);
    VolumeFade;
  finally
    If Assigned(VolTimer)then
      VolTimer.Enabled:=TRUE;
  end;
end;


procedure TMain.OnCommandGet;

Begin;
  TVolumeFader.Create(...);
End;

在对象的构造函数中,您正在创建 TTimer ok,但您将其 Enabled 属性 设置为 False。因此,请确保在构造函数退出后实际激活计时器。或者在构造函数中将 False 更改为 True

话虽这么说,您的代码仍然无法正常工作。这是因为 TIdHTTPServer 是一个 multi-threaded 组件,它的 OnCommand... 事件是在客户端连接到服务器时 TIdHTTPServer 为自己创建的工作线程的上下文中触发的。但是 TTimer 是一个 message-based 计时器,它为自己创建了一个内部 HWND ,它与创建它的线程相关联,并且该线程必须有一个消息循环才能 TTimer 来处理 WM_TIMER 条消息。您在其中创建 TTimer 的工作线程没有消息循环,因此 TTimer 将无法触发其 OnTimer 事件。

因此,您必须:

  1. 运行 在创建对象后 OnCommand... 事件处理程序内您自己的消息循环,然后在事件处理程序退出之前释放对象。一旦 OnCommand... 事件处理程序退出,无法保证调用线程将继续 运行ning。请注意,当计时器为 运行ning:
  2. 时,客户端将被阻止向服务器发送任何进一步的 HTTP 命令
procedure TMain.OnCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  fader: TVolumeFader;
  msg: tagMSG;
begin
  ...
  fader := TVolumeFader.Create(...);
  try
    while (timer should keep running) do
    begin
      //Application.ProcessMessages;
      if PeekMessage(@msg, 0, 0, 0, PM_REMOVE) then
      begin
        TranslateMessage(@msg);
        DispatchMessage(@msg);
      end else
        Sleep(100);
    end;
  finally
    fader.Free;
  end;
  ...
end;
  1. 将您的对象及其 TTimer 的创建委托给您的主 UI 线程,以便您的 OnTimer 事件处理程序将在该线程的上下文中被触发而不是在服务器的工作线程的上下文中。只要确保您的 OnTimer 代码是 thread-safe,如果它需要访问与服务器共享的任何内容:
procedure TMain.OnCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  ...
  TThread.Synchronize(nil, // or TThread.Queue()
    procedure
    begin
      TVolumeFader.Create(...); // when do you destroy this object?
    end
  );
  ...
end;