タイマーを使う

今回は、まずメッセージイベントとして用意されている
(VBなどでもおなじみ?)タイマーを使ってみたいと思います。
タイマーとは、一定時間ごとにキューにメッセージをポストすることで
WM_TIMERメッセージが随時呼ばれるイベントのことです。
これを使うことによって、今までの待つだけだったプログラムから
動的なプログラムが使えるようになります。

・SetTimer

「多重起動の防止」のソースをスケルトンとして使います。
いろいろ別のことをしていますが、TIMERに関するところだけを抜き出します。

     //タイマーを作成
     SetTimer (hwnd, 1, 50, NULL);

     while (GetMessage (&msg, NULL, 0, 0))
          {
          TranslateMessage (&msg) ;
          DispatchMessage (&msg) ;
          }
     return msg.wParam ;
     }

Getmessageループに入る前に、SetTimer 関数で
50m秒ごとにタイマーメッセージが発行されるタイマーを作成します。
第二引数の1というのはタイマーの識別子で、作れる限り
別のタイマーを何個でも作ることができます。

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
     {
     ・・・・・・
          //タイマーが呼ばれた
          case WM_TIMER:
               Point_Move();
               InvalidateRect(hwnd , NULL , FALSE);
               return 0 ;

ウィンドウプロシージャ内にタイマー時の処理を追加します。
Point_Move() という自作関数内に処理をまとめて記述してあるので
それを行った後、InvalidateRectで無効な領域を作成し
画面を再描画するようメッセージを出しています。

          //ウィンドウが破棄された
          case WM_DESTROY :
               //タイマーを削除する
               KillTimer (hwnd, 1) ;
               PostQuitMessage (0) ;
               return 0 ;

プログラムが終了する前に、KillTimer関数で作成した
タイマー識別子のタイマーを削除しています。では実行してみましょう。

タイマーによるラインアート

一定時間ごとにそれぞれの点が座標を移動し、壁に来たら跳ねかえるという
スクリーンセーバでおなじみの、ラインアート風味にしてみました。

UINT SetTimer (HWND hWnd, UINT nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc );
・タイマーをセットします
第一引数 hWnd には、ウィンドウのハンドルを指定します
第二引数 nIDEvent には、作成するタイマーの識別子を指定します(0以外)
第三引数 uElapse には、ミリ秒でタイムアウト値を指定します
第四引数 lpTimerFunc には、タイムアウト値が経過するとき通知されるファンクションへのポインタを指定します。NULLならシステムはWM_TIMERメッセージをポストします。

戻り値 成功なら新しいタイマー識別値の整数が、失敗なら0が返ります。

BOOL KillTimer (HWND hWnd, UINT uIDEvent );
・タイマーを削除します
第一引数 hWnd には、ウィンドウのハンドルを指定します
第二引数 nIDEvent には、削除するタイマーの識別子を指定します
NULLならシステムはWM_TIMERメッセージをポストします。

戻り値 成功ならTRUEが、失敗ならFALSEが返ります。

ソースの表示

・タイマー識別子による分岐処理

それぞれ違ったタイミングで処理されるタイマーのサンプルです。

     ShowWindow (hwnd, iCmdShow) ;
     UpdateWindow (hwnd) ;

     //3種類のタイミングのタイマーを作成
     SetTimer (hwnd, 1, 50, NULL);
     SetTimer (hwnd, 2, 5, NULL);
     SetTimer (hwnd, 3, 100, NULL);

     while (GetMessage (&msg, NULL, 0, 0))
          {

それぞれタイミングの違うタイマーを3個作ります。
「そこそこ・速い・遅い」それぞれの識別子は1・2・3です。

LRESULT CALLBACK WndProc (HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
     {
     //デバイスコンテキスト
     HDC hdc;
     PAINTSTRUCT paint ;
     RECT Rect1={70,80,200,100};
     RECT Rect2={70,110,200,130};
     RECT Rect3={70,140,200,160};
     WCHAR wcState[64];
     static int iCount1;
     static int iCount2;
     static int iCount3;

iCount1~3はタイマーが呼ばれるごとに増減する数値を格納します。
Rect1~3はそれぞれの描画領域、wcStateはDrawText用変数です。

          //タイマーが呼ばれた
          case WM_TIMER:
               //タイマーの識別値を判定
               switch(wParam)
               {
                   case 1:
                        iCount1++;
                        break;
                   case 2:
                        iCount2++;
                        break;
                   case 3:
                        iCount3--;
                        break;
               }
               InvalidateRect(hwnd , NULL , FALSE);
               return 0 ;

呼ばれたタイマーの値がウィンドウプロシージャの第3引数に格納されているので
switchでタイマー1・2には増加、タイマー3には数値を減少とタイマーごとに別の処理をしています。
iCount1~3の値はどう動くでしょう。

タイマーごとによる数値の増減

タイマーが呼ばれている速さ(=数値の増減の速さ)は、指定した通り
順に 2>1>3 になっているということがわかりました。

ソースの表示

2005/6/3


戻る