では、早速プログラムを見てみましょう。
メニューのリソース・スクリプトです。// process03.rcの一部 ///////////////////////////////////////////////////////////////////////////// // // Menu // MYMENU MENU DISCARDABLE BEGIN POPUP "ファイル(&F)" BEGIN MENUITEM "終了(&X)", IDM_END END POPUP "プロセス(&P)" BEGIN MENUITEM "子プロセスを起動(&C)", IDM_CHILD END END
このへんはいつもと同じですが、WM_ENDTHREADという自作メッセージを 用意しました。// process03.cpp #ifndef STRICT #define STRICT #endif #define WM_ENDTHREAD WM_USER #include <windows.h> #include "resource.h" LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); ATOM InitApp(HINSTANCE); BOOL InitInstance(HINSTANCE, int); BOOL MakeMyProcess(HWND); DWORD WINAPI MyThreadProc(LPVOID); char szClassName[] = "process03"; //ウィンドウクラス HANDLE hRead, hProcess, hThread; DWORD dwThreadID; HWND hMain, hEdit; HINSTANCE hInst; char szBuf[256]; 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座標 210, //幅 120, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; hMain = hWnd; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }
メインウィンドウのプロシージャです。//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { int id; BOOL bResult; static HMENU hMenu; switch (msg) { case WM_ENDTHREAD: if (CloseHandle(hThread)) { MessageBox(hWnd, "hThreadをクローズしました", "OK", MB_OK); hThread = NULL; } else { MessageBox(hWnd, "hThreadのクローズ失敗", "Error", MB_OK); } break; case WM_CREATE: hMenu = GetMenu(hWnd); hEdit = CreateWindowEx(0, "EDIT", "", WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_WANTRETURN, 0, 0, 0, 0, hWnd, (HMENU)100, hInst, NULL); SendMessage(hEdit, EM_SETLIMITTEXT, (WPARAM)255, 0); break; case WM_SIZE: MoveWindow(hEdit, 0, 0, LOWORD(lp), HIWORD(lp), TRUE); break; case WM_INITMENU: if (hRead) { EnableMenuItem(hMenu, IDM_CHILD, MF_BYCOMMAND | MF_GRAYED); } else { EnableMenuItem(hMenu, IDM_CHILD, MF_BYCOMMAND | MF_ENABLED); } break; case WM_COMMAND: switch (LOWORD(wp)) { case IDM_END: SendMessage(hWnd, WM_CLOSE, 0, 0); break; case IDM_CHILD: bResult = MakeMyProcess(hWnd); if (bResult) hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MyThreadProc, NULL, 0, &dwThreadID); break; } break; case WM_CLOSE: id = MessageBox(hWnd, "終了してもよいですか", "終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { if (hRead) if (CloseHandle(hRead)) MessageBox(hWnd, "hReadを閉じました", "OK", MB_OK); if (hThread) if (CloseHandle(hThread)) MessageBox(hWnd, "hThreadを閉じました", "OK", MB_OK); if (hProcess) if (CloseHandle(hProcess)) MessageBox(hWnd, "hProcessを閉じました", "OK", MB_OK); DestroyWindow(hEdit); DestroyWindow(hWnd); } break; case WM_DESTROY: PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0; }
WM_ENDTHREAD自作メッセージが来たら、スレッドハンドルをクローズします。
WM_CREATEメッセージが来たらクライアント領域にエディットコントロールを作ります。
WM_SIZEメッセージが来たら、エディットコントロールの大きさを親のクライアント領域に ぴったり重なるよう調整します。
WM_INITMENUメッセージが来たらhReadの有効、無効でメニューの「子プロセスを起動」 を無効にしたり有効にしたりします。
メニューでIDM_CHILDが選択されたら、自作関数MakeMyProcessを呼んで 子プロセスを起動します。これがうまくいったら読み出し専用スレッドを作ります。
プログラム終了時にいろいろ後始末をします。
匿名パイプを作って子プロセスを起動する関数です。BOOL MakeMyProcess(HWND hWnd) { HANDLE hWrite; SECURITY_ATTRIBUTES sa; STARTUPINFO si; PROCESS_INFORMATION pi; sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; if (!CreatePipe(&hRead, &hWrite, &sa, 0)) { MessageBox(hWnd, "パイプの作成に失敗しました", "Error", MB_OK); return FALSE; } if (!DuplicateHandle(GetCurrentProcess(), //ソースプロセス hRead, //duplicateするハンドル(オリジナルハンドル) GetCurrentProcess(), //ターゲットプロセス(行先) NULL, //複製ハンドルへのポインタ(コピーハンドル) 0, //アクセス権 FALSE, //子供がハンドルを継承するかどうか DUPLICATE_SAME_ACCESS)) { //オプション MessageBox(hWnd, "DuplicateHandle Error", "Error", MB_OK); CloseHandle(hWrite); CloseHandle(hRead); hRead = NULL; return FALSE; } memset(&si, 0, sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); si.hStdOutput = hWrite; si.hStdError = GetStdHandle( STD_ERROR_HANDLE ); if (!CreateProcess(NULL, "process03x.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi)) { MessageBox(hWnd, "CreateProcess Error", "Error", MB_OK); CloseHandle(hRead); hRead = NULL; return FALSE; } hProcess = pi.hProcess; CloseHandle(pi.hThread); CloseHandle(hWrite); return TRUE; }
パイプを作ったあと読み出しハンドル(hRead)を継承不能にします。
STARTUPINFO構造体のhStdOutputに継承ハンドルhWriteを設定します。
CreateProcess構造体でprocess03x.exeを起動します。5番目の引数をTRUEにしないと hWriteが子プロセスに継承されないので注意してください。
読み出し専用のスレッドです。DWORD WINAPI MyThreadProc(LPVOID lpParam) { DWORD dwResult; BOOL bSuccess; while (1) { bSuccess = ReadFile(hRead, szBuf, sizeof(szBuf), &dwResult, NULL); if (!bSuccess) { break; } SetWindowText(hEdit, szBuf); } MessageBox(NULL, "スレッドを抜けます", "OK", MB_OK); if (CloseHandle(hRead)) { MessageBox(NULL, "hReadをクローズしました", "OK", MB_OK); hRead = NULL; } if (CloseHandle(hProcess)) { MessageBox(NULL, "hProcessをクローズしました", "OK", MB_OK); hProcess = NULL; } SendMessage(hMain, WM_ENDTHREAD, 0, 0); return 0; }
読み出しに失敗するとループを抜けてスレッドは終了します。 この関数が終了する直前に親ウィンドウにWM_ENDTHREAD自作メッセージを送って スレッドが終了することを知らせます。実はこの関数内でCloseHandle(hThread)を 実行してもCloseHandleは成功します。いろいろ試してみてください。
また、このプログラムから呼び出されるprcess03x.exeも作ってみてください。
Update 06/Dec/1999 By Y.Kumei