//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 1999
//
//  File:      comerror.h
//
//  Contents:  Support for Rich COM errors
//
//  History:   14-Oct-99 VivekJ    Created
//
//--------------------------------------------------------------------------

#ifndef COMERROR_H
#define COMERROR_H
#pragma once

#include "tiedobj.h"



//############################################################################
//############################################################################
//
//  COM Rich Error support
//
//############################################################################
//############################################################################

/*+-------------------------------------------------------------------------*
 *
 * MMCReportError
 *
 * PURPOSE: Calls AtlReportError and sets the last error condition based
 *          on an SC.
 *
 * PARAMETERS:
 *    SC &   sc :
 *    const  IID :
 *    const  CLSID :
 *
 * RETURNS:
 *    inline HRESULT
 *
 *+-------------------------------------------------------------------------*/
inline
HRESULT MMCReportError(SC &sc, const IID &iid, const CLSID & clsid)
{
    // only report the error if there was one.
    if(sc)
    {
        USES_CONVERSION;

        const  int length = 256;
        TCHAR sz[length];

        sc.GetErrorMessage(length, sz);

        // set everything we know about the error - the description, the help file,
        // the help ID, and the CLSID and IID of the source.
        return AtlReportError(clsid, sz, sc.GetHelpID(), sc.GetHelpFile(), iid, sc.ToHr());
    }
    else
        return S_OK;
}

// see "WHY NAMESPACES ?" comment at the top of mmcerror.h file
namespace comerror {
/*+-------------------------------------------------------------------------*
 * class _SC
 *
 *
 * PURPOSE: a local version of the SC class that automatically sets the error
 *          information when deleted.
 *
 * NOTE: Because of a compiler bug, this class cannot directly be internal
 *       to IMMCSupportErrorInfoImpl<>
 *
 *+-------------------------------------------------------------------------*/
template<const IID *piid, const CLSID *pclsid>
class SC : public mmcerror::SC
{
    typedef mmcerror::SC BaseClass;
public:
    SC (HRESULT hr = S_OK) : BaseClass(hr)
    {
    }

    ~SC()
    {
        MMCReportError(*this, *piid, *pclsid);
    }

    // copy constructor
    SC(const BaseClass &rhs) : BaseClass(rhs)
    {
    }

    // assignment
    SC&  operator= (HRESULT hr)             { BaseClass::operator =(hr);  return *this; }
    SC&  operator= (const BaseClass &rhs)   { BaseClass::operator =(rhs); return *this; }
    SC&  operator= (const SC &rhs)          { BaseClass::operator =(rhs); return *this; }
};

} // namespace comerror

/*+-------------------------------------------------------------------------*
 * class IMMCSupportErrorInfoImpl
 *
 *
 * PURPOSE: Inherits from ISupportErrorInfoImpl. local definition of the
 *          status code class SC, which makes it easy to return error information
 *          via the COM rich error handling system, without any extra effort
 *          on the part of the programmer.
 *
 *+-------------------------------------------------------------------------*/
template<const IID *piid, const CLSID *pclsid>
class IMMCSupportErrorInfoImpl : public ISupportErrorInfoImpl<piid>
{
    // this makes sure that AtlReportError is called in the destructor
    public:
        typedef comerror::SC<piid, pclsid> SC;
};

#define NYI_COM_METHOD()  {SC sc = E_NOTIMPL; return sc.ToHr();}


/************************************************************************
 * The following macros make it easy to implement a lightweight
 * COM object that "connects" to a non-COM tied object and delegates
 * all its methods to the non-COM object. The tied object pointer
 * is checked as well.
 ************************************************************************/
#define MMC_METHOD_PROLOG()                                         \
    SC  sc;                                                         \
                                                                    \
    CMyTiedObject *pTiedObj = NULL;                                 \
                                                                    \
    sc = ScGetTiedObject(pTiedObj);                                 \
    if(sc)                                                          \
        return (sc.ToHr())

#define MMC_METHOD_CALL(_fn)                                        \
    sc = pTiedObj->Sc##_fn

#define MMC_METHOD_EPILOG()                                         \
    return sc.ToHr()

#define MMC_METHOD0(_fn)                                            \
STDMETHOD(_fn)()                                                    \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)();                                         \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD0_PARAM(_fn, param)                               \
STDMETHOD(_fn)()                                                    \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(param);                                    \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD1(_fn, T1)                                        \
STDMETHOD(_fn)(T1 p1)                                               \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1);                                       \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD1_PARAM(_fn, T1, param)                           \
STDMETHOD(_fn)(T1 p1)                                               \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, param);                                \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD2(_fn, T1, T2)                                    \
STDMETHOD(_fn)(T1 p1, T2 p2)                                        \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2);                                   \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD2_PARAM(_fn, T1, T2, param)                       \
STDMETHOD(_fn)(T1 p1, T2 p2)                                        \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, param);                            \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD3(_fn, T1, T2, T3)                                \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3)                                 \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, p3);                               \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD3_PARAM(_fn, T1, T2, T3, param)                   \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3)                                 \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, p3, param);                        \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD4(_fn, T1, T2, T3, T4)                            \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3, T4 p4)                          \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, p3, p4);                           \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD4_PARAM(_fn, T1, T2, T3, T4, param)               \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3, T4 p4)                          \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, p3, p4, param);                    \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD5(_fn, T1, T2, T3, T4, T5)                        \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)                   \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, p3, p4, p5);                       \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD5_PARAM(_fn, T1, T2, T3, T4, T5, param)           \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5)                   \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_fn)(p1, p2, p3, p4, p5, param);                \
    MMC_METHOD_EPILOG();                                            \
}

/************************************************************************
 * A version of the above macros that adds a prefix to the Sc methods.
 * This is useful for disambiguating method names if the same object serves
 * as the tied object for more than one COM object with identical methods.
 ************************************************************************/
#define MMC_METHOD0_EX(_prefix, _fn)                                \
STDMETHOD(_fn)()                                                    \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_prefix##_fn)();                                \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD1_EX(_prefix, _fn, T1)                            \
STDMETHOD(_fn)(T1 p1)                                               \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_prefix##_fn)(p1);                              \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD2_EX(_prefix, _fn, T1, T2)                        \
STDMETHOD(_fn)(T1 p1, T2 p2)                                        \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_prefix##_fn)(p1, p2);                          \
    MMC_METHOD_EPILOG();                                            \
}

#define MMC_METHOD3_EX(_prefix, _fn, T1, T2, T3)                    \
STDMETHOD(_fn)(T1 p1, T2 p2, T3 p3)                                 \
{                                                                   \
    MMC_METHOD_PROLOG();                                            \
    MMC_METHOD_CALL(_prefix##_fn)(p1, p2, p3, param);               \
    MMC_METHOD_EPILOG();                                            \
}

#endif  // COMERROR_H