第252章 メッセージの到着を監視する


前章までで作ったメールスロットは、メッセージが届いているかどうかは メニューの「読みとり」などでいちいち確認しなくてはいけませんでした。 また、ネットワークの設定の仕方によっては他のコンピュータにメッセージを 送ると複数のメッセージが届いてしまうことがありました。今回は このへんを改良してみます。



メッセージが届いたかどうかを常に監視するにはどうしたらよいでしょうか。 メッセージ監視用のスレッドを作ってこれに任せればよいですね。

同じメールが届いてしまった場合、一方を無視することにします。 メッセージを保存用のバッファにコピーしておいて、次のメッセージと内容を 比較します。違っていれば新しいメッセージとして処理します。同じであれば 無視します。

さて、そうすると偶然同じ内容のメッセージが2通届いた場合片方が無視される危険が あります。この対策としてはメッセージを送るときにメールの場合のように メッセージIDを冒頭につけてこれを調べればよいですね。そうすると偶然内容が 同じメッセージが届いても無視されることはありません。では、メッセージIDは どのようにつければよいでしょうか。ここでは、簡単にメッセージを送る時点での TickCount(Windowsを起動してからのミリセコンド)をIDとします。 これなら偶然に同じIDのメッセージができる可能性は0に近いと思われます。

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

// mailslot03.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 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_AUTOVSCROLL | 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

前章とほとんど同じですがメニューの「読みとり」が なくなりました。また、ちょっとしたことですがダイアログボックスのエディットコントロールの スタイルにES_AUTOHSCROLLを加えました。これがないとエディットコントロールに一度に 表示しきれない行数のメッセージをコピーすることができません。

// mailslot03.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); DWORD WINAPI ReadFunc(LPVOID); char szClassName[] = "mailslot03"; //ウィンドウクラス char szMailSlot[128];//メールスロットの名前(フルパス付き) char szSlotName[64];//メールスロットの名前(パスなし) HANDLE hSlot; HINSTANCE hInst;

MyRead関数は廃止しました。代わりにスレッド関数のReadFuncを採用しました。

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; }

前章と全く同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; static HMENU hMenu; char szBuf[64]; DWORD dwThreadID; static BOOL bEnd; static HANDLE hThread; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); 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); } else { EnableMenuItem(hMenu, IDM_NAME, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_GRAYED); GetWindowText(hWnd, szBuf, sizeof(szBuf)); strcat(szBuf, "["); strcat(szBuf, szSlotName); strcat(szBuf, "]"); SetWindowText(hWnd, szBuf); hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ReadFunc, (LPVOID)&bEnd, 0, &dwThreadID); if (hThread == NULL) MessageBox(hWnd, "CreateThread Error", "Error", MB_OK); } 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 (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); } DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メニューの「読みとり」がなくなったので、これに関したEnableMenuItem(...)がすべて なくなりました。

メニューのIDM_CREATESLOTが選択されたら、メールスロットを作った後で読みとりようの スレッドを作成します。

終了時にbEndをTRUEにしてWaitForSingleObject関数でスレッド関数が終了するのを 待ちます。スレッド関数が終了したらスレッドハンドルをクローズします。 スレッドは作るよりもきちんと終了させるのが面倒ですね。

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; }

自分のメールスロットの名前を入力するダイアログのプロシージャです。 前章と全く同じです。

LRESULT CALLBACK MyWriteProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit, hEditTo, hCheck, hComputor; char szBuf[1024], szBuf2[1024], szTo[64]; static char szShortTo[64], szComputor[64]; HANDLE hMail; DWORD dwWritten, dwTickCount; 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); } dwTickCount = GetTickCount(); wsprintf(szBuf, "[Message ID = %d]\r\n", dwTickCount); Edit_GetText(hEdit, szBuf2, sizeof(szBuf)); strcat(szBuf, 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); 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; }

メッセージ書き込み用のダイアログのプロシージャです。 OKボタンが押されたら、GetTickCount関数でWindowsを起動してからのミリセコンドを取得します。 GetTickCount関数については第164章を参照してください。

さて、Windowsを起動してからのミリセコンドがわかったら、szBufに[Message ID = ****]と書き込みます。 次にエディットコントロールからメッセージ本文をszBuf2に読み出します。 そして、szBufにszBuf2をstrcatして、これをメッセージとして送り出せばよいですね。

DWORD WINAPI ReadFunc(LPVOID lp) { BOOL *lpbEnd; DWORD dwNextSize, dwCount, dwRead; char szBuf1[1024], szBuf2[1024]; lpbEnd = (BOOL *)lp; while (1) { if (*lpbEnd) { MessageBox(NULL, "*lpbEndがTRUEとなりました", "LOOP END", MB_OK); break; } GetMailslotInfo(hSlot, NULL, &dwNextSize, &dwCount, NULL); if (dwNextSize != MAILSLOT_NO_MESSAGE) { MessageBox(NULL, "メッセージが届いています", "メッセージ", MB_OK); while (dwCount) { ReadFile(hSlot, szBuf1, dwNextSize, &dwRead, NULL); if (strcmp(szBuf1, szBuf2) != 0) { MessageBox(NULL, szBuf1, "Message", MB_OK); } strcpy(szBuf2, szBuf1); GetMailslotInfo(hSlot, NULL, &dwNextSize, &dwCount, NULL); } } Sleep(100); } MessageBox(NULL, "ループを抜けました", "OK", MB_OK); return 0; }

メッセージ到着監視のスレッド関数です。

無限ループ内ではまず、スレッド終了の合図(*lpbEndがTRUEになる)がこないかを調べて、 合図が来たらbreakでループを抜けます。

次にGetMailslotInfo関数でメッセージがあるかどうかを調べます。メッセージがあれば これをszBuf1に読み出します。そしてszBuf2と比較して違っていればszBuf1を メッセージボックスで表示します。同じであれば無視します。そして、szBuf2にszBuf1をコピーします。 その後再度GetMailslotInfo関数でメールの有無を調べます。

ここではwhileループが2重になっている点に注意してください。

さて、今回の改良で少し使いやすくなりました。このプログラムをもっと改良すると イントラネット内のチャットプログラムができそうです。いろいろ試してみてください。


[SDK第3部 Index] [総合Index] [Previous Chapter] [Next Chapter]

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