前回、次の質問を残しました。
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_ERASEBKGRDやWM_PAINT)」を出してくるということです。
では、このWM_PAINTメッセージ処理(OnPaint関数)をコメントを外し、"retuern FALSE;"をコメントアウトしてコンパイルするとどうなるでしょう?今度はウィンドウを最小化したり、サイズを変更しても、ビットマップ画像が消えなくなることにご注意ください。
どうでしょう?
あーっ、めんどーくせー!
と思いませんでしたか?実は私もこのWindowsの描画処理のお約束(WM_PAINTメッセージ時にBeginPaintとEndPaintの中に描画コードをいれる)というのが面倒且つ(プログラムのどこでも描画命令が描けないという)柔軟性の欠如でうんざりしていました。
それがBCCSkeltonで
CANVASクラス
を書く動機になりました。(後は、次回にしましょう。)