円と矩形を同時に描画

共通のウインドウに「メインからは円」を「スレッドからは矩形」を重ねて描画します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プロジェクトの説明

  1. 新規プロジェクト(Th_Draw)を作成して下さい。
  2. ID_TIMER はタイマーのIDです。
    DATA はスレッドに渡すパラメータの構造体の宣言です。
    DATA Data; でグローバル領域に構造体を確保しています。
    DWORD Thread(LPVOID); はスレッドで起動する関数の宣言です。
        #define     ID_TIMER    (WM_APP + 0)
    
        typedef struct
        {   HWND    hwnd;
            BOOL    end;
        }   DATA,   *PDATA;
    
        DATA        Data;
        HANDLE      hThread;
        DWORD   WINAPI      Thread(LPVOID);
        
  3. WinMain() ではサイズを設定してウインドウを表示するだけです。
  4. CALLBACK 関数です。
    スレッドのIDと円を描画する領域を定義します。
    WM_CREATE: では CreateThread(); でスレッドを生成します。
    メインプログラムではタイマを設定してウインドウに円を描画します。
        LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
        {
            DWORD           threadID;
            PAINTSTRUCT     ps;
            HDC             hdc;
            RECT            rc;
            int             i,x,y,wx,wy;
    
            switch(msg)
            {
                case WM_CREATE:
                    Data.hwnd = hWnd;
                    Data.end = FALSE;
                    hThread = CreateThread(NULL, 0,
                        (LPTHREAD_START_ROUTINE)Thread,
                        (LPVOID)&Data,
                        0, (LPDWORD)&threadID);
                    SetTimer(hWnd,ID_TIMER,100,NULL);
                    break;
        
  5. CALLBACK 関数の続きです。
    WM_TIMER: で円を描画するために InvalidateRect() と UpdateWindow() を実行します。
            case WM_TIMER:
                InvalidateRect(hWnd,NULL,TRUE);
                UpdateWindow(hWnd);
                break;
        
  6. WM_PAINT: で乱数で座標を設定して円を描画します。
            case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                GetClientRect(hWnd, &rc);
                wx= rc.right - rc.left;
                wy= rc.bottom - rc.top;
                for(i=0; i<10; i++)
                {   x= rand() % wx;
                    y= rand() % wy;
                    Ellipse(hdc,x,y,x+30,y+30);
                }
                EndPaint(hWnd, &ps);
                break;
        
  7. WM_CLOSE: でプログラムを終了します。
    マルチスレッドでは、スレッドの終了を確認してからメインプログラムを終わらせて下さい。
    終了手順を確認するためにメッセージボックスを表示しています。
    タイマを停止して Data.end に TRUE を設定してスレッドが終了するのを待ちます。
    WaitForSingleObject() が終了を待ち合わせる関数です。
    CloseHandle(hThread) でスレッドを閉じます。
            case WM_CLOSE:
                KillTimer(hWnd, ID_TIMER);
                Data.end = TRUE;
                WaitForSingleObject(hThread, INFINITE);
                MessageBox(NULL, "スレッド終了成功!", "終了確認", MB_OK);
                if (CloseHandle(hThread) != 0)
                    MessageBox(NULL, "ハンドルクローズ成功!", "終了確認", MB_OK);
                DestroyWindow(hWnd);
                break;
        
  8. Thread で動作する関数です。
    パラメータで渡された構造体のポインタを設定します。
    pData->end がゼロの間、メインウインドウに矩形を描画し続けます。
        DWORD  WINAPI  Thread(LPVOID param)
        {   PDATA       pData;
            HDC         hdc;
            RECT        rc;
            int         xp,yp,i;
    
            pData = (PDATA)param;
            hdc = GetDC(pData->hwnd);
            while(!pData->end)              //end==0 の間ループ
            {   GetClientRect(pData->hwnd, &rc);
                for(i=0; i<5; i++)
                {   xp= rand() % (rc.right-rc.left-50);
                    yp= rand() % (rc.bottom-rc.top-50);
                    Rectangle(hdc,xp,yp,xp+50,yp+50);
                }
                Sleep(300);
            }
            ReleaseDC(pData->hwnd, hdc);
            return 0L;
        }
        

【演習】

  1. プログラムを完成させて下さい。
  2. マウスの右ボタンでメインの処理を5秒間 Sleep して下さい。
            case WM_RBUTTONDOWN:
                MessageBox(NULL,"RBUTTON","Sleep 5000",MB_OK);
                Sleep(5000);
                break;
        
  3. マウスの右ボタンをクリックしてみて下さい。
    メッセージボックスが表示されて OK をクリックすると5秒間 Sleep() します。
    円の描画が止まり、スレッドで描画している矩形だけが表示されることを確認して下さい。

超初心者のプログラム入門(Win32API C++)