const int BUF_SIZE = 8192;

CRedirect::CRedirect
(
	LPCTSTR		szCommand,
	CEdit		*pEdit,
	LPCTSTR		szCurrentDirectory
)
{
	m_bStopped				= false;
	m_dwSleepMilliseconds	= 100;
	m_pEdit					= pEdit;
	m_szCommand				= szCommand;
	m_szCurrentDirectory	= szCurrentDirectory;
}

CRedirect::~CRedirect()
{
}

void CRedirect::Run()
{
	HANDLE					PipeReadHandle;
	HANDLE					PipeWriteHandle;
	PROCESS_INFORMATION		ProcessInfo;
	SECURITY_ATTRIBUTES		SecurityAttributes;
	STARTUPINFO				StartupInfo;
	BOOL					Success;

	//--------------------------------------------------------------------------
	//	Zero the structures.
	//--------------------------------------------------------------------------
	ZeroMemory( &StartupInfo,			sizeof( StartupInfo ));
	ZeroMemory( &ProcessInfo,			sizeof( ProcessInfo ));
	ZeroMemory( &SecurityAttributes,	sizeof( SecurityAttributes ));

	//--------------------------------------------------------------------------
	//	Create a pipe for the child's STDOUT.
	//--------------------------------------------------------------------------
	SecurityAttributes.nLength              = sizeof(SECURITY_ATTRIBUTES);
	SecurityAttributes.bInheritHandle       = TRUE;
	SecurityAttributes.lpSecurityDescriptor = NULL;

	Success = CreatePipe
	(
		&PipeReadHandle,		// address of variable for read handle
		&PipeWriteHandle,		// address of variable for write handle
		&SecurityAttributes,	// pointer to security attributes
		0						// number of bytes reserved for pipe (use default size)
	);

	if ( !Success )
	{
		ShowLastError(_T("Error creating pipe"));
		return;
	}	

	//--------------------------------------------------------------------------
	//	Set up members of STARTUPINFO structure.
	//--------------------------------------------------------------------------
	StartupInfo.cb           = sizeof(STARTUPINFO);
	StartupInfo.dwFlags      = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
	StartupInfo.wShowWindow  = SW_HIDE;//SW_SHOW;//
	StartupInfo.hStdOutput   = PipeWriteHandle;
	StartupInfo.hStdError    = PipeWriteHandle;

	//----------------------------------------------------------------------------
	//	Create the child process.
	//----------------------------------------------------------------------------
	Success = CreateProcess
	( 
		NULL,					// pointer to name of executable module
		LPTSTR(m_szCommand),	// command line 
		NULL,					// pointer to process security attributes 
		NULL,					// pointer to thread security attributes (use primary thread security attributes)
		TRUE,					// inherit handles
		0,						// creation flags
		NULL,					// pointer to new environment block (use parent's)
		m_szCurrentDirectory,	// pointer to current directory name
		&StartupInfo,			// pointer to STARTUPINFO
		&ProcessInfo			// pointer to PROCESS_INFORMATION
	);                 

	if ( !Success )
	{
		ShowLastError(_T("Error creating process"));
		return;
	}

	DWORD	BytesLeftThisMessage = 0;
	DWORD	NumBytesRead;
	TCHAR	PipeData[BUF_SIZE]; 
	DWORD	TotalBytesAvailable = 0;

	for ( ; ; )
	{ 
		NumBytesRead = 0;

		Success = PeekNamedPipe
		( 
			PipeReadHandle,				// handle to pipe to copy from 
			PipeData,					// pointer to data buffer 
			1,							// size, in bytes, of data buffer 
			&NumBytesRead,				// pointer to number of bytes read 
			&TotalBytesAvailable,		// pointer to total number of bytes available
			&BytesLeftThisMessage		// pointer to unread bytes in this message 
		);

		if ( !Success )
		{
			ShowLastError(_T("PeekNamedPipe fialed"));
			break;
		}

		if ( NumBytesRead )
		{
			Success = ReadFile
			(
				PipeReadHandle,		// handle to pipe to copy from 
				PipeData,			// address of buffer that receives data
				BUF_SIZE - 1,		// number of bytes to read
				&NumBytesRead,		// address of number of bytes read
				NULL				// address of structure for data for overlapped I/O
			);

			if ( !Success )
			{
				ShowLastError(_T("ReadFile fialed"));
				break;
			}

			//------------------------------------------------------------------
			//	Zero-terminate the data.
			//------------------------------------------------------------------
			PipeData[NumBytesRead] = '\0';

			//------------------------------------------------------------------
			//	Replace backspaces with spaces.
			//------------------------------------------------------------------
			for ( DWORD ii = 0; ii < NumBytesRead; ii++ )
			{
				if ( PipeData[ii] == _T('\b') )
				{
					PipeData[ii] = ' ';
				}
			}
			
			//------------------------------------------------------------------
			//	If we're running a batch file that contains a pause command, 
			//	assume it is the last output from the batch file and remove it.
			//------------------------------------------------------------------
			TCHAR  *ptr = _tcsstr(PipeData, _T("Press any key to continue . . ."));
			if ( ptr )
			{
				*ptr = '\0';
			}

            OutputDebugStringA((char*)PipeData);

			//------------------------------------------------------------------
			//	Append the output to the CEdit control.
			//------------------------------------------------------------------
			AppendText(PipeData);

			//------------------------------------------------------------------
			//	Peek and pump messages.
			//------------------------------------------------------------------
			PeekAndPump();
		}
		else
		{
			//------------------------------------------------------------------
			//	If the child process has completed, break out.
			//------------------------------------------------------------------
			if ( WaitForSingleObject(ProcessInfo.hProcess, 0) == WAIT_OBJECT_0 )	//lint !e1924 (warning about C-style cast)
			{
				break;
			}

			//------------------------------------------------------------------
			//	Peek and pump messages.
			//------------------------------------------------------------------
			PeekAndPump();

			//------------------------------------------------------------------
			//	If the user cancelled the operation, terminate the process.
			//------------------------------------------------------------------
			if ( m_bStopped )
			{
				Success = TerminateProcess
				(
					ProcessInfo.hProcess,
					0
				);

				if ( Success )
				{
					AppendText(_T("\r\nCancelled.\r\n\r\nProcess terminated successfully.\r\n"));
				}
				else
				{
					ShowLastError(_T("Error terminating process."));
				}

				break;
			}

			//------------------------------------------------------------------
			//	Sleep.
			//------------------------------------------------------------------
			Sleep(m_dwSleepMilliseconds);
		}
		
	}

	//--------------------------------------------------------------------------
	//	Close handles.
	//--------------------------------------------------------------------------
	Success = CloseHandle(ProcessInfo.hThread);
	if ( !Success )
	{
		ShowLastError(_T("Error closing thread handle."));
	}

	Success = CloseHandle(ProcessInfo.hProcess);
	if ( !Success )
	{
		ShowLastError(_T("Error closing process handle."));
	}

	Success = CloseHandle(PipeReadHandle);
	if ( !Success )
	{
		ShowLastError(_T("Error closing pipe read handle."));
	}

	Success = CloseHandle(PipeWriteHandle);
	if ( !Success )
	{
		ShowLastError(_T("Error closing pipe write handle."));
	}

}


void CRedirect::ShowLastError(LPCTSTR szText)
{
	LPVOID		lpMsgBuf;
	DWORD		Success;

	//--------------------------------------------------------------------------
	//	Get the system error message.
	//--------------------------------------------------------------------------
	Success = FormatMessage
	(
		FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
		NULL,
		GetLastError(),
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),	//lint !e1924 (warning about C-style cast)
		LPTSTR(&lpMsgBuf),
		0,
		NULL
	);

	CString	Msg;

	Msg = szText;
	Msg += _T("\r\n");
	if ( Success )
	{
		Msg += LPTSTR(lpMsgBuf);
	}
	else
	{
		Msg += _T("No status because FormatMessage failed.\r\n");
	}

	AppendText(Msg);

}

void CRedirect::PeekAndPump()
{
    MSG Msg;
    while (::PeekMessage(&Msg, NULL, 0, 0, PM_NOREMOVE)) 
    {
        (void)AfxGetApp()->PumpMessage(); //lint !e1924 (warning about C-style cast)
    }
}

void CRedirect::Stop()
{
	m_bStopped = true;
}

void CRedirect::AppendText(LPCTSTR Text)
{
	int Length = m_pEdit->GetWindowTextLength();

	m_pEdit->SetSel(Length, Length);
	m_pEdit->ReplaceSel(Text);
	m_pEdit->LineScroll( m_pEdit->GetLineCount() );
}

void CRedirect::SetSleepInterval(DWORD dwMilliseconds)
{
	m_dwSleepMilliseconds = dwMilliseconds;
}
