Click here to Skip to main content
15,894,343 members
Articles / Programming Languages / C++

Real-Time Console Output Redirection

Rate me:
Please Sign up or sign in to vote.
4.81/5 (47 votes)
31 Oct 2006Zlib7 min read 375.8K   5.1K   116  
Console process output flushing is no longer a problem with this little stub.
#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers
#include <windows.h>
#include <tchar.h>

/*
 Quick note: 
*/

#define SLEEP_TIME 50 // reactivity: sleep time to wait for subprocess to output some more data

CONST COORD origin = { 0, 0 };

// we should have been spawned using SW_HIDE, so our console window is not visible
int main(int argc, char* argv[])
{
	// get pipe/console to output to
	HANDLE hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
	DWORD dwDummy;

	// parse command line : skip to RTconsole's arguments
	LPTSTR commandLine = GetCommandLine();
	if (*commandLine == '"')
		commandLine = _tcschr(commandLine+1, _T('"'));
	else
		commandLine = _tcspbrk(commandLine, _T(" \t"));
	if (!commandLine) return -1;
	commandLine += _tcsspn(commandLine+1, _T(" \t"))+1;
	if (commandLine[0] == '\0') return -1;

	// prepare the console window & inherited screen buffer
	SECURITY_ATTRIBUTES sa;
	sa.nLength = sizeof(sa);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;
	HANDLE hConsole = CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,&sa,CONSOLE_TEXTMODE_BUFFER,NULL);
	FillConsoleOutputCharacter(hConsole, '\0', MAXLONG, origin, &dwDummy); // fill screen buffer with zeroes
	SetStdHandle(STD_OUTPUT_HANDLE, hConsole); // to be inherited by child process

	// start the subprocess
	PROCESS_INFORMATION pi;
	STARTUPINFO si;
	ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = STARTF_FORCEOFFFEEDBACK; // we don't want the "app starting" cursor
	// all other default options are already good : we want subprocess to share the same console and to inherit our STD handles
	if (!CreateProcess(	NULL, commandLine, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
	{
		CloseHandle(hConsole);
		return -2;
	}
	CloseHandle(pi.hThread); // always close the hThread after a CreateProcess

	COORD lastpos = { 0, 0 };
	CONSOLE_SCREEN_BUFFER_INFO csbi;
	bool exitNow = false;
	do
	{
		if (WaitForSingleObject(pi.hProcess, 0) != WAIT_TIMEOUT)
			exitNow = true; // exit after this last iteration

		// get screen buffer state
		GetConsoleScreenBufferInfo(hConsole, &csbi);
		int lineWidth = csbi.dwSize.X;
		
		if ((csbi.dwCursorPosition.X == lastpos.X) && (csbi.dwCursorPosition.Y == lastpos.Y))
			Sleep(SLEEP_TIME); // text cursor did not move, sleep a while
		else
		{
			DWORD count = (csbi.dwCursorPosition.Y-lastpos.Y)*lineWidth+csbi.dwCursorPosition.X-lastpos.X;
			// read newly output characters starting from last cursor position
			LPTSTR buffer = (LPTSTR) LocalAlloc(0, count*sizeof(TCHAR));
			ReadConsoleOutputCharacter(hConsole, buffer, count, lastpos, &count);
			// fill screen buffer with zeroes
			FillConsoleOutputCharacter(hConsole, '\0', count, lastpos, &dwDummy);
			
			SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
			lastpos = csbi.dwCursorPosition;
			GetConsoleScreenBufferInfo(hConsole, &csbi);
			if ((csbi.dwCursorPosition.X == lastpos.X) && (csbi.dwCursorPosition.Y == lastpos.Y))
			{ // text cursor did not move since this treatment, hurry to reset it to home
				SetConsoleCursorPosition(hConsole, origin);
				lastpos = origin;
			}
			SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);

			// scan screen buffer and transmit character to real output handle
			LPTSTR scan = buffer;
			do
			{
				if (*scan)
				{
					DWORD len = 1;
					while (scan[len] && (len < count))
						len++;
					WriteFile(hOutput, scan, len, &dwDummy, NULL);
					scan += len;
					count -= len;
				}
				else
				{
					DWORD len = 1;
					while (!scan[len] && (len < count))
						len++;
					scan += len;
					count -= len;
					len = (len+lineWidth-1)/lineWidth;
					for (;len;len--)
						WriteFile(hOutput, "\r\n", 2, &dwDummy, NULL);
				}
			} while (count);
			//FlushFileBuffers(hOutput); // seems unnecessary
			LocalFree(buffer);
		}
		// loop until end of subprocess
	} while (!exitNow);
	
	CloseHandle(hConsole);
	
	// release subprocess handle
	DWORD exitCode;
	if (!GetExitCodeProcess(pi.hProcess, &exitCode))
		exitCode = -3;
	CloseHandle(pi.hProcess); 
	return exitCode;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License



Comments and Discussions