第3日では、タイマーの使い方を理解します。
タイマーを作動させるには、SetTimer関数を使います。
不要になったらKillTimer関数で破棄します。
指定した間隔ごとに、WM_TIMERメッセージが来ます。(p103-)
さて、もう少し詳しくみてみると、
UINT_PTR SetTimer( HWND hWnd, // ウィンドウのハンドル UINT_PTR nIDEvent, // タイマの識別子 UINT uElapse, // タイムアウト値 TIMERPROC lpTimerFunc // タイマのプロシージャ );lpTimerFuncをNULLに指定した場合、WM_TIMERメッセージはhWndで指定した ウィンドウのプロシージャにやって来ます。
nIDEventには、タイマーを識別する番号を割り振ります。
uElapseには、タイマーメッセージをどのくらいの間隔で発行してもらうかを ミリ秒単位で指定します。
通常は、lpTimerFuncをNULLにして使いますが、これを自分で指定するには、 lpTimerFunc関数は、次のような形をしていなくてはいけません。
VOID CALLBACK TimerProc( HWND hwnd, // ウィンドウのハンドル UINT uMsg, // WM_TIMER メッセージ UINT_PTR idEvent, // タイマの識別子 DWORD dwTime // 現在のシステム時刻 );まずは、lpTimerFuncをNULLにした例を見てみましょう。
// timera.cpp #define ID_MYTIMER 100 #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "timera"; //ウィンドウクラス int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; BOOL bRet; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるタイマ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static int x, y, r, wx, wy; HDC hdc; PAINTSTRUCT ps; static BOOL bDec = FALSE; switch (msg) { case WM_CREATE: SetTimer(hWnd, ID_MYTIMER, 100, NULL); break; case WM_TIMER: if (wp != ID_MYTIMER) { return DefWindowProc(hWnd, msg, wp, lp); } if (bDec) { r -= 5; if (r <= 0) bDec = FALSE; } else { r += 5; if (r >= wy || r >= wx) bDec = TRUE; } InvalidateRect(hWnd, NULL, TRUE); break; case WM_SIZE: wx = LOWORD(lp); wy = HIWORD(lp); x = wx / 2; y = wy / 2; break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); Ellipse(hdc, x - r / 2, y - r / 2, x + r / 2, y + r / 2); EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよろしいですか", "終了確認", MB_YESNO); if (id == IDYES) { KillTimer(hWnd, ID_MYTIMER); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }WM_CREATEメッセージが来たら、SetTimer関数を呼んでいます。
プログラム終了時にはKillTimer関数でタイマを破棄しています。
WM_TIMERメッセージは、このウィンドウのプロシージャに来ます。
WM_TIMERメッセージが来たら、描画する円の半径を計算しています。 そして、InvalidateRect関数を呼んでいます。こうすることにより、WM_PAINT メッセージがやって来ます。
さて、このプログラムは、おまけでWM_SIZEメッセージの処理もしています。 WM_SIZEメッセージは、ウィンドウのサイズが変更されたらやって来ます。
また、この時lParam値の下位WORDはクライアント領域の幅、上位WORDは高さ を表しています。
以上をまとめると、
WM_TIMERメッセージが来る→半径を変更する、InvalidateRect関数で無効領域を発生 WM_SIZEメッセージが来る→中心の座標の計算やり直し WM_PAINTメッセージが来る→円の描画ということになります。
では、同じ内容のプログラムを書換えてみましょう。
// timerb.cpp #define ID_MYTIMER 100 #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); VOID CALLBACK MyTimer(HWND, UINT, UINT_PTR, DWORD); char szClassName[] = "timerb"; //ウィンドウクラス int x, y, r, wx, wy; BOOL bDec = FALSE; int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; BOOL bRet; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) { if (bRet == -1) { break; } else { TranslateMessage(&msg); DispatchMessage(&msg); } } return (int)msg.wParam; } //ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hCursor = (HCURSOR)LoadImage(NULL, MAKEINTRESOURCE(IDC_ARROW), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE | LR_SHARED); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = (HICON)LoadImage(NULL, MAKEINTRESOURCE(IDI_APPLICATION), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_SHARED); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるタイマ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; } //ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; HDC hdc; PAINTSTRUCT ps; switch (msg) { case WM_CREATE: SetTimer(hWnd, ID_MYTIMER, 100, MyTimer); break; case WM_SIZE: wx = LOWORD(lp); wy = HIWORD(lp); x = wx / 2; y = wy / 2; break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); Ellipse(hdc, x - r / 2, y - r / 2, x + r / 2, y + r / 2); EndPaint(hWnd, &ps); break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよろしいですか", "終了確認", MB_YESNO); if (id == IDYES) { KillTimer(hWnd, ID_MYTIMER); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; } VOID CALLBACK MyTimer(HWND hWnd, UINT msg, UINT_PTR idEvent, DWORD dwTime) { if (idEvent != ID_MYTIMER) return; if (bDec) { r -= 5; if (r <= 0) bDec = FALSE; } else { r += 5; if (r >= wy || r >= wx) bDec = TRUE; } InvalidateRect(hWnd, NULL, TRUE); return; }第3日ではWM_TIMERメッセージの処理の仕方がわかればOKです。