Popen()を作る 復刻版
Unix系のpopen()とかVC++系の_popen()じゃなくて、C++ Builder CEで使えるPopen()を作ります。ソースをまず示して、続いて使い方を解説します。
まずヘッダー Popen.h から、
String Popen(const String command,const String arguments,DWORD& rc);
プロトタイプ宣言のみです。次に、本体のPopen.cppです。
#include <iostream>
#include <fstream>
#include <string>
#include <memory>
#include <array>
#include <Windows.h>
String Popen(const String command, const String arguments,DWORD& rc)
{
SECURITY_ATTRIBUTES sa;
HANDLE read, write;
std::string rtn;
//const std::string cmd_str = command + std::string(" ") + arguments;
const String cmd_str = command + " " + arguments + "\0";
std::shared_ptr<char> cmd(new char[cmd_str.Length() + 1], std::default_delete<char[]>());
std::copy(cmd_str.begin(), cmd_str.end(), cmd.get());
cmd.get()[cmd_str.Length()] = '\0';
// String cmd = cmd_str;
sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL;
if (!CreatePipe(&read, &write, &sa, 0))
{
std::cerr << "CreatePipe is Error!" << std::endl;
return String("");
}
STARTUPINFO si = {};
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
si.hStdOutput = write;
si.hStdError = write;
if (si.hStdOutput == INVALID_HANDLE_VALUE || si.hStdError == INVALID_HANDLE_VALUE)
{
std::cerr << "GetStdHandle is Error!" << std::endl;
return String("");
}
PROCESS_INFORMATION pinfo = {};
if (!CreateProcess(NULL, cmd.get(), NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pinfo))
{
std::cerr << "CreateProcess is Error!" << std::endl;
return String("");
}
HANDLE child = pinfo.hProcess;
if (!CloseHandle(pinfo.hThread))
{
std::cerr << "CloseHandle(hThread) is Error!" << std::endl;
return String("");
}
DWORD r = WaitForSingleObject(child, INFINITE);
switch (r)
{
case WAIT_FAILED:
std::cerr << "WaitResult:WAIT_FAILED" << std::endl;
return String("");
case WAIT_ABANDONED:
std::cerr << "WaitResult:WAIT_ABANDONED" << std::endl;
return String("");
case WAIT_OBJECT_0:
GetExitCodeProcess(child,&rc);
break;
case WAIT_TIMEOUT:
std::cerr << "WaitResult:WAIT_TIMEOUT" << std::endl;
return String("");
default:
std::cerr << "WaitResult:" << r << std::endl;
return String("");
}
if (!CloseHandle(write))
{
std::cerr << "CloseHandle(write) is Error!" << std::endl;
return String("");
}
write = NULL;
std::array<unsigned char, 1024> buf = { 0 };
DWORD rlen = 0;
while (ReadFile(read, buf.data(), buf.size(), &rlen, NULL))
{
std::copy(buf.begin(), buf.begin() + rlen, std::back_inserter(rtn));
}
if (!CloseHandle(read))
{
std::cerr << "CloseHandle(read) is Error!" << std::endl;
return String("");
}
read = NULL;
return rtn.c_str();
}
使い方は、このPopen.hとPopen.cppをプロジェクトに含みます。テストプログラムは、まずフォームは、

こんな感じでTMemoを適当に配置してください。Unit1.cppは、
#include <vcl.h>
#pragma hdrstop
#include "popen.h"
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
String command = "ping";
String args = "www.google.com";
DWORD rc;
String rtn;
rtn = Popen(command, args,rc);
Memo1->Lines->Add(rtn);
}
これだけです。

プロジェクトにpopen.hとpopen.cppを組み込んで(windowsはファイル名の大文字小文字を区別しません。)build and run(F9)してみましょう。

肝心なコード部分は、
rtn = Popen(command, args,rc);
のみです。これだけで、コマンドプロンプトで、

ping www.google.comしたのと同じ結果が得られていて、その内容はString rtnに格納されているわけです。別途pingの戻り値もDWORD rcに得られています。
このPopen()は色々なプログラムで多用しています。
コメント