So I am trying to modify the code provided by Microsoft(here) to use WriteConsoleInput, instead of WriteFile, but it says the handle is invalid (I'm betting it is something really silly), like how the handle is initially created or something.
So my question, what's the difference between the handles needed for WriteConsoleInput, and the handle for WriteFile?
WriteConsoleInput
WriteFile
I'm guessing it has to do with the permissions flag of the HANDLE created by CreateFile compared to the inherited handles created by the CreateProcess/CreatePipe/DuplicateHandle process.
I decided it would be easier if you can see the problem, so here is my full solution (Using Visual Studio 2012) Both the child and parent app are included.
ConsoleRedir on GitHub
As a note, I need the child app to use the ReadConsoleInput, and that's been the source of my frustration.
Original Method:
///////////////////////////////////////////////////////////////////////
// GetAndSendInputThreadOrig
// Thread procedure that monitors the console for input and sends input
// to the child process through the input pipe.
// This thread ends when the child application exits.
// Original from http://support.microsoft.com/kb/190351
///////////////////////////////////////////////////////////////////////
DWORD WINAPI GetAndSendInputThreadOrig(LPVOID lpvThreadParam)
{
CHAR read_buff[256];
DWORD nBytesRead,nBytesWrote;
HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
// Get input from our console and send it to child through the pipe.
while (bRunThread)
{
if(!ReadConsole(hStdIn,read_buff,1,&nBytesRead,NULL))
DisplayError("ReadConsole");
read_buff[nBytesRead] = '\0'; // Follow input with a NULL.
if (!WriteFile(hPipeWrite,read_buff,nBytesRead,&nBytesWrote,NULL))
{
if (GetLastError() == ERROR_NO_DATA)
break; // Pipe was closed (normal exit path).
else
DisplayError("WriteFile");
}
}
return 1;
}
My modified version (have to build keystroke):
///////////////////////////////////////////////////////////////////////
// GetAndSendInputThread
// Thread procedure that monitors the console for input and sends input
// to the child process through the input pipe.
// This thread ends when the child application exits.
///////////////////////////////////////////////////////////////////////
DWORD WINAPI GetAndSendInputThread(LPVOID lpvThreadParam)
{
CHAR read_buff[256];
DWORD nBytesWrote;
HANDLE hPipeWrite = (HANDLE)lpvThreadParam;
// Get input from our console and send it to child through the pipe.
while (bRunThread)
{
INPUT_RECORD inputRecords[4];
// Build a keyboard event, press '?' and then press RETURN
inputRecords[0].EventType = KEY_EVENT;
inputRecords[0].Event.KeyEvent.bKeyDown = TRUE;
inputRecords[0].Event.KeyEvent.uChar.UnicodeChar = '?';
inputRecords[1].EventType = KEY_EVENT;
inputRecords[1].Event.KeyEvent.bKeyDown = FALSE;
inputRecords[1].Event.KeyEvent.uChar.UnicodeChar = '?';
inputRecords[2].EventType = KEY_EVENT;
inputRecords[2].Event.KeyEvent.bKeyDown = TRUE;
inputRecords[2].Event.KeyEvent.dwControlKeyState = 0;
inputRecords[2].Event.KeyEvent.uChar.UnicodeChar = '\r';
inputRecords[2].Event.KeyEvent.wRepeatCount = 1;
inputRecords[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
inputRecords[2].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
inputRecords[3].EventType = KEY_EVENT;
inputRecords[3].Event.KeyEvent.bKeyDown = FALSE;
inputRecords[3].Event.KeyEvent.dwControlKeyState = 0;
inputRecords[3].Event.KeyEvent.uChar.UnicodeChar = '\r';
inputRecords[3].Event.KeyEvent.wRepeatCount = 1;
inputRecords[3].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
inputRecords[3].Event.KeyEvent.wVirtualScanCode = MapVirtualKey(VK_RETURN, MAPVK_VK_TO_VSC);
if (!WriteConsoleInput(hPipeWrite,inputRecords,sizeof(inputRecords) / sizeof(*inputRecords),&nBytesWrote))
{
if (GetLastError() == ERROR_NO_DATA)
break; // Pipe was closed (normal exit path).
else
DisplayError("WriteConsoleInput");
}
}
return 1;
}