2012-12-19 11 views
7

programmatori Delphi Caro,Timer One-Shot

Sto cercando aiuto come scrivere un timer one-shot (No GUI, in modo da VCL Timer fuori questione) ...

Mi spiego ancora un po.

Nel mio codice (che spiega con temporizzatore VCL ma in questo particolare progetto non ho forme):

  1. chiamata una procedure che inviano un carattere sopra porta seriale
  2. abilitare un timer con una quantità X di Interval

In OnTimer evento:

ho un codice che inviano un carattere quindi disattivare ° Il timer stesso non verrà mai più eseguito.

Il problema è che ho bisogno di rendere dinamica la creazione di questi timer. Ho pensato alla funzione SetTimer() quindi KillTimer() nell'evento "OnTimer" per disabilitarla (liberarla).

È un buon modo (sicuro)?

Grazie!

+0

Ho creato ['qualcosa di simile] (http://stackoverflow.com/q/10468787/960757) qualche tempo fa. Il tuo approccio sembra buono, ma tieni presente che se inizierai più di un timer alla volta, dovrai distinguere, quale di essi ha attivato quel timeout se usi un proc callback comune per tutti loro. – TLama

+0

Ho letto che la funzione 'SetTimer()' puoi mettere un "ID univoco" al timer e uccidere con i loro ID – ELCouz

+0

Sì, questo è quello che sto facendo in quel codice. Sto memorizzando una raccolta di ID timer e procedure che devono essere eseguite quando scade l'intervallo del timer. Quando succede, cerco in quella raccolta un oggetto per ID di un timer che è scaduto e, se viene trovato, uccido il timer, eseguo quella procedura ed elimina l'elemento trovato dalla raccolta. – TLama

risposta

14

È sicuro uccidere il timer dall'interno di un evento del timer?

Sì, è perfettamente sicuro.

Come implementare il timer one shot più semplice?

Il metodo più semplice implementazione di un secondo timer 1 colpo è questo, ma è da notare, che se si avvia più di loro, non sarà in grado di distinguere quale uno di loro trascorsi suo intervallo:

procedure TimerProc(hwnd: HWND; uMsg: UINT; idEvent: UINT_PTR; 
    dwTime: DWORD); stdcall; 
begin 
    KillTimer(0, idEvent); 
    ShowMessage('I''m done!'); 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    SetTimer(0, 0, 1000, @TimerProc); 
end; 
+2

+1 La tua risposta non può essere più chiara e precisa! Grazie! Spero che questo aiuti qualcun altro :) – ELCouz

+2

Prego! Solo un sidenote; quando parliamo della classe 'TTimer' e della sua disabilitazione piuttosto frequente dall'evento' OnTimer', accade internamente lo stesso. In realtà, ogni volta che si modifica lo stato della proprietà 'Enabled', il timer viene prima ucciso dalla funzione' KillTimer' e il nuovo viene facoltativamente creato dalla funzione 'SetTimer'. Quindi, questo è un esempio di un uso reale di 'KillTimer' dall'evento' OnTimer'. – TLama

+4

La domanda dice che non c'è GUI, nel qual caso non si può necessariamente presumere che ci sarà un ciclo di messaggi. I timer di base di Windows richiedono loop di messaggi. –

2

L'API timer multimediale fornisce supporto per un timer one shot. Il vantaggio è che i tempi sono molto più precisi della soluzione SetTimer/KillTimer e possono essere utilizzati con intervalli < 50 ms. Questo ha un prezzo, in quanto il callback non ritorna nel contesto del thread principale. Qui è la mia realizzazione di un timer one-shot con il timer API multimediale:

unit MMTimer; 

interface 
uses windows, Classes, mmsystem, SysUtils; 
TOneShotCallbackEvent = procedure (const UserData: Pointer) of object; 

(* 
    The MMOneShotCallback function calls the Callback after the Interval passed. 
    ** Attention: ** 
    The Callback is not called within the context of the main thread. 
*) 

type TMMOneShotTimer = class(TObject) 
    private 
    FTimeCaps: TTimeCaps; 
    FResult: Integer; 
    FResolution: Cardinal; 
    public 
    constructor Create; 
    function MMOneShotCallback(const Interval: Cardinal; UserData: Pointer; Callback: TOneShotCallbackEvent): Boolean; 
    property Result: Integer read FResult; 
    property Resolution: Cardinal read FResolution; 
end; 
implementation 
type 
    TOneShotCallbackData = record 
    Callback: TOneShotCallbackEvent; 
    UserData: Pointer; 
    end; 
    POneShotCallbackData = ^TOneShotCallbackData; 

procedure OneShotCallback(TimerID, Msg: UINT; 
        dwUser, dw1, dw2: DWord); pascal; 
var pdata: POneShotCallbackData; 
begin 
    pdata := Pointer(dwUser); 
    pdata.Callback(pdata.UserData); 
    FreeMemory(pdata); 
end; 

constructor TMMOneShotTimer.Create; 
begin 
    FResult := timeGetDevCaps(@FTimeCaps, SizeOF(FTimeCaps)); 
    Assert(FResult=TIMERR_NOERROR, 'Call to timeGetDevCaps failed'); 
    FResolution := FTimeCaps.wPeriodMin; 
    FResult := timeBeginPeriod(FResolution); 
    Assert(FResult=TIMERR_NOERROR, 'Call to timeBeginPeriod failed'); 
end; 

function TMMOneShotTimer.MMOneShotCallback(const Interval: Cardinal; UserData: Pointer; Callback: TOneShotCallbackEvent): Boolean; 
var pdata: POneShotCallbackData; 
begin 
    GetMem(pdata, SizeOf(TOneShotCallbackData)); 
    pdata.Callback := Callback; 
    pdata.UserData := UserData; 
    result := (0 <> timeSetEvent(Interval, FResolution, @OneShotCallback, DWord(pdata), TIME_ONESHOT)); 
    if not result then 
    FreeMemory(pdata); 
    end; 
end. 
0

Ti rendi conto, che non deve avere una GUI, di utilizzare un timer VCL finché si dispone di un maniglia della finestra? Si può semplicemente istanziare uno dal codice

fTimer := TTimer.Create(hWindowHandle); 

E anche se non si dispone di un handle di finestra È possibile creare un chiamando

fVirtualWindowHWND := AllocateHWnd(WndMethod); 

ma in questo caso si hanno anche per scrivere il proprio ciclo di messaggi. So che chiamare l'API di Windows sembra essere una soluzione più semplice, ma ha anche i suoi cavilli (come se non si potesse passare ad un metodo di classe ...), e ho pensato, potresti voler sapere di questo.