描画をしながら Probress Bar を表示

VC++ を使ってウインドウに図形を描画しながらモードレス Dialog Box に貼り付けた Probress Bar を動かします。
空のプロジェクトから始めてリソースも全て自前で作成します。

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

プロジェクトの作成

  1. 空の新規プロジェクト(PmbBar2)を作成します。
    空のプロジェクトの作成方法は Dialog Box のプログラム を参照して下さい。
  2. 次のファイルをフォルダーに格納して[プロジェクト][既存項目の追加]からプロジェクトに追加して下さい。
    メニューや Dialog Box は、全て自前で作成したものを使用します。
    ファイルの詳細は、この後の説明を参照して下さい。
    ファイル名 説明
    PmbBar2.cpp ソースプログラム
    PmbBar2.rc リソース定義ファイル
    resource.h リソースヘッダーファイル
  3. 自前で作成したリソースを使うと警告のメッセージが表示されますが無視して下さい。
    警告の意味はリソースのソースコードを調べれば原因がつかめると思うのですが暇なときの課題です。 (^_^;
  4. ビルドに続いて実行を行います。
    ウインドウの描画と Probress Bar が独立して動いていることに注目して下さい。
    「円を描画」と「矩形を描画」をクリックすると画像が切り替わります。

プログラムの説明

  1. リソース定義ファイル(PmbBar2.rc) の説明です。
    VC++ でリソースを定義すると非常に多くのソースコードが生成されますが、実際に必要なものは数行だけです。
    Dialog Box のリソース定義です。
    書かれている内容を見れば、大体理解できるでしょう。
    リソースファイルの先頭で windows.h を #include します。
    また、リソースで使われている ID が定義されているヘッダファイルも #include して下さい。
        /////////////////////////////////////////////////////////////////////////////
        //
        // Dialog
        //
    
        IDD_PROGRESS DIALOG DISCARDABLE  0, 0, 190, 67
        STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
        CAPTION "File Copy"
        FONT 9, "MS Pゴシック"
        BEGIN
            PUSHBUTTON      "キャンセル",IDCANCEL,130,45,50,14
            CONTROL         "Progress1",IDC_PROGRESS,"msctls_progress32",WS_BORDER,7,
                            7,175,14
            EDITTEXT        IDC_FILENAME,7,25,174,14,ES_AUTOHSCROLL
            PUSHBUTTON      "円を描画",IDC_BUTTON1,8,45,50,14
            PUSHBUTTON      "矩形を描画",IDC_BUTTON2,69,45,50,14
        END
        
  2. リソースのヘッダーファイル(resource.h) の説明です。
    ID で定義する値は、重複しないように適当な値を設定します。
    何を設定しても大抵うまく行くのですが、あまり変な値を設定すると思わぬことが起こるかも知れません。 (^_^;
    IDC_STATIC はプログラムからは参照しないことを意味する値「-1」に設定します。
        #define IDD_PROGRESS    120
        #define IDC_PROGRESS    121
        #define IDC_FILENAME    122
        #define IDC_BUTTON1     123
        #define IDC_BUTTON2     124
        
  3. Main Program(PmbBar2.cpp) の説明です。
    commctrl.h をインクルードして下さい。
    ID_MYTIMER はタイマーのIDです。
    Probress Bar と図形の描画はタイマ割り込みで行います。
    g_hDlg は DialogBox のハンドルです。
    SW はウインドウに表示する図形を円と矩形に切り替えます。
    SetProgBar() は Probress Bar を初期化する関数です。
        #include    <commctrl.h>
        #define     ID_TIMER        32767
    
        HINSTANCE   g_hInst;
        HWND        g_hDlg;
        int         SW;
    
        // Function-prototypes
        LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
        LRESULT CALLBACK    DlgProc(HWND, UINT, WPARAM, LPARAM);
        void                SetProgBar();
        
  4. CALLBACK 関数です。
    WM_CREATE: では CreateDialog() で Dialog Box を生成して ShowWindow(); UpdateWindow(g_hDlg); で表示します。
    hWnd のタイマを設定してウインドウの描画を開始します。
        LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
        {   PAINTSTRUCT ps;
            HDC         hdc;
            RECT        rc;
            int         i, wx, wy, x, y;
    
            switch (msg)
            {   case WM_CREATE:
                    g_hDlg = CreateDialog(g_hInst,MAKEINTRESOURCE(IDD_PROGRESS),hWnd,(DLGPROC)DlgProc);
                    if (g_hDlg == NULL)    return FALSE;
                    SetProgBar();
                    ShowWindow(g_hDlg, SW_SHOW);
                    UpdateWindow(g_hDlg);
                    SetTimer(hWnd,ID_TIMER,100,NULL);
                    break;
        
  5. ウインドウに描画する処理です。
    SW で円と矩形を切り替えます。
    ウインドウを描画するタイミングは、タイマ割り込みで知らせます。
    プログラムを終了する前にタイマを停止して下さい。
            case WM_PAINT:
                hdc = BeginPaint(hWnd, &ps);
                GetClientRect(hWnd, &rc);
                wx = rc.right - rc.left;
                wy = rc.bottom - rc.top;
                if (wx == 0)    wx = 1;
                if (wy == 0)    wy = 1;
                for(i=0; i<100; i++)
                {   x = rand() % wx;
                    y = rand() % wy;
                    if (SW)     Rectangle(hdc,x,y,x+30,y+30);
                    else        Ellipse(hdc,x,y,x+30,y+30);
                }
                EndPaint(hWnd, &ps);
                break;
            case WM_TIMER:
                InvalidateRect(hWnd,NULL,TRUE);
                UpdateWindow(hWnd);
                break;
                    :
        
  6. Dialog Box の CALLBACK 関数です。
    ボタンのクリックで円と矩形を切り替えます。
    キャンセルボタンで DialogBox を閉じます。
    Probress Bar の表示もタイマ割り込みで行います。
        LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
        {
            switch(msg)
            {   case WM_COMMAND:
                    switch (LOWORD(wp))
                    {   case IDC_BUTTON1:
                            SW = 0;
                            return TRUE;
                        case IDC_BUTTON2:
                            SW = 1;
                            return TRUE;
                        case IDCANCEL:
                            KillTimer(hDlg, ID_TIMER);
                            EndDialog(hDlg, IDOK);
                            return TRUE;
                    }
                    return FALSE;
                case WM_TIMER:
                    SendMessage(GetDlgItem(hDlg,IDC_PROGRESS),PBM_STEPIT,0,0);
                    return TRUE;
                        :
        
  7. Probress Bar を初期化する関数です。
    Probress Bar を進めるためにタイマを使っていますが、ウインドウの描画とは別の割り込みが発生します。
    ウインドウの描画では hWnd に、Probress Bar は g_hDlg に設定しています。
        void SetProgBar()
        {   HWND    hProg;
    
            hProg = GetDlgItem(g_hDlg,IDC_PROGRESS);
            SendMessage(hProg,PBM_SETRANGE,(WPARAM)0,MAKELPARAM(0,100));
            SendMessage(hProg,PBM_SETSTEP,(WPARAM)10,0);
            SendMessage(GetDlgItem(g_hDlg,IDC_PROGRESS),PBM_SETPOS,0,0);
            SetTimer(g_hDlg,ID_TIMER,500,NULL);
            return;
        }
        

ウインドウの描画と Probress Bar が独立して動いているように見えますが、実際には一個のスレッドをタイマ割り込みで分け合って処理しています。
試しに右クリックしたとき5秒間 SLEEP するコードを入れてみました。
    case WM_RBUTTONDOWN:
        Sleep(5000);
        break;

ウインドウの描画だけで無く Probress Bar も5秒間停止していまいます。
これを避けるにはマルチスレッドの処理が必要になります。

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