I have got a GUI program which is a "wrapper" to cmd.exe. From this GUI program I can send and receive command via cmd.exe
I am using pipe redirection, and already read various references:
Creating a Child Process with Redirected Input and Output
Redirect Input and Output of Powershell.exe to Pipes in C++
windows cmd pipe not unicode even with /U switch
First of all I launch an instance of "cmd.exe /U", so that I am telling cmd.exe to generate Unicode output then I use ReadFile/WriteFile to read and write from/to pipe.
Everything works fine if I use ANSI, But I have got 2 problems related to Unicode:
1) If I want to pass data to the pipe using WriteFile, I have to convert it from Unicode to Ansi first. Passing data in Unicode does not work: specifically, when reading the output after my WriteFile, the cmd outputs a "More?" string. If I write the input in ANSI, it works fine and cmd correctly outputs the result of the command. The cmd has been launched with /U switch.
2) While the console output is successfully unicode for internal commands such as cd, dir, etc. when I launch from the cmd an external program such as ping, netstat, ipconfig etc. the output I receive is in ANSI, so I get corrupted data. I suppose that the /U switch does not have effect when external programs are running through cmd? Is there a possible solution to this, and make everything in the cmd gets output in Unicode?
Here is a sample of my code, I placed comments on lines where the errors are:
bool StartCmdPipe()
{
static SECURITY_ATTRIBUTES SA;
static STARTUPINFOW SI;
static PROCESS_INFORMATION PI;
static HANDLE StdInPipeRead;
static HANDLE StdInPipeWrite;
static HANDLE StdOutPipeRead;
static HANDLE StdOutPipeWrite;
static wstring sWorkDir;
WCHAR* pBuffer;
unsigned long BytesRead;
sWorkDir = _wgetenv(L"SystemDrive");
sWorkDir += L"\\";
bJustOpened = true;
SA.nLength = sizeof(SA);
SA.bInheritHandle = true;
SA.lpSecurityDescriptor = NULL;
if (!CreatePipe(&StdInPipeRead, &StdInPipeWrite, &SA, 0) || !CreatePipe(&StdOutPipeRead, &StdOutPipeWrite, &SA, 0))
{
return false;
}
// Set Pipe for process to create
ZeroMemory(&SI, sizeof(SI));
SI.cb = sizeof(SI);
SI.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
SI.wShowWindow = SW_HIDE;
SI.hStdInput = StdInPipeRead; //GetStdHandle(STD_INPUT_HANDLE);
SI.hStdOutput = StdOutPipeWrite;
SI.hStdError = StdOutPipeWrite;
// Launch cmd process
g_bCreateProcessSuccess = CreateProcessW(
NULL,
L"cmd.exe /U",
NULL,
NULL,
true,
0,
NULL,
(WCHAR*)sWorkDir.c_str(),
&SI,
&PI);
g_sCommandLineInput = L"";
g_bkeepCmdRunning = true;
if (g_bCreateProcessSuccess)
{
do
{
DWORD TotalBytesAvail = 0;
PeekNamedPipe(StdOutPipeRead, 0, 0, 0, &TotalBytesAvail, 0);
pBuffer = (WCHAR*)malloc(TotalBytesAvail);
if (TotalBytesAvail > 0)
{
// First problem is: while internal commands work fine (dir, cd, etc.) and return output in unicode format,
// when I launch another process (ipconfig, netstat, ping, etc.), output is returned in ANSI format.
// Even if I launched cmd.exe with /U switch
ReadFile(StdOutPipeRead, pBuffer, TotalBytesAvail, &BytesRead, NULL);
}
else BytesRead = 0;
if (BytesRead > 0)
{
wprintf(pBuffer);
}
free(pBuffer);
// g_sCommandLineInput is a global wstring variable which gets filled each time user wants to send new command.
if (g_sCommandLineInput.length() > 0)
{
DWORD numberofbyteswritten = 0;
g_sCommandLineInput += L"\n"; //Append /n to make the cmd process interpret the data as a command to launch
// Second problem is, why do I have to send command in ANSI format, even if I launched cmd with the /U switch?
string sCommndLineAnsi = WStringToString(g_sCommandLineInput);
WriteFile(StdInPipeWrite, sCommndLineAnsi.c_str(), sCommndLineAnsi.length() * sizeof(CHAR), &numberofbyteswritten, NULL);
//Reset command
g_sCommandLineInput = L"";
}
Sleep(100);
}
while (g_bkeepCmdRunning);
TerminateProcess(PI.hProcess, 0);
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
}
CloseHandle(StdOutPipeRead);
CloseHandle(StdOutPipeWrite);
return true;
}