2012-12-19 16 views
7

Sevgili Delphi programcıları,One-Shot Zamanlayıcılar

Ben

Açıklayayım ... (soru dışında VCL Sayacindan böylece hiçbir GUI) tek seferlik zamanlayıcı nasıl yazılacağını yardım arıyorum biraz daha. Benim kod

(VCL zamanlayıcı ile açıklayan ama bu özel projede hiçbir formları var):

  1. Çağrı seri port
  2. üzerinde bir char gönder procedure bir X miktarının ile bir zamanlayıcı etkinleştirme OnTimer durumunda Interval

:

sonra th devre dışı bir char göndermek bir kod var e zamanlayıcı kendini asla tekrar çalıştırılamaz.

sorun

Bunları zamanlayıcılar yaratılması dinamik hale getirmek için ihtiyaç olmasıdır. SetTimer() ve KillTimer() işlevlerini "OnTimer olayında" devre dışı bırakmak için onu (ücretsiz olarak) düşünmüştüm. İyi bir (güvenli) yolu

mi?

Teşekkür ederiz!

+0

Bir süre önce ['benzer bir şey '] (http://stackoverflow.com/q/10468787/960757) yaptım. Yaklaşımınız kulağa hoş geliyor, ama unutmayın ki, bir seferde birden fazla zamanlayıcıya başlayacak olursanız, bunların hepsinin ortak bir geri çağırma provası kullandıysanız hangisinin bir zaman aşımına uğradığını ayırt etmeniz gerekir. – TLama

+0

'SetTimer()' fonksiyonunu, zamanlayıcıya "Benzersiz Kimlik" koyabiliyor ve kimlikleri – ELCouz

+0

ile öldürebiliyordum. Evet, işte bu kodda yapıyorum. Zamanlayıcı aralığı geçtiğinde gerçekleştirilmesi gereken bir zamanlayıcı kimlikleri ve prosedürleri koleksiyonu saklıyorum. Olduğunda, o koleksiyonda geçen bir zamanlayıcının kimliğine göre bir öğe için arama yaptım ve bulunduğunda, zamanlayıcıyı öldürürüm, bu yordamı gerçekleştirir ve koleksiyonda bulunan bu öğeyi silerim. – TLama

cevap

14

Bir zamanlayıcı olayının içinden zamanlayıcıyı öldürmek güvenli midir?

Evet, bu kesinlikle güvenli.

En basit tek çekim zamanlayıcısı nasıl uygulanır?

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; 
:

1 saniyelik bir atış zamanlayıcı kolay uygulanması bu, ancak not onlardan daha başlarsanız, bunlardan biri kendi aralığını geçen hangi ayırt etmek mümkün olmayacaktır yani,

+2

+1 Cevabınız bundan daha açık ve net olamaz! Teşekkür ederim! Umarım bu yardımcı olur başkası :) – ELCouz

+2

Rica ederim! Sadece bir sidenote; “TTimer” sınıfı ve “OnTimer” olayından oldukça sık kullanılan devre dışı bırakma hakkında konuştuğumuzda, içsel olarak aynı şey gerçekleşir. Aslında, "Enabled" özelliğinin durumunu değiştirdiğinizde, zamanlayıcı ilk olarak "KillTimer" işlevi tarafından öldürülür ve yenisi de isteğe bağlı olarak "SetTimer" işlevi tarafından oluşturulur. Yani, bu 'OnTimer' olayından 'KillTimer' gerçek kullanımının bir örneğidir. – TLama

+4

Soru, hiçbir GUI olmadığını söylüyor, bu durumda mutlaka bir mesaj döngüsü olacağını düşünemezsiniz. Temel Windows zamanlayıcıları mesaj döngüleri gerektirir. –

2

Multimedya zamanlayıcı API'si bir çekim zamanlayıcısı için destek sağlar. Fayda, zamanlamanın SetTimer/KillTimer çözümünden çok daha hassas olması ve < 50 ms aralıklarla kullanabilmenizdir. Bu, geri dönüş, ana iş parçacığı bağlamında geri dönmediği için bir fiyattır. İşte multimedya zamanlayıcı API kullanarak bir atışlık zamanlayıcı benim uygulamasıdır:

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

size sahip olduğu sürece sizin kadar VCL zamanlayıcı kullanmak, bir GUI sahip olmak zorunda değilsiniz, farkında mısın bir pencere tutacağı? Sadece

fTimer := TTimer.Create(hWindowHandle); 

tarafından kodundan birini örneğini Ve eğer bir pencere kolu olmasa bile eğer

fVirtualWindowHWND := AllocateHWnd(WndMethod); 

arayarak tane oluşturabilirsiniz ancak bu durumda ayrıca yazmak zorunda kendi mesaj döngüsü. Windows API'yi çağırmanın daha kolay bir çözüm gibi göründüğünü biliyorum, ama aynı zamanda kendi mağaraları da var.) ve bunu öğrendim, bunu bilmek isteyebilirsiniz.

İlgili konular