第255章 ダイアログを子ウィンドウとして張り付ける


さて、前章までのプログラムを実際に使ってみると不便な点がいくつかあります。 それは、メッセージを送信するときいちいちダイアログボックスを呼び出さなくてはならず 煩わしいです。できることなら、チャット中ずっとダイアログが出ていた方が便利です。



「ファイル」「書き込み用ウィンドウ」を選択すると右半分に 書き込み用のダイアログが張り付きます。メッセージを書き込んだら 送信ボタンを押します。「閉じる」ボタンを押すまで、このダイアログは 表示されたままです。メインウィンドウのサイズを縦に大きくすると ダイアログの背丈ものびます。また、ダイアログが表示されているとき左の図より メインウィンドウの背丈を縮めることができないようにしてあります。



作り方は簡単です。まず、通常のダイアログボックスは「タスクモーダル」になっているので ダイアログボックスが表示されている間中、メインウィンドウの操作はできません。 そこで、モードレス・ダイアログボックスを使うことになります。 モードレス・ダイアログボックスについては第30章を参照してください。

さて、ダイアログボックスをモードレスにするとずっと出たままになりますが、 メインウィンドウ中での位置決めが面倒くさいです。手っ取り早いのはダイアログを 「ポップアップ」ではなく、「チャイルド」にしてしまうことです。これで扱いが 楽になります。

ウィンドウの大きさ制限についてはすでに 第208章で解説していますのでそちらを参照してください。

では、プログラムを見てみましょう。

// mailslot06.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "タスクトレーに格納(&T)", IDM_TASK MENUITEM "書き込み用ウィンドウ(&W)...", IDM_WRITE MENUITEM SEPARATOR MENUITEM "終了(&X)...", IDM_END END POPUP "メールスロット(&M)" BEGIN MENUITEM "作成(&C)", IDM_CREATESLOT END POPUP "オプション(&O)" BEGIN MENUITEM "IDを表示する(&I)", IDM_ID END END RTRAYMENU MENU DISCARDABLE BEGIN POPUP " ダミーです" BEGIN MENUITEM "タスクトレーから出す", IDM_SHOW MENUITEM "終了", IDM_END END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MAILSLOTNAME DIALOG DISCARDABLE 0, 0, 118, 61 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メールスロット名" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,19,104,16,ES_AUTOHSCROLL DEFPUSHBUTTON "OK",IDOK,7,39,50,14 PUSHBUTTON "キャンセル",IDCANCEL,60,39,50,14 LTEXT "自分のメールスロットの名前",IDC_STATIC,7,7,83,8 END MYWRITE DIALOG DISCARDABLE 0, 0, 163, 153 STYLE DS_MODALFRAME | WS_CHILD FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT3,70,23,86,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT2,70,44,86,14,ES_AUTOHSCROLL EDITTEXT IDC_EDIT1,7,69,149,56,ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "送信",IDOK,7,132,50,14 PUSHBUTTON "閉じる",IDCANCEL,104,132,50,14 LTEXT "メールスロット名",IDC_STATIC,7,45,48,8 LTEXT "コンピュータ名",IDC_STATIC,7,25,43,8 CONTROL "他のコンピュータにメッセージを送る",IDC_CHECK1,"Button", BS_AUTOCHECKBOX | WS_TABSTOP,7,7,119,10 END ///////////////////////////////////////////////////////////////////////////// // // Icon // MYICON ICON DISCARDABLE "myicon.ico"

前章のリソース・スクリプトとほとんど同じですが、メニューの「書き込み(&W)...」を 「書き込み用ウィンドウ(&W)...」に変えました。

MYWRITEダイアログのところを見てください。
STYLEのWS_POPUP | WS_CAPTION | WS_SYSMENUWS_CHILDに変わっています。 これで、タイトルバーなしの子供ウィンドウとして扱えます。

また、「OK」ボタンを「送信」ボタンに、「キャンセル」ボタンを「閉じる」ボタンと 名称を変更しました。

// mailslot06.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" #define MYMSG_TRAY WM_USER #define ID_MYTRAY 101 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyNameProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyWriteProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); DWORD WINAPI ReadFunc(LPVOID); HANDLE MakeMySlot(HWND, BOOL *); void MyTaskTray(HWND, PNOTIFYICONDATA); int MakeRTrayMenu(HWND); char szClassName[] = "mailslot06"; //ウィンドウクラス char szMailSlot[128];//メールスロットの名前(フルパス付き) char szSlotName[64];//メールスロットの名前(パスなし) HANDLE hSlot; HINSTANCE hInst; HWND hMainEdit; BOOL bID = FALSE;//メッセージIDを表示するかどうか BOOL bIsTask = FALSE;//タスクトレーに格納されているかどうか HWND hWriteDlg;//書き込み用モードレスダイアログボックス int DlgX, DlgY;//書き込み用ダイアログの大きさ BOOL bWriteDlg = FALSE;//書き込み用ダイアログが表示されいるかどうか

前章に比べてグローバル変数が3つほど増えました。コメントを見ればわかると思います。

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; hInst = hCurInst; if (!InitApp(hCurInst)) return FALSE; if (!InitInstance(hCurInst, nCmdShow)) return FALSE; while (GetMessage(&msg, NULL, 0, 0)) { if (hWriteDlg == 0 || !IsDialogMessage(hWriteDlg, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } return msg.wParam; }

モードレス・ダイアログボックスを取り扱うのでメッセージループのところに ちょっと変更があります。もし、if(hWriteDlg == 0 || !IsDialogMessage.....)を 忘れてしまうと、リソース・エディタで設定したタブオーダーが効かなくなってしまうので 注意してください。

//ウィンドウ・クラスの登録 ATOM InitApp(HINSTANCE hInst) { WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst;//インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = "MYMENU"; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; wc.hIconSm = LoadIcon(hInst, "MYICON");//アイコン return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメールスロット", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 600, //幅 281, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

いつもと大して変わりませんがメインウィンドウの初期状態の大きさは前章より 大きめにしてあります。高さの281というのはダイアログがちょうど収まる高さです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static BOOL bEnd; static HANDLE hThread; static HMENU hMenu; MENUITEMINFO mi; static NOTIFYICONDATA ni; RECT rc; MINMAXINFO *lpmm; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_TASK, MF_BYCOMMAND | MF_GRAYED); hMainEdit = CreateWindow("EDIT", "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN | ES_AUTOHSCROLL | ES_AUTOVSCROLL | WS_HSCROLL | WS_VSCROLL, 0, 0, 0, 0, hWnd, (HMENU)100, hInst, NULL); break; case WM_SIZE: if (!bWriteDlg) MoveWindow(hMainEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); else { MoveWindow(hMainEdit, 0, 0, LOWORD(lp) - DlgX, HIWORD(lp), TRUE); MoveWindow(hWriteDlg, LOWORD(lp) - DlgX, 0, DlgX, HIWORD(lp), TRUE); } break; case WM_GETMINMAXINFO: if (bWriteDlg) { lpmm = (MINMAXINFO *)lp; lpmm->ptMinTrackSize.y = 281; lpmm->ptMinTrackSize.x = 450; } else { return (DefWindowProc(hWnd, msg, wp, lp)); } break; case MYMSG_TRAY: if (wp == ID_MYTRAY) { switch (lp) { case WM_RBUTTONDOWN: MakeRTrayMenu(hWnd); break; case WM_LBUTTONDBLCLK: bIsTask = FALSE; Shell_NotifyIcon(NIM_DELETE, &ni); ShowWindow(hWnd, SW_SHOWNORMAL); default: break; } } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_CREATESLOT: id = DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); if (id == IDOK) { bEnd = FALSE; hThread = MakeMySlot(hWnd, &bEnd); } else { MessageBox(hWnd, "メールスロットは作成されませんでした", "中止", MB_OK); } break; case IDM_WRITE: hWriteDlg = CreateDialog(hInst, "MYWRITE", hWnd, (DLGPROC)MyWriteProc); if (hWriteDlg != NULL) { EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_GRAYED); bWriteDlg = TRUE; ShowWindow(hWriteDlg, SW_SHOW); GetWindowRect(hWriteDlg, &rc); DlgX = rc.right - rc.left; DlgY = rc.bottom - rc.top; GetClientRect(hWnd, &rc); MoveWindow(hWriteDlg, rc.right - rc.left -DlgX, rc.top, DlgX, rc.bottom -rc.top, TRUE); MoveWindow(hMainEdit, 0, 0, rc.right - rc.left - DlgX, rc.bottom - rc.top, TRUE); } else { MessageBox(hWnd, "ダイアログボックス作成失敗です", "Error", MB_OK); return 0; } break; case IDM_ID: memset(&mi, 0, sizeof(MENUITEMINFO)); mi.cbSize = sizeof(MENUITEMINFO); mi.fMask = MIIM_STATE; if (!bID) { mi.fState = MFS_CHECKED; SetMenuItemInfo(hMenu, IDM_ID, FALSE, &mi); bID = TRUE; break; } else { mi.fState = MFS_UNCHECKED; SetMenuItemInfo(hMenu, IDM_ID, FALSE, &mi); bID = FALSE; } break; case IDM_TASK: MyTaskTray(hWnd, &ni); ShowWindow(hWnd, SW_HIDE); bIsTask = TRUE; break; case IDM_SHOW: Shell_NotifyIcon(NIM_DELETE, &ni); ShowWindow(hWnd, SW_SHOWNORMAL); bIsTask = FALSE; break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hThread) { bEnd = TRUE; WaitForSingleObject(hThread, INFINITE); if (CloseHandle(hThread)) MessageBox(hWnd, "hThreadをクローズしました", "OK", MB_OK); } if (hSlot) { if (CloseHandle(hSlot)) MessageBox(hWnd, "メールスロットを削除しました", "OK", MB_OK); } if (bIsTask) Shell_NotifyIcon(NIM_DELETE, &ni); DestroyWindow(hMainEdit); if (hWriteDlg) DestroyWindow(hWriteDlg); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メインウィンドウのプロシージャです。

WM_SIZEメッセージが来た時、ダイアログが表示されていない時は従来通り、エディットコントロールの 大きさを調整します。ダイアログが表示されている時(bWriteDlgがTRUEの時)はダイアログの大きさを 考慮してエディットコントロールの大きさを調整する必要があります。また、ダイアログ自体の高さは 親ウィンドウのクライアント領域の高さにします。(オリジナルのダイアログの高さにすると 親の背丈がのびたとき格好が悪くなります)

WM_GETMINIMAXINFOメッセージが来た時、ダイアログが存在する時は、高さ、幅制限を行い 存在しないときは、デフォルトに任せます。

メニューからIDM_WRITEが選択された時は、CreateDialogマクロでモードレス・ダイアログボックスを 作ります。オリジナルのダイアログボックスの大きさをグローバル変数に保存しておくと何かと便利です。

プログラム終了時にダイアログが存在していれば、これを破棄します。

MakeMySlot, MyNameProc関数に変更はありません。

LRESULT CALLBACK MyWriteProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit, hEditTo, hCheck, hComputor; char szBuf[1024], szBuf2[1024], szTo[64], *lpszEdit, szID[32], szFrom[32]; HGLOBAL hMem; static char szShortTo[64], szComputor[64]; HANDLE hMail; DWORD dwWritten, dwTickCount; BOOL bResult; static BOOL bOtherComputor; int iSize, iLine, i; RECT rc; HMENU hMenu; switch (msg) { case WM_INITDIALOG: hCheck = GetDlgItem(hDlg, IDC_CHECK1); hEdit = GetDlgItem(hDlg, IDC_EDIT1); hEditTo = GetDlgItem(hDlg, IDC_EDIT2); hComputor = GetDlgItem(hDlg, IDC_EDIT3); if (bOtherComputor) { Button_SetCheck(hCheck, BST_CHECKED); EnableWindow(hComputor, TRUE); } else { EnableWindow(hComputor, FALSE); } Edit_SetText(hComputor, szComputor); Edit_SetText(hEditTo, szShortTo); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDC_CHECK1: if (HIWORD(wp) == BN_CLICKED) { if (Button_GetCheck(hCheck) == BST_CHECKED) { EnableWindow(hComputor, TRUE); SetFocus(hComputor); bOtherComputor = TRUE; } else { EnableWindow(hComputor, FALSE); SetFocus(hEditTo); bOtherComputor = FALSE; } } else return FALSE; return TRUE; case IDOK: Edit_GetText(hEditTo, szShortTo, sizeof(szShortTo)); if (!bOtherComputor) { wsprintf(szTo, "\\\\.\\mailslot\\%s", szShortTo); } else { Edit_GetText(hComputor, szComputor, sizeof(szComputor)); wsprintf(szTo, "\\\\%s\\mailslot\\%s", szComputor, szShortTo); } dwTickCount = GetTickCount(); wsprintf(szID, "[Message ID = %d]\r\n", dwTickCount); Edit_GetText(hEdit, szBuf2, sizeof(szBuf2)); hMail = CreateFile(szTo, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hMail == INVALID_HANDLE_VALUE) { MessageBox(hDlg, "CreateFile Error", "Error", MB_OK); return TRUE; } strcpy(szBuf, szID); wsprintf(szFrom, "%s>", szSlotName); strcat(szBuf, szFrom); strcat(szBuf, szBuf2); bResult = WriteFile(hMail, szBuf, strlen(szBuf) + 1, &dwWritten, NULL); if (!bResult) { MessageBox(hDlg, "WriteFile Error", "Error", MB_OK); } CloseHandle(hMail); iSize = GetWindowTextLength(hMainEdit); hMem = GlobalAlloc(GHND, iSize + 1024); if (hMem == NULL) { MessageBox(hDlg, "メモリ確保に失敗しました", "Error", MB_OK); return TRUE; } lpszEdit = (char *)GlobalLock(hMem); Edit_GetText(hMainEdit, lpszEdit, iSize + 1024); strcat(lpszEdit, "\r\n"); if (bID) strcat(lpszEdit, szID); strcat(lpszEdit, szFrom); strcat(lpszEdit, szBuf2); Edit_SetText(hMainEdit, lpszEdit); iLine = Edit_GetLineCount(hMainEdit); for (i = 0; i < iLine; i++) SendMessage(hMainEdit, EM_SCROLL, (WPARAM)SB_LINEDOWN, 0); GlobalUnlock(hMem); GlobalFree(hMem); Edit_SetText(hEdit, ""); SetFocus(hEdit); return TRUE; case IDCANCEL: GetClientRect(GetParent(hDlg), &rc); hMenu = GetMenu(GetParent(hDlg)); EnableMenuItem(hMenu, IDM_WRITE, MF_BYCOMMAND | MF_ENABLED); MoveWindow(hMainEdit, 0, 0, rc.right - rc.left, rc.bottom - rc.top, TRUE); DestroyWindow(hDlg); bWriteDlg = FALSE; return TRUE; } return FALSE; } return FALSE; }

書き込み用ダイアログボックスのプロシージャです。

いままでは、送信ボタン(OKボタン)が押されるとEndDialogでダイアログボックスを 閉じていましたが、モードレスなのでその必要はなくなりました。「閉じる」ボタンが押されたときは DestroyWindowでダイアログを破棄します。

ReadFunc, MyTaskTray, MakeRTrayMenuの各関数に変更はありません。

さて、今回もメールスロットとは直接関係のないプログラムでした。あと、使ってみて 不便なところはいろいろ改良してみてください。
[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

Update 08/Feb/2000 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。