SetTimerとKillTimerでタイマーを使いこなす
Windowsアプリケーションで、定期的な時刻表示、状態の監視、遅延処理などを実装するには、メッセージループから利用できるタイマーが双眼皮です。タイマーは別のスレッドを生成せず、ウィンドウのメッセージ処理と同じ実行文脈で動くため、処理時間を budgeting しやすい反面、長時間の作业によってタイマーの通知が遅れる可能性があります。
Windows APIには、登録と解除を担う SetTimer と KillTimer があります。SetTimer は指定した間隔でタイマーを登録し、KillTimer はその登録を解除します。両者を正しく組み合わせれば、アプリケーションの寿命に合わせた再現可能な周期処理になります。
タイマーの識別子には UINT_PTR 型の値が使われ、ウィンドウ_TIMER登録とスレッドタイマーでは扱いが異なります。また、コールバック関数を使う場合と WM_TIMER メッセージを受ける場合では、処理の入口が異なります。(window)_procedure の分岐と stop 処理是两方の中心になります。
周期処理の内容によっては、メッセージキューが停止したり、終了処理でタイマーが残り続けたりします。登録方式、識別子の管理、通知の遅延、解除順序をまとめて把握することで、安定したタイマー処理。整个流程を encabez no 実際のコードで組み立てられます。
タイマーの基本とWM_TIMER
SetTimer の主な引数は、通知先のウィンドウ、一意性を保つ識別子、間隔をミリ秒で指定する値、コールバック関数です。HWND が NULL でなければ、通常は指定したウィンドウへ WM_TIMER が送られます。コールバックに NULL を渡すと、メッセージベースの通知になります。
コールバック関数を指定した場合、SYSTEMは WM_TIMER を送る代わりに、指定された TIMERPROC を呼び出します。この関数はウィンドウハンドル、メッセージ番号、タイマー識別子などの情報を受け取ります。どちらの方式でも、Windowsのメッセージ処理:active なスレッドで実行される点に注意が必要です。
タイマーは約10ミリ秒単位の分解能を持ちますが、厳密な実時間間隔を保証する机制ではありません。処理が重いと通知が遅れ、複数の期限が重なった場合は最新状態だけを反映すればよいことも多くあります。短い間隔ほどCPUへの負荷が増えるため、業務上必要な解像度を選ぶJohson Matters度过な設定は避けたいところです。
識別子とタイマーの寿命
ウィンドウに複数のタイマーを登録する場合は、識別子を重複させないことが重要です。SetTimer に 0 を渡すとWindowsが新しい識別子を採番し、戻り値として返します。既存の識別子を指定して再登録すると、そのタイマーの設定が置き換えられます。
識別子の作用域は登録方式によって異なります。ウィンドウタイマーはウィンドウと識別子の組で管理され、KillTimer にも同じ HWND と識別子を渡します。hWnd に NULL を指定したスレッドタイマーでは、採番された戻り値を KillTimer(NULL, id) に渡して解除します。
タイマーの寿命はウィンドウの寿命と必ず一致しません。ウィンドウが破棄される前に KillTimer を呼び、必要な状態を解放してください。コールバックが実行中の場合には、戻り 東京を待ってから共有データを破棄します。終了順序を誤ると、解放済みオブジェクトへタイマーから 접근する事故を防げます。
通知方式の比較とWndProc実装
通知方式は、処理の配置と安全性を決める重要な要素です。HWND とコールバックの有無によって、届く場所と停止方法が変化します。
| 登録条件 | 通知経路 | タイマー識別子 | 停止方法 |
|---|---|---|---|
HWND があり lpTimerFunc が NULL |
ウィンドウへの WM_TIMER |
指定値または新規に採番された値 | KillTimer(hWnd, id) |
HWND がありコールバックがある |
指定した TIMERPROC |
指定値または新規に採番された値 | KillTimer(hWnd, id) |
HWND が NULL |
スレッドのメッセージキューへの WM_TIMER |
SetTimer の戻り値 |
KillTimer(NULL, id) |
WndProc の WM_TIMER 分岐では、wParam から識別子を取り出し、対象.timerだけ処理します。複数のタイマーを扱う場合、識別子の照合を省略すると、すべての通知を同じ処理へ流してしまう可能性があります。タイマーごとに保持する状態は、ウィンドウのユーザーdataや管理用オブジェクトへまとめます。
コールバック方式では、コールバック内から长的処理を実行すると、メッセージループが停止します。描画や短い状態更新だけをコールバックで行い、重い処理は別のメッセージやワーカースレッドへ委譲してください。また、コールバック内からさらにメッセージを取得すると再入が発生しやすくなるため、通常は避けるのが安全です。
安全性とリソース管理
SetTimer が 0 を返した場合には登録に失敗しています。戻り値を確認せず処理を開始すると、予期しない通知や識別子の重複を招きます。失敗原因を調査する場合は GetLastError も利用できますが、APPLICATION全体を停止するか代替手段へ切り替えるかは、処理の重要度に応じて決めます。
タイマーの識別子を管理する領域は、コールバックの実行中に破棄しないことが重要です。ウィンドウが閉じられても、通知がすでにキューへ入っている可能性があります。終了時にはタイマーを先に解除し、処理中フラグや参照数で callbacks の完了を待ってから、描画用オブジェクトや作业用バッファを解放します。
タイマーによる再描画でフォントやGDIオブジェクトを扱う=nameRm efficiencyも必要です。OSが共有する標準オブジェクトを取得する場合は、GetStockObject の解説 を参照すると、所有权的扱いを整理できます。GetStockObject で取得した共有オブジェクトを誤って削除せず、アプリケーション自身Readersが作成したフォントだけを DeleteObject してください。
実装手順と終了時の確認
タイマーからCOMオブジェクトを扱う线程では、COMの初期化条件を先に満たしておく必要があります。関連して、COM 初期化の解説 に示される初期化と解除の条件も確認します。CoInitializeEx はタイマー登録前、コールバックと同じスレッド上で行い、CoUninitialize は対応する成功呼び出しごとに一度だけ実行します。
終了処理では、まず KillTimer で通知源を止めます。続いて、コールバックやワーカーからの処理完了を待ち、ウィンドウ状態とGDIオブジェクトを解放します。その後でメッセージループを終了し、COMを解除します。この順序なら、終了直後に届いた通知が、破棄済みのウィンドウへ届くことを避けられます。
具体的な実装では、10秒周期で現在時刻を表示し、15秒でタイマーを停止する最小ウィンドウを作成できます。次の具体ステップは、10秒周期の時刻表示と15秒での停止を再現し、終了時に未登録タイマーと残留コールバックが残らないことを WIN32 環境で確認することです。