Click here to Skip to main content
15,884,537 members
Articles / Desktop Programming / MFC

Multiple consoles for a single application

Rate me:
Please Sign up or sign in to vote.
4.96/5 (26 votes)
9 Mar 2006Public Domain5 min read 168.1K   12.3K   73  
Sometimes, it's not enough to have only a single console window for your applcation... let's provide more!!
// ConsoleLogger.cpp: implementation of the CConsoleLogger class.
//
//////////////////////////////////////////////////////////////////////

#include "ConsoleLogger.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////



// CTOR: reset everything
CConsoleLogger::CConsoleLogger()
{
	InitializeCriticalSection();
	m_name[0]=0;
	m_hPipe = INVALID_HANDLE_VALUE;
}

// DTOR: delete everything
CConsoleLogger::~CConsoleLogger()
{
	DeleteCriticalSection();
	
	// Notice: Because we want the pipe to stay alive until all data is passed,
	//         it's better to avoid closing the pipe here....
	//Close();
}



//////////////////////////////////////////////////////////////////////////
// Create: create a new console (logger) with the following OPTIONAL attributes:
//
// lpszWindowTitle : window title
// buffer_size_x   : width
// buffer_size_y   : height
// logger_name     : pipe name . the default is f(this,time)
// helper_executable: which (and where) is the EXE that will write the pipe's output
//////////////////////////////////////////////////////////////////////////
long CConsoleLogger::Create(const char	*lpszWindowTitle/*=NULL*/,
							int			buffer_size_x/*=-1*/,int buffer_size_y/*=-1*/,
							const char	*logger_name/*=NULL*/,
							const char	*helper_executable/*=NULL*/)
{
	
	// Ensure there's no pipe connected
	if (m_hPipe != INVALID_HANDLE_VALUE)
	{
		DisconnectNamedPipe(m_hPipe);
		CloseHandle(m_hPipe);
		m_hPipe=INVALID_HANDLE_VALUE;
	}
	strcpy(m_name,"\\\\.\\pipe\\");

	
	if (!logger_name)
	{	// no name was give , create name based on the current address+time
		// (you can modify it to use PID , rand() ,...
		unsigned long now = GetTickCount();
		logger_name = m_name+ strlen(m_name);
		sprintf((char*)logger_name,"logger%d_%lu",(int)this,now);
	}
	else
	{	// just use the given name
		strcat(m_name,logger_name);
	}

	
	// Create the pipe
	m_hPipe = CreateNamedPipe( 
		  m_name,					// pipe name 
		  PIPE_ACCESS_OUTBOUND,		// read/write access, we're only writing...
		  PIPE_TYPE_MESSAGE |       // message type pipe 
		  PIPE_READMODE_BYTE|		// message-read mode 
		  PIPE_WAIT,                // blocking mode 
		  1,						// max. instances  
		  4096,						// output buffer size 
		  0,						// input buffer size (we don't read data, so 0 is fine)
		  1,						// client time-out 
		  NULL);                    // no security attribute 
	if (m_hPipe==INVALID_HANDLE_VALUE)
	{	// failure
		MessageBox(NULL,"CreateNamedPipe failed","ConsoleLogger failed",MB_OK);
		return -1;
	}

	// Extra console : create another process , it's role is to display the pipe's output
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	GetStartupInfo(&si);
	
	char cmdline[MAX_PATH];;
	if (!helper_executable)
		helper_executable=DEFAULT_HELPER_EXE;

	sprintf(cmdline,"%s %s",helper_executable,logger_name);
	BOOL bRet = CreateProcess(NULL,cmdline,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);
	if (!bRet)
	{	// on failure - try to get the path from the environment
		char *path = getenv("ConsoleLoggerHelper");
		if (path)
		{
			sprintf(cmdline,"%s %s",path,logger_name);
			bRet = CreateProcess(NULL,cmdline,NULL,NULL,FALSE,CREATE_NEW_CONSOLE,NULL,NULL,&si,&pi);
		}
		if (!bRet)
		{
			MessageBox(NULL,"Helper executable not found","ConsoleLogger failed",MB_OK);
			CloseHandle(m_hPipe);
			m_hPipe = INVALID_HANDLE_VALUE;
			return -1;
		}
	}
	
	
	BOOL bConnected = ConnectNamedPipe(m_hPipe, NULL) ? 
		 TRUE : (GetLastError() == ERROR_PIPE_CONNECTED); 
	if (!bConnected)
	{
		MessageBox(NULL,"ConnectNamedPipe failed","ConsoleLogger failed",MB_OK);
		
		CloseHandle(m_hPipe);
		m_hPipe = INVALID_HANDLE_VALUE;
		return -1;
	}
	
	DWORD cbWritten;

	//////////////////////////////////////////////////////////////////////////
	// In order to easily add new future-features , i've chosen to pass the "extra"
	// parameters just the HTTP protocol - via textual "headers" .
	// the last header should end with NULL
	//////////////////////////////////////////////////////////////////////////
	

	char buffer[128];
	// Send title
	if (!lpszWindowTitle)	lpszWindowTitle=m_name+9;
	sprintf(buffer,"TITLE: %s\r\n",lpszWindowTitle);
	WriteFile(m_hPipe,buffer,strlen(buffer),&cbWritten,NULL);
	if (cbWritten!=strlen(buffer))
	{
		MessageBox(NULL,"WriteFile failed(1)","ConsoleLogger failed",MB_OK);
		DisconnectNamedPipe(m_hPipe);
		CloseHandle(m_hPipe);
		m_hPipe=INVALID_HANDLE_VALUE;
		return -1;
	}

	
	if (buffer_size_x!=-1 && buffer_size_y!=-1)
	{	// Send buffer-size
		sprintf(buffer,"BUFFER-SIZE: %dx%d\r\n",buffer_size_x,buffer_size_y);
		WriteFile(m_hPipe,buffer,strlen(buffer),&cbWritten,NULL);
		if (cbWritten!=strlen(buffer))
		{
			MessageBox(NULL,"WriteFile failed(2)","ConsoleLogger failed",MB_OK);
			DisconnectNamedPipe(m_hPipe);
			CloseHandle(m_hPipe);
			m_hPipe=INVALID_HANDLE_VALUE;
			return -1;
		}
	}

	// Send more headers. you can override the AddHeaders() function to 
	// extend this class
	if (AddHeaders())
	{	
		DisconnectNamedPipe(m_hPipe);
		CloseHandle(m_hPipe);
		m_hPipe=INVALID_HANDLE_VALUE;
		return -1;
	}



	// send NULL as "end of header"
	buffer[0]=0;
	WriteFile(m_hPipe,buffer,1,&cbWritten,NULL);
	if (cbWritten!=1)
	{
		MessageBox(NULL,"WriteFile failed(3)","ConsoleLogger failed",MB_OK);
		DisconnectNamedPipe(m_hPipe);
		CloseHandle(m_hPipe);
		m_hPipe=INVALID_HANDLE_VALUE;
		return -1;
	}
	return 0;
}


// Close and disconnect
long CConsoleLogger::Close(void)
{
	if (m_hPipe==INVALID_HANDLE_VALUE || m_hPipe==NULL)
		return -1;
	else
		return DisconnectNamedPipe( m_hPipe );
}


//////////////////////////////////////////////////////////////////////////
// print: print string lpszText with size iSize
// if iSize==-1 (default) , we'll use strlen(lpszText)
// 
// this is the fastest way to print a simple (not formatted) string
//////////////////////////////////////////////////////////////////////////
inline int CConsoleLogger::print(const char *lpszText,int iSize/*=-1*/)
{
	if (m_hPipe==INVALID_HANDLE_VALUE)
		return -1;
	return _print(lpszText,(iSize==-1) ? strlen(lpszText) : iSize);
}

//////////////////////////////////////////////////////////////////////////
// printf: print a formatted string
//////////////////////////////////////////////////////////////////////////
int CConsoleLogger::printf(const char *format,...)
{
	if (m_hPipe==INVALID_HANDLE_VALUE)
		return -1;

	int ret;
	char tmp[1024];

	va_list argList;
	va_start(argList, format);
	#ifdef WIN32
	 		ret = _vsnprintf(tmp,sizeof(tmp)-1,format,argList);
	#else
	 		ret = vsnprintf(tmp,sizeof(tmp)-1,format,argList);
	#endif
	tmp[ret]=0;


	va_end(argList);


	return _print(tmp,ret);

}



//////////////////////////////////////////////////////////////////////////
// set the default (CRT) printf() to use this logger
//////////////////////////////////////////////////////////////////////////
int CConsoleLogger::SetAsDefaultOutput(void)
{
	int hConHandle = _open_osfhandle(/*lStdHandle*/ (long)m_hPipe, _O_TEXT);
	if (hConHandle==-1)
		return -2;
	FILE *fp = _fdopen( hConHandle, "w" );
	if (!fp)
		return -3;
	*stdout = *fp;
	return setvbuf( stdout, NULL, _IONBF, 0 );
}

///////////////////////////////////////////////////////////////////////////
// Reset the CRT printf() to it's default
//////////////////////////////////////////////////////////////////////////
int CConsoleLogger::ResetDefaultOutput(void)
{
	long lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
	if (lStdHandle ==  (long)INVALID_HANDLE_VALUE)
		return -1;
	int hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	if (hConHandle==-1)
		return -2;
	FILE *fp = _fdopen( hConHandle, "w" );
	if (!fp)
		return -3;
	*stdout = *fp;
	return setvbuf( stdout, NULL, _IONBF, 0 );
}


//////////////////////////////////////////////////////////////////////////
// _print: print helper
// we use the thread-safe funtion "SafeWriteFile()" to output the data
//////////////////////////////////////////////////////////////////////////
int CConsoleLogger::_print(const char *lpszText,int iSize)
{
	DWORD dwWritten=(DWORD)-1;
	
	return (!SafeWriteFile( m_hPipe,lpszText,iSize,&dwWritten,NULL)
		|| (int)dwWritten!=iSize) ? -1 : (int)dwWritten;
}




//////////////////////////////////////////////////////////////////////////
// Implementation of the derived class: CConsoleLoggerEx
//////////////////////////////////////////////////////////////////////////

// ctor: just set the default color
CConsoleLoggerEx::CConsoleLoggerEx()
{
	m_dwCurrentAttributes = COLOR_WHITE | COLOR_BACKGROUND_BLACK;
}


	
//////////////////////////////////////////////////////////////////////////
// override the _print.
// first output the "command" (which is COMMAND_PRINT) and the size,
// and than output the string itself	
//////////////////////////////////////////////////////////////////////////
int CConsoleLoggerEx::_print(const char *lpszText,int iSize)
{
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_PRINT) , and 3 bytes for size
	
	DWORD command_plus_size = (COMMAND_PRINT <<24)| iSize;
	EnterCriticalSection();
	if ( !WriteFile (m_hPipe, &command_plus_size,sizeof(DWORD),&dwWritten,NULL) 
		|| dwWritten != sizeof(DWORD))
	{
		LeaveCriticalSection();
		return -1;
	}
	
	int iRet = (!WriteFile( m_hPipe,lpszText,iSize,&dwWritten,NULL)
		|| (int)dwWritten!=iSize) ? -1 : (int)dwWritten;
	LeaveCriticalSection();
	return iRet;
}

	
//////////////////////////////////////////////////////////////////////////
// cls: clear screen  (just sends the COMMAND_CLEAR_SCREEN)
//////////////////////////////////////////////////////////////////////////
void CConsoleLoggerEx::cls(void)
{
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_PRINT) , and 3 bytes for size
	DWORD command = COMMAND_CLEAR_SCREEN<<24;
	SafeWriteFile (m_hPipe, &command,sizeof(DWORD),&dwWritten,NULL);
}	


//////////////////////////////////////////////////////////////////////////
// cls(DWORD) : clear screen with specific color
//////////////////////////////////////////////////////////////////////////
void CConsoleLoggerEx::cls(DWORD color)
{
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_PRINT) , and 3 bytes for size
	DWORD command = COMMAND_COLORED_CLEAR_SCREEN<<24;
	EnterCriticalSection();
	WriteFile (m_hPipe, &command,sizeof(DWORD),&dwWritten,NULL);
	WriteFile (m_hPipe, &color,sizeof(DWORD),&dwWritten,NULL);
	LeaveCriticalSection();
}	

//////////////////////////////////////////////////////////////////////////
// clear_eol() : clear till the end of current line
//////////////////////////////////////////////////////////////////////////
void CConsoleLoggerEx::clear_eol(void)
{
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_PRINT) , and 3 bytes for size
	DWORD command = COMMAND_CLEAR_EOL<<24;
	SafeWriteFile (m_hPipe, &command,sizeof(DWORD),&dwWritten,NULL);
}	

//////////////////////////////////////////////////////////////////////////
// clear_eol(DWORD) : clear till the end of current line with specific color
//////////////////////////////////////////////////////////////////////////
void CConsoleLoggerEx::clear_eol(DWORD color)
{
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_PRINT) , and 3 bytes for size
	DWORD command = COMMAND_COLORED_CLEAR_EOL<<24;
	EnterCriticalSection();
	WriteFile (m_hPipe, &command,sizeof(DWORD),&dwWritten,NULL);
	WriteFile (m_hPipe, &color,sizeof(DWORD),&dwWritten,NULL);
	LeaveCriticalSection();
}	


//////////////////////////////////////////////////////////////////////////
// gotoxy(x,y) : sets the cursor to x,y location
//////////////////////////////////////////////////////////////////////////
void CConsoleLoggerEx::gotoxy(int x,int y)
{
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_PRINT) , and 3 bytes for size
	DWORD command = COMMAND_GOTOXY<<24;
	EnterCriticalSection();
	WriteFile (m_hPipe, &command,sizeof(DWORD),&dwWritten,NULL);
	command = (x<<16)  | y;
	WriteFile (m_hPipe, &command,sizeof(DWORD),&dwWritten,NULL);
	LeaveCriticalSection();
}	


//////////////////////////////////////////////////////////////////////////
// cprintf(attr,str,...) : prints a formatted string with the "attributes" color
//////////////////////////////////////////////////////////////////////////
int CConsoleLoggerEx::cprintf(int attributes,const char *format,...)
{
	if (m_hPipe==INVALID_HANDLE_VALUE)
		return -1;

	int ret;
	char tmp[1024];

	va_list argList;
	va_start(argList, format);
	#ifdef WIN32
	 		ret = _vsnprintf(tmp,sizeof(tmp)-1,format,argList);
	#else
	 		ret = vsnprintf(tmp,sizeof(tmp)-1,format,argList);
	#endif
	tmp[ret]=0;


	va_end(argList);


	return _cprint(attributes,tmp,ret);

}


//////////////////////////////////////////////////////////////////////////
// cprintf(str,...) : prints a formatted string with current color
//////////////////////////////////////////////////////////////////////////
int CConsoleLoggerEx::cprintf(const char *format,...)
{
	if (m_hPipe==INVALID_HANDLE_VALUE)
		return -1;

	int ret;
	char tmp[1024];

	va_list argList;
	va_start(argList, format);
	#ifdef WIN32
	 		ret = _vsnprintf(tmp,sizeof(tmp)-1,format,argList);
	#else
	 		ret = vsnprintf(tmp,sizeof(tmp)-1,format,argList);
	#endif
	tmp[ret]=0;


	va_end(argList);


	return _cprint(m_dwCurrentAttributes,tmp,ret);

}


//////////////////////////////////////////////////////////////////////////
// the _cprintf() helper . do the actual output
//////////////////////////////////////////////////////////////////////////
int CConsoleLoggerEx::_cprint(int attributes,const char *lpszText,int iSize)
{
	
	DWORD dwWritten=(DWORD)-1;
	// we assume that in iSize < 2^24 , because we're using only 3 bytes of iSize 
	// 32BIT: send DWORD = 4bytes: one byte is the command (COMMAND_CPRINT) , and 3 bytes for size
	DWORD command_plus_size = (COMMAND_CPRINT <<24)| iSize;
	EnterCriticalSection();
	if ( !WriteFile (m_hPipe, &command_plus_size,sizeof(DWORD),&dwWritten,NULL) 
		|| dwWritten != sizeof(DWORD))
	{
		LeaveCriticalSection();
		return -1;
	}
	
	command_plus_size = attributes;	// reuse of the prev variable
	if ( !WriteFile (m_hPipe, &command_plus_size,sizeof(DWORD),&dwWritten,NULL) 
		|| dwWritten != sizeof(DWORD))
	{
		LeaveCriticalSection();
		return -1;
	}
	
	int iRet = (!WriteFile( m_hPipe,lpszText,iSize,&dwWritten,NULL)
		|| (int)dwWritten!=iSize) ? -1 : (int)dwWritten;
	LeaveCriticalSection();
	return iRet;
}

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 A Public Domain dedication


Written By
Architect Protegrity
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions