BitBlt()とWM_CLOSEメッセージ

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
AC

BitBlt()とWM_CLOSEメッセージ

#1

投稿記事 by AC » 16年前

ただいまWin32APIを勉強中なのですが、ちょっと原因不明なことが起きましたので質問させていただきます。
とりあえずソースから。ウィンドウプロシージャ部分です。
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
	// ビットマップハンドル
	static HBITMAP hBitmap, hPrevBitmap;
	// デバイスコンテキストハンドル
	static HDC hDC, hCompatDC;
	
	switch(msg)
	{
	case WM_CREATE:
		// ビットマップをファイルから読み込む
		hBitmap = (HBITMAP)LoadImage(NULL, _T(".\\test.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		if(hBitmap == NULL)
		{
			MessageBox(hWnd, _T("読み込みエラー"), _T("警告"), MB_OK | MB_ICONWARNING);
			break;
		}		
		// ウィンドウのデバイスコンテキストハンドルを取得
		hDC = GetDC(hWnd);
		// メモリデバイスコンテキストを作成
		hCompatDC = CreateCompatibleDC(hDC);
		// ロードしたビットマップを選択
		hPrevBitmap = (HBITMAP)SelectObject(hCompatDC, hBitmap);
		break;
	case WM_CLOSE:
		if(MessageBox(hWnd, _T("終了しますか?"), _T("WM_CLOSE"), MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			// WM_DESTROYメッセージを送信
			DestroyWindow(hWnd);
		}
		break;
	case WM_DESTROY:
		// 以前のビットマップに戻す
		SelectObject(hCompatDC, hPrevBitmap);
		// ロードしたビットマップを削除
		DeleteObject(hBitmap);
		// メモリデバイスコンテキストを削除
		DeleteObject(hCompatDC);
		// デバイスコンテキストハンドル削除
		ReleaseDC(hWnd, hDC);
		PostQuitMessage(0);
		break;
	case WM_PAINT:
		// ビットマップをウィンドウに転送
		BitBlt(hDC, 10, 10, 500, 500, hCompatDC, 0, 0, SRCCOPY);
		break;
	default:
		return DefWindowProc(hWnd, msg, wp, lp);
	}
	
	return 0;
}
この結果画像は正常表示されて、上手くいったかのように思えたのですが、×ボタンを押すと固まってしまいます。
WM_CLOSEメッセージでメッセージボックスを表示し、終了確認するようにしているのですが、どうもこの処理が原因のようで、コメントアウトしたところ正常に動作しました。
あと、いろいろやってみた結果、理由は良く分からないのですが、固まっている時にAltキーを押すとメッセージボックスが表示されました。
また、固まっているときにスペースキー、エンターキーでプログラムが終了したので、固まっているときもメッセージボックスは表示されているつもり(?)なようです。

ということで、この現象の起きる原因のわかる方は、どうかご教授願います。

dic

Re:BitBlt()とWM_CLOSEメッセージ

#2

投稿記事 by dic » 16年前

WM_PAINT は BeginPaint(), で取得したデバイスコンキテストで描画しなければなりません
終了後 EndPaint() 関数で開放します
コートに示すと以下のようになります
// デバイスコンテキストハンドル
	static HDC hDC, hCompatDC;
	static HDC hdc2;	//	追加
	PAINTSTRUCT	pt;		//	追加

	case WM_PAINT:
		// ビットマップをウィンドウに転送
		hdc2 = BeginPaint( hWnd, &pt );
		BitBlt(hdc2, 10, 10, 500, 500, hCompatDC, 0, 0, SRCCOPY);
		EndPaint( hWnd, &pt );
		break;

AC

Re:BitBlt()とWM_CLOSEメッセージ

#3

投稿記事 by AC » 16年前

dicさん、解答ありがとうございます。

言われてみればそうでした。
画像表示されてたのですっかり失念していました。
以下のように訂正しました。
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
	// ビットマップハンドル
	static HBITMAP hBitmap;
	
	switch(msg)
	{
	case WM_CREATE:
		// ビットマップをファイルから読み込む
		hBitmap = (HBITMAP)LoadImage(NULL, _T(".\\test.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
		if(hBitmap == NULL)
		{
			MessageBox(hWnd, _T("読み込みエラー"), _T("警告"), MB_OK | MB_ICONWARNING);
			break;
		}
		break;
	case WM_CLOSE:
		if(MessageBox(hWnd, _T("終了しますか?"), _T("WM_CLOSE"), MB_YESNO | MB_ICONQUESTION) == IDYES)
		{
			// WM_DESTROYメッセージを送信
			DestroyWindow(hWnd);
		}
		break;
	case WM_DESTROY:
		// ロードしたビットマップを削除
		DeleteObject(hBitmap);
		PostQuitMessage(0);
		break;
	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hDC = BeginPaint(hWnd, &ps);
			// メモリデバイスコンテキストを作成
			HDC hCompatDC = CreateCompatibleDC(hDC);
			// ロードしたビットマップを選択
			HBITMAP hPrevBitmap = (HBITMAP)SelectObject(hCompatDC, hBitmap);
			// ビットマップをウィンドウに転送
			BitBlt(hDC, 10, 10, 500, 500, hCompatDC, 0, 0, SRCCOPY);
			// 以前のビットマップに戻す
			SelectObject(hCompatDC, hPrevBitmap);
			DeleteObject(hCompatDC);
			EndPaint(hWnd, &ps);
		}
		break;
	default:
		return DefWindowProc(hWnd, msg, wp, lp);
	}
	
	return 0;
}
一応これで解決にしたしますが、何か他に間違いやコメント等があったら書いてくれると嬉しいです。

御津凪

Re:BitBlt()とWM_CLOSEメッセージ

#4

投稿記事 by 御津凪 » 16年前

通常、ウインドウをクローズすると、 WM_CLOSE メッセージが送られた後、
(そのメッセージでFALSEを返さなかった場合)
WM_DESTROY メッセージが呼ばれます。

上記のメッセージ処理だと、二重に WM_DESTROY が送られて、
異常終了になるか、デバッグ出力ウインドウに例外メッセージが表示されます。

また、上記メッセージボックスで「いいえ」を選択、またはメッセージボックスを×ボタンで閉じると、
そのままウインドウが閉じてしまいます。(こちら側は問題なく終了すると思います)

質問の表示されない問題ですが(既に解決していますが)、
dic さんの通りに HDC を取得しないといけませんね。
うまく描画されているようでも、必要の無い描画まで行うため、メッセージボックスの表示が
塗りつぶされてしまいます。
(BeginPaint で取得されたものは、必要の無い領域を描画しません)

AC

Re:BitBlt()とWM_CLOSEメッセージ

#5

投稿記事 by AC » 16年前

御津凪さん、補足ありがとうございます。

>通常、ウインドウをクローズすると、 WM_CLOSE メッセージが送られた後、
>(そのメッセージでFALSEを返さなかった場合)
>WM_DESTROY メッセージが呼ばれます。
>上記のメッセージ処理だと、二重に WM_DESTROY が送られて、
>異常終了になるか、デバッグ出力ウインドウに例外メッセージが表示されます。

とのことですが、WM_CLOSEメッセージでDefWindowProc()が呼ばれた場合、DestroyWindow()が実行されて、WM_DESTROYメッセージが送られるのではないのでしょうか?
ブレークポイントを↑のソースの
①if(MessageBox(hWnd, _T("終了しますか?"), _T("WM_CLOSE"), MB_YESNO | MB_ICONQUESTION) == IDYES)
②DestroyWindow(hWnd);
③DeleteObject(hBitmap);
に入れて調べたところ、
×ボタンを押すと①で停止。
そのまま続行してメッセージボックスが表示されNOを押すと何も起こらず、YESを押すと②で静止。
そのまま続行すると③で静止。
という流れでしたので、二重に WM_DESTROYが送られているということはないと思われます。
例外メッセージもなさそうですので。

御津凪

Re:BitBlt()とWM_CLOSEメッセージ

#6

投稿記事 by 御津凪 » 16年前

> WM_CLOSEメッセージでDefWindowProc()が呼ばれた場合、DestroyWindow()が実行されて、WM_DESTROYメッセージが送られるのではないのでしょうか?

DefWindowProc ()の位置を良く見てませんでした…。
すみません。

私はメッセージ処理に default を使わず、個々で return 処理をし、しなかった処理は DefWindowProc() を呼び出す、
という方法を用いていましたので。

AC

Re:BitBlt()とWM_CLOSEメッセージ

#7

投稿記事 by AC » 16年前

>私はメッセージ処理に default を使わず、個々で return 処理をし、しなかった処理は DefWindowProc() を呼び出す
なるほど、そういうことでしたか。確かにいろんなパターンありますからね。
とりあえず、自分の理解が間違ってなかったようでよかったです。

では以上を持って本当に解決ということにさせていだたきます。
回答してくださった皆さん、ありがとうございました。

閉鎖

“C言語何でも質問掲示板” へ戻る