1999/09/14「コンソールアプリを,DOS 窓なしで GUI から呼び出す。」だが,この方法だとデータを得られない,という読者さんからのご指摘を頂く。読んでくれた人がいるというだけでもありがたいが,メールまで頂くと非常に嬉しい。
以下,頂いたサンプル。(見やすくするためと簡略化のため,改行や出力先等,多少変更してあります。)
// コマンドライン
LPTSTR lpCommandLine;
lpCommandLine = "xxx.exe";
// 標準出力用パイプ
HANDLE hReadPipe = NULL, hWritePipe = NULL;
// エラー出力用パイプ
HANDLE hErrReadPipe = NULL, hErrWritePipe = NULL;
// セキュリティ属性(ハンドル継承を指定)
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
// 標準出力パイプの作成
::CreatePipe(&hReadPipe, &hWritePipe, &sa, 8192);
// エラー出力パイプの作成
::CreatePipe(&hErrReadPipe, &hErrWritePipe, &sa, 8192);
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInfo;
::ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));
StartupInfo.cb = sizeof(STARTUPINFO);
// ハンドルの継承を指定
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
// DOS窓を表示しない
StartupInfo.wShowWindow = SW_HIDE;
// 標準出力ハンドルとエラー出力ハンドルを設定
StartupInfo.hStdOutput = hWritePipe;
StartupInfo.hStdError = hErrWritePipe;
// コンソールアプリ起動
if (::CreateProcess(
NULL,
lpCommandLine,
NULL,
NULL,
TRUE, // ハンドルの継承
DETACHED_PROCESS, // DOS窓を表示しないための指定
NULL,
NULL,
&StartupInfo, &ProcessInfo))
{
// パイプ内容受け取り用バッファ
char bufStdOut[8192], bufErrOut[8192];
DWORD dwStdOut = 0, dwErrOut = 0;
DWORD dwRet;
// プロセス起動中のパイプ内容受け取り処理
while ( (dwRet = ::WaitForSingleObject(ProcessInfo.hProcess, 0)) !=
WAIT_ABANDONED)
{
memset(bufStdOut, 0, sizeof(bufStdOut));
memset(bufErrOut, 0, sizeof(bufErrOut));
// 標準出力パイプの内容を調べる
::PeekNamedPipe(hReadPipe, NULL, 0, NULL, &dwStdOut, NULL);
if (dwStdOut > 0)
{
// 内容が存在すれば、読み取る
::ReadFile(hReadPipe, bufStdOut, sizeof(bufStdOut) - 1, &dwStdOut,
NULL);
// bufStdOut にパイプ出力が入る
}
// 同様にエラー出力の処理
::PeekNamedPipe(hErrReadPipe, NULL, 0, NULL, &dwErrOut, NULL);
if (dwErrOut > 0)
{
::ReadFile(hErrReadPipe, bufErrOut, sizeof(bufErrOut) - 1, &dwErrOut,
NULL);
// bufErrOut にエラー出力が入る
}
// メッセージキューを取得し、存在すれば、処理を促す
MSG msg;
if (::PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
// プロセス終了なら、ループを抜ける
if (dwRet == WAIT_OBJECT_0)
break;
}
// プロセスハンドルとスレッドハンドルを閉じる
DWORD res;
::GetExitCodeProcess(ProcessInfo.hProcess, &res);
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
}
// すべてのパイプを閉じる
::CloseHandle(hWritePipe);
::CloseHandle(hReadPipe);
::CloseHandle(hErrWritePipe);
::CloseHandle(hErrReadPipe);
修正個所としては,
- CreateProcess の bInheritHandles,dwCreationFlags の値。
- 全体的なエラーチェック。
というところ。
その後読者さんとは話がスクロールバーの話題へ。スクロールバーを MFC で使っていると,OnVScroll() の SB_THUMBPOSITION,SB_THUMBTRACK において,nPos が 16bit を越えると負の値となる。また,MFC のヘルプには
nMinPos と nMaxPos で指定された値の差は、32、767以下でなければいけません。
ということが書いてあったので,てっきりスクロールバーは 16bit しか扱えないんだと思っていたが,これは WM_VSCROLL が nPos を 16bit しか渡してくれないから,ということだ。(nPos = (short int) HIWORD(wParam); となっている。)
読者さんの指摘通り,OnVScroll() 内で GetScrollInfo() を直接呼び出し,スクロール位置を得ることで 32bit の値をとることができた。
いや,いつも身内だけでやってるのでこういうのはいいですね。上記以外でも,もしこの日記を読んだ方がいて,何か変だと思うことがあったら是非知らせて欲しいです。