C++ Builder CE プログラミング

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.hPopen.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()は色々なプログラムで多用しています。

コメント