//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1991 - 1992.
//
//  File:       Except.hxx
//
//  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.
//              CTry                     -- Per/TRY state.
//
//  Functions:  Macros to implement TRY ... CATCH
//              Macros to implement unwind for classes with destructors
//
//  History:    22-May-91   KyleP       Created Interface.
//              15-Aug-91   SethuR      modified THROW,CATCH,AND_CATCH,
//                                      END_CATCH macros
//              18-Oct-91   KyleP       Win32 try/except implementation
//              20-Feb-92   KyleP       Fixed destruction of classes with
//                                      virtual methods.
//              03-Aug-92   KyleP       Kernel implementation
//              13-Nov-92   KyleP       Bugfix destruction of heap classes.
//                                        Added assertion checking.
//              27-May-93   Mackm       Added definitions for un-exceptional
//                                      operator new.
//              01-Jun-93   RobBear     Added definitions for alloc hooking
//              30-Sep-93   KyleP       DEVL obsolete
//              14-Dec-93   AlexT       Add NOEXCEPTIONS support
//
//----------------------------------------------------------------------------

#pragma warning(4:4509)   // SEH used in function w/ _trycontext

#ifndef __EXCEPT_HXX_
#define __EXCEPT_HXX_

#ifdef DISPLAY_INCLUDES
#pragma message( "#include <" __FILE__ ">..." )
#endif

#if defined( KERNEL )
# include <ntstatus.h>
# include <stddef.h>
#else  // !KERNEL
# include <memory.h>
# include <stdlib.h>
# include <string.h>
# include <stdio.h>
#endif

#include <debnot.h>

const int maxExceptionSize = 256;
#define DEB_BUDDY  0x10000
#define DEB_UNWIND 0x20000

//
// 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.
//

//
// REAL CODE -- REAL CODE -- REAL CODE -- REAL CODE
//

#ifndef EXCEPT_TEST
//#  if !defined( KERNEL )
//#    include <win4p.h>
//#  endif // !KERNEL
# if (DBG == 1 || OFSDBG == 1)

//
// VERY UNUSUAL SITUATION:
//      Out xxxDebugOut macros are designed to be inline.  This causes
//      a problem in exceptions because the _xxxInfoLevel flag which
//      determines if a message should be printed is not declared in
//      every DLL.  So.  Instead of calling the macro:
//
//     DECLARE_DEBUG(ex)
//
//      I will:

void exInlineDebugOut2(unsigned long fDebugMask, char const *pszfmt, ...);

//      and then export this function from OSM.DLL (or whatever DLL
//      exports exception handling.
//

#  define exDebugOut( x ) exInlineDebugOut2 x
#  define exAssert( x ) Win4Assert( x )
# else  // DBG == 0 && OFSDBG == 0
#  define exDebugOut( x )
#  define exAssert( x )
# endif

//
// TEST ONLY -- TEST ONLY -- TEST ONLY -- TEST ONLY
//

#else // EXCEPT_TEST
   typedef unsigned long ULONG;
   typedef unsigned char BYTE;
# define TRUE 1
# include <assert.h>
# define exAssert( x ) assert( (x) )

# include <stdarg.h>
inline void exInlineDebugOut( ULONG,
                         char * msg,
                         ... )
{
    va_list arglist;

    va_start(arglist, msg);
    vfprintf(stderr, msg, arglist);
}

# define exDebugOut( x ) exInlineDebugOut x


# define _PopUpError( p1, p2, p3 ) 0xFFFFFFFF
# define Win4ExceptionLevel 0xFFFFFFFF
# define EXCEPT_MESSAGE     0x1
# define EXCEPT_POPUP       0x0
# define EXCEPT_BREAK       0x0
# define DEB_FORCE          0x0
# define DEB_WARN           0x0
# define DEB_ERROR          0x0
# define DEB_ITRACE         0x0

#endif // EXCEPT_TEST

//
// END TEST -- END TEST -- END TEST -- END TEST
//

#if defined( WIN32 )
# if !defined( KERNEL )
#  include <windows.h>
# endif  // !KERNEL
#else   // !WIN32
# include <setjmp.h>
#endif  // WIN32

class CTry;

typedef void(*PFV)();

extern void terminate();
extern void unexpected();

extern PFV  set_terminate(PFV  terminate_fn);
extern PFV  set_unexpected(PFV unexpected_fn);

#if DBG==1 || OFSDBG == 1
typedef BOOL            (* ALLOC_HOOK)(size_t nSize);
EXPORTDEF ALLOC_HOOK    MemSetAllocHook( ALLOC_HOOK pfnAllocHook );

extern EXPORTDEF void WINAPI
ExceptionReport(
    unsigned int iLine,
    char *szFile,
    char *szMsg);
#endif  // DBG == 1  || OFSDBG == 1

//+---------------------------------------------------------------------------
//
//  Class:      CException
//
//  Purpose:    All exception objects (e.g. objects that are THROW)
//              inherit from CException.
//
//  Interface:  ~CException - Destructor
//              IsKindOf    - Class membership query
//
//  History:    22-May-91   KyleP   Created.
//
//  Notes:      This class is a hack, until C7 can provide some support
//              for runtime inheritance detection (e.g. a way to generate
//              a mangled name).  When we get this support a new
//              implementation of IsKindOf should be developed. The concept,
//              however, would remain the same: C++ exception objects are
//              instances of an exception class, but they are caught by class.
//
//              A compiler can implement IsKindOf by throwing a
//              decorated name which uniquely identifies a class and
//              describes its inheritance.
//
//              Don't do anything too fancy with subclasses of CException.
//              Instances of your subclass are memcpy-ed around.  Having
//              a user defined destructor, for example, is probably a bad
//              idea.
//
//----------------------------------------------------------------------------

class CException
{
public:

    EXPORTDEF                     CException(long lError);
                      long        GetErrorCode() { return _lError;}
    EXPORTDEF virtual int  WINAPI IsKindOf(const char * szClass) const;

protected:

    long       _lError;
};

//+---------------------------------------------------------------------------
//
//  Class:      CExceptionContext
//
//  Purpose:    Per-thread exception state and throw exception methods.
//
//  Interface:  CExceptionContext -- Constructor
//             ~CExceptionContext -- Destructor
//              Push              -- Push a new element on the stack of
//                                   elements-to-be-destroyed.
//              Pop               -- Pop an an element off the stack.
//              SetTop            -- Set the top of stack to the specified
//                                   element.
//              GetTop            -- Return the top element.
//              Throw             -- Throw an exception
//
//  History:    19-Nov-91   KyleP   Fix heap unwind, multiple inheritance
//              22-May-91   KyleP   Created.
//
//  Notes:      There is only 1 CExceptionContext object per thread.
//
//----------------------------------------------------------------------------

class CExceptionContext
{
public:

    CExceptionContext();

   ~CExceptionContext();

    EXPORTDEF void WINAPI Reserve(void * pthis);

    inline    void        Pop(void * pthis);

    EXPORTDEF void WINAPI SetTop(void * pthis,
                                 ULONG cbElement,
                                 void (* pfn)(void *));

    inline    int         GetTop();

#ifndef WIN32

           void Throw();

#endif  /* WIN32 */

           void Unwind( CTry * ptry );

    //
    // _pTopTry contains the head of a linked list of CTry objects,
    // each of which corresponds to a TRY clause.
    //

    CTry *       _pTopTry;

    //
    // _pLastNew records the address of the most recent free
    // store allocation, _cbLastNew the size.
    //

    BYTE *       _pLastNew;
    int          _cbLastNew;
    int          _fNewReadyForUnwind;

    //
    // _exception contains a pointer to the current exception,
    // if any. The character buffer following it is for storing
    // additional data belonging to subclasses.
    //

    CException   _exception;
    char         buffer[maxExceptionSize];

private:

    //
    // Each StackElement contains enough information to destroy a
    // completly constructed object, and the determine whether
    // objects which have not yet been fully constructed were
    // allocated on the heap or the stack.
    //

    class StackElement
    {
    public:

        BOOL  IsThisEqual( void * pthis ) { return( pthis == _pthis ); }
        BOOL  IsSizeEqual( ULONG cb ) { return( cb == _cbElement ); }
        BOOL  IsOnStack() { return( _cbElement == 0 ); }
        BOOL  IsOnHeap() { return( _cbElement != 0 ); }
        BOOL  IsActive() { return( _pfn != 0 ); }

        void  SetOnStack() { _cbElement = 0; }
        void  SetOnHeap( ULONG size ) { _cbElement = size; }
        void  SetThis( void * pthis ) { _pthis = pthis; }
        void  SetDtor( void (* pfn)(void *) ) { _pfn = pfn; }

        void * GetDtor() { return( _pfn ); }
        void * GetThis() { return( _pthis ); }

        void  Destroy() { _pfn( (BYTE *)_pthis ); }
        void  Delete() { delete (BYTE *)_pthis; }

    private:

        void * _pthis;          // This for CUnwindable
        void (* _pfn)(void *);  // 'Static destructor'
        ULONG _cbElement;       // 0 == On stack. > 0 = Size of class
    };

    //
    // _aStack is the stack of objects-to-be-destroyed in the face
    // of an exception (stack unwind).
    //

    StackElement *  _aStack;

    //
    // _StackTop is the index of the first free element on the stack.
    //

    int             _StackTop;

    //
    // _StackSize is the maximum number of elements which can be
    // stored on the stack.
    //

    int             _StackSize;


    EXPORTDEF void APINOT _Grow();

};

//+---------------------------------------------------------------------------
//
//  Member:     CExceptionContext::Pop, public
//
//  Synopsis:   Pops the top element off the stack of objects to be destroyed.
//
//  Effects:    Pop only has an effect if the top element on the stack
//              is for [pthis].
//
//  Arguments:  [pthis] -- Must correspond to the top of stack if the
//                         element is to be popped.
//
//  History:    22-May-91   KyleP       Created.
//
//----------------------------------------------------------------------------

inline void CExceptionContext::Pop(void * pthis)
{
    //
    // Remove the top of stack only if it corresponds to pthis.
    // Thus the element will be removed only if this object was
    // declared on the stack (as opposed to embedded) and no
    // exception was encountered.
    //

    //
    // You should *always* find what you're looking for.  No checking
    // for runoff.  The very bottom element of the stack is a sentinel
    // for the case of heap objects which have been fully removed from
    // the stack.
    //

    exAssert( _StackTop > 0 );

    if ( _aStack[_StackTop-1].IsThisEqual( pthis ) )
    {
        //
        // The following assertion is hit if an unwindable object
        // is destroyed without END_CONSTRUCTION being called.
        //

        exAssert( _aStack[_StackTop-1].IsActive() );

        _StackTop--;

#if DBG == 1  || OFSDBG == 1
        exDebugOut(( DEB_ITRACE, "POP %lx %lx( %lx )\n",
                     _StackTop,
                     _aStack[_StackTop].GetDtor(),
                     _aStack[_StackTop].GetThis() ));
#endif // DBG  || OFSDBG == 1
    }
}

//+---------------------------------------------------------------------------
//
//  Member:     CExceptionContext::GetTop, public
//
//  Returns:    The index of the top of the stack of objects to be
//              destroyed. This is the first available element.
//
//  History:    22-May-91   KyleP       Created.
//
//----------------------------------------------------------------------------

inline int  CExceptionContext::GetTop()
{
    return(_StackTop);
}

EXPORTDEF CExceptionContext& APINOT _ExceptionContext(void);

//----------------------------------------------------------------------------
//
//  Various operator new implementations
//
//----------------------------------------------------------------------------

EXPORTDEF int APINOT LeakCheck ( ULONG* pCount );

#if defined( KERNEL )

//
// Prototype for extra operator new used in kernel to specify
// resident vs. swappable heap.
//

void InitExceptionSystem();
void* _CRTAPI1 operator new ( size_t size, POOL_TYPE pool );

#if OFSDBG == 1
# define NEW_POOL( pool, pCounter ) new(pool, pCounter)
        void* _CRTAPI1 operator new ( size_t size,  POOL_TYPE pool, ULONG* pCounter );
#else
# define NEW_POOL( pool, pCounter ) new(pool)
#endif

#endif // KERNEL

//
// Allocation with trace context
//

#if OFSDBG == 1 || DBG == 1
# define NEW( pCounter ) new ( pCounter )
        void* _CRTAPI1 operator new ( size_t size, ULONG* pCounter );
#else
# define NEW( pCounter ) new
#endif

//
// Prototype for extra operator new used in routines which do not
// have an exception handler set up and prefer a null return on
// failed allocations rather than an exception.
//

typedef enum _FAIL_BEHAVIOR
{
        NullOnFail,             // BUGBUG: should be deleted ASAP
        ExceptOnFail
} FAIL_BEHAVIOR ;

void* _CRTAPI1 operator new ( size_t size, FAIL_BEHAVIOR FailBehavior );

#define newx new(ExceptOnFail)

//+---------------------------------------------------------------------------
//
//  Class:      CSystemException
//
//  Purpose:    Base class for system (hardware) exceptions which are
//              not handled by a specific exception class
//
//  Interface:
//
//  History:    18-Oct-91   KyleP   Created.
//
//----------------------------------------------------------------------------

class CSystemException : public CException
{
public:

    EXPORTDEF                     CSystemException(ULONG ulExcept);

              virtual int  WINAPI IsKindOf(const char * szClass) const;

    ULONG GetSysErrorCode() { return( _ulExcept ); }

private:

    ULONG _ulExcept;

};

//+---------------------------------------------------------------------------
//
//  Class:      CUnwindable, CVirtualUnwindable
//
//  Purpose:    Per-object exception state and registration/unwind methods.
//
//  Interface:  CUnwindable -- Constructor
//             ~CUnwindable -- Destructor
//              _Activate                -- Activate object.
//
//  History:    22-May-91   KyleP   Created.
//              13-Nov-92   KyleP   Added virtual version
//
//  Notes:      CVirtualUnwindable should be used if and only if a
//              derived class has some other virtual method.
//
//----------------------------------------------------------------------------

class CUnwindable
{
public:

    inline            CUnwindable();

    inline           ~CUnwindable();

    inline void _Activate( ULONG cbElement,
                           void (* pfn)(void *) );
};

class CVirtualUnwindable
{
public:

    inline             CVirtualUnwindable();

    virtual           ~CVirtualUnwindable();

    inline void _Activate( ULONG cbElement,
                           void (* pfn)(void *) );
};

//+---------------------------------------------------------------------------
//
//  Member:     CUnwindable::CUnwindable, public
//
//  Synopsis:   Initializes the link field of the object.
//
//  Effects:    Essentially sets up enough state to remember the position
//              of the object which inherits from CUnwindable
//              within the stack of objects-to-be-destroyed.
//
//  History:    22-May-91   KyleP       Created.
//
//  Notes:      This call does not fully link the object into the chain
//              of objects to be destroyed. See _Activate.
//
//----------------------------------------------------------------------------

inline CUnwindable::CUnwindable()
{
    //
    // Initialize by allocating a record on the deletion stack.
    //

    _ExceptionContext().Reserve(this);
}

inline CVirtualUnwindable::CVirtualUnwindable()
{
    //
    // Initialize by allocating a record on the deletion stack.
    //

    _ExceptionContext().Reserve(this);
}

//+---------------------------------------------------------------------------
//
//  Member:     CUnwindable::~CUnwindable, public
//
//  Synopsis:   Removes this object from the stack of objects to be destroyed.
//
//  Modifies:   Per/thread exception context.
//
//  History:    22-May-91   KyleP       Created.
//
//----------------------------------------------------------------------------

inline CUnwindable::~CUnwindable()
{
    _ExceptionContext().Pop(this);
}

inline CVirtualUnwindable::~CVirtualUnwindable()
{
    _ExceptionContext().Pop(this);
}

//+---------------------------------------------------------------------------
//
//  Member:     CUnwindable::_Activate, public
//
//  Synopsis:   Fully link object into stack of objects to be destroyed.
//
//  Effects:    After this call, the object which inherits from
//              CUnwindable will be destroyed during unwind.
//
//              If the object was declared on the heap, then it is
//              removed completely from the stack of objects that will
//              be destroyed during unwind.
//
//  Arguments:  [prealthis] -- This pointer of instance.  Will differ
//                             from this pointer of CUnwindable
//                             if instance has a virtual method.
//              [cbElement] -- Size of the instance being activated.  If
//                             an element is declared on the heap and
//                             [cbElement] is smaller than the allocation
//                             then the element won't be deleted.
//
//              [pfn] -- Pointer to a static destructor. [pfn] takes a
//                       this pointer.
//
//  Modifies:   This object is now the top of the stack of objects that
//              will be destroyed (_exceptioncontext is modified).
//
//  History:    22-May-91   KyleP       Created.
//
//----------------------------------------------------------------------------

inline void CUnwindable::_Activate(
        ULONG cbElement,
        void (* pfn)(void *))
{
    _ExceptionContext().SetTop(this, cbElement, pfn);
}

inline void CVirtualUnwindable::_Activate(
        ULONG cbElement,
        void (* pfn)(void *))
{
    _ExceptionContext().SetTop(this, cbElement, pfn);
}

//+---------------------------------------------------------------------------
//
//  Class:      CTry
//
//  Purpose:    Containt per/TRY state.
//
//  Interface:  ~CTry       - Destructor
//              TryIt       - Setup to run the TRY body.
//
//  History:    22-May-91   KyleP   Created.
//
//----------------------------------------------------------------------------

class CTry
{
public:

    inline  CTry();
    inline ~CTry();

    //
    // The following function ***MUST*** be declared inline and
    // actually be ***IMPLEMENTED*** inline. It calls setjmp;
    // and if TryIt is a real procedure then we just set the
    // return for longjmp on a piece of stack that is reclaimed!
    //

    //inline int TryIt();

#ifndef WIN32

    //
    // _jb is the setjmp buffer indicating the location that will
    // be longjmp'ed to when an exception occurs.
    //

    jmp_buf _jb;

#endif /* WIN32 */

    //
    // _pPrevTry is a link to the enclosing TRY clause.
    //

    CTry *  _pPrevTry;

    //
    // _StackTop was the top of the stack of objects-to-be-destroyed
    // when the TRY clause was entered.
    //

    int     _StackTop;
};

//+---------------------------------------------------------------------------
//
//  Member:     CTry::~CTry, public
//
//  Synopsis:   Unlinks the TRY ... CATCH from the per/thread list.
//
//  Modifies:   The per/thread list of try clauses in _exceptioncontext.
//
//  History:    22-May-91   KyleP       Created.
//
//----------------------------------------------------------------------------

inline CTry::~CTry()
{
    exDebugOut(( DEB_ITRACE, "END TRY %lx (%lx)\n", _StackTop, this ));

    //
    // The destructor for CTry is called only during normal (no
    // exception) exit from the TRY ... CATCH or when an
    // exception was successfully caught in this block.
    // In these cases we want to unlink this CTry.
    //
    // Note that we could delete _exceptioncontext._TheException
    // here, which may be non-null if we caught an exception in
    // this TRY ... CATCH block. But why bother? That would add
    // additional code to the normal case. The last exception
    // can wait to be deleted until the next occurs.
    //

    _ExceptionContext()._pTopTry = _pPrevTry;
}

//+---------------------------------------------------------------------------
//
//  Member:     CTry::CTry, public
//
//  Synopsis:   Set up to run the body of a TRY ... CATCH clause.
//
//  Effects:    Links this CTry into the chain of TRY clauses.
//
//  History:    22-May-91   KyleP       Created.
//
//----------------------------------------------------------------------------

inline CTry::CTry()
{
    CExceptionContext& _exceptioncontext = _ExceptionContext();

    _pPrevTry = _exceptioncontext._pTopTry;
    _exceptioncontext._pTopTry = this;
    _StackTop = _exceptioncontext.GetTop();

    exDebugOut(( DEB_ITRACE, "TRY %lx (%lx)\n", _StackTop, this ));
}

//+---------------------------------------------------------------------------
//
// The following macros implement a TRY ... CATCH syntax similar
// to that in C++. They are used as follows:
//
//      TRY
//      {
//          // Body of try goes here...
//      }
//      CATCH(exclass, e)
//      {
//          // We get here when an exception of class exclass has
//          // been thrown. The variable e is declared as * exclass.
//      }
//      AND_CATCH(exclass, e)
//      {
//          // Just like CATCH. Any number of AND_CATCH can follow
//          // CATCH. Handlers are tried in order of appearance.
//      }
//      END_CATCH
//
// To throw an exception, use the THROW macro -- THROW(exclass).  To
// re-throw the same exception from within a catch clause use
// RETHROW().  Note that RETHROW() is only valid in a CATCH/AND_CATCH.
//
//  History:    22-May-91   KyleP       Created Interface.
//              15-Aug-91   SethuR      modified THROW,CATCH,AND_CATCH,END_CATCH
//                                      macros
//              18-Oct-91   KyleP       Win32 try/except implementation
//
//----------------------------------------------------------------------------

extern EXPORTDEF void APINOT ThrowDebugException(unsigned int iLine,
                                                 char * szFile,
                                                 CException & ecE );

extern EXPORTDEF void APINOT ThrowException(CException & ecE,
                                            unsigned dummy);

#if DBG == 1  || OFSDBG == 1// +++++++++++++++++++++++++++++++++++++++++++++++
#  define THROW(e)                                                \
    {                                                             \
        ThrowDebugException(__LINE__,__FILE__,e);                 \
    }

#else  // DBG == 0 && OFSDBG == 0 ++++++++++++++++++++++++++++++++++++++++++++

#  define THROW(e)                                                \
    {                                                             \
        ThrowException(e, 1);                                        \
    }

#endif // DBG == 0 && OFSDBG == 0 ++++++++++++++++++++++++++++++++++++++++++++


#ifdef WIN32  // ----------------------------------------------------

#define EXCEPT_VULCAN 0xE0000001

# define CALLEX( x )                                     \
    __try                                                \
    {                                                    \
        x;                                               \
    }                                                    \
    __except( EXCEPTION_EXECUTE_HANDLER )                \
    {                                                    \
        THROW( CException( GetExceptionCode() ) );       \
    }

#if defined( KERNEL )
#  if DBG == 1  || OFSDBG == 1// +++++++++++++++++++++++++++++++++++++++++++++++
#    define RETHROW()                                       \
        ExceptionReport(__LINE__,__FILE__,"Rethrowing Exception"); \
        ExRaiseStatus( EXCEPT_VULCAN );

#  else  // DBG == 0  && OFSDBG == 0 ++++++++++++++++++++++++++++++++++++++++++++

#    define RETHROW()                                   \
        ExRaiseStatus( EXCEPT_VULCAN );

#  endif // DBG == 0  && OFSDBG == 0 ++++++++++++++++++++++++++++++++++++++++++++


#else  // !KERNEL
#  if DBG == 1  || OFSDBG == 1// +++++++++++++++++++++++++++++++++++++++++++++++
#    define RETHROW()                                       \
        ExceptionReport(__LINE__,__FILE__,"Rethrowing Exception"); \
        RaiseException( EXCEPT_VULCAN, 0, 0, 0 );
#  else  // DBG == 0 && OFSDBG == 0 ++++++++++++++++++++++++++++++++++++++++++++

#    define RETHROW()                                   \
        RaiseException( EXCEPT_VULCAN, 0, 0, 0 );

#  endif // DBG == 0 && OFSDBG == 0 ++++++++++++++++++++++++++++++++++++++++++++

#endif // !KERNEL

#define TRY                                             \
    {                                                   \
        CTry _trycontext;                               \
        __try

EXPORTDEF ULONG APINOT Unwind( struct _EXCEPTION_POINTERS * pdisp,
                               CTry * ptry );

#if DBG == 1 || OFSDBG == 1  // +++++++++++++++++++++++++++++++++++++++++++++++

#define CATCH(class, e)                                                  \
        __except( Unwind( GetExceptionInformation(), &_trycontext ) )      \
        {                                                                \
            CExceptionContext& _exceptioncontext = _ExceptionContext();  \
            if (_exceptioncontext._exception.IsKindOf(#class))           \
            {                                                            \
                ExceptionReport(__LINE__,__FILE__,"Catching Exception"); \
                class & e = (class &)_exceptioncontext._exception;

#define AND_CATCH(class, e)                                                 \
            }                                                               \
            else if (_exceptioncontext._exception.IsKindOf(#class))         \
            {                                                               \
                ExceptionReport(__LINE__,__FILE__,"Catching Exception");    \
                class & e = (class &)_exceptioncontext._exception;

#else  // DBG == 0 ++++++++++++++++++++++++++++++++++++++++++++

#define CATCH(class, e)                                                  \
        __except( Unwind( GetExceptionInformation(), &_trycontext ) )      \
        {                                                                \
            CExceptionContext& _exceptioncontext = _ExceptionContext();  \
            if (_exceptioncontext._exception.IsKindOf(#class))           \
            {                                                            \
                class & e = (class &)_exceptioncontext._exception;

#define AND_CATCH(class, e)                                                 \
            }                                                               \
            else if (_exceptioncontext._exception.IsKindOf(#class))         \
            {                                                               \
                class & e = ( class &)_exceptioncontext._exception;

#endif  // DBG == 0 +++++++++++++++++++++++++++++++++++++++++++

#define END_CATCH                   \
            }                       \
            else                    \
            {                       \
                RETHROW();          \
            }                       \
        }                           \
    }

#else  // NOT WIN32 -------------------------------------------------

#define TRY                                             \
    {                                                   \
        CTry _trycontext;                               \
        if (setjmp(_trycontext._jb) == 0)               \



#if DBG == 1 || OFSDBG == 1 // ++++++++++++++++++++++++++++++++++++++++++++++++

#define CATCH(class, e)                                                 \
                                                                        \
        else                                                            \
        {                                                               \
            CExceptionContext& _exceptioncontext = _ExceptionContext(); \
            if (_exceptioncontext._exception.IsKindOf(#class))          \
            {                                                           \
                ExceptionReport(__LINE__,__FILE__,"Catching Exception");    \
                class & e = (class &)_exceptioncontext._exception;

#define AND_CATCH(class, e)                                                 \
                                                                            \
            }                                                               \
            else if (_exceptioncontext._exception.IsKindOf(#class))         \
            {                                                               \
                ExceptionReport(__LINE__,__FILE__,"Catching Exception");    \
                class & e = (class &)_exceptioncontext._exception;


#else // DBG == 0 +++++++++++++++++++++++++++++++++++++++++++++

#define CATCH(class, e)                                                 \
                                                                        \
        else                                                            \
        {                                                               \
            CExceptionContext& _exceptioncontext = _ExceptionContext(); \
            if (_exceptioncontext._exception.IsKindOf(#class))          \
            {                                                           \
                class & e = (class &)_exceptioncontext._exception;

#define AND_CATCH(class, e)                                                 \
                                                                            \
            }                                                               \
            else if (_exceptioncontext._exception.IsKindOf(#class))         \
            {                                                               \
                class & e = (class &)_exceptioncontext._exception;

#endif  // DBG == 0 +++++++++++++++++++++++++++++++++++++++++++


#define END_CATCH                                           \
            }                                               \
            else                                            \
            {                                               \
                _exceptioncontext.Throw();                  \
            }                                               \
         }                                                  \
    }

#define RETHROW()         \
        _exceptioncontext.Throw()

#endif  // NOT WIN32 ------------------------------------------------

//+---------------------------------------------------------------------------
//
// The following macros prepare a class for stack unwinding. Any class
// with a destructor should be prepared to deal with stack unwind.
// The macros are used as follows:
//
// First, in the .hxx file...
//
//      class CClassWithADestructor : INHERIT_UNWIND  // , other inheritance
//      {
//          DECLARE_UNWIND
//
//          //
//          // The rest of the class declaration goes here...
//          //
//      }
//
// And then in the .cxx file...
//
//      IMPLEMENT_UNWIND(CClassWithADestructor)
//
//      CClassWithADestructor::CClassWithADestructor(...)
//      {
//          //
//          // User construction activity.
//          //
//
//          //
//          // The following should precede every return from the
//          // constructor.
//          //
//
//          END_CONSTRUCTION(CClassWithADestructor)
//      }
//
// INHERIT_UNWIND must be the first inheritance.
//
// For further levels of inheritance, you do not need to inherit from
// INHERIT_UNWIND (more to the point, you can't).  The other macros
// must be used for all levels.
//
// INHERIT_VIRTUAL_UNWIND must be used in place of INHERIT_UNWIND on a class
// that contains virtual methods.  If INHERIT_VIRTUAL_UNWIND also fails,
// then find me (KyleP) and I'll try to figure out what went wrong.
//
// Multiple inheritance is untested.  It may work, or it may not.  If you
// really need multiple inheritance see me (KyleP).
//
//  History:    19-Nov-91   KyleP   Added multiple-inheritance support
//              22-May-91   KyleP   Created Interface.
//              13-Nov-92   KyleP   Added support for classes with virtual
//                                    methods.
//
//----------------------------------------------------------------------------

#define INHERIT_UNWIND                                  \
                                                        \
        public CUnwindable

#define INHERIT_VIRTUAL_UNWIND                          \
                                                        \
        public CVirtualUnwindable


#define END_MULTINHERITED_CONSTRUCTION(class, base1)    \
                                                        \
        base1::_Activate(sizeof(class), &class::_ObjectUnwind);


//
// Why such a complicated test below?  Because we are nearly ready to
// ship Daytona and Ole includes except.hxx but requires the macros
// do nothing.  We are unable to change the Ole sources this late in
// the game, so you get exceptions only if you are:
//   a) In a Cairo build and didn't define NOEXCEPTIONS, or
//   b) Defined YESEXCEPTIONS in any build.
//
// The above would be true and interesting if anybody except OFS wanted
// to use ofsNew or ofsDelete.  As it is, the declarations below (and
// most of the rest of this file) should be moved to the OFS project
//
#if ((WIN32 == 300) && !defined(NOEXCEPTIONS)) || defined(YESEXCEPTIONS)

#if (OFSDBG == 1)
    void* _CRTAPI1 ofsNew( size_t size, FAIL_BEHAVIOR FailBehavior );
    inline void* _CRTAPI1 operator new ( size_t size, FAIL_BEHAVIOR FailBehavior)
    {   return ofsNew(size, FailBehavior); }

    void  _CRTAPI1 ofsDelete( void * p );
    inline void  _CRTAPI1 operator delete ( void * p )
    {   ofsDelete( p ); }
#endif // (OFSDBG == 1)

#define INHERIT_UNWIND_IF_CAIRO : INHERIT_UNWIND

#define DECLARE_UNWIND                                  \
                                                        \
        static void APINOT _ObjectUnwind(void * pthis);

#if DBG == 1 || OFSDBG == 1

# define IMPLEMENT_UNWIND(class)                                            \
                                                                            \
        void APINOT class::_ObjectUnwind(void * pthis)                      \
        {                                                                   \
            ((class *)pthis)->class::~class();                              \
        }                                                                   \
                                                                            \
        struct __Check##class                                               \
        {                                                                   \
            __Check##class()                                                \
            {                                                               \
                if ( (CUnwindable *)((class *)10) != (CUnwindable *)10 ||   \
                     (CVirtualUnwindable *)                                 \
                         ((class *)10) != (CVirtualUnwindable *)10 )        \
                {                                                           \
                    exDebugOut(( DEB_ERROR,                                 \
                                 "INVALID UNWINDABLE CLASS: %s.\n",         \
                                 #class ));                                 \
                }                                                           \
            }                                                               \
        };                                                                  \
                                                                            \
        __Check##class __check_except_##class;

#else  // DBG == 0

# define IMPLEMENT_UNWIND(class)                                            \
                                                                            \
        void APINOT class::_ObjectUnwind(void * pthis)                      \
        {                                                                   \
            ((class *)pthis)->class::~class();                              \
        }

#endif // DBG

#define END_CONSTRUCTION(class)                                 \
                                                                \
        _Activate( sizeof(class), &class::_ObjectUnwind );

// The following macro will (probably) need to be redefined (to null) when
// compiler support for exceptions arrives.

#define INLINE_UNWIND(cls)                              \
        static void _ObjectUnwind ( void * pthis )      \
                { ((cls *)pthis)->cls::~cls(); };
#else

#define INHERIT_UNWIND_IF_CAIRO

#define DECLARE_UNWIND

#define END_CONSTRUCTION(class)

#define INLINE_UNWIND(cls)

#define IMPLEMENT_UNWIND(cls)

#endif

#endif // __EXCEPT_HXX__
