2012-12-19 16 views
7

lập trình viên thân mến Delphi,One-Shot Timers

Tôi đang tìm sự giúp đỡ như thế nào để viết một bộ đếm thời gian one-shot (No GUI, vì vậy VCL Timers ra các câu hỏi) ...

Hãy để tôi giải thích một chút xíu nữa.

Trong mã của tôi (giải thích với VCL hẹn giờ nhưng trong dự án đặc biệt này, tôi không có mẫu đính kèm):

  1. Gọi một procedure đó gửi một char qua cổng nối tiếp
  2. Enable timer với một lượng X Interval

Trong OnTimer sự kiện:

tôi có một mã mà gửi một char sau đó vô hiệu hóa thứ e hẹn giờ chính nó để không bao giờ được thực hiện một lần nữa.

Vấn đề là tôi cần phải tạo ra các bộ hẹn giờ động này. Tôi đã nghĩ đến hàm SetTimer() rồi KillTimer() trong "Sự kiện OnTimer" để tắt nó (miễn phí nó).

Đây có phải là cách tốt (an toàn) không?

Cảm ơn bạn!

+0

Tôi đã thực hiện ['một cái gì đó tương tự'] (http://stackoverflow.com/q/10468787/960757) một số thời gian trước đây. Cách tiếp cận của bạn nghe có vẻ tốt, nhưng lưu ý rằng nếu bạn bắt đầu nhiều hơn một bộ hẹn giờ tại một thời điểm, bạn sẽ cần phải phân biệt, một trong số đó đã kích hoạt thời gian chờ đó nếu bạn sử dụng một cuộc gọi lại chung cho tất cả chúng. – TLama

+0

Tôi đã đọc hàm 'SetTimer()' mà bạn có thể đặt một "ID duy nhất" vào bộ hẹn giờ và giết bằng ID của chúng – ELCouz

+0

Vâng, đó là những gì tôi đang làm trong mã đó. Tôi đang lưu trữ một bộ sưu tập các ID hẹn giờ và các thủ tục cần được thực hiện khi khoảng thời gian hẹn giờ trôi qua. Khi điều đó xảy ra, tôi tìm kiếm trong bộ sưu tập đó cho một mục theo ID của bộ hẹn giờ đã trôi qua và nếu nó được tìm thấy, tôi sẽ xóa bộ hẹn giờ, thực hiện quy trình đó và xóa mục đó khỏi bộ sưu tập. – TLama

Trả lời

14

Có an toàn để giết hẹn giờ từ bên trong sự kiện hẹn giờ không?

Vâng, điều đó hoàn toàn an toàn.

Cách triển khai bộ hẹn giờ một lần đơn giản nhất?

Việc thực hiện đơn giản nhất của một 1 giây một shot timer là thế này, nhưng lưu ý rằng nếu bạn bắt đầu nhiều trong số họ, bạn sẽ không thể phân biệt được cái nào trong số họ trôi qua khoảng thời gian của nó:

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 Câu trả lời của bạn không thể rõ ràng và chính xác hơn thế! Cảm ơn bạn! Hy vọng điều này sẽ giúp người khác :) – ELCouz

+2

Bạn được chào đón! Chỉ cần một sidenote; khi chúng ta nói về lớp 'TTimer' và sự vô hiệu hóa khá thường xuyên của nó từ sự kiện' OnTimer', xảy ra trong nội bộ giống nhau. Trên thực tế, bất cứ khi nào bạn thay đổi trạng thái của thuộc tính 'Enabled', bộ đếm thời gian đầu tiên bị giết bởi hàm' KillTimer' và chức năng mới được tạo tùy chọn bởi hàm 'SetTimer'. Vì vậy, đó là một ví dụ về cách sử dụng thực sự của 'KillTimer' từ sự kiện' OnTimer'. – TLama

+4

Câu hỏi cho biết không có GUI, trong trường hợp đó bạn không nhất thiết phải giả định sẽ có một vòng lặp tin nhắn. Bộ đếm thời gian Windows cơ bản yêu cầu các vòng tin nhắn. –

2

API hẹn giờ đa phương tiện cung cấp hỗ trợ cho bộ hẹn giờ một lần chụp. Lợi ích là, thời gian chính xác hơn nhiều so với giải pháp SetTimer/KillTimer và bạn có thể sử dụng nó với khoảng thời gian < 50 ms. Điều này đi kèm với một mức giá, vì gọi lại không trả về trong bối cảnh của chủ đề chính. Đây là triển khai thực hiện của tôi về một timer one-shot sử dụng bộ đếm thời gian đa phương tiện API:

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

Bạn có nhận ra, rằng bạn không cần phải có một giao diện đồ họa, sử dụng một bộ đếm thời gian VCL miễn là bạn có một Tay nắm cửa sổ? Bạn chỉ có thể thể hiện hóa một từ mã bởi

fTimer := TTimer.Create(hWindowHandle); 

Và thậm chí nếu bạn không có một tay cầm cửa sổ bạn có thể tạo một bằng cách gọi

fVirtualWindowHWND := AllocateHWnd(WndMethod); 

nhưng trong trường hợp đó bạn cũng phải viết của bạn vòng lặp tin nhắn riêng. Tôi biết rằng việc gọi Windows API có vẻ là một giải pháp dễ dàng hơn, nhưng nó cũng có các hang động riêng của nó (như bạn không thể vượt qua một phương pháp lớp học cho nó ...), và tôi nghĩ, Bạn có thể muốn biết về điều này.

Các vấn đề liên quan