SetWaitableTimer 第二參數 lpDueTime (類型 Int64 / TLargeInteger) 控制定時器**首次觸發時間**,支援三種模式:零值 (立即)、負值 (相對時間)、正值 (絕對時間)。本文詳細解析三種模式的賦值計算與實戰應用。
| 模式 | 數值範圍 | 含義 | 單位 | 典型用途 |
|---|---|---|---|---|
| 立即 | 0 | 設定後立即觸發第一次 | - | 即時啟動週期任務 |
| 相對時間 | < 0 | 從當前時間起延遲指定間隔 | 100 奈秒 (0.1μs) | 延遲啟動、倒數計時 |
| 絕對時間 | > 0 | 指定 UTC FILETIME 絕對時間點 | 100 奈秒 (UTC) | 定時啟動、排程任務 |
1 秒 = 1,000 毫秒 (ms)
= 1,000,000 微秒 (μs)
= 1,000,000,000 奈秒 (ns)
= 10,000,000 * 100 奈秒單位 ← FILETIME 基礎單位
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormDestroy(Sender: TObject);
end;
var Form1: TForm1;
implementation {$R *.dfm}
var f: Integer; hWaitableTimer: THandle;
function MyThreadFun(p: Pointer): DWORD; stdcall;
var i, y: Integer;
begin
Inc(f); y := 20 * f;
if WaitForSingleObject(hWaitableTimer, INFINITE) = WAIT_OBJECT_0 then
for i := 0 to 1000 do
begin
Form1.Canvas.Lock; Form1.Canvas.TextOut(20, y, IntToStr(i)); Form1.Canvas.Unlock;
Sleep(1);
end;
Result := 0;
end;
procedure TForm1.Button1Click(Sender: TObject);
var ThreadID: DWORD; DueTime: Int64;
begin
hWaitableTimer := CreateWaitableTimer(nil, True, nil); // True = 手動重置,喚醒所有等待線程
DueTime := -3 * 10000000; // -30,000,000 = -3 秒 (100ns 單位)
SetWaitableTimer(hWaitableTimer, DueTime, 0, nil, nil, False);
Repaint; f := 0;
CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
CloseHandle(hWaitableTimer);
end;
end.
procedure TForm1.Button1Click(Sender: TObject);
const strTime = '2009-02-18 13:10:05';
var ThreadID: DWORD; DueTime: Int64;
st: TSystemTime; ft, utc: TFileTime; dt: TDateTime;
begin
{ 字串 -> TDateTime -> TSystemTime -> FILETIME (本地) -> FILETIME (UTC) }
DateTimeToSystemTime(StrToDateTime(strTime), st);
SystemTimeToFileTime(st, ft); // 本地 FILETIME
LocalFileTimeToFileTime(ft, utc); // 轉 UTC FILETIME (SetWaitableTimer 需 UTC)
DueTime := Int64(utc); // 整數轉換
hWaitableTimer := CreateWaitableTimer(nil, True, nil);
SetWaitableTimer(hWaitableTimer, DueTime, 0, nil, nil, False);
Repaint; f := 0;
CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
CreateThread(nil, 0, @MyThreadFun, nil, 0, ThreadID);
end;
字串 '2009-02-18 13:10:05'
↓ StrToDateTime
TDateTime (Double)
↓ DateTimeToSystemTime
TSystemTime (Year/Month/Day/Hour/Min/Sec/MSec)
↓ SystemTimeToFileTime
TFileTime (本地時間, 100ns 單位)
↓ LocalFileTimeToFileTime
TFileTime (UTC 時間) ← SetWaitableTimer 要求 UTC
↓ Int64 強制轉型
Int64 (DueTime)
type
TFileTime = record
dwLowDateTime: DWORD;
dwHighDateTime: DWORD;
end; // 相當於 Int64
TSystemTime = record
wYear, wMonth, wDayOfWeek, wDay: Word;
wHour, wMinute, wSecond, wMilliseconds: Word;
end;
TDateTime = type Double;

| 錯誤 | 後果 | 正確做法 |
|---|---|---|
直接傳遞常數 SetWaitableTimer(h, 0, ...) | 編譯錯誤: lpDueTime 為 var 參數 | 用變數 DueTime: Int64; DueTime := 0; |
| 相對時間用正值 | 被視為絕對時間 (通常為極遠未來) | 負值表示相對,如 -50000000 = 5 秒 |
| 絕對時間未轉 UTC | 觸發時間偏移本地時區差 | 必須 LocalFileTimeToFileTime 轉 UTC |
忽略 bManualReset | True=喚醒所有等待線程, False=喚醒單一線程 | 根據同步需求選擇 |
掌握起始時間控制後,下一階段將深入 SetWaitableTimer 第 4、5 參數:APC 回調函數指標 與 用戶指標參數,實現真正的非阻塞高精度定時任務。