Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Real-Time Console Output Redirection

, 31 Oct 2006 Zlib
Console process output flushing is no longer a problem with this little stub.
rtconsoledemo.zip
RTconsoleDemo
Demo
Demo.dsp
res
Demo.ico
Demo.dsw
DemoConsole
DemoConsole.dsp
Release
Demo.exe
DemoConsole.exe
RTconsole.exe
RTconsole
RTconsole.dsp
#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

Share

About the Author

_Olivier_
Architect
France France
Programmer since the age of 7. Windows programmer for more than 10 years
I'm now 30 years old, working as software architect, team leader and lead programmer.
 
You can find some of the programs I released for Internet users at my web site

| Advertise | Privacy | Terms of Use | Mobile
Web01 | 2.8.141223.1 | Last Updated 31 Oct 2006
Article Copyright 2006 by _Olivier_
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid