#pragma twice

KAB-studio > プログラミング > #pragma twice > 141 Version 7.21 タイマーの限界

#pragma twice 141 Version 7.21 タイマーの限界

前のページへ 表紙・目次へ 次のページへ

 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 情報の見つけ方 >
『につづく!』
「実は次回、デバイスコンテキスト編最終回」
『げ! なにいきなり!』
 
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
RSSに登録
del.icio.us 登録する
Yahoo!ブックマーク 詳細を表示 users
livedoorクリップ 詳細を表示 livedoorクリップ ブックマーク数
はてなブックマーク 詳細を表示 はてなブックマーク ブックマーク数
 
このページは、Visual C++ 6.0を用いた C++ 言語プログラミングの解説を行う#pragma twiceの一コンテンツです。
詳しい説明は#pragma twiceのトップページをご覧ください。