在 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
事件。
因此,您必须:
- 运行 在创建对象后
OnCommand...
事件处理程序内您自己的消息循环,然后在事件处理程序退出之前释放对象。一旦 OnCommand...
事件处理程序退出,无法保证调用线程将继续 运行ning。请注意,当计时器为 运行ning: 时,客户端将被阻止向服务器发送任何进一步的 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;
- 将您的对象及其
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;
在 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
事件。
因此,您必须:
- 运行 在创建对象后
OnCommand...
事件处理程序内您自己的消息循环,然后在事件处理程序退出之前释放对象。一旦OnCommand...
事件处理程序退出,无法保证调用线程将继续 运行ning。请注意,当计时器为 运行ning: 时,客户端将被阻止向服务器发送任何进一步的 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;
- 将您的对象及其
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;