第247章 最も簡単な名前付パイプ


今回は、最も簡単な名前付きパイプのプログラムを作ってみます。 サーバー側のプログラムはクライアント側で書き込んだ内容を読み出すだけです。



サーバー側の仕組みは次のようにします。

1.メニューの「パイプ」「パイプの作成」が選択されると  名前付きパイプを作成します。同時に読み出しようスレッドを作ります。 2.イベントオブジェクトを作っておいてスレッドをストップさせておきます。 3.スレッド関数では、無限ループ内でイベントオブジェクトがシグナル状態になるのを  待ちます。  シグナル状態になるとReadFile関数で読み出しを行います。  読み出した後イベントを非シグナル状態にして待機します。  読み出しエラーか、終了フラグが来たらループを抜けて関数を終了します。  このときパイプハンドルもクローズします。 4.このプログラムが終了するとき、スレッド関数に終了フラグを送ります。  スレッドハンドルをクローズして、後始末をします。

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

// namedp01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)...", IDM_END END POPUP "パイプ(&P)" BEGIN MENUITEM "パイプの作成(&C)", IDM_PIPE END END

普通のメニューのリソース・スクリプトです。

// namedp01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include "resource.h" typedef struct _tagmydata { BOOL bEnd; HANDLE hPipe; } MYDATA; LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); int MakeMyPipe(HWND, MYDATA *); DWORD WINAPI MyThreadProc(LPVOID); char szClassName[] = "namedp01"; //ウィンドウクラス HANDLE hEvent, hThread;

スレッド関数に渡すデータを格納する構造体を定義しておきます。 この辺は第87章付近を参考にしてみてください。

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 MYDATA md; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_PIPE: MakeMyPipe(hWnd, &md); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { md.bEnd = TRUE; SetEvent(hEvent); if (CloseHandle(hThread)) MessageBox(hWnd, "hThreadをクローズしました", "OK", MB_OK); Sleep(2000); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

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

メニューからIDM_PIPEが来たらMakeMyPipe関数を呼んで名前付きパイプとスレッドを作ります。

WM_CLOSEメッセージが来たら、終了フラグをTRUEにします。このままでは スレッドは待機したままなのでイベントをシグナル状態にしてスレッドを動かします。 その後スレッドハンドルを閉じて、このプログラム自体を終了させます。

int MakeMyPipe(HWND hWnd, MYDATA *lpmd) { DWORD dwThreadID; static HANDLE hPipe; hPipe = CreateNamedPipe("\\\\.\\pipe\\mypipe", PIPE_ACCESS_INBOUND, //オープンモード PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT, //パイプモード 1, //パイプに対する最大インスタンス 1024,//出力バッファ 1024,//入力バッファ 0,//タイムアウト NULL); if (hPipe == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "パイプ作成失敗", "Error", MB_OK); return -1; } hEvent = CreateEvent(NULL, //セキュリティ属性 FALSE, //手動リセット FALSE, //初期状態 "yasutaka");//名前 lpmd->bEnd = FALSE; lpmd->hPipe = hPipe; hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThreadProc, (LPVOID)lpmd, 0, &dwThreadID); if (hThread == NULL) { MessageBox(hWnd, "スレッドの作成に失敗しました", "Error", MB_OK); CloseHandle(hPipe); return -3; } return 0; }

名前付きパイプ、スレッドを作る関数です。

CreateNamedPipe関数で"mypipe"という名前のパイプを作ります。 今回は読み込みのみなのでオープンモードはPIPE_ACCESS_INBOUNDとします。

パイプを作ったらイベントオブジェクトを作ってスレッドを停止させる準備をしておきます。 イベントハンドルはプロセス終了時に自動的にクローズされるので、後始末の心配はいりません。

MYDATA構造体にパイプハンドルと、終了フラグがFALSEであることを書き込んで、スレッド関数に 渡します。

DWORD WINAPI MyThreadProc(LPVOID lpParam) { char szBuf[1024]; HANDLE hPipe; DWORD dwRead; BOOL bRead = FALSE; MYDATA *lpmd; lpmd = (MYDATA *)lpParam; hPipe = lpmd->hPipe; while (1) { WaitForSingleObject(hEvent, INFINITE); if (lpmd->bEnd == TRUE) { MessageBox(NULL, "スレッド終了フラグが来ました", "OK", MB_OK); break; } memset(szBuf, '\0', sizeof(szBuf)); bRead = ReadFile(hPipe, szBuf, sizeof(szBuf), &dwRead, NULL); ResetEvent(hEvent); if (bRead == FALSE) { MessageBox(NULL, "読み出し失敗", "Error", MB_OK); break; } MessageBox(NULL, szBuf, "OK", MB_OK); } MessageBox(NULL, "ループを抜けました", "OK", MB_OK); if (CloseHandle(hPipe)) MessageBox(NULL, "hPipeをクローズしました", "OK", MB_OK); return 0; }

無限ループが始まると最初にWaitForSingleObject関数で待機します。

イベントがシグナル状態になったら終了フラグがTRUEになっていないかどうかを検査します。 TRUEならループを抜けて関数を終了させます。

終了フラグがTRUEでないときはパイプから読み出しをします。
読み出したらすぐにイベントを非シグナル状態にします。

読み出したデータはメッセージボックスで表示します。

ループを抜けたときは(終了フラグがTRUEになったか読み出しに失敗した)パイプハンドルを クローズして関数を終了します。

これで、サーバー側のプログラムは終わりです。このプログラムを起動したら メニューの「パイプ」「パイプの作成」でクライアントからの連絡を待ちます。

では、クライアント側のプログラムをみてみましょう。

// namedpx01.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "通信(&C)" BEGIN MENUITEM "パイプに書き込む(&P)", IDM_PIPE END END ///////////////////////////////////////////////////////////////////////////// // // Dialog // MYDLG DIALOG DISCARDABLE 0, 0, 151, 93 STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "パイプへの書き込み" FONT 9, "MS Pゴシック" BEGIN DEFPUSHBUTTON "OK",IDOK,7,72,50,14 PUSHBUTTON "キャンセル",IDCANCEL,94,72,50,14 EDITTEXT IDC_EDIT1,7,7,134,52,ES_MULTILINE | ES_AUTOHSCROLL | ES_WANTRETURN END

メニューとダイアログボックスのリソース・スクリプトです。

// namedpx01.cpp #ifndef STRICT #define STRICT #endif #include <windows.h> #include <windowsx.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK MyDlgProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); char szClassName[] = "namedpx01"; //ウィンドウクラス HINSTANCE hInst; HANDLE hEvent, hFile;

エディットコントロールのマクロを使いますのでwindowsx.hをインクルードするのを 忘れないでください。

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座標 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_CREATE: hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "yasutaka"); if (hEvent == NULL) { MessageBox(hWnd, "Error OpenEvent", "Error", MB_OK); DestroyWindow(hWnd); } hFile = CreateFile("\\\\.\\pipe\\mypipe", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hFile == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "パイプをオープンできませんでした", "Error", MB_OK); DestroyWindow(hWnd); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_PIPE: DialogBox(hInst, "MYDLG", hWnd, (DLGPROC)MyDlgProc); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hFile) CloseHandle(hFile); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }

WM_CREATEメッセージが来たら、OpenEvent関数で名前付きイベントオブジェクトの ハンドルを調べます。この名前のオブジェクトがない時は(サーバー側が起動していないか、 もしくはパイプを作っていない)このプログラムを終了させます。

HANDLE OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName );

dwDesiredAccessには、イベントオブジェクトに対するアクセスの種類を指定します。
EVENT_ALL_ACCESSは、アクセスのすべてが可能です。
EVENT_MODIFY_STATEはSetEvent, ResetEvent関数による状態の変化に対して アクセス可能です。
SYNCHRONIZEはWindows NTのみ。

bInheritHandleには、取得したハンドルを子プロセスに継承するかどうかを指定します。

lpNameには、イベントオブジェクトの名前を指定します。

次にCreateFileで名前付きパイプをオープンします。オープンに失敗したらプログラムを 終了させます。

メニューからIDM_PIPEが選択されたらダイアログボックスを出します。

終了時にhFileを閉じます。

LRESULT CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp) { DWORD dwWritten; char szBuf[1024]; switch (msg) { case WM_COMMAND: switch (LOWORD(wp)) { case IDOK: Edit_GetText(GetDlgItem(hDlg, IDC_EDIT1), szBuf, sizeof(szBuf)); if (!SetEvent(hEvent)) MessageBox(hDlg, "SetEvent Error", "Error", MB_OK); WriteFile(hFile, szBuf, strlen(szBuf), &dwWritten, NULL); EndDialog(hDlg, IDOK); return TRUE; case IDCANCEL: EndDialog(hDlg, IDCANCEL); return TRUE; } return FALSE; } return FALSE; }

ダイアログボックスのプロシージャです。

エディットコントロールに文字列を記入してOKボタンを押すと、 szBufに内容を読み込み、WriteFileでパイプに書き込みます。

さて、今回は匿名パイプと大して変わらないプログラムでした。 いろいろ作り替えて名前付きパイプらしい機能を発揮できるようにしてみてください。


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

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