Version 7.21
タイマーの限界
「今回はこれまでのまとめみたいな感じ」
『いよいよアニメーション!』
「の幻想をうち砕くっていうか」
『げげ』
「まずは、 Ver 7.18 ( No.138 ) のタイマーの機能と、
Ver 7.19 ( No.139 ) のランダムを組み合わせてみます」
『ほいほい』
「まずは、ダイアログが表示される時に呼ばれる
CAnimeDlg::OnInitDialog() から」
BOOL CAnimeDlg::OnInitDialog()
{
// 略。
// TODO: 特別な初期化を行う時はこの場所に追加してください。
::SetTimer
( GetSafeHwnd()
, 100 // タイマー ID 。
, 125 // タイマーのインターバル。
, NULL
);
return TRUE; // TRUE を返すと(略)。
}
『この API でタイマーがオンになるんだよね』
「そうそう。次は、そのタイマーをオフにする部分」
void CAnimeDlg::OnBDraw()
{
::KillTimer( GetSafeHwnd(), 100 );
}
『これも 7.18 のと同じね』
「最後に本命、タイマーから呼ばれる関数」
void CAnimeDlg::OnTimer(UINT nIDEvent)
{
time_t lTime_t;
time( &lTime_t );
srand( lTime_t );
// ランダムに3色作成。
const int iRed = rand() % 256;
const int iGreen = rand() % 256;
const int iBlue = rand() % 256;
// その3色でブラシを作ります。
HBRUSH hBrush
= ::CreateSolidBrush( RGB( iRed, iGreen, iBlue ) );
RECT stRect;
::GetClientRect( m_cCanvasStatic.GetSafeHwnd(), &stRect );
HDC hDC = ::GetDC( m_cCanvasStatic.GetSafeHwnd() );
::FillRect( hDC, &stRect, hBrush );
::DeleteObject( hBrush );
::ReleaseDC( m_cCanvasStatic.GetSafeHwnd(), hDC );
CDialog::OnTimer(nIDEvent);
}
『おおっ、ランダムが使われてる!』
「 7.18 では少しずつ色を変えていったけど、今回はランダムで色を変えて
いきます」
『ってことは、その時使った m_iRed ってメンバ変数は?』
「もう必要なし」
『あらま。あれ? % って何?』
「これは【割り算の余り】を計算する演算子。 rand() ってテキトーな整数
値が出てくるでしょ」
『うん、出てきた』
「でも、 RGB に使えるのは 0 から 255 までの整数値。そこで % を使いま
す。整数値を割った時の余りは、 0 から割る数 - 1 の間に入るから」
『 256 で割った余りなら、必ず 0 から 255 の間に入るわけね』
「これは乱数を使う時によく使う方法だから憶えておいてね。じゃ、これを
試してみて」
『ほい、ビルドして実行! ……なんか……』
「ものすごーく紙芝居っぽい感じでしょ」
『紙芝居って言い得て妙ねー』
「言い得て妙、ってのもなんか……」
『ってゆーか、なんでこんなに変なの? だって、タイマーの間隔って
SetTimer() の第3引数だよね』
「そう、単位はミリセカンド、つまり1秒の千分の1」
『ってことは 125 は8分の1秒だよね。なんでこんなにパラパラな感じな
の?』
「それは、タイマーってものの性質と、メッセージそのもののシステムを理
解しないと」
『な、なんか複雑そう……』
「うん、その辺は複雑。今年はその方向へと掘り下げていく1年になりそう
かな」
『新年の抱負にしてはなんか……』
「とりあえず今回は、どんな感じになってるかって説明だけ。実際に見てい
くのは少し後ってことで」
『はーい』
「まずメッセージの復習。 Ver 5.27 ( No.092 ) を読み返して」
『メッセージって SendMessage() で送れるんだよね』
「その送られたメッセージはウィンドウプロシージャで受け取るってことも
確認してね」
『ほい。次の Ver 5.28 ( No.093 ) もその関係だよね』
「そうそう、イベントとメッセージの関係。で、今回の復習だけど」
BOOL CAnimeDlg::OnInitDialog()
「も」
void CAnimeDlg::OnPaint()
「も」
void CAnimeDlg::OnBDraw()
「も」
void CAnimeDlg::OnDrawItem
(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
「も」
void CAnimeDlg::OnTimer(UINT nIDEvent)
「も、ぜーんぶイベント」
『どゆこと?』
「たとえば CAnimeDlg::OnInitDialog() は、ダイアログが作られて表示さ
れるってイベントが起きた時に、ウィンドウプロシージャに
WM_INITDIALOG ってメッセージが送られてきて、それを MFC が受け取って
CAnimeDlg::OnInitDialog() を呼び出すって仕組みになってます」
『送られてくるって、どこから?』
「ウィンドウズそのものから」
『ウィンドウズが SendMessage() するような感じ?』
「そんな感じ。ウィンドウズは、どんなちっちゃなイベントが起きた時で
も、そのイベントに合わせてメッセージを送ってきます。同じように、他
のメンバ関数も」
『メッセージが送られてきて呼ばれる?』
「そう。 CAnimeDlg::OnPaint() は WM_PAINT は〈描画して欲しい〉って度
に送られてきて、それを MFC のウィンドウプロシージャが受け取って
CAnimeDlg::OnPaint() を呼び出すって仕組みになってます」
『再描画ってイベントでメッセージが送られてくるわけね』
「今はこの、メッセージを受け取って、そこからメンバ関数が呼ばれるま
でって部分が見えてないからよく分かんないだろうけど、いつかそれが見え
ればどういうことか分かってくると思うから」
『いつかなぁ』
「近いうちに教えるから大丈夫。で、タイマーの話。 SetTimer() を呼ぶ
と、一定時間ごとにウィンドウズが WM_TIMER ってメッセージを送ってくる
ようになります」
『〈一定時間〉がイベントってことね』
「そゆこと。ところが、このメッセージは特別で、他のメッセージより優先
度が低いんです」
『 WM_PAINT とかよりも?』
「そう、そういうメッセージが WM_TIMER の後ろに控えている場合には、
WM_TIMER を無視して他のメッセージを先に受け取るような仕組みになって
るんです」
『あ! だからこんなにがたがたになるんだ!』
「そゆこと。これは、ウィンドウズのタイマーの仕様だから、どうしようも
ないかな」
『そんなー。じゃあこのアニメーションってできないの?』
「今はね。将来的にはできるようになってもらうけど。ちょっとかじってみ
ようか。 CAnimeDlg::OnInitDialog() の前にこの関数を置いて」
unsigned int _stdcall DrawThread( void *p_pvPara )
{
time_t lTime_t;
time( &lTime_t );
srand( lTime_t );
HWND hCanvasWnd = (HWND)p_pvPara;
int iRed;
int iGreen;
int iBlue;
HBRUSH hBrush;
RECT stRect;
::GetClientRect( hCanvasWnd, &stRect );
HDC hDC = ::GetDC( hCanvasWnd );
while( 1 )
{
iRed = rand() % 256;
iGreen = rand() % 256;
iBlue = rand() % 256;
hBrush = ::CreateSolidBrush( RGB( iRed, iGreen, iBlue ) );
::FillRect( hDC, &stRect, hBrush );
::DeleteObject( hBrush );
Sleep( 50 );
}
::ReleaseDC( hCanvasWnd, hDC );
return TRUE;
}
『普通の関数だね。あ! これって Ver 7.18 ( No.138 ) の無限ループと
やってること同じじゃん! 嫌! また止まっちゃう!』
「これが止まらないんだよね」
『へ?』
「あ、あと CAnimeDlg::OnInitDialog() の SetTimer() のとこを」
BOOL CAnimeDlg::OnInitDialog()
{
// 略。
// TODO: 特別な初期化を行う時はこの場所に追加してください。
HANDLE hCoreThread;
DWORD dwCoreThreadId;
hCoreThread
= (HANDLE)_beginthreadex
( 0, 0, DrawThread
, ( void * )( m_cCanvasStatic.GetSafeHwnd() )
, 0, ( unsigned int * )( &dwCoreThreadId )
);
return TRUE; // TRUE を返すと(略)。
}
「に置き換えて」
『うわ、アンダーバー付いてる謎な関数……とりあえずビルドして実行。お
おっ! ちかちかしつつもちゃんといろんな色が!! かなり早い!』
「これは【マルチスレッド】って機能。少し先になるけど、これもいつか教
えるからねー」
『うん、とりあえずやり方はあるってことが分かっただけでもいいかも』
/*
Preview Next Story!
*/
『ってゆーか、こういう方法って教えてもらわないと分かんないよね』
「そのために教えてるんだけど」
『はいはいはい』
「あ、そういう意味じゃなくて、そうなんだけど」
『??』
「火美ちゃんも自分で調べられないとね、って話」
『調べられるのかなぁ』
「というわけで次回」
< Version 7.22 情報の見つけ方 >
『につづく!』
「実は次回、デバイスコンテキスト編最終回」
『げ! なにいきなり!』