CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しない

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
wasawasa
記事: 94
登録日時: 6年前

CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しない

#1

投稿記事 by wasawasa » 5年前

こんにちは、何度もお世話になっております。
親ウィンドウとその子ウィンドウであるスタティックがあるプログラムで「親ウィンドウのサイズを変更した時にその時親ウィンドウに渡されたWM_SIZEメッセージを子ウィンドウに渡し、子ウィンドウは親ウィンドウからWM_SIZEを渡された時に自身のサイズを親ウィンドウのクライアント領域と同じサイズに変更する」という動作を期待して下記のようなソースを記述しました。その結果、通常のサイズ変更をした時と最大化ボタンを押した時と元のサイズに戻すボタンを押した時には子ウィンドウは期待通りの動作をするようになったのですが、親ウィンドウが生成された時と最小化した後に通常のサイズに戻した時には子ウィンドウが表示されませんでした。
原因を調べてみたところ、親ウィンドウが生成された時に子ウィンドウが表示されないのは親ウィンドウが生成した時にはhMainWndに何のデータも格納できていない為にrecClient2にクライアント領域の座標が格納できなかった事が原因だという事が分かりましたが、最小化した後に通常のサイズに戻した時に子ウィンドウが表示されない原因が分かりません。
また、29行目や75行目の処理でhMainWndやhCWndにウィンドウハンドルを受け取れない理由も分かりません。
以上の2点についてお聞きしたいと思い質問させて頂きました。どなたかよろしくお願いします。

コード:

#include <windows.h>

HWND hMainWnd;
HWND hCWnd;
FARPROC Org_StaticWndProc;

LRESULT CALLBACK StaticProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	RECT recClient2;
	GetClientRect(hMainWnd,&recClient2);
	switch(msg){
		case WM_SIZE:
			MoveWindow(hCWnd,recClient2.left , recClient2.top , recClient2.right , recClient2.bottom ,FALSE);
			break;
	}return (CallWindowProc((WNDPROC)Org_StaticWndProc,hCWnd,msg, wp, lp));
}

LRESULT CALLBACK WndProc(HWND hwnd , UINT msg , WPARAM wp , LPARAM lp) {
	static CONST PSTR strKitty =
		TEXT("SubClass Dounyuu Test\nHyouzi Sarereba Seikou");
	RECT recClient1;
	GetClientRect(hwnd,&recClient1);

	switch (msg) {
	case WM_DESTROY:
		SetWindowLong(hCWnd,GWL_WNDPROC,(LONG)Org_StaticWndProc);
		PostQuitMessage(0);
		return 0;
	case WM_CREATE:
		hCWnd=CreateWindow(
			TEXT("STATIC") , strKitty ,
			WS_CHILD | WS_VISIBLE | SS_CENTER ,
			recClient1.left , recClient1.top , recClient1.right , recClient1.bottom ,
			hwnd , (HMENU)1 ,
			((LPCREATESTRUCT)(lp))->hInstance , NULL
		);
		Org_StaticWndProc = (FARPROC)GetWindowLong(hCWnd,GWL_WNDPROC);
		SetWindowLong(hCWnd,GWL_WNDPROC,(LONG)StaticProc);
		return 0;
	case WM_SYSCOMMAND:
		DefWindowProc(hwnd,msg,wp,lp);
		return 0;
	case WM_SIZE:
		SendMessage(hCWnd, msg, wp, lp);
		return 0;
	default:
		return DefWindowProc(hwnd , msg , wp , lp);
	}
	return 0;
}

int WINAPI WinMain(HINSTANCE hInstance , HINSTANCE hPrevInstance ,
			PSTR lpCmdLine , int nCmdShow ) {
	MSG msg;
	WNDCLASSEX winc;

	winc.cbSize        = sizeof(WNDCLASSEX);
	winc.style		= CS_HREDRAW | CS_VREDRAW;
	winc.lpfnWndProc	= WndProc;
	winc.cbClsExtra	= 0;
	winc.cbWndExtra = 0;
	winc.hInstance		= hInstance;
	winc.hIcon		= LoadIcon(NULL , IDI_APPLICATION);
	winc.hCursor		= LoadCursor(NULL , IDC_ARROW);
	winc.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	winc.lpszMenuName	= NULL;
	winc.lpszClassName	= TEXT("MeinWindou");
	winc.hIconSm       = (HICON)LoadImage(NULL,
						MAKEINTRESOURCE(IDI_APPLICATION),
						IMAGE_ICON,
						0, 0,
						LR_DEFAULTSIZE|LR_SHARED);

	if (!RegisterClassEx(&winc)) return -1;

	hMainWnd = CreateWindow(
			TEXT("MeinWindou") , TEXT("SubClassTest") ,
			WS_OVERLAPPEDWINDOW | WS_VISIBLE ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			CW_USEDEFAULT , CW_USEDEFAULT ,
			NULL , NULL , hInstance , NULL
	);

	if (hMainWnd == NULL) return -1;

	while(GetMessage(&msg , NULL , 0 , 0)) DispatchMessage(&msg);
	return msg.wParam;
}

アバター
へろりくしょん
記事: 92
登録日時: 9年前
住所: 福岡

Re: CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しな

#2

投稿記事 by へろりくしょん » 5年前

wasawasa さんが書きました:最小化した後に通常のサイズに戻した時に子ウィンドウが表示されない原因が分かりません。
MoveWindow() API の第6引数に、FALSE を指定しているからでは無いでしょうか。
これは再描画フラグですから、FALSE の場合は再描画されません。
wasawasa さんが書きました: また、29行目や75行目の処理でhMainWndやhCWndにウィンドウハンドルを受け取れない理由も分かりません。
hCWnd はともかく hMainWnd は受け取れないと、続く83行目で終了すると思うのですが、受け取れていないと考えた理由はなんでしょうか。
とりあえず、CreateWindow() API が失敗した場合は、GetLastError() API を呼んでみると幸せになれるかもしれません。


幾分と突っ込みどころの多いコードですが、とりあえず今回の問題では
親ウィンドウが WM_SIZE を受け取った時に

SetWindowPos(hCWnd, NULL, 0, 0, LOWORD(lp), HIWORD(lp), SWP_NOZORDER);

とでもするのが定石と言うか、一番スマートだと思いますよ。
この場合は、StaticProc() プロシージャの WM_SIZE メッセージ処理はコメントアウトしておいてください。

また、StaticProc() プロシージャですが、
せっかく WM_SIZE メッセージを独自に処理してるのに、それを CallWindowProc() API で元のプロシージャに投げるのはちょっと考え直した方が良いかと思います。

zeek

Re: CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しな

#3

投稿記事 by zeek » 5年前

> 最小化した後に通常のサイズに戻した時に子ウィンドウが表示されない原因が分かりません。
背景から再描画していないことが原因では?

> 29行目や75行目の処理でhMainWndやhCWndにウィンドウハンドルを受け取れない理由も分かりません。
受け取れていないことをどのように切り分けたのでしょうか?

hMainWnd = CreateWindow(...);
の動作は
CreateWindow(...) 実行開始
WM_CREATE メッセージ
WM_SIZE メッセージ
CreateWindow(...) 実行終了
hMainWnd にウィンドウハンドル格納
の順番です。
#正確には、CreateWindow() 内で他のメッセージも発生しています。

コードの修正例を提示しておきます。

コード:

//  GetClientRect(hMainWnd,&recClient2);   ここではどんなメッセージが来ても実行する★ムダ★あり
    switch(msg){
    case WM_SIZE:
        if (hMainWnd) {  // ★修正
            GetClientRect(hMainWnd,&recClient2);  // ★移動すべき
//          MoveWindow(hCWnd,recClient2.left , recClient2.top , recClient2.right , recClient2.bottom ,FALSE);
            MoveWindow(hwnd,recClient2.left , recClient2.top , recClient2.right , recClient2.bottom ,TRUE);  // ★背景から再描画、ローカルで済むものはローカルが基本
        }
//      break;
        return 0;  // 意図はこちらでは?

------------------------------------------------------------------------------

    case WM_SIZE:
        if (hCWnd) {  // WM_CREATE で確定しているため必須ではないがグローバル Wnd 使用時は推奨
//          SendMessage(hCWnd, msg, wp, lp);
            PostMessage(hCWnd, msg, wp, lp);         // 必須ではないが Post で済むものは Post にしましょう
        }
//      return 0;
        return DefWindowProc(hwnd , msg , wp , lp);  // 意図はこちらでは?

zeek

Re: CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しな

#4

投稿記事 by zeek » 5年前

訂正です。
WM_SIZE で MoveWindow() 実行すると SIZE 変更時はまた WM_SIZE 発生しますから
WM_APP_SIZE でも用意すべきです。

コード:

#define WM_APP_SIZE  (WM_APP + 0x100)

    switch(msg){
//  case WM_SIZE:
    case WM_APP_SIZE:

---------------------------------------------------------

    case WM_SIZE:
        if (hCWnd) {  // WM_CREATE で確定しているため必須ではないがグローバル Wnd 使用時は推奨
//          SendMessage(hCWnd, msg, wp, lp);
//          PostMessage(hCWnd, msg, wp, lp);         // 必須ではないが Post で済むものは Post にしましょう
            PostMessage(hCWnd, WM_APP_SIZE, wp, lp); // 必須ではないが Post で済むものは Post にしましょう

wasawasa
記事: 94
登録日時: 6年前

Re: CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しな

#5

投稿記事 by wasawasa » 5年前

返信ありがとうございます。
MoveWindow() API の第6引数がFALSEになっていたのは完全に失念していました。ご指摘頂いてありがとうございます。
hMainWndが受け取れていないと考えたのは、WndProcがhMainWnd にウィンドウハンドルが格納されてから処理されるものだろ思っていて、9行目にブレークポイントを設定して実行した時にhMainWndの値が0x00000000 {unused=??? }と表示されていたためです。
ですが、zeekさんの返信からWndProcが先に処理されている為に9行目の処理が上手くいかなかったという事が分かりました。
なので、次のように書き換えたら上手くいくようになりました。ありがとうございます。

コード:

//9行目
GetClientRect(GetAncestor(hwnd,GA_PARENT),&recClient2);
//12行目
MoveWindow(hCWnd,recClient2.left , recClient2.top , recClient2.right , recClient2.bottom ,FALSE);
なお、へろりずむさんのご指摘通り親ウィンドウに子ウィンドウのサイズの変更処理をさせるのが理想的なのは承知しておりましたが、今回は練習の為にサブクラスの導入をしてみる事が目的だったので、サイズの変更の処理は子ウィンドウにさせたままにしています。
言葉足らずで申し訳ございませんでした。

zeek

Re: CreateWindowでHWNDを受け取れない&最小化状態での親ウィンドウのサイズ変更に子ウィンドウが対応しな

#6

投稿記事 by zeek » 5年前

wasawasa さんが書きました: なお、へろりずむさんのご指摘通り親ウィンドウに子ウィンドウのサイズの変更処理をさせるのが理想的なのは承知しておりました...
あらら、「親ウィンドウに子ウィンドウのサイズの変更処理をさせるのが理想的」なんだ。
こう考えてしまうとオブジェクト指向設計は理解しにくくなるかもね。

閉鎖

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