#ifndef _CCOMERRORINFO_H__ 
#define _CCOMERRORINFO_H__ 

#include <atlbase.h> 

// Requires that chainedei.h is on the include path, and that you link with chainedei.lib
#include "chainedei.h" 

class CComErrorInfo
{
public:
    // Static methods for creating and setting error info
    static void Clear()
    {
        ::SetErrorInfo(0, NULL);
    }

    static HRESULT Create(HRESULT hrInner, REFGUID guid, BSTR bstrSource, BSTR bstrDescription, BSTR bstrHelpFile, DWORD dwHelpContext, IErrorInfo** ppErrorInfo)
    {
        return CreateChainedErrorInfo(hrInner, guid, bstrSource, bstrDescription, bstrHelpFile, dwHelpContext, ppErrorInfo);
    }

    static HRESULT Create(REFGUID guid, BSTR bstrSource, BSTR bstrDescription, BSTR bstrHelpFile, DWORD dwHelpContext, IErrorInfo** ppErrorInfo)
    {
        HRESULT hr;

        if(ppErrorInfo == NULL) return E_POINTER;

        CComPtr<ICreateErrorInfo> spCreateErrorInfo;
        hr = CreateErrorInfo(&spCreateErrorInfo);
        if(FAILED(hr)) return hr;

        spCreateErrorInfo->SetGUID(guid);
        spCreateErrorInfo->SetSource(bstrSource);
        spCreateErrorInfo->SetDescription(bstrDescription);
        spCreateErrorInfo->SetHelpFile(bstrHelpFile);
        spCreateErrorInfo->SetHelpContext(dwHelpContext);

        return spCreateErrorInfo->QueryInterface(ppErrorInfo);
    }

    static HRESULT SetErrorInfo(IErrorInfo* pErrorInfo)
    {
        return ::SetErrorInfo(0, pErrorInfo);
    }

    static HRESULT SetErrorInfo(HRESULT hrInner, REFGUID guid, BSTR bstrSource, BSTR bstrDescription, BSTR bstrHelpFile, DWORD dwHelpContext)
    {
        HRESULT hr;

        CComPtr<IErrorInfo> spErrorInfo;
        hr = Create(hrInner, guid, bstrSource, bstrDescription, bstrHelpFile, dwHelpContext, &spErrorInfo);
        if(FAILED(hr)) return hr;

        return SetErrorInfo(spErrorInfo);
    }

    static HRESULT SetErrorInfo(REFGUID guid, BSTR bstrSource, BSTR bstrDescription, BSTR bstrHelpFile, DWORD dwHelpContext)
    {
        HRESULT hr;

        CComPtr<IErrorInfo> spErrorInfo;
        hr = Create(guid, bstrSource, bstrDescription, bstrHelpFile, dwHelpContext, &spErrorInfo);
        if(FAILED(hr)) return hr;

        return SetErrorInfo(spErrorInfo);
    }

    // Constructors
    CComErrorInfo()
    {
        // Automatically calls GetErrorInfo, to make sure that whenever 
        // we create an instance of CComErrorInfo, it refers to the latest
        // error information available
        GetErrorInfo(0, &m_spErrorInfo);
    }

    CComErrorInfo(IErrorInfo* pErrorInfo)
    {
        m_spErrorInfo = pErrorInfo;
    }

    // IChainedErrorInfo wrappers
    CComErrorInfo Inner() const
    {
        ATLASSERT(m_spErrorInfo);

        CComPtr<IErrorInfo> spErrorInfo;

        CComPtr<IChainedErrorInfo> spChained;
        if(SUCCEEDED(m_spErrorInfo->QueryInterface(&spChained)))
        {
            spChained->GetInnerErrorInfo(&spErrorInfo);
        }

        return spErrorInfo;
    }

    HRESULT InnerHRESULT() const
    {
        ATLASSERT(m_spErrorInfo);

        HRESULT hrInner = S_OK;

        CComPtr<IChainedErrorInfo> spChained;
        if(SUCCEEDED(m_spErrorInfo->QueryInterface(&spChained)))
        {
            spChained->GetInnerHRESULT(&hrInner);
        }

        return hrInner;
    }

    // Type accessors
    IErrorInfo* operator->() const
    {
        ATLASSERT(m_spErrorInfo);
        return m_spErrorInfo;
    }

    operator IErrorInfo*() const
    {
        return m_spErrorInfo;
    }

private:
    CComPtr<IErrorInfo> m_spErrorInfo;
};

#endif // _CCOMERRORINFO_H__