// WinException.h: interface for the CWinException class.
// Copyright (C) Alexey Veremenko 2009. All rights reserved.
// Overview:
//
// Use this class to track COM/Win32 errors. 
// It checks return value for failure and throws if an error occured.
// Several kinds of 'wrap' functions provided, even MFC exception could be wrapped.
// Typical use is: HR(pComPtr->SomeCall(...)), or WE(::CopyFile(...))
// Also exceptions with user messages allowed: USR_EX("My error"), 
// note that corresponding wrap functions ANSI and UNICODE supportable.
// Look for more samples in TestCase() member function defined .cpp file
//
//////////////////////////////////////////////////////////////////////

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#ifndef __WINEXCEPTION_H_
#define __WINEXCEPTION_H_

// ATL required
#include <atlbase.h>

// COM support header
#include <comdef.h>

// _tcsncpy() used
#include <string.h>

// Disable claims on throw()
#pragma warning( disable : 4290 ) 

// Use MFC assertions if possible
#ifndef ASSERT
#define ASSERT ATLASSERT
#endif

class CWinException
{
// Attributes
protected:
// Maximum length of error describing string
enum
{
DESC_LEN = 1024
};

// Error description string
TCHAR m_szDescription[DESC_LEN];

// Exception types
enum ErrorType
{
eWinError,
eComError,
eUserDefined,
eUserResourceString
};

// Type of this exception
ErrorType m_eType;

// Win32/COM error value
union
{
HRESULT m_hrComError;
DWORD m_dwWinError;
UINT m_uResID;
};

// Construction/Destruction
protected:
// Construct from Win32 error code
inline CWinException(DWORD dwWinError) throw()
{
m_eType = eWinError;
m_dwWinError = dwWinError;
m_szDescription[0] = 0;
ASSERT(CheckBreakPoint());
}

// Construct from COM HRESULT value
inline CWinException(HRESULT hrComError) throw()
{
m_eType = eComError;
m_hrComError = hrComError;
m_szDescription[0] = 0;
ASSERT(CheckBreakPoint());
}

// Construct from resource string
inline CWinException(UINT uID) throw()
{
m_eType = eUserResourceString;
m_uResID = uID;
m_szDescription[0] = 0;
ASSERT(CheckBreakPoint());
}

// Construct from user provided description
inline CWinException(LPCTSTR pszDesc) throw()
{
m_eType = eUserDefined; ASSERT(pszDesc != NULL);
::_tcsncpy(m_szDescription, pszDesc, DESC_LEN);
m_szDescription[DESC_LEN-1] = 0;
ASSERT(CheckBreakPoint());
}

// Construct from _com_error exception
inline CWinException(const _com_error& e) throw()
{
m_eType = eUserDefined;
::_tcsncpy(m_szDescription, e.ErrorMessage(), DESC_LEN);
ASSERT(CheckBreakPoint());
}

#ifdef __AFX_H__
// Construct from MFC exception
inline CWinException(CException* p) throw()
{
m_eType = eUserDefined; ASSERT(p != NULL);
p->GetErrorMessage(static_cast(m_szDescription), DESC_LEN);
p->Delete();
ASSERT(CheckBreakPoint());
}
#endif

public:
inline virtual ~CWinException() throw() {}

// Operations
public:
// Handle COM result
inline static void WrapComError(HRESULT hRes) throw(CWinException)
{
if (FAILED(hRes)) throw CWinException(hRes);
}

// Handle Win32 error
inline static void WrapWinError(DWORD dwCode) throw(CWinException)
{
if (dwCode != NO_ERROR) throw CWinException(dwCode);
}

// Handle Win32 result
inline static void WrapWinError(BOOL bRes) throw(CWinException)
{
if (bRes == FALSE) WrapWinError(::GetLastError());
}

// Handle user exception from resource string
inline static void WrapUserError(UINT uID) throw(CWinException)
{
throw CWinException(uID);
}

// Handle user exception from UNICODE string
inline static void WrapUserError(LPCWSTR pszDesc) throw(CWinException)
{
USES_CONVERSION;
if (pszDesc != NULL) throw CWinException(W2T(const_cast(pszDesc)));
}

// Handle user exception from ANSI string
inline static void WrapUserError(LPCSTR pszDesc) throw(CWinException)
{
USES_CONVERSION;
if (pszDesc != NULL) throw CWinException(A2T(const_cast(pszDesc)));
}

// Handle _com_error exception
inline static void WrapComException(const _com_error& e) throw(CWinException)
{
throw CWinException(e);
}

#ifdef __AFX_H__
// Handle MFC exception
inline static void WrapMfcError(CException* p) throw(CWinException)
{
if (p != NULL) throw CWinException(p);
}
#endif

// Get error description
inline LPCTSTR GetErrorMessage()
{
GetDescriptionFromCode();	// Prepare message
return m_szDescription;
}

// Implementation
protected:
bool GetComMessage(HRESULT hrErr);
bool GetWinMessage(DWORD dwErr);
bool GetResMessage(UINT uID);
bool GetErrorInfoMessage();
bool GetUnexpectedMessage();
void GetDescriptionFromCode();

// Diagnostic support
private:
// This function is called from any constructor.
// Set a breakpoint inside and catch the source of any exception
inline bool CheckBreakPoint()
{
return true;
}

public:
// Show messagebox with description or write to console
// Always returns true for convinient use in debug mode: ASSERT(e.Display());
bool Display();

// Sample usage
static void TestCase();
};

// Synonym
typedef CWinException CWEx;

// Throw new exception macros
#define HR(hRes) CWEx::WrapComError(hRes);
#define WE(bRes) CWEx::WrapWinError(bRes);
#define WE_CODE(dwRes) CWEx::WrapWinError((DWORD)dwRes);
#define USR_EX(uID) CWEx::WrapUserError(uID);

#endif // __WINEXCEPTION_H_