メッセージを出すときに相手のメールスロットの指定の仕方は
前章でも
少し書きましたが、
\\A\mailslot\B
の形になります。Aにはコンピュータ名かグループ名となります。 コンピュータ名というのはデスクトップの「ネットワークコンピュータ」アイコンを 右クリックして「プロパティ」を指定すればわかります。 グループ名は「ワークグループ」名です。
メニューの「メールスロット」「メールスロットの名前」を選択すると 左のようなダイアログボックスが出てきます。ここに自分のメールスロット の名前を入力します。「KUMEI\YASUTAKA」のように仮想ディレクトリを 指定することもできます。名前を指定したら、メニューの「メールスロット」 「作成」を選択してメールスロットを作成します。
メッセージを送るには「ファイル」「書き込み」を選択します。
「他のコンピュータにメッセージを送る」にチェックをつけると
「コンピュータ名」を入力できるようになります。
「コンピュータ名」のところにワークグループ名を入れてもかまいません。
また、「コンピュータ名」のところに「*」を入力すると同じ名前の
すべてのメールスロットに配信されます。
では、プログラムを見てみましょう。
メニューとダイアログボックスのリソース・スクリプトです。// mailslot02.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END MENUITEM SEPARATOR MENUITEM "書き込み(&W)...", IDM_WRITE END POPUP "メールスロット(&M)" BEGIN MENUITEM "メールスロットの名前(&M)...", IDM_NAME MENUITEM "作成(&C)", IDM_CREATESLOT MENUITEM "読みとり(&R)", IDM_READ 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_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メールスロットに書き込み" 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_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "OK",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
メールスロットの名前と、ハンドル、インスタンスハンドルを グローバル変数にしてみました。// mailslot02.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" 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); BOOL MyRead(HWND); char szClassName[] = "mailslot02"; //ウィンドウクラス char szMailSlot[128];//メールスロットの名前(フルパス付き) char szSlotName[64];//メールスロットの名前(パスなし) HANDLE hSlot; HINSTANCE hInst;
いつもと同じですが、メインウィンドウは少し小さめにしてあります。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)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } //ウィンドウ・クラスの登録 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(NULL, IDI_APPLICATION); return (RegisterClassEx(&wc)); } //ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow) { HWND hWnd; hWnd = CreateWindow(szClassName, "猫でもわかるメールスロット", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 300, //幅 250, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
WM_CREATEメッセージが来たら、メニューハンドルを取得してIDM_READと IDM_CREATEを灰色表示にして選択付加にしておきます。メニューハンドルは 他のメッセージ処理の時も使うのでstatic変数にしておきます。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HMENU hMenu; char szBuf[64]; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_READ, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_GRAYED); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_NAME: DialogBox(hInst, "MAILSLOTNAME", hWnd, (DLGPROC)MyNameProc); break; case IDM_CREATESLOT: hSlot = CreateMailslot( szMailSlot, 0, MAILSLOT_WAIT_FOREVER, NULL); if (hSlot == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "メールスロット作成失敗", "Error", MB_OK); EnableMenuItem(hMenu, IDM_NAME, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_READ, MF_BYCOMMAND | MF_GRAYED); } else { EnableMenuItem(hMenu, IDM_NAME, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_READ, MF_BYCOMMAND | MF_ENABLED); GetWindowText(hWnd, szBuf, sizeof(szBuf)); strcat(szBuf, "["); strcat(szBuf, szSlotName); strcat(szBuf, "]"); SetWindowText(hWnd, szBuf); } break; case IDM_READ: MyRead(hWnd); break; case IDM_WRITE: DialogBox(hInst, "MYWRITE", hWnd, (DLGPROC)MyWriteProc); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hSlot) { if (CloseHandle(hSlot)) MessageBox(hWnd, "メールスロットを削除しました", "OK", MB_OK); } DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
メニューで「メールスロットの名前」(IDM_NAME)が選択されたら 「MAILSLOTNAME」ダイアログボックスを出します。
メニューで「作成」(IDM_CREATESLOT)が選択されると、メールスロットを
作ります。成功したらメニューのIDM_NAME, IDM_READを使用不可に、
IDM_CREATESLOTを使用可能にします。
また、メインウィンドウのタイトルバーにメールスロットの名前を
表示するようにします。
メニューの「読みとり」(IDM_READ)が選択されたらMyRead関数を 呼び出します。
メニューの「書き込み」(IDM_WRITE)が選択されたら「MYWRITE」ダイアログボックス を出します。
プログラム終了時にhSlotがNULLでないときは、これを閉じて終了します。
メールスロットからメッセージを読み出す関数です。BOOL MyRead(HWND hWnd) { BOOL bResult; DWORD dwNextSize, dwCount, dwRead; char szBuf[1024]; bResult = GetMailslotInfo(hSlot, NULL, &dwNextSize, &dwCount, NULL); if (!bResult) { MessageBox(hWnd, "メールスロット情報が取得できません", "Error", MB_OK); return FALSE; } if (dwNextSize == MAILSLOT_NO_MESSAGE) { MessageBox(hWnd, "メッセージはありません", "No Next Message", MB_OK); return FALSE; } while (dwCount) { ReadFile(hSlot, szBuf, dwNextSize, &dwRead, NULL); MessageBox(hWnd, szBuf, "Message", MB_OK); GetMailslotInfo(hSlot, NULL, &dwNextSize, &dwCount, NULL); if (dwCount != 0) MessageBox(hWnd, "まだメッセージがあります", "More Message", MB_OK); } return TRUE; }
「MAILSLOTNAME」ダイアログボックスのプロシージャです。LRESULT CALLBACK MyNameProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { HWND hParent; static HWND hEdit; static HMENU hMenu; switch (msg) { case WM_INITDIALOG: hParent = GetParent(hDlg); hMenu = GetMenu(hParent); hEdit = GetDlgItem(hDlg, IDC_EDIT1); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(hEdit, szSlotName, sizeof(szSlotName)); wsprintf(szMailSlot, "\\\\.\\mailslot\\%s", szSlotName); EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_ENABLED); EnableMenuItem(hMenu, IDM_NAME, MF_BYCOMMAND | MF_GRAYED); MessageBox(hDlg, "メールスロットの名前が決まりました。\n" "メールスロットを作成してください", "OK", MB_OK); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }
WM_INITDIALOGメッセージが来たら親のメニューハンドルを取得してstatic変数に格納しておきます。 これは、あとでOKボタンが押されたときに親のメニュー項目の状態を変えるために必要です。 GetParent関数については第29章を参照してください。
OKボタンが押されたら、エディットコントロールから文字列を取得してszSlotNameグローバル変数に
格納します。また、これにパスをつけたものをszMailSlotグローバル変数に格納します。
メニュー項目の状態を変更して、ダイアログを閉じます。
「MYWRITE」ダイアログボックスのプロシージャです。LRESULT CALLBACK MyWriteProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit, hEditTo, hCheck, hComputor; char szBuf[1024], szTo[64]; static char szShortTo[64], szComputor[64]; HANDLE hMail; DWORD dwWritten; BOOL bResult; static BOOL bOtherComputor; 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); } Edit_GetText(hEdit, szBuf, sizeof(szBuf)); 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); EndDialog(hDlg, IDOK); return TRUE; } bResult = WriteFile(hMail, szBuf, strlen(szBuf) + 1, &dwWritten, NULL); if (!bResult) { MessageBox(hDlg, "WriteFile Error", "Error", MB_OK); } CloseHandle(hMail); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }
WM_INITDIALOGメッセージが来たら、それぞれのエディットコントロールのウィンドウハンドルを スタティック変数に取得しておきます。そして、メッセージ本文以外の前回の記入したものを表示します。
チェックボックスがクリックされたら、チェックがついたのかはずれたのかで処理を変えます。 チェックがついたときはhComputorを有効にしてコンピュータ名を入力できるようにします。 チェックがはずれたときはhComputorを無効にして入力をできなくします。
OKボタンが押されたときの処理はbOtherComputorがTRUEかどうかで処理が若干異なります。 TRUEの時は、hComputorからも文字列を取得します。
後は順番にプログラムを見ていけばわかると思います。
さて、プログラムができたら実験をしてみましょう。まず、1つだけプログラムを 立ち上げてメールスロットを作ります。そして、これに対してメッセージを送ります。 メニューの「読みとり」でメッセージが来ていることがわかります。 次に同一のマシン上で複数のプログラムを立ち上げて、それぞれ違う名前を付けておきます。 それぞれのマシンにメッセージを送ってみてください。思った通りの結果となると思います。
次に別のマシンにプログラムをおいて、お互いにメッセージを送ってみます。すると なぜか同じメッセージが2通来ます。この辺を工夫してみてください。
Update 17/Jan/2000 By Y.Kumei