/////////////////////////////////////////////////////////////////////////////
//
//	Copyright (c) 1996-2000 Microsoft Corporation
//
//	Module Name:
//		ExcOper.cpp
//
//	Abstract:
//		Implementation of exception classes.
//
//	Author:
//		David Potter (davidp)	May 20, 1996
//
//	Revision History:
//
//	Notes:
//		TraceTag.h and resource.h are pulled from the project directory.
//
//		stdafx.h must disable some W4 warnings.
//
//		TraceTag.h must define TraceError.
//
//		resource.h must define IDS_ERROR_MSG_ID, and the string must be
//		defined something like "\n\nError ID: %d (%08.8x)." in the resource file.
//
//		IDP_NO_ERROR_AVAILABLE must defined as a string for displaying when
//		no error code is available.
//
//		EXC_AppMessageBox(LPCTSTR...) and EXC_AppMessageBox(UINT...) must be
//		defined and implemented.
//
//		EXC_GetResourceInstance must be defined and implemented to return the
//		resource instance handle of the application or DLL.
//
/////////////////////////////////////////////////////////////////////////////

#include <string.h>
#include "ExcOper.h"
#include "TraceTag.h"

#include "resource.h"

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

/////////////////////////////////////////////////////////////////////////////
// class CException
/////////////////////////////////////////////////////////////////////////////

#ifndef __AFX_H__

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CException::ReportError
//
//	Routine Description:
//		Report an error from the exception.  Overriding to get a bigger
//		error message buffer.
//
//	Arguments:
//		nType		[IN] Type of message box.
//		nError		[IN] ID of a mesage to display if exception has no message.
//
//	Return Value:
//		Return value from MessageBox.
//
//--
/////////////////////////////////////////////////////////////////////////////
int CException::ReportError( UINT nType /* = MB_OK */, UINT nError /* = 0 */ )
{
	TCHAR	szErrorMessage[128];
	int		nDisposition;
	UINT	nHelpContext;

	if ( GetErrorMessage(szErrorMessage, sizeof( szErrorMessage ) / sizeof( TCHAR ), &nHelpContext ) )
	{
		nDisposition = EXC_AppMessageBox( szErrorMessage, nType, nHelpContext );
	} // if:  error message retrieved successfully
	else
	{
		if ( nError == 0 )
		{
			nError = IDP_NO_ERROR_AVAILABLE;
		} // if:  no error code
		nDisposition = EXC_AppMessageBox( nError, nType, nHelpContext );
	} // else:  error retrieving error message
	return nDisposition;

} //*** CException::ReportError()

#endif // __AFX_H__

/////////////////////////////////////////////////////////////////////////////
// class CExceptionWithOper
/////////////////////////////////////////////////////////////////////////////

#ifdef __AFX_H__
IMPLEMENT_DYNAMIC(CExceptionWithOper, CException)
#endif // __AFX_H__

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CExceptionWithOper::ReportError
//
//	Routine Description:
//		Report an error from the exception.  Overriding to get a bigger
//		error message buffer.
//
//	Arguments:
//		nType		[IN] Type of message box.
//		nError		[IN] ID of a mesage to display if exception has no message.
//
//	Return Value:
//		Return value from MessageBox.
//
//--
/////////////////////////////////////////////////////////////////////////////
int CExceptionWithOper::ReportError(
	UINT nType /* = MB_OK */,
	UINT nError /* = 0 */
	)
{
	TCHAR   szErrorMessage[EXCEPT_MAX_OPER_ARG_LENGTH * 3];
	int     nDisposition;
	UINT    nHelpContext;

	if ( GetErrorMessage( szErrorMessage, sizeof( szErrorMessage ) / sizeof( TCHAR ), &nHelpContext ) )
	{
		nDisposition = EXC_AppMessageBox( szErrorMessage, nType, nHelpContext );
	} // if:  error message retrieved successfully
	else
	{
		if ( nError == 0 )
		{
			nError = IDP_NO_ERROR_AVAILABLE;
		} // if:  no error code
		nDisposition = EXC_AppMessageBox( nError, nType, nHelpContext );
	} // else:  error retrieving error message
	return nDisposition;

} //*** CExceptionWithOper::ReportError()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CExceptionWithOper::ReportError
//
//	Routine Description:
//		Report an error from the exception.  This method should be used from
//		all threads except the main thread.
//
//	Arguments:
//		pfnMsgBox	[IN] Message box function pointer.
//		dwParam		[IN] Parameter to pass to the message box function.
//		nType		[IN] Type of message box.
//		nError		[IN] ID of a mesage to display if exception has no message.
//
//	Return Value:
//		Return value from MessageBox.
//
//--
/////////////////////////////////////////////////////////////////////////////
int CExceptionWithOper::ReportError(
	PFNMSGBOX	pfnMsgBox,
	DWORD		dwParam,
	UINT		nType /* = MB_OK */,
	UINT		nError /* = 0 */
	)
{
	TCHAR   szErrorMessage[EXCEPT_MAX_OPER_ARG_LENGTH * 3];
	int     nDisposition;
	UINT    nHelpContext;

	ASSERT( pfnMsgBox != NULL );

	if ( GetErrorMessage( szErrorMessage, sizeof( szErrorMessage ) / sizeof( TCHAR ), &nHelpContext ) )
	{
		nDisposition = (*pfnMsgBox)( dwParam, szErrorMessage, nType, nHelpContext );
	} // if:  error message retrieved successfully
	else
	{
		if ( nError == 0 )
		{
			nError = IDP_NO_ERROR_AVAILABLE;
		} // if:  no error code
		CString strMsg;
		strMsg.LoadString( nError );
		nDisposition = (*pfnMsgBox)( dwParam, strMsg, nType, nHelpContext );
	} // else:  error retrieving error message
	return nDisposition;

}  //*** CExceptionWithOper::ReportError( pfnMsgBox )

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CExceptionWithOper::ReportError
//
//	Routine Description:
//		Report an error from the exception.  This method should be used from
//		all threads except the main thread.
//
//	Arguments:
//		hwndParent	[IN] Parent window.
//		nType		[IN] Type of message box.
//		nError		[IN] ID of a mesage to display if exception has no message.
//
//	Return Value:
//		Return value from MessageBox.
//
//--
/////////////////////////////////////////////////////////////////////////////
int CExceptionWithOper::ReportError(
	HWND	hwndParent,
	UINT	nType /* = MB_OK */,
	UINT	nError /* = 0 */
	)
{
	ASSERT(hwndParent != NULL);

	TCHAR   szErrorMessage[EXCEPT_MAX_OPER_ARG_LENGTH * 3];
	int     nDisposition;
	UINT    nHelpContext;

	if ( GetErrorMessage( szErrorMessage, sizeof( szErrorMessage ) / sizeof( TCHAR ), &nHelpContext ) )
	{
		nDisposition = EXC_AppMessageBox( hwndParent, szErrorMessage, nType, nHelpContext );
	} // if:  error message retrieved successfully
	else
	{
		if ( nError == 0 )
		{
			nError = IDP_NO_ERROR_AVAILABLE;
		} // if:  no error code
		CString strMsg;
		strMsg.LoadString( nError );
		nDisposition = EXC_AppMessageBox( hwndParent, szErrorMessage, nType, nHelpContext );
	} // else:  error retrieving error message
	return nDisposition;

} //*** CExceptionWithOper::ReportError( pfnMsgBox )

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CExceptionWithOper::SetOperation
//
//	Routine Description:
//		Constructor.
//
//	Arguments:
//		idsOperation	[IN] String ID for operation occurring during exception.
//		pszOperArg1		[IN] 1st argument to operation string.
//		pszOperArg2		[IN] 2nd argument to operation string.
//
//	Return Value:
//		None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CExceptionWithOper::SetOperation(
	IN UINT			idsOperation,
	IN LPCTSTR		pszOperArg1,
	IN LPCTSTR		pszOperArg2
	)
{
	m_idsOperation = idsOperation;

	if ( pszOperArg1 == NULL )
	{
		m_szOperArg1[0] = _T('\0');
	} // if:  first argument not specified
	else
	{
		::_tcsncpy( m_szOperArg1, pszOperArg1, (sizeof( m_szOperArg1 ) / sizeof( TCHAR )) - 1 );
		m_szOperArg1[(sizeof( m_szOperArg1 ) / sizeof( TCHAR ))- 1] = _T('\0');
	}  // else:  first argument specified

	if ( pszOperArg2 == NULL )
	{
		m_szOperArg2[0] = _T('\0');
	} // if:  second argument not specified
	else
	{
		::_tcsncpy( m_szOperArg2, pszOperArg2, (sizeof( m_szOperArg2 ) / sizeof( TCHAR )) - 1 );
		m_szOperArg2[(sizeof( m_szOperArg2 ) / sizeof( TCHAR )) - 1] = _T('\0');
	}  // else:  second argument specified

} //*** CExceptionWithOper::SetOperation()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CExceptionWithOper::FormatWithOperation
//
//	Routine Description:
//		Get the error message represented by the exception.
//
//	Arguments:
//		lpszError		[OUT] String in which to return the error message.
//		nMaxError		[IN] Maximum length of the output string.
//		pszMsg			[IN] Message to format with the operation string.
//
//	Return Value:
//		None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void CExceptionWithOper::FormatWithOperation(
	OUT LPTSTR	lpszError,
	IN UINT		nMaxError,
	IN LPCTSTR	pszMsg
	)
{
	DWORD		dwResult;
	TCHAR		szOperation[EXCEPT_MAX_OPER_ARG_LENGTH];
	TCHAR		szFmtOperation[EXCEPT_MAX_OPER_ARG_LENGTH * 3];

	ASSERT( lpszError != NULL );
	ASSERT( nMaxError > 0 );

	// Format the operation string.
	if ( m_idsOperation )
	{
		void *		rgpvArgs[2]	= { m_szOperArg1, m_szOperArg2 };

		// Load the operation string.
		dwResult = ::LoadString( EXC_GetResourceInstance(), m_idsOperation, szOperation, (sizeof( szOperation ) / sizeof( TCHAR )) );
		ASSERT( dwResult != 0 );

		// Format the operation string.
		::FormatMessage(
					FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY,
					szOperation,
					0,
					MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ),
					szFmtOperation,
					sizeof( szFmtOperation ) / sizeof( TCHAR ),
					(va_list *) rgpvArgs
					);
		szFmtOperation[(sizeof( szFmtOperation ) / sizeof( TCHAR )) - 1] = _T('\0');

		// Format the final error message.
		if ( pszMsg != NULL )
		{
			::_sntprintf( lpszError, nMaxError - 1, _T("%s\n\n%s"), szFmtOperation, pszMsg );
		} // if:  additional message specified
		else
		{
			::_tcsncpy(lpszError, szFmtOperation, nMaxError - 1);
		} // else:  no additional message specified
		lpszError[nMaxError - 1] = _T('\0');
	}  // if:  operation string specified
	else
	{
		if ( pszMsg != NULL )
		{
			::_tcsncpy( lpszError, pszMsg, nMaxError - 1 );
			lpszError[nMaxError - 1] = _T('\0');
		}  // if:  additional message specified
		else
		{
			lpszError[0] = _T('\0');
		} // if:  no additional message specified
	}  // else:  no operation string specified

} //*** CExceptionWithOper::FormatWithOperation()


//***************************************************************************


/////////////////////////////////////////////////////////////////////////////
// class CNTException
/////////////////////////////////////////////////////////////////////////////

#ifdef __AFX_H__
IMPLEMENT_DYNAMIC( CNTException, CExceptionWithOper )
#endif // __AFX_H__

/////////////////////////////////////////////////////////////////////////////
//++
//
//	CNTException::FormatErrorMessage
//
//	Routine Description:
//		Format the error message represented by the exception.
//
//	Arguments:
//		lpszError		[OUT] String in which to return the error message.
//		nMaxError		[IN] Maximum length of the output string.
//		pnHelpContext	[OUT] Help context for the error message.
//		bIncludeID		[IN] Include the ID in the message.
//
//	Return Value:
//		TRUE		Message available.
//		FALSE		No message available.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL CNTException::FormatErrorMessage(
	LPTSTR	lpszError,
	UINT	nMaxError,
	PUINT	pnHelpContext,
	BOOL	bIncludeID
	)
{
	DWORD		dwResult;
	TCHAR		szNtMsg[1024];

	// Format the NT status code.
	::FormatErrorMessage( m_sc, szNtMsg, sizeof( szNtMsg ) / sizeof( TCHAR ) );

	// Format the message with the operation string.
	FormatWithOperation( lpszError, nMaxError, szNtMsg );

	// Add the error ID.
	if ( bIncludeID )
	{
		UINT	nMsgLength = _tcslen( lpszError );
		TCHAR	szErrorFmt[EXCEPT_MAX_OPER_ARG_LENGTH];

		if ( nMsgLength - 1 < nMaxError )
		{
			dwResult = ::LoadString( EXC_GetResourceInstance(), IDS_ERROR_MSG_ID, szErrorFmt, (sizeof( szErrorFmt ) / sizeof( TCHAR )) );
			ASSERT( dwResult != 0 );
			::_sntprintf( &lpszError[nMsgLength], nMaxError - nMsgLength - 1, szErrorFmt, m_sc, m_sc );
		}  // if:  there is room for the error ID
	}  // if:  error ID should be included

	return TRUE;

} //*** CNTException::FormatErrorMessage()


//***************************************************************************


/////////////////////////////////////////////////////////////////////////////
// Global Functions
/////////////////////////////////////////////////////////////////////////////

static CNTException			gs_nte( ERROR_SUCCESS, NULL, NULL, NULL, FALSE );
static CExceptionWithOper	gs_ewo( NULL, NULL, NULL, FALSE );

/////////////////////////////////////////////////////////////////////////////
//++
//
//	ThrowStaticException
//
//	Purpose:
//		Throw the static NT Exception.
//
//	Arguments:
//		sc				[IN] NT status code.
//		idsOperation	[IN] String ID for operation occurring during exception.
//		pszOperArg1		[IN] 1st argument to operation string.
//		pszOperArg2		[IN] 2nd argument to operation string.
//
//	Returns:
//		None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void ThrowStaticException(
	IN SC			sc,
	IN UINT			idsOperation,
	IN LPCTSTR		pszOperArg1,
	IN LPCTSTR		pszOperArg2
	)
{
	gs_nte.SetOperation( sc, idsOperation, pszOperArg1, pszOperArg2 );
	TraceError( gs_nte );
	throw &gs_nte;

} //*** ThrowStaticException()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	ThrowStaticException
//
//	Purpose:
//		Throw the static Cluster Administrator Exception.
//
//	Arguments:
//		idsOperation	[IN] String ID for operation occurring during exception.
//		pszOperArg1		[IN] 1st argument to operation string.
//		pszOperArg2		[IN] 2nd argument to operation string.
//
//	Returns:
//		None.
//
//--
/////////////////////////////////////////////////////////////////////////////
void ThrowStaticException(
	IN UINT			idsOperation,
	IN LPCTSTR		pszOperArg1,
	IN LPCTSTR		pszOperArg2
	)
{
	gs_ewo.SetOperation( idsOperation, pszOperArg1, pszOperArg2 );
	TraceError( gs_ewo );
	throw &gs_ewo;

} //*** ThrowStaticException()

/////////////////////////////////////////////////////////////////////////////
//++
//
//	FormatErrorMessage
//
//	Routine Description:
//		Format the error message represented by the exception.
//
//	Arguments:
//		sc				[IN] Status code.
//		lpszError		[OUT] String in which to return the error message.
//		nMaxError		[IN] Maximum length of the output string.
//
//	Return Value:
//		TRUE		Message available.
//		FALSE		No message available.
//
//--
/////////////////////////////////////////////////////////////////////////////
BOOL FormatErrorMessage(
	DWORD	sc,
	LPTSTR	lpszError,
	UINT	nMaxError
	)
{
	DWORD		_cch;

	// Format the NT status code from the system.
	_cch = FormatMessage(
					FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL,
					sc,
					MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ),
					lpszError,
					nMaxError,
					0
					);
	if ( _cch == 0 )
	{
		Trace( g_tagError, _T("Error %d getting message from system for error code %d"), GetLastError(), sc );

		// Format the NT status code from NTDLL since this hasn't been
		// integrated into the system yet.
		_cch = FormatMessage(
						FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_IGNORE_INSERTS,
						GetModuleHandle( _T("NTDLL.DLL") ),
						sc,
						MAKELANGID( LANG_NEUTRAL, SUBLANG_NEUTRAL ),
						lpszError,
						nMaxError,
						0
						);
		if ( _cch == 0 )
		{

#ifdef _DEBUG

			DWORD	_sc = GetLastError();
                        _sc=_sc;
			Trace( g_tagError, _T("Error %d getting message from NTDLL.DLL for error code %d"), _sc, sc );

#endif

			lpszError[0] = _T('\0');

		}  // if:  error formatting status code from NTDLL
	}  // if:  error formatting status code from system

	return TRUE;

} //*** FormatErrorMessage()