453 lines
13 KiB
C++
453 lines
13 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996-2000
|
|
//
|
|
// File: ciexcpt.cxx
|
|
//
|
|
// Contents: Macro package for C++ exception support
|
|
//
|
|
// Classes: CException -- The base for all exception classes
|
|
// CExceptionContext -- Per-thread exception context.
|
|
// CUnwindable -- Classes with destructors inherit
|
|
// from this.
|
|
//
|
|
// History: 22-May-91 KyleP Created Interface.
|
|
// 15-Aug-91 SethuR Included terminate(),unexpected()
|
|
// set_unexpected(),set_terminate()
|
|
// 18-Oct-91 KyleP Win32 try/except implementation
|
|
// 19-Nov-91 KyleP Fix heap unwind, multiple inheritance
|
|
// 14-May-92 BryanT Disable heapchk for FLAT builds.
|
|
// 25-Apr-95 DwightKr Native C++ exception support
|
|
// 30-Oct-98 KLam Translate in page error to disk full
|
|
// when appropriate
|
|
// 07-Jan-99 KLam Debug out when in page error occurs
|
|
//
|
|
// Notest: This file is a hack until C 9.x supports exceptions for
|
|
// compilers on all platforms
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
//#include <nulock.hxx>
|
|
|
|
#if CIDBG == 1
|
|
|
|
extern "C" {
|
|
#include <imagehlp.h>
|
|
#define MAX_TRANSLATED_LEN 80
|
|
}
|
|
|
|
#include "snapimg.hxx"
|
|
|
|
void TranslateAddress(
|
|
ULONG p,
|
|
char *achBuffer,
|
|
ULONG cchBuffer)
|
|
{
|
|
BYTE symbolInfo[sizeof(IMAGEHLP_SYMBOL) + MAX_TRANSLATED_LEN];
|
|
PIMAGEHLP_SYMBOL psym = (PIMAGEHLP_SYMBOL) symbolInfo;
|
|
|
|
DWORD_PTR dwDisplacement;
|
|
|
|
psym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
|
|
psym->MaxNameLength = MAX_TRANSLATED_LEN;
|
|
|
|
HANDLE hProcess = GetCurrentProcess();
|
|
|
|
if ( SnapToImageHlp( ) &&
|
|
LocalSymGetSymFromAddr( hProcess,
|
|
(ULONG_PTR)p,
|
|
&dwDisplacement,
|
|
psym )
|
|
)
|
|
{
|
|
char * pszFormat = dwDisplacement ? "%s+0x%p\n" : "%s\n";
|
|
char achTmpBuf[ MAX_TRANSLATED_LEN ];
|
|
|
|
if ( LocalSymUnDName( psym, achTmpBuf, MAX_TRANSLATED_LEN ) )
|
|
{
|
|
_snprintf( achBuffer, cchBuffer,
|
|
pszFormat,
|
|
achTmpBuf,
|
|
dwDisplacement );
|
|
}
|
|
else
|
|
{
|
|
_snprintf( achBuffer, cchBuffer,
|
|
pszFormat,
|
|
psym->Name,
|
|
dwDisplacement );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_snprintf( achBuffer, cchBuffer,
|
|
"%px (symbolic name unavailable)",
|
|
(ULONG_PTR)p );
|
|
}
|
|
}
|
|
#endif // CIDBG
|
|
|
|
//
|
|
// If EXCEPT_TEST is defined, then the exception code can be compiled
|
|
// without use the the 'Win4 environment'. This is only to facilitate
|
|
// testing. When EXCEPT_TEST is defined debug messages are printed
|
|
// to stdout instead of the debug terminal.
|
|
//
|
|
|
|
#if !defined( EXCEPT_TEST )
|
|
|
|
# if CIDBG == 1
|
|
|
|
DECLARE_DEBUG( ex )
|
|
|
|
//
|
|
// See ciexcpt.hxx for an explanation of why exInlineDebugOut isn't used.
|
|
//
|
|
|
|
void exInlineDebugOut2(unsigned long fDebugMask, char const *pszfmt, ...)
|
|
{
|
|
va_list ArgList;
|
|
va_start(ArgList, pszfmt);
|
|
|
|
if (exInfoLevel & fDebugMask)
|
|
{
|
|
vdprintf(fDebugMask, exInfoLevelString, (char const *) pszfmt, ArgList);
|
|
}
|
|
|
|
va_end(ArgList);
|
|
}
|
|
|
|
# endif // (CIDBG == 1)
|
|
|
|
# if CIDBG == 1
|
|
|
|
//
|
|
// The default is to print exception messages to debug terminal.
|
|
//
|
|
|
|
unsigned long Win4ExceptionLevel = EXCEPT_MESSAGE;
|
|
|
|
# if !defined( DECLARE_INFOLEVEL )
|
|
extern EXTRNC unsigned long comp##InfoLevel = DEF_INFOLEVEL;
|
|
extern EXTRNC char *comp##InfoLevelString = #comp;
|
|
# else
|
|
DECLARE_INFOLEVEL(ex)
|
|
# endif
|
|
|
|
# endif // (CIDBG == 1)
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SetWin4ExceptionLevel
|
|
//
|
|
// Synopsis: Sets global exception level
|
|
//
|
|
// History: 15-Sep-91 KevinRo Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
# if (CIDBG == 1)
|
|
|
|
EXPORTIMP unsigned long APINOT
|
|
SetWin4ExceptionLevel(
|
|
unsigned long ulNewValue)
|
|
{
|
|
unsigned long ul;
|
|
|
|
ul = Win4ExceptionLevel;
|
|
Win4ExceptionLevel = ulNewValue;
|
|
return(ul);
|
|
}
|
|
|
|
ULONG GetWin4ExceptionLevel(void)
|
|
{
|
|
return Win4ExceptionLevel;
|
|
}
|
|
|
|
# endif // (CIDBG == 1)
|
|
|
|
#endif // !EXCEPT_TEST
|
|
|
|
//
|
|
// Heap checking can be turned on with this variable.
|
|
// 0x00000001 = Always check new/delete
|
|
// 0x00000002 = Check delete during unwind.
|
|
//
|
|
|
|
extern "C"
|
|
{
|
|
int newHeapCheck = 0;
|
|
}
|
|
|
|
//
|
|
// In a multi-threaded (e.g. Win32) environment, we need one
|
|
// exception context per thread.
|
|
//
|
|
|
|
enum Unwindability
|
|
{
|
|
NonUnwindable
|
|
};
|
|
|
|
void * __cdecl
|
|
operator new ( size_t s, Unwindability dummy )
|
|
{
|
|
#if defined ( _AUTOCHECK_ )
|
|
return RtlAllocateHeap( RtlProcessHeap(), 0, s );
|
|
#else
|
|
return(malloc(s));
|
|
#endif // _AUTOCHECK_
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Function: GetScodeError, public
|
|
//
|
|
// Synopsis: Translates an NTSTATUS or HRESULT into an HRESULT (SCODE)
|
|
//
|
|
// Arguments: [e] -- The exception object
|
|
//
|
|
// Returns A SCODE equivalent
|
|
//
|
|
// History: 3-Feb-98 dlee Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
EXPORTIMP SCODE APINOT GetScodeError( CException & e )
|
|
{
|
|
//
|
|
// This function normalizes NTSTATUS, HRESULTS, and Win32 error codes
|
|
// into a Win32 error. Note that it is illegal in CI to throw Win32
|
|
// error codes because they will be confused with HRESULT success codes
|
|
//
|
|
// n.b. A side effect is that this error is set in the thread's LastError
|
|
//
|
|
|
|
SCODE sc = e.GetErrorCode();
|
|
|
|
// If it looks like an HRESULT already, just return it.
|
|
|
|
if ( 0x80000000 == ( 0xC0000000 & sc ) )
|
|
return sc;
|
|
|
|
//
|
|
// If it's a CI 0xC error, don't try to map it since it'll return
|
|
// ERROR_MR_MID_NOT_FOUND (and DbgBreak on checked builds).
|
|
//
|
|
|
|
if ( 0xC0041000 == ( 0xC0041000 & sc ) )
|
|
return sc;
|
|
|
|
DWORD dwError = RtlNtStatusToDosError( sc );
|
|
|
|
if ( ERROR_MR_MID_NOT_FOUND == dwError )
|
|
{
|
|
ciDebugOut(( DEB_WARN, "mr mid for error %#x\n", sc ));
|
|
return sc;
|
|
}
|
|
|
|
return HRESULT_FROM_WIN32( dwError );
|
|
} //GetScodeError
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: IsOleError, public
|
|
//
|
|
// Synopsis: return TRUE if sc looks like an OLE SCODE.
|
|
//
|
|
// History: 19-Apr-95 BartoszM Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
inline BOOL IsOleError (NTSTATUS sc)
|
|
{
|
|
// if it's a win32 facility error in an hresult, only certain
|
|
// instances are allowed returned by oledb. Map others to E_FAIL.
|
|
|
|
if ( 0x80070000 == ( sc & 0x80070000 ) )
|
|
{
|
|
if ( E_OUTOFMEMORY == sc ||
|
|
E_INVALIDARG == sc ||
|
|
E_HANDLE == sc ||
|
|
E_ACCESSDENIED == sc )
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
else if ( 0x80030000 == ( sc & 0x80030000 ) )
|
|
{
|
|
// storage errors aren't allowed by ole-db
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return ((sc & 0xFFF00000) == 0x80000000) ||
|
|
(SUCCEEDED(sc) && (sc & 0xFFFFF000) != 0);
|
|
} //IsOleError
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Method: GetOleError, public
|
|
//
|
|
// History: 19-Apr-95 BartoszM Created
|
|
// 26-Apr-95 DwightKr Converted from method to function
|
|
// so that it can be independent from
|
|
// the class CException which is now
|
|
// part of the C++ run-time package.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
EXPORTIMP SCODE APINOT GetOleError(CException & e)
|
|
{
|
|
NTSTATUS scError = e.GetErrorCode();
|
|
|
|
if (IsOleError(scError))
|
|
{
|
|
return scError;
|
|
}
|
|
else if ( ( STATUS_NO_MEMORY == scError ) ||
|
|
( STATUS_COMMITMENT_LIMIT == scError ) ||
|
|
( STATUS_INSUFFICIENT_RESOURCES == scError ) ||
|
|
( HRESULT_FROM_WIN32( ERROR_COMMITMENT_LIMIT ) == scError ) ||
|
|
( HRESULT_FROM_WIN32( ERROR_NO_SYSTEM_RESOURCES ) == scError ) ||
|
|
( STG_E_TOOMANYOPENFILES == scError ) ||
|
|
( STG_E_INSUFFICIENTMEMORY == scError ) )
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
else if ( STATUS_ACCESS_DENIED == scError )
|
|
return E_ACCESSDENIED;
|
|
else if ( STATUS_INVALID_HANDLE == scError )
|
|
return E_HANDLE;
|
|
else if ( STATUS_INVALID_PARAMETER == scError )
|
|
return E_INVALIDARG;
|
|
else
|
|
{
|
|
exDebugOut(( DEB_ITRACE, "GetOleError - mapping %08x to E_FAIL\n", scError));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
CException::CException()
|
|
: _lError( HRESULT_FROM_WIN32( GetLastError() ) ),
|
|
_dbgInfo( 0 )
|
|
{
|
|
} //CException
|
|
|
|
#if CIDBG == 1
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ExceptionReport
|
|
//
|
|
// Synopsis: Outputs Exception messages based on the Win4ExceptionLevel
|
|
// variable. Used by programs compiled with DEBUG defined
|
|
//
|
|
// History: 15-Sep-91 KevinRo Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
EXPORTIMP void APINOT
|
|
ExceptionReport(
|
|
unsigned int iLine,
|
|
char *szFile,
|
|
char *szMsg,
|
|
long lError )
|
|
{
|
|
# if DBG
|
|
if (Win4ExceptionLevel & EXCEPT_MESSAGE)
|
|
{
|
|
DWORD tid = GetCurrentThreadId();
|
|
exDebugOut((DEB_FORCE,
|
|
"%s - line: %u file: %s, thread id %x\n",
|
|
szMsg, iLine, szFile, tid));
|
|
if (lError != -1)
|
|
exDebugOut((DEB_FORCE,"\terror code 0x%lx\n", lError ));
|
|
|
|
}
|
|
|
|
# endif
|
|
|
|
if (Win4ExceptionLevel & EXCEPT_POPUP)
|
|
{
|
|
if(PopUpError(szMsg,iLine,szFile) == IDCANCEL)
|
|
{
|
|
DebugBreak();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Win4ExceptionLevel & EXCEPT_BREAK)
|
|
{
|
|
DebugBreak();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // CIDBG == 1
|
|
|
|
//+-------------------------------------------------------------------------
|
|
//
|
|
// Setup the runtime package to translate system exceptions (i.e. those
|
|
// usually processed by try / except blocks) into C++ exceptions (those
|
|
// processed by try / catch blocks). This has no effect on try / except
|
|
// blocks; as they will still continue to see the standard C exception.
|
|
//
|
|
// The translation is accomplished by telling the C run-time package to
|
|
// call a user-specified function each time a system exception occurs. The
|
|
// function translates the exception into a class and rethrows with the
|
|
// class. If their is a following CATCH block, then the C++ class will
|
|
// be caught. If there is a following except, then the original system
|
|
// exception will be caught.
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
//
|
|
// Convert system exceptions to a C++ exception.
|
|
//
|
|
|
|
void _cdecl SystemExceptionTranslator( unsigned int uiWhat,
|
|
struct _EXCEPTION_POINTERS * pexcept )
|
|
{
|
|
//
|
|
// Intentionally don't translate STATUS_PRIVILEGED_INSTRUCTION (see
|
|
// Win4AssertEx for details.
|
|
//
|
|
|
|
//
|
|
// In certain situations when a compressed or sparse file is mapped and we
|
|
// run out of disk space the system can throw an in page error instead of
|
|
// the appropriate error
|
|
//
|
|
// Throw the proper exception code so that the error can be handled correctly
|
|
//
|
|
|
|
if ( STATUS_IN_PAGE_ERROR == pexcept->ExceptionRecord->ExceptionCode &&
|
|
pexcept->ExceptionRecord->NumberParameters >= 3 )
|
|
{
|
|
exDebugOut(( DEB_WARN, "SystemExceptionTranslator: Received In Page IO Error\n"));
|
|
Win4Assert ( STATUS_IN_PAGE_ERROR == uiWhat );
|
|
|
|
if ( STATUS_DISK_FULL == pexcept->ExceptionRecord->ExceptionInformation[2] ||
|
|
STATUS_VOLUME_DISMOUNTED == pexcept->ExceptionRecord->ExceptionInformation[2] ||
|
|
STATUS_INSUFFICIENT_RESOURCES == pexcept->ExceptionRecord->ExceptionInformation[2] )
|
|
{
|
|
exDebugOut(( DEB_WARN, "SystemExceptionTranslator: Translating In Page IO Error to 0x%x\n",
|
|
(unsigned int) pexcept->ExceptionRecord->ExceptionInformation[2] ));
|
|
uiWhat = (unsigned int) pexcept->ExceptionRecord->ExceptionInformation[2];
|
|
pexcept->ExceptionRecord->ExceptionCode = (NTSTATUS) pexcept->ExceptionRecord->ExceptionInformation[2];
|
|
}
|
|
|
|
}
|
|
|
|
#if CIDBG == 1
|
|
if ( ASSRT_STRESSEXCEPTION != uiWhat )
|
|
throw CException( uiWhat ) ;
|
|
#else
|
|
throw CException( uiWhat ) ;
|
|
#endif
|
|
} //SystemExceptionTranslator
|
|
|