Hello all,
I've been trying to redirect the I/O of cmd.exe so that my app can send cmd input, and take cmd's output. My main resource for this has been this msdn article
http://msdn2.microsoft.com/en-us/library/ms682499.aspx
Creating a Child Process with Redirected Input and Output (Windows)
I've been focusing on using anonymous pipes, which seem to be suited to my needs, but there is a problem...
It works alright the first time I input something, I get the console's opening screen (Microsoft Windows...Copyright...current_directory>[cursor here]).
When I type in any command, dir for example, I then get a newline with just "More?" and no output from the previous command. After the "More?" line, I don't get any more output from the pipe.
As best I can tell, the code in the prior article, which mine is based off of, is meant to only make one read/write pass with the child process- as opposed to doing all the reading/writing that I need to. The suspect code segments are my Read and Write pipe functions:
ReadFromPipe
DWORD ReadFromPipe( char* buff, DWORD readLength )
{
DWORD dwRead;
if ( hChildStdoutWr != NULL )
{
CloseHandle(hChildStdoutWr);
hChildStdoutWr = NULL;
}
// Read output from the child process, and write to parent's STDOUT.
memset( buff, -1, BUFSIZE );
DWORD x = ReadFile( hChildStdoutRd, buff, readLength, &dwRead, NULL);
return x;
}
WriteToPipe
DWORD WriteToPipe( LPCVOID data, DWORD length )
{
DWORD writtenBytes;
BOOL b = WriteFile(hChildStdinWr, data, length, &writtenBytes, NULL);
if ( hChildStdinWr != NULL )
{
CloseHandle(hChildStdinWr);
hChildStdinWr = NULL;
}
return writtenBytes;
}
Now, you see those if statements, which close my StdinWr and StdoutWr handles?
I don't think I should have those there, but without them, I don't even get the "More?" line...
Here is the main loop of my program:
//Inside of void ClientProc( const char* ip )
while( true ) //ClientDisConn should end this program.
{
PollEvents( host );
PeekNamedPipe( hChildStdoutRd, data, BUFSIZE, &bytesRead, &availBytes, NULL );
if( bytesRead > 0 )
{
ReadFromPipe( data, (DWORD)BUFSIZE );
int length = 0;
for( int i = 0; i < BUFSIZE; i ++ )
if( data > -1 )
length++;
else
break;
printf( "Message sent\n" );
PACKET* p = CreatePacket( data , (UINT)length );
SendPacket( p, peer );
}
}
And incase the problem isn't in there, here is my entire program:
#include "ServerAndClientProcs.h"
#include "NetWrapper.h"
#include "enet.h"
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <iostream>
#include <list>
HANDLE hChildStdinRd, hChildStdinWr,
hChildStdoutRd, hChildStdoutWr,
hInputFile, hStdout;
std::list<PEER*> peerList;
bool CreateControlledCMD();
bool CreateChildProcess();
DWORD WriteToPipe( LPCVOID data, DWORD length );
DWORD ReadFromPipe( char* buff, DWORD readLength );
void ServerConn( EVENT &e );
void ServerMsgRecv( EVENT &e );
void ServerDisConn( EVENT &e );
void ClientConn( EVENT &e );
void ClientMsgRecv( EVENT &e );
void ClientDisConn( EVENT &e );
void ServerProc()
{
ADDRESS addr;
HOST host;
addr.host = ENET_HOST_ANY;
addr.port = 1029;
host = *CreateHost( addr, 2 );
NetEvent nConn = ServerConn;
NetEvent nMsg = ServerMsgRecv;
NetEvent nDisConn = ServerDisConn;
RegisterCallBacks( &nConn, &nMsg, &nDisConn );
std::string input;
std::string terminate = "END";
while( input != terminate )
{
PollEvents( &host );
if( _kbhit() ) //Asynchronous console I/O -- almost an oxymoron..
{
char in = _getch();
std::cout << in;
if( in == 0xD ) //0xD == ENTER
{
std::cout << std::endl;
PACKET* p;
p = CreatePacket( (char*)input.c_str() , (UINT)input.length() + 1 );
printf( "Packets sent\n" );
for( std::list<PEER*>::iterator itor = peerList.begin(); itor != peerList.end(); ++itor )
SendPacket( p, (*itor), 0 );
}
else
input += in;
}
}
}
void ServerConn( ENetEvent &e )
{
printf( "Client connected\n" );
peerList.push_back( e.peer );
}
void ServerMsgRecv( ENetEvent &e )
{
for( unsigned int i = 0; i < e.packet->dataLength; i++ )
std::cout << e.packet->data;
}
void ServerDisConn( ENetEvent &e )
{
printf( "Client disconnected\n" );
peerList.remove( e.peer );
}
void ClientProc( const char* ip )
{
ADDRESS addr;
char data[BUFSIZE];
DWORD bytesRead, availBytes;
RtlZeroMemory( data, BUFSIZE );
HOST* host;
PEER* peer;
addr.port = 1029;
host = CreateClient( );
peer = Connect( host, addr, ip );
if( peer != NULL )
CreateControlledCMD();
NetEvent nConn = ClientConn;
NetEvent nMsg = ClientMsgRecv;
NetEvent nDisConn = ClientDisConn;
RegisterCallBacks( &nConn, &nMsg, &nDisConn );
while( true ) //ClientDisConn should end this program.
{
PollEvents( host );
PeekNamedPipe( hChildStdoutRd, data, BUFSIZE, &bytesRead, &availBytes, NULL );
if( bytesRead > 0 )
{
ReadFromPipe( data, (DWORD)BUFSIZE );
int length = 0;
for( int i = 0; i < BUFSIZE; i ++ )
if( data > -1 )
length++;
else
break;
printf( "Message sent\n" );
PACKET* p = CreatePacket( data , (UINT)length );
SendPacket( p, peer );
}
}
}
void ClientConn( EVENT &e )
{
}
void ClientMsgRecv( ENetEvent &e )
{
printf( "Message received\n" );
WriteToPipe( (LPCVOID)e.packet->data, (DWORD)e.packet->dataLength );
}
void ClientDisConn( ENetEvent &e )
{
ShutdownNet();
ExitProcess(1);
}
bool CreateControlledCMD()
{
SECURITY_ATTRIBUTES saAttr;
BOOL fSuccess;
// Set the bInheritHandle flag so pipe handles are inherited.
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
// Get the handle to the current STDOUT.
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
// Create a pipe for the child process's STDOUT.
if (! CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
return false;
// Ensure that the read handle to the child process's pipe for STDOUT is not inherited.
SetHandleInformation( hChildStdoutRd, HANDLE_FLAG_INHERIT, 0);
// Create a pipe for the child process's STDIN.
if (! CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
return false;
// Ensure that the write handle to the child process's pipe for STDIN is not inherited.
SetHandleInformation( hChildStdinWr, HANDLE_FLAG_INHERIT, 0);
// Now create the child process.
fSuccess = CreateChildProcess();
if (! fSuccess)
return false;
return true;
}
bool CreateChildProcess()
{
TCHAR szCmdline[]=TEXT("cmd.exe");
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
bool bFuncRetn = FALSE;
// Set up members of the PROCESS_INFORMATION structure.
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = hChildStdoutWr;
siStartInfo.hStdOutput = hChildStdoutWr;
siStartInfo.hStdInput = hChildStdinRd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
// Create the child process.
bFuncRetn = (bool)CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION
if (bFuncRetn == 0)
return false;
else
{
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
return bFuncRetn;
}
}
DWORD WriteToPipe( LPCVOID data, DWORD length )
{
DWORD writtenBytes;
BOOL b = WriteFile(hChildStdinWr, data, length, &writtenBytes, NULL);
if ( hChildStdinWr != NULL )
{
CloseHandle(hChildStdinWr);
hChildStdinWr = NULL;
}
return writtenBytes;
}
DWORD ReadFromPipe( char* buff, DWORD readLength )
{
DWORD dwRead;
if ( hChildStdoutWr != NULL )
{
CloseHandle(hChildStdoutWr);
hChildStdoutWr = NULL;
}
// Read output from the child process, and write to parent's STDOUT.
memset( buff, -1, BUFSIZE );
DWORD x = ReadFile( hChildStdoutRd, buff, readLength, &dwRead, NULL);
return x;
}
VOID ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
ExitProcess(0);
}
Btw, this program is actually a networked one, eg I have a server, which the client connect too, and the server acts like a remote command line.