前回、次の質問を残しました。

Q:では、ウィンドウに対する描画命令はどこに描けば(「書けば」でした)よいのでしょうか?

A1:ウィンドウを登録、作成表示した後ならどこでもよい。

A2:ユーザー処理はこのコールバック関数内で行う。

A3:殊に描画処理は、WM_PAINTメッセージが来た際にこのコールバック関数内で行う。

またまた「やっちまったな」という感じです。「~すればよい」という言葉は多義的なので、質問の意図が曖昧、不明瞭(vague and ambiguous)ですよね?(英語でいえば、suggested or recommended to doとかmay doの意味でしょうか?)

「書いてもよい」という意味であれば、総て〇です。(実際、ChatGPTのプログラミングだとウィンドウ作成、表示後にコードを置いています。)

翻って「再描画されても画像が消えないようにする方が良いので、そうするならば」という意味であればA3のみが〇です。

 

私は実験好きなので、こういう時は必ず実際にやってみます。

 

以下(↓)のようなテスト用関数を作り、SKDスケルトンファイルのProcedure.hファイルの冒頭に置きます。また実行プログラム(Program.exe)のあるフォールダーに何かビットマップイメージを"Image.bmp"というファイル名で保存してください。

//テスト用関数
BOOL ShowImage(HWND hWnd) {

    // 画像の読み込み
    HBITMAP hBitmap = (HBITMAP)LoadImage(NULL, "C:\\Users\\ysama\\Programing\\Windows Program\\WinTemplate\\Debug\\Image.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
    // ウィンドウの描画
    HDC hDC = GetDC(hWnd);    //ウィンドウのデバイスコンテキスト(DC)の取得
    HDC hMemDC = CreateCompatibleDC(hDC);    //DCと同じ構造の仮想DCをメモリー上に確保(コンパチDCの作成)
    SelectObject(hMemDC, hBitmap);     //コンパチDCにビットマップを取り込む
    BITMAP bitmap = {};    //ビットマップ情報を取得するためのITMAP構造体
    GetObject(hBitmap, sizeof(BITMAP), &bitmap);    //ビットマップ情報の取得

    //同じサイズのビットマップを単に表示するのであればBitBlt関数を使うが、ここでは縦横を2倍に引き伸ばしている
    bool result = StretchBlt(hDC, 0, 0, bitmap.bmWidth * 2, bitmap.bmHeight * 2, hMemDC, 0, 0, 32, 32, SRCCOPY);
    DeleteObject(hBitmap);    //使ったビットマップは必ずお片づけ(メモリーの開放)

    DeleteDC(hMemDC);    //使ったコンパチDCも必ずお片づけ(メモリーの開放)
    ReleaseDC(hWnd, hDC);    //最後にウィンドウのデバイスコンテキストを開放
    return result;    //StretchBlt関数の成功、失敗を返す
}

A1:次に、何処でもよいのですが(コールバック関数内でもよいので、A2、A3と論理的にオーバーラップ=共に真となるする余地があるということですよね?)まずSDI.hのInitInstance関数のreturn TRUE;文の前に

//テスト用-再描画されると消えてしまう
ShowImage(hWnd);

を入れてコンパイルしてください。ビットマップが表示されるのが分かります。この時、ウィンドウを最小化したり、サイズを変更したりすると「再描画」が行われ、ビットマップ画像が消えてしまうことを確かめてください。

 

A2:これはコールバック関数の何処でもよいのですが(WM_PAINT以外のとは書かれていないので、A2とA3は論理的にオーバーラップ=共に真となるする余地があるということですよね?)、例えばWM_CREATEに同様にテスト用関数を置くと全く同じ動作をします。

 

A3:最後にWM_PAINTメッセージの場合に呼び出されるOnPaint(HWND hWnd)関数内にテスト用関数を置くのですが、唯単に置くのではなく、以下のコメントにあるように

 

bool OnPaint(HWND hWnd) {
/*
    PAINTSTRUCT paint;
    HDC hDC = BeginPaint(hWnd, &paint);
    //ユーザー処理←ここにShowImage(hWnd);を置く
    EndPaint(hWnd, &paint);
    return TRUE;         //WM_PAINTメッセージ処理をした場合はTRUEを返す
*/

    return FALSE;        //WM_PAINTメッセージ処理をしない場合はFALSEを返す
}

PAINTSTRUCT構造体を使ったBeginPaint(hWnd, &paint);文とEndPaint(hWnd, &paint);文の間に描画処理を行う必要があります。

何故か?

これ(OSが行う描画処理)については、Microsoft Learnの良い記事を見つけましたので、特にこれを読んでいただけるとよいと思います。(前回分かっていればリンクを張ったのですが...)要すれば、前回書いたように「矩形(「位置」および幅と高さを含めた「領域」)を描画単位」としており、そこに変更が生じればOSが検知して「無効領域(Invalidated region)」として「再描画要求(WM_ERASEBKGRDWM_PAINT)」を出してくるということです。

では、このWM_PAINTメッセージ処理(OnPaint関数)をコメントを外し、"retuern FALSE;"をコメントアウトしてコンパイルするとどうなるでしょう?今度はウィンドウを最小化したり、サイズを変更しても、ビットマップ画像が消えなくなることにご注意ください。

どうでしょう?

あーっ、めんどーくせー!

と思いませんでしたか?実は私もこのWindowsの描画処理のお約束(WM_PAINTメッセージ時にBeginPaintEndPaintの中に描画コードをいれる)というのが面倒且つ(プログラムのどこでも描画命令が描けないという)柔軟性の欠如でうんざりしていました。

 

それがBCCSkeltonで

CANVASクラス

を書く動機になりました。(後は、次回にしましょう。)