・ パイプでプロセス間通信の基本 (98/12/29/Tue,99/12/9/Thu 補足)

プロセスの間で通信させるのに一番てっとり早い方法はパイプを使う方法です。 パイプというのは一言で言えばパイプみたいなもん(なんだそりゃ)なのですが、 中を流れているのは水や石油やカレーではなくて情報(メッセージ)であります。

このパイプには主に「名前なしパイプ」と「名前付パイプ」の2種類があり、 「名前付パイプ」を使えばネットワーク上の他のマシンで動いているプロセスと 通信する事が出来ますが、Win95/98 では「名前付パイプ」は作成出来ません。 CreateNamedPipe() のヘルプをみても対応していませんと書いてあります。 さすがゲーム用の OS(しつこい)。

って訳で今回は「名前なしパイプ」を使って子プロセスとの通信をする 方法を書きます。

流れとしては

(1) GetStdHandle() で親の入出力ハンドルを保存
(2) CreatePipe() でパイプ作成
(3) SetStdHandle() でパイプの片側を入出力ハンドルに割り当てる
(4) DuplicateHandle() で (3) で割り当てた側と逆の側を継承しない様に指定
    ついでにハンドルの複製を作る
(5) CloseHandle() で継承しないハンドルをクローズ
(5) CreateProcess() で子プロセス起動
(6) (1) で保存しておいた親の入出力ハンドルを戻す。
(6) 後は WriteFile() 、 ReadFile() で子プロセスと会話出来る
となります。ちなみに DuplicateHandle()で「継承しない」ようにするのを 忘れることが多いので気をつけましょう。
# そういや ア○キーから出てる 公式 API 辞典とかいう本には
# DuplicateHandle() の説明が無かったなあ。だいたいあの本の
# 内容はほとんど MSDN ライブラリの内容の丸写しだし。あれで
# 5000円は高いよなあ、って買ってもいないのにケチつけて
# はいかんな < 私 (^^;;
なおパイプの流れは一方通行ですので、入力と出力(場合によっては エラー出力)それぞれにパイプを作って割り当てる必要があります。 ま、くどくどと説明するより下のサンプルを見ればパイプの作り方が分かると思います。

(補足) 99/12/9

下のソースは今見ると結構いい加減な処理をしています(^^;。 ちゃんとパイプを作りたい時はここに ある「pipe.exe」のソースを参考にしてみて下さい

// パイプのテスト 12/29/98 (c)tokai@fides.dti.ne.jp

#include <windows.h>

#define R 0
#define W 1

#define CHR_BUF 4048
	
int WINAPI WinMain(  HINSTANCE hInst,  HINSTANCE hPrevInst,  LPSTR lpCmdLine,  int nCmdShow)         
{
	
	HANDLE hPipeP2C[2];  // 親 → 子 のパイプ(stdin)
	HANDLE hPipeC2P[2];  // 子 → 親 のパイプ(stdout)
	HANDLE hPipeC2PE[2]; // 子 → 親 のパイプ(stderr)
	HANDLE hDupPipeP2CW; // 親 → 子 のパイプ(stdin)の複製
	HANDLE hDupPipeC2PR; // 子 → 親 のパイプ(stdout)の複製
	HANDLE hDupPipeC2PE; // 子 → 親 のパイプ(stderr)の複製

	SECURITY_ATTRIBUTES secAtt;
	STARTUPINFO startInfo;
	PROCESS_INFORMATION proInfo;
	
	HANDLE hParent = GetCurrentProcess();
	
	char str[CHR_BUF],processName[CHR_BUF];
	DWORD dwByte;

	// コンソール割り当て
	FreeConsole();
	AllocConsole();

	//------------------------------------------------------
	// パイプ作成(STDOUT,STDERR,STDIN の3本)
	
	// 親の STDOUT , STDIN ,STDERR のハンドルを保存
	HANDLE hOldIn = GetStdHandle(STD_INPUT_HANDLE);
	HANDLE hOldOut = GetStdHandle(STD_OUTPUT_HANDLE);
	HANDLE hOldErr = GetStdHandle(STD_ERROR_HANDLE);
	
	// SECURITY_ATTRIBUTES の設定(パイプを作るのに必要
	secAtt.nLength = sizeof(SECURITY_ATTRIBUTES);
	secAtt.lpSecurityDescriptor = NULL;
	secAtt.bInheritHandle = TRUE;  // ハンドル継承
	
	//------------------------------------------------------
	// STDOUT
	
	// パイプ作成
	CreatePipe(&hPipeC2P[R],&hPipeC2P[W],&secAtt,0);
	
	// 「子」プロセスの STDOUT をセット
	SetStdHandle(STD_OUTPUT_HANDLE,hPipeC2P[W]);
	
	// 「子」からくるパイプの読み側(つまり親がリードする側)は継承しない
	DuplicateHandle(hParent,hPipeC2P[R],hParent,&hDupPipeC2PR,0,FALSE,DUPLICATE_SAME_ACCESS);
	
	// 読み側のハンドルをクローズ
	CloseHandle(hPipeC2P[R]);
	
	//------------------------------------------------------
	// STDERR
	
	// パイプ作成
	CreatePipe(&hPipeC2PE[R],&hPipeC2PE[W],&secAtt,0);
	
	// 「子」プロセスの STDERR をセット
	SetStdHandle(STD_ERROR_HANDLE,hPipeC2PE[W]);
	
	// 「子」からくるパイプの読み側(つまり親がリードする側)は継承しない
	DuplicateHandle(hParent,hPipeC2PE[R],hParent,&hDupPipeC2PE,0,FALSE,DUPLICATE_SAME_ACCESS);
	
	// 読み側のハンドルをクローズ
	CloseHandle(hPipeC2PE[R]);
	
	//------------------------------------------------------
	// STDIN
	
	//パイプ作成
	
	CreatePipe(&hPipeP2C[R],&hPipeP2C[W],&secAtt,0); 
	
	// 「子」プロセスの STDIN をセット
	SetStdHandle(STD_INPUT_HANDLE,hPipeP2C[R]);
	
	// 「子」からくるパイプの書き込み側(つまり親がライトする側)は継承しない
	DuplicateHandle(hParent,hPipeP2C[W],hParent,&hDupPipeP2CW,0,FALSE,DUPLICATE_SAME_ACCESS);
	
	// 書き込み側のハンドルをクローズ
	CloseHandle(hPipeP2C[W]);
	
	// パイプ作成終了
	//------------------------------------------------------


	// STARTUPINFO の設定
	memset(&startInfo,0,sizeof(STARTUPINFO));
	startInfo.cb = sizeof(STARTUPINFO);
	
	// 子プロセスでコマンドインタープリタを起動
	// STDIN,STDOUT,STDIN のハンドルが継承される(つまり親とパイプでつながる)

	GetEnvironmentVariable("ComSpec",processName,CHR_BUF);

	if(CreateProcess(processName,"",NULL,NULL,TRUE,
		0,NULL,NULL,&startInfo,&proInfo)==TRUE){
		
		// 子プロセスが起動したら親の STDIN と STDOUT を戻す
		SetStdHandle(STD_OUTPUT_HANDLE,hOldOut);
		SetStdHandle(STD_INPUT_HANDLE,hOldIn);
		SetStdHandle(STD_ERROR_HANDLE,hOldErr);
		
		// "dir" コマンドを子プロセスに送る
		wsprintf(str,"dir\r\n");  // (注) CR-LF を入れないとコマンドを受け取ってくれない
		WriteFile(hDupPipeP2CW,str,strlen(str),&dwByte,NULL);
		
		// "exit" 
		wsprintf(str,"exit\r\n");  
		WriteFile(hDupPipeP2CW,str,strlen(str),&dwByte,NULL);
		
		// バッファのフラッシュ
		FlushFileBuffers(hDupPipeP2CW);
		FlushFileBuffers(hDupPipeC2PR);
		
		// 子プロセスが終るまで停止
		WaitForSingleObject(proInfo.hProcess,INFINITE);
		
		// 子からきたメッセージを読む
		ReadFile(hDupPipeC2PR,str,CHR_BUF,&dwByte,NULL);
		str[dwByte] = '\0';
		
		MessageBox(NULL,str,"",NULL);
		
	}
	
	return 0;
}