ich habe grad mal den TTimer ausprobiert und war entsetzt wie ungenau der ist

Da ich in schon früher in Delphi 6 das gleiche Problem hatte, habe ich eine MultiMedia Timer Komponente gebaut
und die funktioniert (leider nur in Windows

Den Code stelle ich euch gern zur Verfügung, vielleicht kann man ja davon was gebrauchen...
Code: Alles auswählen
{ Siro, am 23.01.2017 fuer Lazarus Windows angepasst }
unit PrecTimer;
interface
uses
Windows, Classes, MMSystem;
CONST DefaultTimerResolution = 1; { minimale Auflösung des Timers 1 ms }
DefaultTimerIntervall = 1000; { Standard Intervallzeit 1 Sekunde }
{ neue Definition des Ereignisses OnTimer. }
{ Bei OnTimer wird ein Zeitwert "Time" übergeben. Hier wird die Zeit in }
{ Mili Sekunden nach dem Start bzw. letztem Reset des Timers übergeben. }
Type TOnTimer = procedure(Sender:TObject) of Object;
Type TPreciseTimer = class(TComponent)
private
FAutoStart : Boolean; { TRUE = Timer automatisch starten }
FInTimerProc : Boolean; { Anti Rekursions Flag }
FTimerRes : DWORD; { minimale Auflösung in milli Sekunden }
FTimerID : DWORD; { von Windows vergebene Timer Ident Nummer }
FInterval : DWord; { Aufrufintervall in milli Sekunden }
FRunning : Boolean; { TRUE = Timer lauft }
FOnTimer : TOnTimer; { Benutzer Routine "OnTimer" im Objectinspector }
protected
procedure SetInterval(NewInterval:DWORD);
procedure SetTimerRes(NewRes:DWORD);
procedure Loaded; override; { wird für AutoStart benötigt }
public
constructor create(AOwner: TComponent); override;
destructor destroy; override;
procedure Start; { Startet den Timer }
procedure Stop; { Stoppt den Timer }
procedure Reset; { setzt Timerzähler "Time" auf 0 }
published
property Interval : DWORD read FInterval write SetInterval;
property Resolution : DWORD read FTimerRes write SetTimerRes;
property OnTimer : TOnTimer read FOnTimer write FOnTimer;
property Running : Boolean read FRunning;
property AutoStart : Boolean read FAutoStart write FAutoStart;
end;
procedure Register;
implementation
{ ! ALLE Timer rufen diese Funktion auf. Anhand von "Sender" können die Timer }
{ auseinander gehalten werden. }
{ Diese Routine darf auf keinen Fall in das Object mit aufgenommen werden, }
{ sonst stimmen die Stackübergabe Parameter nicht mehr, da Delphi Komponenten }
{ immer einen unsichtbaren Parameter "Self" mit auf den Stack übergibt. }
{ Die sogenannte "Callback Funktion" von Windows weis davon aber nichts. }
{ Hat ewig gedauert bis der Fehler offensichtlich wurde. }
{ Hier wurde Sicherheitshalber ein Rekursionsflag mit eingebaut. }
{ Falls der letzte Aufruf noch nicht beendet wurde, wird der neue ignoriert, }
{ damit das System nicht hängen bleibt. }
procedure TimerProc(uID:UINT;msg:UINT; Sender,dw1,dw2:DWORD); stdcall;
begin
With TPreciseTimer(TObject(Sender)) do begin
if FInTimerProc then begin
Stop;
FInTimerProc:=FALSE;
exit; { der letzte Aufruf wurde noch nicht beendet }
end;
FInTimerProc:=TRUE; { der neue Aufruf ist in Arbeit }
if Assigned(OnTimer) then OnTimer(TObject(Sender)); { Benutzer Routine }
FInTimerProc:=FALSE; { der Aufruf wurde beendet }
end;
end;
constructor TPreciseTimer.create(AOwner: TComponent);
var TimeCaps:TTimeCaps;
begin
inherited create(AOwner);
FInterval := DefaultTimerIntervall;
FTimerRes := DefaultTimerResolution;
{ prüfen ob Windows diese Auflösung unterstütz. Wenn nicht wird }
{ die minimale Auflösung die Windows ermöglicht eingestellt. }
timeGetDevCaps(@TimeCaps,SizeOf(TTimeCaps));
if FTimerRes < TimeCaps.wPeriodMin then FTimerRes:=TimeCaps.wPeriodMin;
end;
destructor TPreciseTimer.destroy;
var t:DWORD;
begin
if FInTimerProc then begin
t:=GetTickCount64+3000;
repeat
until (GetTickCount64 > t) or (NOT FInTimerProc);
end;
Stop;
inherited destroy;
end;
procedure TPreciseTimer.Start; { Startet den Timer }
begin
if FRunning then exit; { Timer läuft schon }
timeBeginPeriod(FTimerRes);
FTimerId:=timeSetEvent(FInterval,FTimerRes,@TimerProc,DWORD(self),TIME_PERIODIC);
if FTimerId = 0 then begin { Windows konnte keinen Timer erzeugen }
timeEndPeriod(FTimerRes); { sofort timeEndPeriod aufrufen }
FRunning:=FALSE; { Timer läuft demnach nicht }
end else FRunning:=TRUE; { ansonsten läuft der Timer }
end;
procedure TPreciseTimer.Stop; { Stoppt den Timer }
begin
if NOT FRunning then exit; { wenn Timer schon gestoppt ist }
if FTimerId = 0 then exit; { es wurde kein Timer erzeugt }
timeKillEvent(FTimerId);
timeEndPeriod(FTimerRes);
FRunning:=FALSE; { Timer steht nun }
end;
procedure TPreciseTimer.Reset; { setzt den Zählerstand auf 0 }
begin
end;
{ setzt einen neuen Zeitintervall. Wenn der Timer schon läuft, }
{ wird er kurz ausgeschaltet, dann der neue Intervall gesetzt und }
{ wieder eingeschaltet }
procedure TPreciseTimer.SetInterval(NewInterval:DWORD);
var F:Boolean;
begin
if NewInterval = FInterval then exit; { steht schon auf dem Wert }
F:=FRunning; { merken ob Timer gerade läuft }
Stop; { auf jeden Fall den Timer stoppen }
FInterval:=NewInterval; { neune Intervall setzen }
if F then Start; { evtl. Timer wieder starten }
end;
procedure TPreciseTimer.SetTimerRes(NewRes:DWORD);
var F:Boolean; TimeCaps:TTimeCaps;
begin
if NewRes = FTimerRes then exit; { steht schon auf dem Wert }
F:=FRunning; { merken ob Timer gerade läuft }
Stop; { auf jeden Fall Timer stoppen }
FTimerRes:=NewRes; { Neue Auflösung setzen }
{ prüfen ob Windows diese Auflösung unterstützt. Wenn nicht wird }
{ die minimale Auflösung die Windows angibt benutzt. }
timeGetDevCaps(@TimeCaps,SizeOf(TTimeCaps));
if FTimerRes < TimeCaps.wPeriodMin then FTimerRes:=TimeCaps.wPeriodMin;
if F then Start; { evtl. Timer wieder Starten }
end;
{ nachdem alle Komponenten initialisiert und geladen wurden, wird automatisch }
{ für jede Kompoonente "Loaded" aufgerufen. Hier wird, wenn AutoStart auf TRUE }
{ steht der Timer gestartet. }
procedure TPreciseTimer.Loaded;
begin
if AutoStart then Start;
end;
procedure Register;
begin
RegisterComponents('SiroNeu', [TPreciseTimer]);
end;
end.