質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.23%

WinAPI 自作の円形プログレスバーの描画

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 393

Weapon

score 78

 前提・実現したいこと

自作の円形プログレスバーを作成しましたがバーの描画がウィンドウのリサイズ時のみ一瞬表示されるのですが常には表示されません.どこが問題となっているのでしょうか.
また全体としてボトルネックとなっている箇所,自作ウィンドウメッセージのデザイン等ご教授願いたいです.

 コードの内容

親ウィンドウに対して子ウィンドウ二つ
親ウィンドウはタイマー生成
子ウィンドウ1(chwnd)はステータスのプレビューおよびタイマーを受けたステータスの更新
子ウィンドウ2(graphhwnd)は子ウィンドウ1のメッセージに応じてグラフの描画

自作メッセージ
OPRBM_SETRANGE バーの最大値定義 default:100(今回未使用)
OPRBM_SETSTEP  OPRBM_SETINC/OPRBM_SETDECのステップを定義
OPRBM_SETINC   OPRBM_SETSTEPの定義分値を増やす
OPRBM_SETDEC   OPRBM_SETSTEPの定義分値を減らす(今回未使用)
OPRBM_SET      進捗ステータスをLPARAMで投げる(今回未使用)
なお最大値,ステータスをグローバル定義

CalcArg(int, int)については2つの値から得られる角度をarctangentの[-pi/2,pi/2]から[-pi,pi]に拡張したもの

 該当のソースコード

#ifndef UNICODE
#define UNICODE
#endif

#define OPRBM_SETRANGE    (WM_APP+0x0001)
#define OPRBM_SETSTEP    (WM_APP+0x0002)
#define OPRBM_SETINC    (WM_APP+0x0003)
#define OPRBM_SETDEC    (WM_APP+0x0004)
#define OPRBM_SET        (WM_APP+0x0005)


#include <Windows.h>
#include <math.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK ProgressiveWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
LRESULT CALLBACK GraphWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

const wchar_t CLASS_NAME[] = L"Class Name";
const wchar_t CLASS_NAME_2[] = L"Class Name 2";
const wchar_t CLASS_NAME_3[] = L"Class Name 3";

#define PI (double)3.141592653589793

/*Initial Parameter*/
int width = 300;
int height = 300;
/*Init Param End*/

long max = 100;
long step = 1;
long value = 0;

HINSTANCE hInst;
HWND chwnd;
HWND graphhwnd;
RECT rc;

double CalcArg(int, int);

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int nCmdShow)
{
    hInst = hInstance;

    WNDCLASSEX wc = {
        sizeof(WNDCLASSEX),CS_VREDRAW | CS_HREDRAW,WindowProc,0,
        0,hInstance,NULL,LoadCursor(NULL, IDC_ARROW),
        (HBRUSH)GetStockObject(WHITE_BRUSH),NULL,CLASS_NAME,NULL
    };

    RegisterClassEx(&wc);

    HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, L"Title", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL
    );

    if (hwnd == NULL)return 0;

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    MSG msg = {};

    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{

    switch (uMsg) {
    case WM_CREATE:
    {
        step = 1;
        SetTimer(hwnd, 101, 10000, NULL);

        WNDCLASSEX wc2 = {
            sizeof(WNDCLASSEX),CS_VREDRAW | CS_HREDRAW,ProgressiveWindowProc,0,
            0,hInst,NULL,LoadCursor(NULL, IDC_ARROW),
            (HBRUSH)GetStockObject(WHITE_BRUSH),NULL,CLASS_NAME_2,NULL
        };
        WNDCLASSEX wc3 = {
            sizeof(WNDCLASSEX),CS_VREDRAW | CS_HREDRAW,GraphWindowProc,0,
            0,hInst,NULL,LoadCursor(NULL, IDC_ARROW),
            (HBRUSH)GetStockObject(WHITE_BRUSH),NULL,CLASS_NAME_3,NULL
        };

        RegisterClassEx(&wc2);
        RegisterClassEx(&wc3);

        chwnd = CreateWindowEx(
            0, CLASS_NAME_2, L"Title", WS_OVERLAPPEDWINDOW | WS_CHILD,
            0,0,0,0,
            hwnd, NULL, hInst, NULL
        );
        graphhwnd = CreateWindowEx(
            0, CLASS_NAME_3, L"Title", WS_OVERLAPPEDWINDOW | WS_CHILD,
            10, 10, 500, 500,
            hwnd, NULL, hInst, NULL
        );

        ShowWindow(chwnd, SW_SHOW);
        ShowWindow(graphhwnd, SW_SHOW);
    }
    break;

    case WM_DESTROY:
        KillTimer(hwnd, 101);
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        GetClientRect(hwnd, &rc);
        MoveWindow(chwnd, rc.right - 480, 0, 480, 360, TRUE);

        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        FillRect(hdc, &ps.rcPaint, (HBRUSH)GetStockObject(GRAY_BRUSH));

        EndPaint(hwnd, &ps);

    }
    break;

    case WM_TIMER:
        SendMessage(chwnd, OPRBM_SETINC, 0, 0);

        break;

    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}


LRESULT CALLBACK ProgressiveWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE:
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hwnd, &ps);

        FillRect(hdc, &ps.rcPaint, (HBRUSH)GetStockObject(WHITE_BRUSH));

        wchar_t message[256] = { 0 };
        wsprintf(message, L"max : %d%  step: %d  value : %d", max, step, value);
        TextOut(hdc, 10, 10, message, lstrlen(message));

        EndPaint(hwnd, &ps);
    }
    break;

    case OPRBM_SETRANGE:
        if ((long)lParam > 0) {
            long tmpmax = max;
            max = (long)lParam;
            if ((long)lParam > value)value *= (tmpmax / max);
        }


        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        break;

    case OPRBM_SETSTEP:
        if (((long)lParam >= 0) && ((long)lParam <= max)) {
            step = (long)lParam;
        }

        break;

    case OPRBM_SETINC:

        if (value > (max - step)) {
            value = 100;
        }
        else {
            value += step;
            if (value > 100)value = 100;
        }


        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);

        break;

    case OPRBM_SETDEC:
        if (value < step) {
            value = 0;
        }
        else {
            value -= step;
            if (value < 0)value = 0;
        }


        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        break;

    case OPRBM_SET:
        if ((long)lParam >= 0 && (long)lParam <= max) {
            value = (long)lParam;
        }

        InvalidateRect(hwnd, NULL, TRUE);
        UpdateWindow(hwnd);
        break;
    }

    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

LRESULT CALLBACK GraphWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch (uMsg) {
    case WM_CREATE:
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;

    case WM_PAINT:
    {
        DWORD **image = (DWORD**)malloc(sizeof(DWORD*)*width);
        DWORD *image_c = (DWORD*)malloc(sizeof(DWORD)*width*height);
        for (int i = 0; i < width; i++)image[i] = image_c + i * height;
        for (int i = 0; i < width; i++)for (int j = 0; j < height; j++)image[i][j] = 0xffffffff;

        DWORD *dib = (DWORD*)calloc(sizeof(DWORD)*width*height, sizeof(sizeof(DWORD)*width*height));

        //from here

        double center_x = 150, center_y = 150;
        double radius = 90;

        for (int i = 0; i < width; i++)for (int j = 0; j < height; j++) {
            if (radius*radius - (i - center_x)*(i - center_x) - (j - center_y)*(j - center_y) > 0) {
                if ((CalcArg(i - center_x, j - center_y) >= (1 - (double)value / (double)max * 2)*PI) && (CalcArg(i - center_x, j - center_y) <= PI)) {
                    image[i][j] = 0x00000000;
                }
            }
        }
        image[(int)center_x][(int)center_y] = 0x00000000;

        //to here

        // traca
        for (int i = 0; i < width; i++)for (int j = 0; j < height; j++)dib[i + j * width] = image[height - j - 1][i];

        HDC hdc = GetDC(hwnd);

        BITMAPINFO bmpi;
        ZeroMemory(&bmpi, sizeof(bmpi));
        bmpi.bmiHeader.biSize = sizeof(bmpi);
        bmpi.bmiHeader.biWidth = width;
        bmpi.bmiHeader.biHeight = height;
        bmpi.bmiHeader.biPlanes = 1;
        bmpi.bmiHeader.biBitCount = 32;
        bmpi.bmiHeader.biCompression = BI_RGB;

        StretchDIBits(
            hdc, 0, 0, width * 2, height * 2, 0, 0, width, height,
            dib, &bmpi,
            DIB_RGB_COLORS, SRCCOPY
        );
        free(dib);
        free(image_c);
        free(image);

    }
    break;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

double CalcArg(int x, int y) {
    double arg = atan((double)y / (double)x);

    if (x >= 0)return arg;
    else {
        if (y >= 0)return arg + PI;
        else return arg - PI;
    }
}

 補足情報

Windows10 Pro
VisualStudio 2017 Community

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+2

親ウィンドウにWS_CLIPCHILDRENつけてないから子ウィンドウを上書きしてるんじゃないですかね?

 HWND hwnd = CreateWindowEx(
        0, CLASS_NAME, L"Title", WS_OVERLAPPEDWINDOW | WSS_CLIPCHILDREN,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, hInstance, NULL
    );

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/10/30 00:53

    WS_CLIPCHILDREN使わせていただき上手くいきました.
    もう少し質問なのですがWS_CLIPCHILDREN自体にはどういう効果があるものなのでしょうか.情報が少し少ないのでお願いします.
    WM_CHILDで子ウィンドウを設けた場合は基本必須の項目でしょうか?

    キャンセル

  • 2018/10/30 01:29

    WS_CLIPCHILDRENを付けるとウィンドウの描画を開始した時にHDCのクリッピングリージョンから自動的に子ウィンドウの領域が除外されます。
    ですので、描画の順番で子ウィンドウを親が上書きしてしまうことがなくなります。
    WindowsのGDIは描画の途中が見えてしまう古~~~い設計なので大抵はつけます。

    ただ、例えば一昔前の音楽プレーヤーみたいに見た目上複雑な(四角形の集合でない)GUIを作るので子は置くけど透明でWM_PAINTを処理せず、親のWM_PAINTで子を含む全てを描画してしまう場合はWS_CLIPCHILDRENを付けません。
    この時、一部の子ウィンドウは子が描画したい、という場合は親の描画が開始した時に描画を行う子だけは自前でクリッピングリージョンから除外してから描画するようにしないといけません。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.23%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる