第250章 メールスロットの基礎


今回からメールスロットをやります。メールスロットは比較的簡単に プロセス間で通信ができます。名前付きパイプのようにWindows NTでないと だめということもありません。サーバープロセスでメールスロットを作り、 送られてきたメッセージを読みとります。クライアント側はメールスロットに メッセージを送るだけです。



では、サーバー側から見ていきます。

HANDLE CreateMailslot( LPCTSTR lpName, DWORD nMaxMessageSize, DWORD lReadTimeout, LPSECURITY_ATTRIBUTES lpSecurityAttributes );

メールスロットを作成します。

lpNameにはメールスロットの名前を指定します。
\\.\mailslot\mailslotname
の形をとります。mailslotnameは自分で好きな名前を付けることができます。また \\.\mailslot\abc\def\mymailslo
のように仮想的なディレクトリを指定することもできます。

nMaxMessageSizeには1つのメッセージの最大バイト数を指定します。0を指定すると 制限がなくなります。

lReadTimeoutには、メッセージ読みとりまでのタイムアウト値(ミリセコンド)を指定します。 0を指定するとメッセージがないときにはすぐに制御を返します。
MAILSLOT_WAIT_FOREVERを指定するとメッセージが送られるまで待ち続けます。

これらの読みとり値は引き続いての読みとり操作に適応されます。

lpSecurityAttributesには、SECURITY_ATTRIBUTES 構造体へのポインタを指定します。 これで、ハンドルを子プロセスに継承するかどうかを指定できます。また、NTの場合 lpSecurityDescriptorメンバを指定することができます。(Windows95/98では無視)

メールスロットのハンドルがクローズされるか、メールスロットを所有しているプロセスが 終了するとメールスロットは破棄されます。

BOOL GetMailslotInfo( HANDLE hMailslot, LPDWORD lpMaxMessageSize, LPDWORD lpNextSize, LPDWORD lpMessageCount, LPDWORD lpReadTimeout );

特定のメールスロットの情報を取得します。

hMailslotには、メールスロットのハンドルを指定します。
lpMaxMessageSizeには、受け取ることのできる最大のメッセージサイズを 格納するバッファのポインタを指定します。不要ならNULLを指定することもできます。
lpNextSizeには次のメッセージのサイズを格納するバッファのポインタを指定します。 MAILSLOT_NO_MESSAGEが格納されたら次のメッセージはないことを表します。 不要ならNULLを指定します。
lpMessageCountには、読まれるのを待っているメッセージの数を格納するバッファへの ポインタを指定します。不要ならNULLを指定します。
lpReadTimeoutにはタイムアウト時間を格納するバッファへのポインタを指定します。 これも不要ならNULLを指定します。

関数が成功したら0以外、失敗したら0を返します。

では、サーバー側のプログラムを見てみましょう。

// mailslot01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END END POPUP "メールスロット(&M)" BEGIN MENUITEM "作成(&C)...", IDM_CREATESLOT MENUITEM "読みとり(&R)", IDM_READ END END

普通のメニューのリソース・スクリプトです。このプログラムを起動したら まず、メニューの「メールスロット」「作成」でメールスロットを作成して メッセージを待ちます。メッセージが来てもなにも起こらないので時々 「読みとり」をさせます。普通のメール・ソフトみたいな感じで使います。

// mailslot01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MyRead(HWND); char szClassName[] = "mailslot01"; //ウィンドウクラス char szMailSlot[] = "\\\\.\\mailslot\\mymailslot"; //メールスロットの名前 HANDLE hSlot;

特に説明はいらないと思いますがメールスロットの名前付けの時「\」の数に 注意してください。(C/C++では「\」の文字そのものは「\\」)

int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; 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座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ 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; switch (msg) { case WM_CREATE: hMenu = GetMenu(hWnd); EnableMenuItem(hMenu, IDM_READ, MF_BYCOMMAND | MF_GRAYED); break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_CREATESLOT: hSlot = CreateMailslot( szMailSlot, 0, MAILSLOT_WAIT_FOREVER, NULL); if (hSlot == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "メールスロット作成失敗", "Error", MB_OK); } else { EnableMenuItem(hMenu, IDM_CREATESLOT, MF_BYCOMMAND | MF_GRAYED); EnableMenuItem(hMenu, IDM_READ, MF_BYCOMMAND | MF_ENABLED); } break; case IDM_READ: MyRead(hWnd); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { 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; }

WM_CREATEメッセージが来たらIDM_READを使用不能にしておきます。 (まだメールスロットが作られていないので読みとり作業は意味がない)

メニューのIDM_CREATESLOTが選択されたらメールスロットを作り、 メニューのIDM_READを有効にしてIDM_CREATESLOTを無効にします。

メニューでIDM_READが選択されたらMyRead関数を呼んで読みとりを始めます。

BOOL MyRead(HWND hWnd) { BOOL bResult; DWORD dwNextSize, dwCount, dwRead; char szBuf[1024]; bResult = GetMailslotInfo(hSlot, 0, &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; } ReadFile(hSlot, szBuf, dwNextSize, &dwRead, NULL); MessageBox(hWnd, szBuf, "Message", MB_OK); return TRUE; }

メッセージを読みとる関数です。

まずGetMailslotInfo関数を呼んで、読みとるメッセージがあるのかどうか調べます。 そしてメッセージがある場合はReadFile関数でメッセージを読みとります。 ファイルハンドルにはメールスロットハンドルを指定します。 読みとるメッセージの長さは先ほどの関数で調べた長さ(dwNextSize)とします。 ここでは、プログラムを簡略化するために、やってくるメッセージの長さは1キロを 超えることがないことを暗黙の前提にしています。正しくはメッセージを受け取る バッファは動的に確保してください。

次にメッセージを送るクライアント側のプログラムを見てみましょう。

メニューの「ファイル」「書き込み」を選択すると左のようなダイアログ ボックスがでるので、これにメッセージを書き込みOKボタンを押します。



// slotclient01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END MENUITEM "書き込み(&W)...", IDM_WRITE END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYWRITE DIALOG DISCARDABLE 0, 0, 187, 93 STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "メールスロットに書き込み" FONT 9, "MS Pゴシック" BEGIN EDITTEXT IDC_EDIT1,7,7,173,53,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN DEFPUSHBUTTON "OK",IDOK,7,72,50,14 PUSHBUTTON "キャンセル",IDCANCEL,130,72,50,14 END

メニューとダイアログボックスのリソース・スクリプトです。エディットコントロール は複数行入力可能にしておいてください。(ES_MULTILINEとES_WANTRETURN)

// slotclient01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyWriteProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "slotclient01"; //ウィンドウクラス char szSlotName[] = "\\\\.\\mailslot\\mymailslot"; 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, "猫でもわかるMailSlot-Client", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ 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; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); 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) { DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

メニューでIDM_WRITEが選択されたら書き込み用ダイアログボックスを出します。

LRESULT CALLBACK MyWriteProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { static HWND hEdit; char szBuf[1024]; DWORD dwWritten; BOOL bResult; HANDLE hFile; switch (msg) { case WM_INITDIALOG: hEdit = GetDlgItem(hDlg, IDC_EDIT1); return TRUE; case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: hFile = CreateFile(szSlotName, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hDlg, "メールスロットがありません", "Error", MB_OK); EndDialog(hDlg, IDOK); return TRUE; } Edit_GetText(hEdit, szBuf, sizeof(szBuf)); bResult = WriteFile(hFile, szBuf, strlen(szBuf) + 1, &dwWritten, NULL); if (bResult == FALSE) MessageBox(hDlg, "Error WriteFie", "Error", MB_OK); CloseHandle(hFile); wsprintf(szBuf, "%sにメッセージを送りました", szSlotName); MessageBox(hDlg, szBuf, "OK", MB_OK); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

ダイアログボックスのプロシージャです。OKボタンが押されたらエディットコントロール の内容をメールスロットに書き込みます。メールスロットへの書き込みは一般のファイル への書き込みとにています。CreateFile関数のファイル名のところにはメールスロットの 名前を指定します。3番目の引数は必ずFILE_SHARE_READにします。CreateFile関数関数で 取得したハンドルを使ってWriteFile関数で書き込みます。このとき最後のヌル文字の分も 書き込みバイトに入れてください。

メールスロットに書き込むのも読み出すのも、一般のファイルと似た扱いができ大変気軽に使えます。


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

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