#include "stdafx.h"
#include "GlobalData.h"
#include "Process.h"
#include "util.h"

#define C_szUserBreakString "\tUser break.\r\n"
#define C_lTimeIntervalForErrorChecking 2000

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// Launch a new process using CreateProcess and return immediatly
// _szExeName contains the name of the executable with FULL PATH.
// Note: _szCommandLine can be NULL.
// return TRUE if process was created normally or FALSE if not.
// Also fill _p_Info with process informations (handle...)
// Display a dialog box to the user in case of error on CreateProcess.
BOOL fn_bSpawnProcess( char *_szExeName, char *_szCommandLine, LPPROCESS_INFORMATION _p_Info )
{
	STARTUPINFO  stWinInfo;
	char         szDirectory[512];
	CString      csMessage;
	int          iPathLength;

	// Spawn process
	GetStartupInfo( &stWinInfo );

	// Get path of executable
	iPathLength = strrchr( _szExeName, '\\' ) - _szExeName;
	strncpy( szDirectory, _szExeName, iPathLength );
	szDirectory[iPathLength] = 0;

	// Create process.
	if( ! CreateProcess( _szExeName, _szCommandLine, NULL, NULL, FALSE, 0, NULL, szDirectory, &stWinInfo, _p_Info ) )
	{
		csMessage = _T("Can't execute '");
		csMessage += _szExeName;
		csMessage += _T("' !\nMake sure path is correct and executable does exist.");
		AfxMessageBox( csMessage, MB_OK | MB_ICONSTOP );

		ZeroMemory( _p_Info, sizeof(_p_Info) );
		return FALSE;
	}

	return TRUE;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


void fn_vCloseProcessHandles( LPPROCESS_INFORMATION _p_stProcess )
{
	if( _p_stProcess->hProcess )
		CloseHandle( _p_stProcess->hProcess );
	if( _p_stProcess->hThread )
		CloseHandle( _p_stProcess->hThread );
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

tdeStatus fn_eSpawnAndWaitForProcess( char *_szExeName, char *_szCommandLine, HANDLE _hEventStop /*= INVALID_HANDLE_VALUE*/ )
{
	HANDLE              a_hObjectsToWaitFor[2];
	int                 iNbObjectsToWaitFor;
	DWORD               dwWaitReturn, dwExitCode;
	char                szMessage[1024];
	PROCESS_INFORMATION stProcess;
	char                szShortExeName[128];

	fn_vGetShortExeName( szShortExeName, _szExeName );

	// spawn process
	if( ! fn_bSpawnProcess( _szExeName, _szCommandLine, &stProcess ) )
	{
		sprintf( szMessage, "\tError launching %s\r\n", _szExeName );
		fn_vOutputLine( szMessage );
		fn_vCloseProcessHandles( &stProcess );
		return STATUS_C_Error;
	}

	// Prepare to wait for end of process
	iNbObjectsToWaitFor = 0;
	a_hObjectsToWaitFor[iNbObjectsToWaitFor++] = stProcess.hProcess;

	// Test if we must wait for abort event too.
	if( _hEventStop != INVALID_HANDLE_VALUE )
		a_hObjectsToWaitFor[iNbObjectsToWaitFor++] = _hEventStop;

	do
	{
		// Wait for process or abort
		dwWaitReturn = WaitForMultipleObjects( iNbObjectsToWaitFor, a_hObjectsToWaitFor, FALSE, C_lTimeIntervalForErrorChecking );

		// Test if user wants to stop (=abort)
		if( iNbObjectsToWaitFor > 1 && (dwWaitReturn == WAIT_OBJECT_0+1 || dwWaitReturn == WAIT_ABANDONED_0+1) )
		{
			// Ask for confirmation
			sprintf( szMessage, "Are you sure you want to terminate '%s' ?", _szExeName );
			if( AfxMessageBox( szMessage, MB_YESNO | MB_ICONQUESTION ) == IDYES )
			{
				// Abort is confirmed, we kill the process
				PostThreadMessage( stProcess.dwThreadId, WM_QUIT, 0, NULL );
				dwWaitReturn = WaitForSingleObject( stProcess.hProcess, 3500 );
				if( dwWaitReturn == WAIT_TIMEOUT  )
					TerminateProcess( stProcess.hProcess, 199 );

				// We exit if process was killed
				fn_vCloseProcessHandles( &stProcess );
				fn_vOutputLine( C_szUserBreakString );

				fn_bCloseSytemErrorDialogBox( szShortExeName, TRUE );
				return STATUS_C_UserBreak;
			}
			else
				g_stTheGlobalData.p_oMainDlg -> m_fn_vCancelStopWorkInProgress();

			// User does not want to stop anymore, we go for another wait.
			ResetEvent( _hEventStop );
		}
		else if( dwWaitReturn == WAIT_OBJECT_0 || dwWaitReturn == WAIT_ABANDONED_0 )
		{
			GetExitCodeProcess( stProcess.hProcess, &dwExitCode );
			if( dwExitCode != 0 )
			{
				sprintf( szMessage, "\t'%s' has exited with error code %i\r\n", _szExeName, dwExitCode );
				fn_vOutputLine( szMessage );
				fn_vCloseProcessHandles( &stProcess );
				return STATUS_C_Error;
			}
			// Process has exited normally. We exit the wait loop.
			break;
		}
		else if( dwWaitReturn == WAIT_TIMEOUT )
		{
			// Here, the time has come to check if an error occured.
			// We try to find and close any system error dialog box related with the exe
			if( fn_bCloseSytemErrorDialogBox( szShortExeName ) )
			{
				// if we found one of these boxes, we kill the process and exit.
				TerminateProcess( stProcess.hProcess, 199 );
				fn_vCloseProcessHandles( &stProcess );
				sprintf( szMessage, "\tError detected with '%s'\r\n", _szExeName );
				fn_vOutputLine( szMessage );
				return STATUS_C_Error;
			}

			// We found nothing... We go for another wait.
		}
		else
		{
			// Error.
			fn_vOutputLine( "\r\n\tInternal error: Error on WaitForMultipleObjects !!!\r\n" );
			fn_vCloseProcessHandles( &stProcess );
			return STATUS_C_Error;
		}

	} while( 1 );

	fn_vCloseProcessHandles( &stProcess );

	// we deal we an enventual dialog box (saying "access violation" or whatever)
	if( fn_bCloseSytemErrorDialogBox( szShortExeName ) )
	{
		// If there was an error dialog box, we return an error.
		sprintf( szMessage, "\tError detected with '%s'\r\n", _szExeName );
		fn_vOutputLine( szMessage );
		return STATUS_C_Error;
	}

	return STATUS_C_OK;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

tdeStatus fn_eWaitForMultipleProcesses( int _iNbProcesses, LPPROCESS_INFORMATION _pa_stInfo, LPCTSTR _szExeNames[], int *_p_iProcessesEnded )
{
	HANDLE              a_hObjectsToWaitFor[64];
	int                 i;
	DWORD               dwWaitReturn, dwExitCode;
	char                szMessage[1024];


	// Prepare to wait for end of process
	for( i=0; i<_iNbProcesses; i ++ )
	{
		a_hObjectsToWaitFor[i] = _pa_stInfo[i].hProcess;
	}
	// we must also wait for abort event.
	a_hObjectsToWaitFor[_iNbProcesses] = g_stTheGlobalData.hEventStop;

	// Wait for the end of one process or abort
	dwWaitReturn = WaitForMultipleObjects( _iNbProcesses+1, a_hObjectsToWaitFor, FALSE, INFINITE );

	// Test if user wants to stop (=abort)
	if( dwWaitReturn == WAIT_OBJECT_0+_iNbProcesses || dwWaitReturn == WAIT_ABANDONED_0+_iNbProcesses )
	{
		// Abort is confirmed, we kill the process
		for( i=0; i<_iNbProcesses; i ++ )
		{
			PostThreadMessage( _pa_stInfo[i].dwThreadId, WM_QUIT, 0, NULL );
		}
		dwWaitReturn = WaitForMultipleObjects( _iNbProcesses, a_hObjectsToWaitFor, TRUE, 3500 );
		if( dwWaitReturn == WAIT_TIMEOUT  )
		{
			for( i=0; i<_iNbProcesses; i ++ )
				TerminateProcess( _pa_stInfo[i].hProcess, 199 );
		}

		// We exit if processes were killed
		for( i=0; i<_iNbProcesses; i ++ )
			fn_vCloseProcessHandles( &_pa_stInfo[i] );
		fn_vOutputLine( C_szUserBreakString );
		return STATUS_C_UserBreak;
	}
	else
	{
		for( i=0; i<_iNbProcesses; i ++ )
		{
			if( dwWaitReturn == WAIT_OBJECT_0+i )
			{
				// Mark witch process has exited.
				*_p_iProcessesEnded = i;
				GetExitCodeProcess( _pa_stInfo[i].hProcess, &dwExitCode );
				fn_vCloseProcessHandles( &_pa_stInfo[i] );
				if( dwExitCode != 0 )
				{
					sprintf( szMessage, "\t'%s' has exited with error code %i\r\n", _szExeNames[i], dwExitCode );
					fn_vOutputLine( szMessage );
					return STATUS_C_Error;
				}
				break;
			}
		}
		if( i == _iNbProcesses )
		{
			// Error.
			fn_vOutputLine( "\r\n\tInternal error: Error on WaitForMultipleObjects !!!\r\n" );
			for( i=0; i<_iNbProcesses; i ++ )
				fn_vCloseProcessHandles( &_pa_stInfo[i] );
			return STATUS_C_Error;
		}
	}

	return STATUS_C_OK;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

tdeStatus fn_eSpawnAndWaitForProcessWithTimeout( char *_szExeName, char *_szCommandLine, DWORD _dwMilliseconds /*=INFINITE*/ )
{
	HANDLE              a_hObjectsToWaitFor[2];
	DWORD               dwWaitReturn, dwExitCode;
	char                szMessage[1024];
	PROCESS_INFORMATION stProcess;
	DWORD               dwStartTime = GetTickCount();
	DWORD               dwElapsedTime = 0;
	char                szShortExeName[128];

	fn_vGetShortExeName( szShortExeName, _szExeName );

	// spawn process
	if( ! fn_bSpawnProcess( _szExeName, _szCommandLine, &stProcess ) )
	{
		sprintf( szMessage, "\tError launching %s\r\n", _szExeName );
		fn_vOutputLine( szMessage );
		fn_vCloseProcessHandles( &stProcess );
		return STATUS_C_Error;
	}

	// Prepare to wait for end of process
	a_hObjectsToWaitFor[0] = stProcess.hProcess;
	a_hObjectsToWaitFor[1] = g_stTheGlobalData.hEventStop;

	do
	{
		// Wait for process, abort or timeout
		// We wait for 1500 millisecond, then we check for a system error dialog box.
		dwWaitReturn = WaitForMultipleObjects( 2, a_hObjectsToWaitFor, FALSE, C_lTimeIntervalForErrorChecking );


		dwElapsedTime = GetTickCount() - dwStartTime;
		// If total time is greater than user timeout, we kill the process
		if( dwElapsedTime >= _dwMilliseconds )
		{
			fn_vOutputLine( "\t*** Timeout! Killing process...***\r\n" );
			PostThreadMessage( stProcess.dwThreadId, WM_QUIT, 0, NULL );
			dwWaitReturn = WaitForSingleObject( stProcess.hProcess, 1500 );
			if( dwWaitReturn == WAIT_TIMEOUT  )
				TerminateProcess( stProcess.hProcess, 199 );

			fn_vCloseProcessHandles( &stProcess );

			// Ok, now we deal we an enventual dialog box (saying "acces violation" or whatever)
			fn_bCloseSytemErrorDialogBox( szShortExeName, TRUE );

			// We exit
			return STATUS_C_Error;
		}

		// Test if user wants to stop (=abort)
		if( dwWaitReturn == WAIT_OBJECT_0+1 || dwWaitReturn == WAIT_ABANDONED_0+1 )
		{
			// Ask for confirmation
			sprintf( szMessage, "Are you sure you want to terminate '%s' ?", _szExeName );
			if( AfxMessageBox( szMessage, MB_YESNO | MB_ICONQUESTION ) == IDYES )
			{
				// Abort is confirmed, we kill the process
				PostThreadMessage( stProcess.dwThreadId, WM_QUIT, 0, NULL );
				dwWaitReturn = WaitForSingleObject( stProcess.hProcess, 3500 );
				if( dwWaitReturn == WAIT_TIMEOUT  )
					TerminateProcess( stProcess.hProcess, 199 );

				// We exit if process was killed
				fn_vCloseProcessHandles( &stProcess );
				fn_vOutputLine( C_szUserBreakString );
				// we deal we an enventual dialog box (saying "acces violation" or whatever)
				fn_bCloseSytemErrorDialogBox( szShortExeName, TRUE );
				return STATUS_C_UserBreak;
			}
			else
				g_stTheGlobalData.p_oMainDlg -> m_fn_vCancelStopWorkInProgress();

			// User does not want to stop anymore, we go for another wait.
			ResetEvent( g_stTheGlobalData.hEventStop );
		}
		else if( dwWaitReturn == WAIT_OBJECT_0 || dwWaitReturn == WAIT_ABANDONED_0 )
		{
			GetExitCodeProcess( stProcess.hProcess, &dwExitCode );
			if( dwExitCode != 0 )
			{
				sprintf( szMessage, "\t'%s' has exited with error code %i\r\n", _szExeName, dwExitCode );
				fn_vOutputLine( szMessage );
				fn_vCloseProcessHandles( &stProcess );
				// we deal we an enventual dialog box (saying "acces violation" or whatever)
				fn_bCloseSytemErrorDialogBox( szShortExeName );
				return STATUS_C_Error;
			}
			// Process has exited normally. We exit the wait loop.
			break;
		}
		else if( dwWaitReturn == WAIT_TIMEOUT )
		{
			// Here, the time has come to check if an error occured.
			// We try to find and close any system error dialog box related with the exe
			if( fn_bCloseSytemErrorDialogBox( szShortExeName ) )
			{
				// if we found one of these boxes, we kill the process and exit.
				TerminateProcess( stProcess.hProcess, 199 );
				fn_vCloseProcessHandles( &stProcess );
				sprintf( szMessage, "\tError detected with '%s'\r\n", _szExeName );
				fn_vOutputLine( szMessage );
				return STATUS_C_Error;
			}

			// We found nothing... We go for another wait.
		}
		else
		{
			// Error.
			fn_vOutputLine( "\r\n\tInternal error: Error on WaitForMultipleObjects !!!\r\n" );
			fn_vCloseProcessHandles( &stProcess );
			return STATUS_C_Error;
		}

	} while( 1 );

	fn_vCloseProcessHandles( &stProcess );

	// we deal we an enventual dialog box (saying "access violation" or whatever)
	if( fn_bCloseSytemErrorDialogBox( szShortExeName ) )
	{
		// If there was an error dialog box, we return an error.
		sprintf( szMessage, "\tError detected with '%s'\r\n", _szExeName );
		fn_vOutputLine( szMessage );
		return STATUS_C_Error;
	}

	return STATUS_C_OK;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


tdeStatus fn_eSpawnAndWaitForProcessWithInputEvents( char *_szExeName, char *_szCommandLine )
{
	HANDLE               a_hObjectsToWaitFor[32];
	DWORD                dwWaitReturn, dwExitCode;
	char                 szMessage[1024];
	PROCESS_INFORMATION  stProcess;
	MSG                  stMsg;
	char                 szShortExeName[128];

	fn_vGetShortExeName( szShortExeName, _szExeName );

	// spawn process
	if( ! fn_bSpawnProcess( _szExeName, _szCommandLine, &stProcess ) )
	{
		sprintf( szMessage, "\tError launching %s\r\n", _szExeName );
		fn_vOutputLine( szMessage );
		fn_vCloseProcessHandles( &stProcess );
		return STATUS_C_Error;
	}

	// Prepare to wait for end of process
	a_hObjectsToWaitFor[0] = stProcess.hProcess;
	a_hObjectsToWaitFor[1] = g_stTheGlobalData.hEventStop;

	do
	{
		// Wait for process or abort
		dwWaitReturn = MsgWaitForMultipleObjects( 2, a_hObjectsToWaitFor, FALSE, C_lTimeIntervalForErrorChecking, QS_ALLINPUT );

		// Test if user wants to stop (=abort)
		if( dwWaitReturn == WAIT_OBJECT_0+1 || dwWaitReturn == WAIT_ABANDONED_0+1 )
		{
			// Ask for confirmation
			sprintf( szMessage, "Are you sure you want to terminate '%s' ?", _szExeName );
			if( AfxMessageBox( szMessage, MB_YESNO | MB_ICONQUESTION ) == IDYES )
			{
				// Abort is confirmed, we kill the process
				PostThreadMessage( stProcess.dwThreadId, WM_QUIT, 0, NULL );
				dwWaitReturn = WaitForSingleObject( stProcess.hProcess, 3500 );
				if( dwWaitReturn == WAIT_TIMEOUT  )
					TerminateProcess( stProcess.hProcess, 199 );

				// We exit if process was killed
				fn_vCloseProcessHandles( &stProcess );
				fn_bCloseSytemErrorDialogBox( szShortExeName, TRUE );
				fn_vOutputLine( C_szUserBreakString );
				return STATUS_C_UserBreak;
			}
			else
				g_stTheGlobalData.p_oMainDlg -> m_fn_vCancelStopWorkInProgress();

			// User does not want to stop anymore, we go for another wait.
			ResetEvent( g_stTheGlobalData.hEventStop );
		}
		else if( dwWaitReturn == WAIT_OBJECT_0 || dwWaitReturn == WAIT_ABANDONED_0 )
		{
			GetExitCodeProcess( stProcess.hProcess, &dwExitCode );
			if( dwExitCode != 0 )
			{
				sprintf( szMessage, "\t'%s' has exited with error code %i\r\n", _szExeName, dwExitCode );
				fn_vOutputLine( szMessage );
				fn_vCloseProcessHandles( &stProcess );
				return STATUS_C_Error;
			}
			// Process has exited normally. We exit the wait loop.
			break;
		}
		else if( dwWaitReturn == WAIT_OBJECT_0 + 2 )
		{
			// Else, a user event has arrived: treat it.
			while( PeekMessage( &stMsg, g_stTheGlobalData.p_oMainDlg -> m_hWnd, 0, 0, PM_REMOVE ) )
				if( ! g_stTheGlobalData.p_oMainDlg -> PreTranslateMessage( &stMsg ) )
					DispatchMessage( &stMsg );
		}
		else if( dwWaitReturn == WAIT_TIMEOUT )
		{
			// Here, the time has come to check if an error occured.
			// We try to find and close any system error dialog box related with the exe
			if( fn_bCloseSytemErrorDialogBox( szShortExeName ) )
			{
				// if we found one of these boxes, we kill the process and exit.
				TerminateProcess( stProcess.hProcess, 199 );
				fn_vCloseProcessHandles( &stProcess );
				sprintf( szMessage, "\tError detected with '%s'\r\n", _szExeName );
				fn_vOutputLine( szMessage );
				return STATUS_C_Error;
			}

			// We found nothing... We go for another wait.
		}
		else
		{
			// Error.
			fn_vOutputLine( "\r\n\tInternal error: Error on MsgWaitForMultipleObjects !!!\r\n" );
			fn_vCloseProcessHandles( &stProcess );
			return STATUS_C_Error;
		}

	} while( 1 );

	fn_vCloseProcessHandles( &stProcess );

	// we deal we an enventual dialog box (saying "access violation" or whatever)
	if( fn_bCloseSytemErrorDialogBox( szShortExeName ) )
	{
		// If there was an error dialog box, we return an error.
		sprintf( szMessage, "\tError detected with '%s'\r\n", _szExeName );
		fn_vOutputLine( szMessage );
		return STATUS_C_Error;
	}

	return STATUS_C_OK;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 


tdeStatus fn_eSpawnThreadAndWaitWithInputEvents( AFX_THREADPROC _p_fn_uiThread, LPVOID _p_vParamForThread )
{
	HANDLE       a_hEvents[64];
	DWORD        dwWaitReturn;
	MSG          stMessage;
	DWORD        dwExitCode = 0;
	CWinThread   *p_oThread = AfxBeginThread( _p_fn_uiThread, _p_vParamForThread );

	if( p_oThread == NULL )
	{
		AfxMessageBox( "Unable to create thread !.", MB_OK | MB_ICONEXCLAMATION );
		return STATUS_C_Error;
	}

	// Disable autodelete (to use handle to thread after it is terminated).
	p_oThread->m_bAutoDelete = FALSE;

	// Now, we enter wait loop that wait either for thread end or for input message
	a_hEvents[0] = p_oThread->m_hThread;

	while( 1 )
	{
		dwWaitReturn = MsgWaitForMultipleObjects( 1, a_hEvents, FALSE, INFINITE, QS_ALLINPUT );

		// If an input event has arrived, we dispatch it.
		if( dwWaitReturn == WAIT_OBJECT_0 + 1 )
		{
			while( PeekMessage( &stMessage, g_stTheGlobalData.p_oMainDlg->m_hWnd, 0, 0, PM_REMOVE ) )
				if( g_stTheGlobalData.p_oMainDlg -> PreTranslateMessage( &stMessage ) == 0 )
					DispatchMessage( &stMessage );
		}
		// if thread has exited
		else if( dwWaitReturn == WAIT_OBJECT_0 || dwWaitReturn == WAIT_ABANDONED_0 )
		{
			break;
		}
		else
		{
			// Error.
			TCHAR szMessage[1024];
			::FormatMessage(     FORMAT_MESSAGE_FROM_SYSTEM,
					NULL,    GetLastError(),
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
					szMessage,    1024,    NULL );
			fn_vOutputLine( "\r\n\tInternal error: Error on MsgWaitForMultipleObjects :\r\n\t" );
			fn_vOutputLine( szMessage );
			fn_vOutputLine( "\r\n" );
			delete p_oThread;
			return STATUS_C_Error;
		}
	}

	GetExitCodeThread( p_oThread->m_hThread, &dwExitCode );
//	if( dwExitCode == STATUS_C_UserBreak )
//		fn_vOutputLine( C_szUserBreakString );

	delete p_oThread;

	return (tdeStatus)dwExitCode;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

typedef struct tdstEnumWinStruct_
{
	char   *p_szExeName;
	BOOL   bActionDone;
	UINT   uiNumberOfButton;
} tdstEnumWinStruct;

// Callback to enumerate all controls in a window.
BOOL CALLBACK EnumChildWindowProc( HWND _hwnd, LPARAM _p_stParam )
{
	char                 szTitle[512];
	tdstEnumWinStruct   *p_stEnumChild   = (tdstEnumWinStruct *)_p_stParam;
	DWORD                dwID            = GetClassLong( _hwnd, GCW_ATOM );

	// We only check statics
	switch( dwID )
	{
		case 0xC01B :
			GetWindowText( _hwnd, szTitle, 512 );
			// Check if caption contains the name of the exe
			if( stristr( szTitle, p_stEnumChild->p_szExeName ) != NULL )
			{
				// Name is detected
				p_stEnumChild->bActionDone = TRUE;
			}
			break;
		case 0xC015:
			p_stEnumChild->uiNumberOfButton++;
			break;
		default:
			break;
	}
	return TRUE;
}


BOOL fn_bCheckWindow( HWND _hMessageBox, char *_szString, UINT *_p_uiNbButtons = NULL )
{
	tdstEnumWinStruct stEnumChild;

	stEnumChild.p_szExeName       = _szString;
	stEnumChild.bActionDone       = FALSE;
	stEnumChild.uiNumberOfButton  = 0;

	// Search in controls if one of them contains the given string
	EnumChildWindows( _hMessageBox, EnumChildWindowProc, (LPARAM)&stEnumChild );
	if( _p_uiNbButtons )
		*_p_uiNbButtons = stEnumChild.uiNumberOfButton;
	return stEnumChild.bActionDone;
}


// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 

// Callback to enumerate all windows.
// (called by fn_bCloseSytemErrorDialogBox)
BOOL CALLBACK EnumWindowProc( HWND _hwnd, LPARAM _p_stParam )
{
	char               szTitle[512];
	DWORD              dwID           = GetClassLong( _hwnd, GCW_ATOM );
	tdstEnumWinStruct  *p_stEnumWin   = (tdstEnumWinStruct *)_p_stParam;
	UINT               uiNbButtons;

	GetWindowText( _hwnd, szTitle, 512 );

	// If window is a dialog box 
	if( dwID == 0x8002 )
	{
		// Check if it's an "application error" box.
		if(    stristr( szTitle, p_stEnumWin->p_szExeName )
		    && (stristr( szTitle, "erreur d'application" ) || stristr( szTitle, "application error" )) )
		{
			// Now, we close that box !
			::PostMessage( _hwnd, WM_CLOSE, 0, 0 );
			p_stEnumWin->bActionDone = TRUE;
			// Return FALSE to stop enumeration
			return FALSE;
		}
		// Else, it may be an assert box.
		else if(    (stristr( szTitle, "Debug Lib" ) || stristr( szTitle, "Runtime Lib" ))
			    && fn_bCheckWindow( _hwnd, "??" ) 
			    && fn_bCheckWindow( _hwnd, p_stEnumWin->p_szExeName, &uiNbButtons )
			    && uiNbButtons == 3 )
		{
			::PostMessage( _hwnd, WM_CLOSE, 0, 0 );
			p_stEnumWin->bActionDone = TRUE;
			// Return FALSE to stop enumeration
			return FALSE;
		}
		// Check if it's an "illegal operation" box.
		else
		{
			char *p_szExtension = strrchr( p_stEnumWin->p_szExeName, '.' );
			if( p_szExtension ) *p_szExtension = 0;

			if(    stristr( szTitle, p_stEnumWin->p_szExeName )
			    && fn_bCheckWindow( _hwnd, "Opération non conforme", &uiNbButtons )
			    && uiNbButtons >= 2 && uiNbButtons < 4 )
			{
				// Now, we close that box !
				::PostMessage( _hwnd, WM_CLOSE, 0, 0 );
				p_stEnumWin->bActionDone = TRUE;
				// Return FALSE to stop enumeration
				return FALSE;
			}
			if( p_szExtension ) *p_szExtension = '.';
		}
	}
	// We also handle dr watson's windows...
	else if( stristr( szTitle, "Watson" ) != NULL )
	{
		// Check if this dr. Watson's window talks about us !
		if( fn_bCheckWindow( _hwnd, p_stEnumWin->p_szExeName ) )
		{
			::PostMessage( _hwnd, WM_CLOSE, 0, 0 );
			p_stEnumWin->bActionDone = TRUE;
			// Return FALSE to stop enumeration
			return FALSE;
		}
	}
	// Window not found, we continue
	return TRUE;
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
// Get all windows, check if this is a system or dr. watson's window 
// and it is related to ExeName, then eventually close it.
// Return TRUE if a window was closed, FALSE otherwise.
BOOL fn_bCloseSytemErrorDialogBox( char *_szExeName, BOOL bForceClose /*=FALSE*/)
{
	tdstEnumWinStruct stEnumWin;

	if( ! g_stTheGlobalData.bAutoDetectErrors && ! bForceClose )
		return FALSE;
	
	stEnumWin.p_szExeName = _szExeName;
	stEnumWin.bActionDone = FALSE;

	EnumWindows( EnumWindowProc, (LPARAM)&stEnumWin );
	return stEnumWin.bActionDone;
}

