gpt4 book ai didi

delphi - 在 Delphi 控制台应用程序中使用 VCL TTimer

转载 作者:行者123 更新时间:2023-12-03 14:39:15 25 4
gpt4 key购买 nike

正如问题主题所说。我在 Delphi 中有一个控制台应用程序,其中包含一个 TTimer 变量。我想做的是将事件处理程序分配给 TTimer.OnTimer 事件。我对 Delphi 完全陌生,我曾经使用 C#,向事件添加事件处理程序是完全不同的。我发现,不能简单地为事件分配一个过程作为处理程序,您必须创建一个虚拟类,其中包含一个将作为处理程序的方法,然后将此方法分配给事件。这是我目前拥有的代码:

program TimerTest;

{$APPTYPE CONSOLE}

uses
SysUtils,
extctrls;

type
TEventHandlers = class
procedure OnTimerTick(Sender : TObject);
end;

var
Timer : TTimer;
EventHandlers : TEventHandlers;


procedure TEventHandlers.OnTimerTick(Sender : TObject);
begin
writeln('Hello from TimerTick event');
end;

var
dummy:string;
begin
EventHandlers := TEventHandlers.Create();
Timer := TTimer.Create(nil);
Timer.Enabled := false;
Timer.Interval := 1000;
Timer.OnTimer := EventHandlers.OnTimerTick;
Timer.Enabled := true;
readln(dummy);
end.

这对我来说似乎是正确的,但由于某种原因它不起作用。

编辑
TTimer 组件似乎无法工作,因为控制台应用程序没有消息循环。有没有办法在我的应用程序中创建计时器?

最佳答案

正如其他人提到的,控制台应用程序没有消息泵。

这里是一个模仿 TTimer 类的 TConsoleTimer 线程类。主要区别在于事件中的代码是在 TConsoleTimer 线程中执行的。

更新

本文末尾是在主线程中调用此事件的方法。

unit ConsoleTimer;

interface

uses
Windows, Classes, SyncObjs, Diagnostics;

type
TConsoleTimer = Class(TThread)
private
FCancelFlag: TSimpleEvent;
FTimerEnabledFlag: TSimpleEvent;
FTimerProc: TNotifyEvent; // method to call
FInterval: integer;
procedure SetEnabled(doEnable: boolean);
function GetEnabled: boolean;
procedure SetInterval(interval: integer);
protected
procedure Execute; override;
public
Constructor Create;
Destructor Destroy; override;
property Enabled : boolean read GetEnabled write SetEnabled;
property Interval: integer read FInterval write SetInterval;
// Note: OnTimerEvent is executed in TConsoleTimer thread
property OnTimerEvent: TNotifyEvent read FTimerProc write FTimerProc;
end;

implementation

constructor TConsoleTimer.Create;
begin
inherited Create(false);
FTimerEnabledFlag := TSimpleEvent.Create;
FCancelFlag := TSimpleEvent.Create;
FTimerProc := nil;
FInterval := 1000;
Self.FreeOnTerminate := false; // Main thread controls for thread destruction
end;

destructor TConsoleTimer.Destroy; // Call TConsoleTimer.Free to cancel the thread
begin
Terminate;
FTimerEnabledFlag.ResetEvent; // Stop timer event
FCancelFlag.SetEvent; // Set cancel flag
Waitfor; // Synchronize
FCancelFlag.Free;
FTimerEnabledFlag.Free;
inherited;
end;

procedure TConsoleTimer.SetEnabled(doEnable: boolean);
begin
if doEnable then
FTimerEnabledFlag.SetEvent
else
FTimerEnabledFlag.ResetEvent;
end;

procedure TConsoleTimer.SetInterval(interval: integer);
begin
FInterval := interval;
end;

procedure TConsoleTimer.Execute;
var
waitList: array [0 .. 1] of THandle;
waitInterval,lastProcTime: Int64;
sw: TStopWatch;
begin
sw.Create;
waitList[0] := FTimerEnabledFlag.Handle;
waitList[1] := FCancelFlag.Handle;
lastProcTime := 0;
while not Terminated do
begin
if (WaitForMultipleObjects(2, @waitList[0], false, INFINITE) <>
WAIT_OBJECT_0) then
break; // Terminate thread when FCancelFlag is signaled
if Assigned(FTimerProc) then
begin
waitInterval := FInterval - lastProcTime;
if (waitInterval < 0) then
waitInterval := 0;
if WaitForSingleObject(FCancelFlag.Handle,waitInterval) <> WAIT_TIMEOUT then
break;

if WaitForSingleObject(FTimerEnabledFlag.Handle, 0) = WAIT_OBJECT_0 then
begin
sw.Start;
FTimerProc(Self);
sw.Stop;
// Interval adjusted for FTimerProc execution time
lastProcTime := sw.ElapsedMilliSeconds;
end;
end;
end;
end;

function TConsoleTimer.GetEnabled: boolean;
begin
Result := (FTimerEnabledFlag.Waitfor(0) = wrSignaled);
end;

end.

还有一个测试:

program TestConsoleTimer;

{$APPTYPE CONSOLE}

{$R *.res}

uses
System.SysUtils,ConsoleTimer;

type
TMyTest = class
procedure MyTimerProc(Sender: TObject);
end;

procedure TMyTest.MyTimerProc(Sender: TObject);
begin
// Code executed in TConsoleTimer thread !
WriteLn('Timer event');
end;

var
MyTest: TMyTest;
MyTimer: TConsoleTimer;
begin
MyTest := TMyTest.Create;
try
MyTimer := TConsoleTimer.Create;
MyTimer.Interval := 1000;
MyTimer.OnTimerEvent := MyTest.MyTimerProc;
WriteLn('Press [Enter] key to end.');
MyTimer.Enabled := true;
ReadLn;
MyTimer.Free;
finally
MyTest.Free;
WriteLn('End.');
end;
end.
<小时/>

如上所述,如何让事件在主线程中执行?

阅读Delphi 7: Handling events in console application (TidIRC)给出答案。

TConsoleTimer中添加一个方法:

procedure TConsoleTimer.SwapToMainThread;
begin
FTimerProc(Self);
end;

并将 Execute 方法中的调用更改为:

Synchronize(SwapToMainThread);

要泵送同步调用,请在类单元中使用 CheckSynchronize() 函数:

while not KeyPressed do CheckSynchronize(); // Pump the synchronize queue

注意:控制台KeyPressed函数可以在这里找到:How i can implement a IsKeyPressed function in a delphi console application? .

关于delphi - 在 Delphi 控制台应用程序中使用 VCL TTimer,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12026951/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com