トップ回答者
ウィンドウプロシージャと制御するウィンドウについて

質問
-
ウィンドウプロシージャの第1引数であるHWND型の引数(以下hWndと表記)にメッセージが送られたウィンドウハンドルが渡されますが、
1つのウィンドウプロシージャで複数のウィンドウを制御するときはどうすればよいのでしょうか。
VC2008で作業しています。ウィンドウ1にてメニューバーからファイル(画像)選択をして、ウィンドウ2にて画像を表示する、という動作をさせたいのです。
(実際は元の画像をウィンドウ1で表示し、画像処理後の画像をウィンドウ2で表示する、というのをしたいのですが、
ウィンドウ1の動作からウィンドウ2の動作へとつなげるための練習をしています。)
また、ウィンドウAを閉じたときはアプリケーションを終了し、ウィンドウBを閉じたときはウィンドウBのみを閉じてウィンドウAは
表示したままにしたいと考えています。Createwindow()関数で2つのウインドウの生成をするとき、外部変数として用意したhWnd1、hWnd2にウィンドウハンドルを格納しています。
質問は以下の2つです。
(1)ウィンドウ1でファイル選択するときはウィンドウプロシージャの第1引数はhWnd1なので、ウィンドウ1を制御していますよね。
ウィンドウ1にてファイルのフルパスを取得した後、
PostMessage(hWnd2, WM_PAINT, 0, 0);
とウィンドウ2に対してポストすると、ウィンドウプロシージャの第1引数がhWnd2となりウィンドウ2を制御できるということでしょうか。(2)ウィンドウA、BそれぞれにウィンドウプロシージャA、Bを割り当てる方が普通なのでしょうか(こちらの方が制御しやすい?)。
この場合はPostMessage()関数の呼び出しはそのままでいいのでしょうか。作ってみたプログラムを載せておきます。(GdiPlus.libが必要です。)
Original_Window(ウィンドウA)とConvert_Window(ウィンドウB)が出てきて、Original_Windowにてファイル選択するとウィンドウA、Bともに
画像表示され、Convert_Windowを閉じるとアプリケーションを終了してしまいます。#include <windows.h> #include <gdiplus.h> using namespace Gdiplus; GdiplusStartupInput gdiSI; ULONG_PTR gdiToken; #define IDM_OPEN 2001 #define IDM_CLOSE 2002 LRESULT CALLBACK OriginalImage_Proc(HWND, UINT, WPARAM, LPARAM); //元画像コールバック関数 LRESULT CALLBACK ConvertImage_Proc(HWND, UINT, WPARAM, LPARAM); //変換画像コールバック関数 ATOM WindowClass_Registration(HINSTANCE, int, TCHAR*); //ウィンドウクラス登録関数 HWND Window_Creation(HINSTANCE, int, TCHAR*); //ウィンドウ生成関数 void Menu_Preparation(HWND); //メニュー準備関数 void FileOpen_DialogBox(HWND, TCHAR filename_full[MAX_PATH]); //ファイルオープンダイアログ表示関数 void OriginalImage_Show(HDC, TCHAR fnf[MAX_PATH]); //元画像表示関数 HWND d_hWnd[5]; //ウィンドウを複数作ることを考慮 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int CmdShow) { MSG msg; GdiplusStartup(&gdiToken, &gdiSI, NULL); if(!WindowClass_Registration(hInstance, 0, TEXT("Original_window"))){ //ウィンドウクラスを登録できなければ MessageBox(NULL, TEXT("エラー1"), TEXT("エラー"), MB_OK); return FALSE; //FALSEを返して終了 } if(!Window_Creation(hInstance, 0, TEXT("Original_window"))){ //ウィンドウを生成できなければ MessageBox(NULL, TEXT("エラー2"), TEXT("エラー"), MB_OK); return FALSE; //FALSEを返して終了 } if(!WindowClass_Registration(hInstance, 1, TEXT("Convert_window"))){ //ウィンドウクラスを登録できなければ MessageBox(NULL, TEXT("エラー3"), TEXT("エラー"), MB_OK); return FALSE; //FALSEを返して終了 } if(!Window_Creation(hInstance, 1, TEXT("Convert_window"))){ //ウィンドウを生成できなければ MessageBox(NULL, TEXT("エラー4"), TEXT("エラー"), MB_OK); return FALSE; //FALSEを返して終了 } while(GetMessage(&msg, NULL, 0, 0) > 0){ TranslateMessage(&msg); DispatchMessage(&msg); } GdiplusShutdown(gdiToken); return msg.wParam; } ATOM WindowClass_Registration(HINSTANCE hInst, int i_check, TCHAR* szCN) //ウィンドウクラス登録関数 { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = OriginalImage_Proc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)COLOR_BACKGROUND + 1; wc.lpszMenuName = NULL; wc.lpszClassName = szCN; wc.hIconSm = NULL; return (RegisterClassEx(&wc)); } HWND Window_Creation(HINSTANCE hInst, int i_check, TCHAR* szCN) //ウィンドウ生成関数 { HWND hWnd; d_hWnd[i_check] = hWnd = CreateWindowEx(WS_EX_LEFT, szCN, szCN, WS_OVERLAPPEDWINDOW | WS_VISIBLE, 200, 200, 640, 480, NULL, NULL, hInst, NULL); return hWnd; } void Menu_Preparation(HWND hWnd) { HMENU hMenu, hSubMenu; MENUITEMINFO mii; hMenu = CreateMenu(); hSubMenu = CreatePopupMenu(); mii.cbSize = sizeof(MENUITEMINFO); mii.fMask = MIIM_TYPE | MIIM_ID; mii.fType = MFT_STRING; mii.dwTypeData = TEXT("開く(&O)"); mii.wID = IDM_OPEN; InsertMenuItem(hSubMenu, 0, TRUE, &mii); mii.fType = MFT_SEPARATOR; InsertMenuItem(hSubMenu, 1, TRUE, &mii); mii.fType = MFT_STRING; mii.dwTypeData = TEXT("終了(&X)"); mii.wID = IDM_CLOSE; InsertMenuItem(hSubMenu, 2, TRUE, &mii); mii.fMask = MIIM_TYPE | MIIM_SUBMENU; mii.hSubMenu = hSubMenu; mii.dwTypeData = TEXT("ファイル(&F)"); InsertMenuItem(hMenu, 0, TRUE, &mii); SetMenu(hWnd, hMenu); } LRESULT CALLBACK OriginalImage_Proc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) //元画像コールバック関数 { static TCHAR d_fnf[MAX_PATH]; HDC hdc; PAINTSTRUCT ps; int check = 0; switch(uMsg){ case WM_DESTROY: PostQuitMessage(0); break; case WM_CREATE: Menu_Preparation(hWnd); break; case WM_COMMAND: switch(LOWORD(wParam)){ case IDM_OPEN: FileOpen_DialogBox(hWnd, d_fnf); SendMessage(d_hWnd[1], WM_PAINT, 0, 0); InvalidateRect(hWnd, NULL, TRUE); break; case IDM_CLOSE: PostMessage(hWnd, WM_DESTROY, 0, 0); break; } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); OriginalImage_Show(hdc, d_fnf); EndPaint(hWnd, &ps); break; } return DefWindowProc(hWnd, uMsg, wParam, lParam); } void FileOpen_DialogBox(HWND hWnd, TCHAR d_fnf[MAX_PATH]) { OPENFILENAME ofn; static TCHAR filename_full[MAX_PATH]; // ファイル名(フルパス)を受け取る領域 static TCHAR filename[MAX_PATH]; // ファイル名を受け取る領域 int i; ZeroMemory(&ofn, sizeof(ofn)); // 最初にゼロクリアしておく ofn.lStructSize = sizeof(ofn); // 構造体のサイズ ofn.hwndOwner = hWnd; // コモンダイアログの親ウィンドウハンドル ofn.lpstrFilter = TEXT("jpeg(*.jpg)\0*.jpg\0All files(*.*)\0*.*\0\0"); // ファイルの種類 ofn.lpstrFile = filename_full; // 選択されたファイル名(フルパス)を受け取る変数のアドレス ofn.lpstrFileTitle = filename; // 選択されたファイル名を受け取る変数のアドレス ofn.nMaxFile = sizeof(filename_full); // lpstrFileに指定した変数のサイズ ofn.nMaxFileTitle = sizeof(filename); // lpstrFileTitleに指定した変数のサイズ ofn.Flags = OFN_FILEMUSTEXIST; // フラグ指定 ofn.lpstrTitle = TEXT("ファイルを開く"); // コモンダイアログのキャプション ofn.lpstrDefExt = TEXT("jpeg"); // デフォルトのファイルの種類 // ファイルを開くコモンダイアログを作成 if(!GetOpenFileName(&ofn)) { MessageBox(hWnd, TEXT("エラー"), TEXT("エラー"), MB_OK); } for(i=0; i<MAX_PATH; i++) d_fnf[i] = filename_full[i]; } void OriginalImage_Show(HDC hdc, TCHAR fnf[MAX_PATH]) //元画像表示関数 { Graphics my_Graphics(hdc); Bitmap bmp(fnf); my_Graphics.DrawImage(&bmp, 0, 0); }
ご回答よろしくお願いします。
回答
-
(1)ウィンドウ1でファイル選択するときはウィンドウプロシージャの第1引数はhWnd1なので、ウィンドウ1を制御していますよね。
ウィンドウ1にてファイルのフルパスを取得した後、
PostMessage(hWnd2, WM_PAINT, 0, 0);
とウィンドウ2に対してポストすると、ウィンドウプロシージャの第1引数がhWnd2となりウィンドウ2を制御できるということでしょうか。PostMessage で送っておくと、そのうち、hWnd2 側のメッセージ処理の順番が来るはずです。
第 1 引数が即座に変わるわけではありません。(一度関数から抜けて、また呼ばれるイメージ)
なお、WM_PAINT を自分で送るのは避けるべきです。
InvalidateRect 等の関数について調べてみてください。(2)ウィンドウA、BそれぞれにウィンドウプロシージャA、Bを割り当てる方が普通なのでしょうか(こちらの方が制御しやすい?)。
この場合はPostMessage()関数の呼び出しはそのままでいいのでしょうか。分けるべきかどうかは何とも言えません。
分けようと分けまいと、前述の InvalidateRect 等の関数 を利用することになります。
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 回答としてマーク spring-snow 2009年10月30日 2:52
すべての返信
-
(1)ウィンドウ1でファイル選択するときはウィンドウプロシージャの第1引数はhWnd1なので、ウィンドウ1を制御していますよね。
ウィンドウ1にてファイルのフルパスを取得した後、
PostMessage(hWnd2, WM_PAINT, 0, 0);
とウィンドウ2に対してポストすると、ウィンドウプロシージャの第1引数がhWnd2となりウィンドウ2を制御できるということでしょうか。PostMessage で送っておくと、そのうち、hWnd2 側のメッセージ処理の順番が来るはずです。
第 1 引数が即座に変わるわけではありません。(一度関数から抜けて、また呼ばれるイメージ)
なお、WM_PAINT を自分で送るのは避けるべきです。
InvalidateRect 等の関数について調べてみてください。(2)ウィンドウA、BそれぞれにウィンドウプロシージャA、Bを割り当てる方が普通なのでしょうか(こちらの方が制御しやすい?)。
この場合はPostMessage()関数の呼び出しはそのままでいいのでしょうか。分けるべきかどうかは何とも言えません。
分けようと分けまいと、前述の InvalidateRect 等の関数 を利用することになります。
解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。- 回答としてマーク spring-snow 2009年10月30日 2:52
-
InvalidateRect()関数について調べてみました。
ウィンドウに無効領域が存在する場合にはそれが有効化されるまでウィンドウがWM_PAINTを発行し続けるという性質があり、
InvalidateRect()関数でわざと無効領域を作ることで、ウィンドウからWM_PAINTを発行させていたのですね。
PostMessage()関数でわざわざWM_PAINTを発行する必要はなくて、InvalidateRect()関数を呼び出すだけで充分でした。これを踏まえると、ウィンドウAとB両方に画像が表示されてしまうのも理解できました。
ウィンドウBに画像表示された後、ウィンドウAに無効領域を作ってウィンドウAに対してWM_PAINTを発行させていたためだったことがわかりました。case WM_PAINT: hdc = BeginPaint(hWnd, &ps); OriginalImage_Show(hdc, d_fnf); EndPaint(hWnd, &ps); break;
ウィンドウAに無効領域を作ったときhWndはウィンドウAのハンドルであり、
ファイルのフルパスを格納しているd_fnfはstaticなのでウィンドウBに描画したのが残っていて、
ウィンドウAにも描画されてしまったのですね。分からないことがやっと解決しました。
Azuleanさん、ご回答有り難うございました。