C++ Builder CE 外部プログラムの利用

Popen.cppの修正

以前の記事C++ Builder CE 外部プログラムの利用で掲載したpopen.cppですが、プロジェクトの設定によってはコンパイルエラーが出ることがあります。要は、

[bcc32c エラー] popen.cpp(55): no matching function for call to 'CreateProcessW'

  processthreadsapi.h(373): candidate function not viable: no known conversion from 'char *' to 'LPWSTR' (aka 'wchar_t *') for 2nd argument

というようなことです。これはプロジェクトオプションの設定の、

上図の_TCHARのマップ先がcharないしwchar_tかに依存するエラーなのですが、デフォルトの設定はwchar_tなので、それに合わせて、wchar_tにすべきという意味を込めて、Popen.cppを修正しました。原理は、wchar_tでない場合は、コンパイルエラーを発生させて注意を喚起するという観点です。修正した結果のpopen.cppを掲載します。


#include <iostream>
#include <fstream>
#include <string>
#include <memory>
#include <array>
#include <Windows.h>


#if !defined(UNICODE)
#error Please set _TCHAR mapping to wchar_t in project option C++(common option)
#endif




String Popen(const String command, const String arguments,DWORD& rc)
{
	SECURITY_ATTRIBUTES sa;
	HANDLE read, write;
	std::string rtn;

	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';


	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("");
	}

	STARTUPINFOA 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 (!CreateProcessA(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();
}

デフォルトに合わせて、CreateProcessを明示的にCreateProcessAに、STARTUPINFOを明示的にSTARTUPINFOAに変更しました。

コメント