// Redirect.cpp : implementation file
//

#include "stdafx.h"
#include "StepData.h"
#include "TlHelp32.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define BUFFER_SIZE 256
#define INFO_BUFFER_SIZE 32767

// This class is build from the following MSDN article.
// Q190351 HOWTO: Spawn Console Processes with Redirected Standard Handles.

/////////////////////////////////////////////////////////////////////////////
// API Function

BOOL WINAPI IsWinNT()
{
  // get windows version
  DWORD WindowsVersion = GetVersion();
  DWORD WindowsMajorVersion = (DWORD)(LOBYTE(LOWORD(WindowsVersion)));
  DWORD WindowsMinorVersion = (DWORD)(HIBYTE(LOWORD(WindowsVersion)));

  // Running on WIN9x ?
  if (WindowsVersion >= 0x80000000) return FALSE;

  
  // Running on NT
  return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// CRedirect class

CRedirect::CRedirect()
{
	// Initialisation.
	m_hStdIn = NULL;
	m_hStdOut = NULL;
	m_hStdErr = NULL;
	m_hStdInWrite = NULL;
	m_hStdOutRead = NULL;
	m_hStdErrRead = NULL;
	m_hChildProcess = NULL;
	dwThreadId = 0;
	m_hStdOutThread = NULL;
	m_hStdErrThread = NULL;
	m_hProcessThread = NULL;
	m_hExitEvent = NULL;
	m_bRunThread = FALSE;
	m_dwExitCode = -1;

	m_ApplicationLevelInit = 0;
	m_ApplicationLevelIsBreak = 0;
	m_ApplicationLevelLogMessage = 0;

	m_convert_from_UTF8 = false;

}

CRedirect::~CRedirect()
{
	TerminateChildProcess();
}

BOOL CRedirect::StartChildProcessEx(LPCTSTR _CmdLine, LPCTSTR _Parameters)
{

	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return FALSE;
	m_ApplicationLevelInit();

	if(!StartChildProcess(_CmdLine, _Parameters))
	{
		m_ApplicationLevelLogMessage(APL_T("Запуск с перехватом вывода не удался. Запускаем утилиту в невидимом режиме."));
		// не получился моднючий запуск
		if(aplCreateProcess(_CmdLine, _Parameters,true)==-1)
		{
			CString buf, buf1;
			aplGetDescriptionSystemError(GetLastError(),buf1);
			buf.Format(APL_T("Ошибка! Невозможно запустить утилиту \"%s\". Описание ошибки: %s"), _CmdLine, LPCTSTR(buf1));
			//MY_THROW_EX(FLAG_ERROR_IN_THREAD, APL_T("Невозможно запустить утилиту \"")+CmdLine+ APL_T("\". Описание ошибки: ")+buf1);
			m_ApplicationLevelLogMessage(buf);
			return FALSE;
		}
	}
	return TRUE;
}
// Create standard handles, try to start child from command line.

BOOL CRedirect::StartChildProcess(LPCTSTR _CmdLine, LPCTSTR _Parameters, BOOL bShowChildWindow, bool wrap_in_bat)
{
	CaplStackLogger stack_logger(_T("CRedirect::StartChildProcess"));

	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return false;
	//m_ApplicationLevelInit();

	CString CmdLine = _CmdLine;
	CString Parameters = _Parameters;
	

	HANDLE hProcess = ::GetCurrentProcess();

	// Set up the security attributes struct.
	SECURITY_ATTRIBUTES sa;
	::ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
	sa.nLength= sizeof(SECURITY_ATTRIBUTES);
	sa.lpSecurityDescriptor = NULL;
	sa.bInheritHandle = TRUE;

	HANDLE hStdInWriteTmp, hStdOutReadTmp, hStdErrReadTmp;

	// Create the child stdin pipe.
	VERIFY(::CreatePipe(&m_hStdIn, &hStdInWriteTmp, &sa, 0));

	// Create the child stdout pipe.
	VERIFY(::CreatePipe(&hStdOutReadTmp, &m_hStdOut, &sa, 0));

	// Create the child stderr pipe.
	VERIFY(::CreatePipe(&hStdErrReadTmp, &m_hStdErr, &sa, 0));

	// Create new stdin write, stdout and stderr read handles.
	// Set the properties to FALSE. Otherwise, the child inherits the
	// properties and, as a result, non-closeable handles to the pipes
	// are created.
	
	VERIFY(::DuplicateHandle(hProcess, hStdInWriteTmp,
		hProcess, &m_hStdInWrite, 0, FALSE, DUPLICATE_SAME_ACCESS));

	VERIFY(::DuplicateHandle(hProcess, hStdOutReadTmp,
		hProcess, &m_hStdOutRead, 0, FALSE, DUPLICATE_SAME_ACCESS));

	VERIFY(::DuplicateHandle(hProcess, hStdErrReadTmp,
		hProcess, &m_hStdErrRead, 0, FALSE, DUPLICATE_SAME_ACCESS));

	// Close inheritable copies of the handles you do not want to be
	// inherited.

	VERIFY(::CloseHandle(hStdInWriteTmp));
	VERIFY(::CloseHandle(hStdOutReadTmp));
	VERIFY(::CloseHandle(hStdErrReadTmp));

	CString tmp_bat = _T("");

	// проверяем, что файл есть
	FILE* file;
	file=_fopen(CmdLine,_T("r"));
	if(file==0)
	{
		CString buf;
		buf.Format(APL_T("Ошибка! Файл \"%s\" не существует! Невозможно запустить утилиту."), LPCTSTR(CmdLine));
		//AfxMessageBox(buf);
		OnChildStdOutWrite(buf);
		return FALSE;
	}
	fclose(file);


	if(wrap_in_bat)
	{
		if(CmdLine.Right(1) != _T("\"") && CmdLine.Left(1) != _T("\""))
		{
			CmdLine.Insert(0, _T("\""));
			CmdLine += _T("\"");
		}

		CString char_buf;
		aplGetTempPath(char_buf);

		tmp_bat.Format(_T("%s~CRedirect_%i.bat"), LPCTSTR(char_buf), GetTickCount());
		CaplStringFile file;
		file.Open(tmp_bat, CFile::modeCreate|CFile::modeWrite|CFile::typeBinary, aplANSI); // явно указывыаем кодировку, чтобы юникодные проекты работали корректно
		file.WriteString(_T("@echo off\r\n") );
		file.WriteString(CmdLine + _T(" ") + Parameters);
		file.WriteString(_T("\r\n@echo on\r\npause\r\nEXIT errorlevel\r\n") );
		file.Close();

		// Start child process with redirected stdout, stdin & stderr
		m_hChildProcess = PrepAndLaunchRedirectedChild(tmp_bat,
			m_hStdOut, m_hStdIn, m_hStdErr, bShowChildWindow);
	}
	else
	{
		// Start child process with redirected stdout, stdin & stderr
		m_hChildProcess = PrepAndLaunchRedirectedChild(CmdLine + _T(" ") + Parameters,
			m_hStdOut, m_hStdIn, m_hStdErr, bShowChildWindow);

	}



	if (m_hChildProcess == NULL)
	{
		CString buf, buf1;
		aplGetDescriptionSystemError(GetLastError(),buf1);
		buf.Format(APL_T("Ошибка! Невозможно запустить утилиту \"%s\". Описание ошибки: %s"), LPCTSTR(CmdLine), LPCTSTR(buf1));
		OnChildStdOutWrite(buf);

		// close all handles and return FALSE
		VERIFY(::CloseHandle(m_hStdIn));
		m_hStdIn = NULL;
		VERIFY(::CloseHandle(m_hStdOut));
		m_hStdOut = NULL;
		VERIFY(::CloseHandle(m_hStdErr));
		m_hStdErr = NULL;

		if(wrap_in_bat)
			CFile::Remove(tmp_bat);

		return FALSE;
	}

	DWORD dwThreadID;
	m_bRunThread = TRUE;

	// Create Exit event
	m_hExitEvent = ::CreateEvent(NULL, TRUE, FALSE, NULL);
	VERIFY(m_hExitEvent != NULL);

	// Launch the thread that read the child stdout.
	m_hStdOutThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStdOutThread, //-V513
		(LPVOID)this, 0, &dwThreadID);
	VERIFY(m_hStdOutThread != NULL);

	// Launch the thread that read the child stderr.
	m_hStdErrThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticStdErrThread, //-V513
		(LPVOID)this, 0, &dwThreadID);
	VERIFY(m_hStdErrThread != NULL);

	// Virtual function to notify derived class that the child is started.
	OnChildStarted(CmdLine + _T(" ") + Parameters);

	/* Здесь я вместо запуска новой нити просто жду выхода из функции
	// Launch the thread that monitoring the child process.
	m_hProcessThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)staticProcessThread,
		(LPVOID)this, 0, &dwThreadID);
	VERIFY(m_hProcessThread != NULL);
	*/
	m_hProcessThread=0;
	ProcessThread();

	if(wrap_in_bat)
		CFile::Remove(tmp_bat);

	return TRUE;
}

// Check if the child process is running. 
// On NT/2000 the handle must have PROCESS_QUERY_INFORMATION access.

BOOL CRedirect::IsChildRunning() const
{
	DWORD dwExitCode;
    if (m_hChildProcess == NULL) return FALSE;
	::GetExitCodeProcess(m_hChildProcess, &dwExitCode);
	return (dwExitCode == STILL_ACTIVE) ? TRUE: FALSE;
}

void CRedirect::TerminateChildProcess()
{
	// Tell the threads to exit and wait for process thread to die.
	m_bRunThread = FALSE;
	::SetEvent(m_hExitEvent);
	/*

	// Check the process thread.
	if (m_hProcessThread != NULL)
	{
		VERIFY(::WaitForSingleObject(m_hProcessThread, 1000) != WAIT_TIMEOUT);
		m_hProcessThread = NULL;
	}
	*/
	// Close all child handles first.
	if (m_hStdIn != NULL)
		VERIFY(::CloseHandle(m_hStdIn));
	m_hStdIn = NULL;
	if (m_hStdOut != NULL)
		VERIFY(::CloseHandle(m_hStdOut));
	m_hStdOut = NULL;
	if (m_hStdErr != NULL)
		VERIFY(::CloseHandle(m_hStdErr));
	m_hStdErr = NULL;
    //Sleep(100);

	// Close all parent handles.
	if (m_hStdInWrite != NULL)
		VERIFY(::CloseHandle(m_hStdInWrite));
	m_hStdInWrite = NULL;
	if (m_hStdOutRead != NULL)
		VERIFY(::CloseHandle(m_hStdOutRead));
	m_hStdOutRead = NULL;
	if (m_hStdErrRead != NULL)
		VERIFY(::CloseHandle(m_hStdErrRead));
	m_hStdErrRead = NULL;
    //Sleep(100);

    // Stop the stdout read thread.
	if (m_hStdOutThread != NULL)
	{
		if (!::IsWinNT())
			::TerminateThread(m_hStdOutThread, 1);
		VERIFY(::WaitForSingleObject(m_hStdOutThread, 1000) != WAIT_TIMEOUT);
		m_hStdOutThread = NULL;
	}

    // Stop the stderr read thread.
	if (m_hStdErrThread != NULL)
	{
		if (!::IsWinNT())
			::TerminateThread(m_hStdErrThread, 1);
		VERIFY(::WaitForSingleObject(m_hStdErrThread, 1000) != WAIT_TIMEOUT);
		m_hStdErrThread = NULL;
	}
    //Sleep(100);

	// Stop the child process if not already stopped.
	// It's not the best solution, but it is a solution.
	// On Win98 it may crash the system if the child process is the COMMAND.COM.
	// The best way is to terminate the COMMAND.COM process with an "exit" command.

	if (IsChildRunning())
    {
		VERIFY(::TerminateProcess(m_hChildProcess, 1));
		VERIFY(::WaitForSingleObject(m_hChildProcess, 1000) != WAIT_TIMEOUT);
    }
	m_hChildProcess = NULL;

	// cleanup the exit event
	if (m_hExitEvent != NULL)
		VERIFY(::CloseHandle(m_hExitEvent));
	m_hExitEvent = NULL;
}

// Launch the process that you want to redirect.

HANDLE CRedirect::PrepAndLaunchRedirectedChild(LPCTSTR lpszCmdLine, HANDLE hStdOut, HANDLE hStdIn, HANDLE hStdErr, BOOL bShowChildWindow)
{
	HANDLE hProcess = ::GetCurrentProcess();

	PROCESS_INFORMATION pi;

	// Set up the start up info struct.
	STARTUPINFO si;
	::ZeroMemory(&si, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	si.dwFlags = bShowChildWindow ? STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW : STARTF_USESTDHANDLES;
	si.hStdOutput = hStdOut;
	si.hStdInput  = hStdIn;
	si.hStdError  = hStdErr;
	
	// Use this if you want to show the child.
	si.wShowWindow = bShowChildWindow ? SW_SHOW: SW_HIDE;
	// Note that dwFlags must include STARTF_USESHOWWINDOW if you want to
	// use the wShowWindow flags.

	DWORD dwCreationFlags = bShowChildWindow ? CREATE_NEW_CONSOLE:CREATE_NO_WINDOW;


	// Create the NULL security token for the process
	LPVOID lpSD = NULL;
	LPSECURITY_ATTRIBUTES lpSA = NULL;

	// On NT/2000 the handle must have PROCESS_QUERY_INFORMATION access.
	// This is made using an empty security descriptor. It is not the same
	// as using a NULL pointer for the security attribute!

	if (IsWinNT())
	{
		lpSD = ::GlobalAlloc(GPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
		VERIFY(::InitializeSecurityDescriptor(lpSD, SECURITY_DESCRIPTOR_REVISION));
		VERIFY(::SetSecurityDescriptorDacl(lpSD, -1, 0, 0));

		lpSA = (LPSECURITY_ATTRIBUTES)::GlobalAlloc(GPTR, sizeof(SECURITY_ATTRIBUTES));
		lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
		lpSA->lpSecurityDescriptor = lpSD;
		lpSA->bInheritHandle = TRUE;
	}
	
	TCHAR  infoBuf[INFO_BUFFER_SIZE];
	ExpandEnvironmentStrings(lpszCmdLine, infoBuf,INFO_BUFFER_SIZE ); 

	// Try to spawn the process.
	BOOL bResult = ::CreateProcess(NULL, infoBuf, lpSA, NULL, TRUE,
						dwCreationFlags, NULL, NULL, &si, &pi);

	// Cleanup memory allocation
	if (lpSA != NULL)
		::GlobalFree(lpSA);
	if (lpSD != NULL)
		::GlobalFree(lpSD);

	// Return if an error occurs.
	if (!bResult) return FALSE;

	// Close any unnecessary handles.
	VERIFY(::CloseHandle(pi.hThread));

	// Save global child process handle to cause threads to exit.
	dwThreadId=pi.dwThreadId;
	return pi.hProcess;
}

BOOL CRedirect::m_bRunThread = TRUE;

// Thread to read the child stdout.

BOOL CRedirect::IsRunned()
{
	return CRedirect::m_bRunThread;
}


int CRedirect::StdOutThread()
{
	DWORD nBytesRead;
	// для чтения из потоков std_out и std_err и передачи в поток std_in нужен однобайтный буфер
	char lpszBuffer[BUFFER_SIZE+1];

	while (m_bRunThread)
	{
		if (!::ReadFile(m_hStdOutRead, lpszBuffer, BUFFER_SIZE,
			&nBytesRead, NULL) || !nBytesRead)
		{
			if (::GetLastError() == ERROR_BROKEN_PIPE)
				break;			// pipe done - normal exit path.
			else
			{
				//ASSERT(FALSE);	// Something bad happened.
				ProcessOutPortions(lpszBuffer, nBytesRead);
				break;
			}
		}
		if (nBytesRead)
		{
			// Virtual function to notify derived class that
			// characters are writted to stdout.
			lpszBuffer[nBytesRead] = '\0';
			ProcessOutPortions(lpszBuffer, nBytesRead);
		}
	}
	ProcessOutPortions(0, 0);
	return 0;
}

DWORD tick = 0;

void CRedirect::ProcessOutPortions(char* lpszBuffer, DWORD nBytesRead)
{
	int pos_last_line = nBytesRead;
	bool ignore_remain = false;
	if(lpszBuffer != 0 && nBytesRead != 0)
	{
		CStringA strBufA = lpszBuffer;
		pos_last_line = strBufA.Find("pause"); // даже если вывод в UTF8 - английские буквы совпадают с ANSI 
		if( pos_last_line!=-1)
		{
			// скорей всего, приложение закончилось и батник стоит на pause. Пошлем ему перевод строки, чтобы он закончился
			WriteChildStdIn(_T("\r\n"));
			// остаток, скорее всего текст "нажмите на любую клавишу" из батника. Его игнорируем.
			ignore_remain = true;
		}
		else
		{
			// если не было кодового слова pause, ищем последний перенос строки и конвертируем до него
			// чтобы на конвертацию не ушел разрыв двухбайтной буквы
			// ну или если не будет конвертации - чтобы мой лог не разорвал строчку на две части
			pos_last_line = strBufA.ReverseFind('\n'); // даже если вывод в UTF8 - спецсимволы совпадают с ANSI 
		}
		// если переносов не было - конвертируем всю строку.
		if(pos_last_line == -1) pos_last_line = nBytesRead;
		m_dbuf_portions.Add(lpszBuffer, pos_last_line);
	}

	// добавим нолик в конец обрабатываемого буфера
	char zero_char = 0;
	m_dbuf_portions.Add(&zero_char, 1);

	CString converted_mess = _T("");

	if(m_convert_from_UTF8)
	{
		BYTE *buf2 = 0;
		UINT encoding = CP_UTF8;

		int w_len= ::MultiByteToWideChar(encoding, 0, (LPCSTR)m_dbuf_portions.GetBuffer(), -1, NULL, 0);
		if(w_len>0) 
		{
			int buflen=2*w_len + 2; // символы в байты + 2 на всякий случай
			buf2 = new BYTE[buflen];
			int w_len2= ::MultiByteToWideChar(encoding, 0,  (LPCSTR)m_dbuf_portions.GetBuffer(), -1, (LPWSTR)buf2, buflen);
			buf2[buflen-1] = 0;
			buf2[buflen-2] = 0;

			if(w_len2<=0)
			{
				converted_mess = (LPSTR)m_dbuf_portions.GetBuffer();
			}
			else
			{
				converted_mess += (const wchar_t*)buf2;
			}
		}
		if(0!=buf2) delete buf2;
	}
	else
	{
		converted_mess = (LPSTR)m_dbuf_portions.GetBuffer();
	}

	OnChildStdOutWrite((LPCTSTR)converted_mess);


	m_dbuf_portions.Clear();
	// Если сконвертировали и вывели не весь пришедший буфер - остаток добавляем в m_dbuf_portions. Выведем его в следующий раз,
	// кроме текста после pause
	if(pos_last_line != nBytesRead && !ignore_remain)
	{
		m_dbuf_portions.Add(lpszBuffer+pos_last_line, nBytesRead-pos_last_line);
	}
}

// Thread to read the child stderr.

int CRedirect::StdErrThread()
{
	DWORD nBytesRead;
	// для чтения из потоков std_out и std_err и передачи в поток std_in нужен однобайтный буфер
	char lpszBuffer[BUFFER_SIZE+1];

	while (m_bRunThread)
	{
		if (!::ReadFile(m_hStdErrRead, lpszBuffer, BUFFER_SIZE,
			&nBytesRead, NULL) || !nBytesRead)
		{
			if (::GetLastError() == ERROR_BROKEN_PIPE)
				break;			// pipe done - normal exit path.
			else
			{
				//ASSERT(FALSE);	// Something bad happened.
				ProcessOutPortions(lpszBuffer, nBytesRead);
				break;
			}
		}
		if (nBytesRead)
		{
			// Virtual function to notify derived class that
			// characters are writted to stderr.
			lpszBuffer[nBytesRead] = '\0';
// #ifdef _UNICODE
// 			CaplStringAdapter str_ad(lpszBuffer);
// 			OnChildStdErrWrite((LPCTSTR)str_ad);
// #else // #ifdef _UNICODE
// 			OnChildStdErrWrite(lpszBuffer);
// #endif // #ifdef _UNICODE
			ProcessOutPortions(lpszBuffer, nBytesRead);

		}
	}
	ProcessOutPortions(0, 0);
	return 0;
}


void TerminateProcessTree( __in HANDLE hProcess, __in UINT uExitCode )
{
	// Retrieve all processes on the system
	if(hProcess == 0) return;

	DWORD pid = GetProcessId(hProcess);
	HANDLE pHandle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	PROCESSENTRY32 ProcessEntry;
	ProcessEntry.dwSize = sizeof(ProcessEntry);
	BOOL Loop = Process32First(pHandle, &ProcessEntry);

	while (Loop)
	{
		if( ProcessEntry.th32ParentProcessID == pid)
		{
			HANDLE pHandleChiled = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessEntry.th32ProcessID);
			TerminateProcessTree(pHandleChiled, uExitCode);
		}

		Loop = Process32Next(pHandle, &ProcessEntry);
	}

	// Finally, termine the process itself:
	TerminateProcess(hProcess, uExitCode);
}

// Thread to monitoring the child process.

int CRedirect::ProcessThread()
{
	HANDLE hWaitHandle[2];
	hWaitHandle[0] = m_hExitEvent;
	hWaitHandle[1] = m_hChildProcess;

	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return 1;

	while (m_bRunThread)
	{
		switch (::WaitForMultipleObjects(2, hWaitHandle, FALSE, 1))
		{
			case WAIT_OBJECT_0 + 0:	// exit on event
				ASSERT(m_bRunThread == FALSE);
				break;

			case WAIT_OBJECT_0 + 1:	// child process exit
				ASSERT(m_bRunThread == TRUE);
				m_bRunThread = FALSE;
				GetExitCodeProcess(m_hChildProcess, &m_dwExitCode);
				break;
		}
		if(m_ApplicationLevelIsBreak())
		{
			m_bRunThread = FALSE;
			TerminateProcessTree(m_hChildProcess,0);
		}
	}
	TerminateChildProcess();

	// Virtual function to notify derived class that
	// child process is terminated.
	// Application must call TerminateChildProcess()
	// but not direcly from this thread!
	OnChildTerminate();
	return 0;
}

// Function that write to the child stdin.

void CRedirect::WriteChildStdIn(LPCTSTR lpszInput)
{
	DWORD nBytesWrote;
	// для чтения из потоков std_out и std_err и передачи в поток std_in нужен однобайтный буфер

	if (m_hStdInWrite != 0)
	{
		DWORD Length = _strlen(lpszInput);
		const char* buf = 0;
#ifdef _UNICODE
		CaplStringAdapter str_ad(lpszInput);
		buf = (const char*) str_ad;
#else // #ifdef _UNICODE
		buf = lpszInput;
#endif // #ifdef _UNICODE
		if (!::WriteFile(m_hStdInWrite, buf, Length, &nBytesWrote, NULL))
		{
			if (::GetLastError() == ERROR_NO_DATA)
				;				// Pipe was closed (do nothing).
			else
				ASSERT(FALSE);	// Something bad happened.
		}
	}
}

void CRedirect::OnChildStarted(LPCTSTR lpszCmdLine) 
{
	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return;

	CString str = _T("\r\n\r\n") + CString( APL_T("Запустили утилиту: "))+lpszCmdLine+"\n\n";
	m_ApplicationLevelLogMessage(str);
};


void CRedirect::OnChildStdOutWrite(LPCTSTR lpszOutput)// char* тут специально !!
{
	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return;

	m_ApplicationLevelLogMessage(lpszOutput);
};

void CRedirect::OnChildStdErrWrite(LPCTSTR lpszOutput) //-V524
{
	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return;
	
	m_ApplicationLevelLogMessage(lpszOutput);
};

void CRedirect::OnChildTerminate() 
{
	if(m_ApplicationLevelInit == 0 || m_ApplicationLevelIsBreak == 0 || m_ApplicationLevelLogMessage == 0 ) return;
	if(m_ApplicationLevelIsBreak())
	{
		m_ApplicationLevelLogMessage( APL_T("Работа утилиты прервана."));
		m_dwExitCode = 0;
	}
	else if(m_dwExitCode ==  9009)
		m_ApplicationLevelLogMessage( APL_T("Ошибка при запуске! Возможно, указан неверный пусть к исполняемому файлу."));
	else if(m_dwExitCode == 0xc0000135 || m_dwExitCode == -1 )
		m_ApplicationLevelLogMessage( APL_T("Ошибка при запуске! Возможно, отсутствуют необходимые для запуска dll или утилита обнаружила несоответствие параметров клиента и БД."));
	else
		m_ApplicationLevelLogMessage( APL_T("Утилита завершилась."));
};


bool CRedirect::SetApplicationLevelFunctions(bool (__cdecl *_ApplicationLevelInit)(),
											 bool (__cdecl *_ApplicationLevelIsBreak)(),
											 bool (__cdecl *_ApplicationLevelLogMessageDlg)(LPCTSTR message))
{
	if(_ApplicationLevelInit == 0 || _ApplicationLevelIsBreak == 0 || _ApplicationLevelLogMessageDlg == 0 ) return false;

	m_ApplicationLevelInit = _ApplicationLevelInit;
	m_ApplicationLevelIsBreak = _ApplicationLevelIsBreak;
	m_ApplicationLevelLogMessage = _ApplicationLevelLogMessageDlg;

	return true;
}
