#ifndef __TCLib_h__
#define __TCLib_h__

/////////////////////////////////////////////////////////////////////////////
// {group:Class Libraries}
// TCLib.h | Declarations and definitions of extensions to C++ Runtime
// Library.  These declarations and definitions are to be independent from
// both the Active Template Library (ATL) and the Microsoft Foundation Class
// (MFC) frameworks, but are also to be compatible with them.
//
// The files in this library are intended to be used in much the same way as
// the files in the ATL framework.  The TCLib.h file should be included
// from a project's pch.h file (or the functional equivalent), while the
// TCLib.cpp file should be included by ONLY ONE module in the project.
// Usually this will be the pch.cpp file (or the functional equivalent).
//

// Disable this warning since STL may generate some very long symbolic names
#pragma warning(disable: 4786)
#include <typeinfo.h>
#include <comdef.h>
#include <crtdbg.h>
#include <zassert.h>


/////////////////////////////////////////////////////////////////////////////
// Macros

#ifndef sizeofArray
  ///////////////////////////////////////////////////////////////////////////
  // Counts the number of elements in a fixed-length array.
  // Parameters:  x - The name of the array of which to compute the size.
  #define sizeofArray(x) (sizeof(x) / sizeof(x[0]))
#endif // !sizeofArray


///////////////////////////////////////////////////////////////////////////
// High order bit extraction.
// Parameters:
//   x - value to be shifted.
//   nBitCount - number of high order bits to be shifted into the low order
//   bits.
//
// Return Value: A value of type, /T/, shifted to the right so that the
// specified number of high order bits are now in the low order bits.
//
// See Also: HiBit, LoBits, LoBit
template <class T>
inline T HiBits(T x, unsigned nBitCount)
  {return x >> (sizeof(T) * 8 - nBitCount);}

///////////////////////////////////////////////////////////////////////////
// High order bit extraction.
// Parameters:
//   x - value to be shifted.
//
// Return Value: A value of type, /T/, shifted to the right so that the
// most significant bit is now the least significant bit.
//
// See Also: HiBits, LoBit, LoBits
template <class T>
inline T HiBit(T x)
  {return x >> (sizeof(T) * 8 - 1);}

///////////////////////////////////////////////////////////////////////////
// Low order bit extraction.
// Parameters:
//   x - value to be masked.
//   nBitCount - number of low order bits to be masked.
//
// Return Value: A value of type /T/, masked so that only the specified
// number of low order bits are preserved from the original value, /x/. The
// remaining high order bits are all set to zero.
// See Also: LoBit, HiBits, HiBit
template <class T>
inline T LoBits(T x, unsigned nBitCount)
  {return x & ((1 < nBitCount) - 1);}

///////////////////////////////////////////////////////////////////////////
// Low order bit extraction.
// Parameters:
//   x - value to be masked.
//
// Return Value: A value of type /T/, masked so that only the least
// significant bit is preserved from the original value, /x/. The
// remaining high order bits are all set to zero.
// See Also: LoBits, HiBit, HiBits
template <class T>
inline T LoBit(T x)
  {return x & 0x01;}


///////////////////////////////////////////////////////////////////////////
// Translates a boolean expression to a valid VARIANT_BOOL.
//
// Parameters:  expression - boolean expression to be translated.
//
// Return Value: *VARIANT_TRUE* (short(-1)) if the expression is non-zero,
// otherwise *VARIANT_FALSE* (short(0)).
#define VARBOOL(expression) \
  VARIANT_BOOL((expression) ? VARIANT_TRUE : VARIANT_FALSE)


///////////////////////////////////////////////////////////////////////////
// Sets an [out] parameter to an initial value.  The assignment is
// performed within a *__try* block of an exception handler. The
// corresponding *__except* block of the exception handler simply returns
// E_POINTER.
//
// Return Value: *S_OK* if the assignment succeeded, otherwise *E_POINTER*.
//
// Parameters:
//   dest - Address of variable to initialize.
//   init - Value to be assigned to /dest/.
//
// See Also: CLEAROUT, CLEAROUT_ALLOW_NULL
template <class T>
inline HRESULT ClearOut(T* dest, const T& init)
{
  __try
  {
    *dest = init;
    return S_OK;
  }
  __except(1)
  {
    return E_POINTER;
  }
}


///////////////////////////////////////////////////////////////////////////
// Macro wrapper for the ClearOut function that will return if the ClearOut
// function fails.
//
// Parameters:
//   dest - Address of variable to initialize.
//   init - Value to be assigned to /dest/.
//
// See Also: ClearOut, CLEAROUT_ALLOW_NULL
#define CLEAROUT(dest, init) \
{ \
  HRESULT _hr = ClearOut(dest, init); \
  if (FAILED(_hr)) \
    return _hr; \
}


///////////////////////////////////////////////////////////////////////////
// Macro wrapper for the ClearOut function that will return if the ClearOut
// function fails.  If /dest/ is equal to NULL, the macro does not attempt
// to initialize it.
//
// Parameters:
//   dest - Address of variable to initialize.
//   init - Value to be assigned to /dest/.
//
// See Also: ClearOut, CLEAROUT
#define CLEAROUT_ALLOW_NULL(dest, init) \
  if (NULL != dest) \
    CLEAROUT(dest, init)


///////////////////////////////////////////////////////////////////////////
// Initializes a VARIANT* [out] parameter.  The initialization is performed
// within a *__try* block of an exception handler. The corresponding
// *__except* block of the exception handler simply returns E_POINTER.
//
// Return Value: *S_OK* if the initialization succeeded, otherwise
// *E_POINTER*.
//
// Parameters:
//   pvar - Address of variant to initialize.
//
// See Also: INIT_VARIANT_OUT, CLEAROUT
inline HRESULT InitVariantOut(VARIANT* pvar)
{
  __try
  {
    ::VariantInit(pvar);
    return S_OK;
  }
  __except(1)
  {
    return E_POINTER;
  }
}


///////////////////////////////////////////////////////////////////////////
// Macro wrapper for the InitVariantOut function that will return if the
// InitVariantOut function fails.
//
// Parameters:
//   pvar - Address of variant to initialize.
//
// See Also: InitVariantOut, CLEAROUT
#define INIT_VARIANT_OUT(pvar)        \
do                                    \
{                                     \
  HRESULT _hr = InitVariantOut(pvar); \
  if (FAILED(_hr))                    \
    return _hr;                       \
} while (0)


#ifdef _DEBUG

  /////////////////////////////////////////////////////////////////////////
  // Outputs a debug string under _DEBUG builds indicating that the module
  // is initializing or terminating.
  // Parameters:
  //   hinst - Module instance that is initializing or terminating.
  //   bInit - *true* if module is initializing, otherwise *false*,
  // specifying that the module is terminating.
  inline void TRACE_MODULE_INIT(HINSTANCE hinst, bool bInit)
  {
    TCHAR szModule[_MAX_PATH + 32];
    DWORD cch = GetModuleFileName(hinst, szModule, sizeofArray(szModule));
    LPTSTR pszBackSlash = szModule + cch;
    while (*pszBackSlash != TEXT('\\') && pszBackSlash > szModule)
      --pszBackSlash;
    *pszBackSlash = TEXT('\0');

    TCHAR szInit[] = TEXT(" Initialized\n");
    TCHAR szTerm[sizeofArray(szInit)] = TEXT(" Terminated\n");
    CopyMemory(szModule + cch, bInit ? szInit : szTerm, sizeof(szInit));
    USES_CONVERSION;
    ZDebugOutputImpl(T2CA(pszBackSlash + 1));
  }

#else // _DEBUG

  #define TRACE_MODULE_INIT(hinst, bInit)

#endif // _DEBUG


/////////////////////////////////////////////////////////////////////////////
// An exception-safe and type-safe way to clear a block of memory.
//
template <class T>
inline HRESULT TCZeroMemory(T* p, unsigned cb = sizeof(T))
{
  __try
  {
    ::ZeroMemory(p, cb);
    return S_OK;
  }
  __except(1)
  {
    return E_POINTER;
  }
}


/////////////////////////////////////////////////////////////////////////////
// An exception-safe and type-safe way to QueryInterface an IUnknown pointer.
//
template <class TIID>
inline HRESULT TCSafeQI(IUnknown* punk, TIID** ppOut)
{
  __try
  {
    return punk->QueryInterface(__uuidof(**ppOut), (void**)ppOut);
  }
  __except(1)
  {
    return E_POINTER;
  }
}


/////////////////////////////////////////////////////////////////////////////
// 64-bit Integer Types

typedef __int64 TCINT64;
typedef unsigned __int64 TCUINT64;


/////////////////////////////////////////////////////////////////////////////
// FILETIME Conversions

inline TCUINT64 FileTimeToUINT64(const FILETIME& ft)
{
  #ifdef _M_IX86
    return *((const TCUINT64*)(&ft));
  #else
    return ((TCUINT64)ft.dwHighDateTime << 32) | ft.dwLowDateTime;
  #endif
}

inline void UINT64ToFileTime(TCUINT64 n64, FILETIME& ft)
{
  #ifdef _M_IX86
    ft = *((FILETIME*)(&n64));
  #else
    ft.dwLowDateTime = (DWORD)(n64);
    ft.dwHighDateTime = (DWORD)(n64 >> 32);
  #endif
}

inline TCUINT64 GetSystemTimeAsUINT64()
{
  FILETIME ft;
  GetSystemTimeAsFileTime(&ft);
  return FileTimeToUINT64(ft);
}

inline DWORD FileTimeToMs(TCUINT64 n64)
{
  return (DWORD)(n64 / TCUINT64(10000));
}

inline DWORD FileTimeToMs(const FILETIME& ft)
{
  return FileTimeToMs(FileTimeToUINT64(ft));
}


/////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
  ///////////////////////////////////////////////////////////////////////////
  // Macro Group: _VERIFY, _VERIFYE Macros
  //
  // These macros evaluate an expression and generate a debug report when the
  // result is FALSE (debug version only). Under release builds, the
  // expression is still evaluated, but no debug report is generated.
  //
  // Declaration:
  // #ifdef _DEBUG
  //   #define _VERIFY(f)               _ASSERT(f)
  //   #define _VERIFYE(f)              assert(f)
  // #else
  //   #define _VERIFY(f)               ((void)(f))
  //   #define _VERIFYE(f)              ((void)(f))
  // #endif
  //
  // Parameters:
  //   f - A boolean expression (including pointers) that evaluates to
  // nonzero or 0.
  //
  // See Also: _SVERIFY, _SVERIFYE
  #define _VERIFY(f)             assert(f)
  #define _VERIFYE(f)            assert(f)            // {partof:_VERIFY}

  ///////////////////////////////////////////////////////////////////////////
  // Macro Group: _SVERIFY, _SVERIFYE Macros
  //
  // These macros evaluate an expression and generate a debug report when the
  // result of passing the expression to the SUCCEEDED macro is FALSE (debug
  // version only). Under release builds the expression is still evaluated,
  // but no debug report is generated, and the evaluated expression is not
  // passed to the SUCCEEDED macro.
  //
  // Declaration:
  // #ifdef _DEBUG
  //   #define _SVERIFY(hr)               ZSucceeded(hr)
  //   #define _SVERIFYE(hr)              ZSucceeded(hr)
  // #else
  //   #define _SVERIFY(hr)               ((void)(hr))
  //   #define _SVERIFYE(hr)              ((void)(hr))
  // #endihr
  //
  // Parameters:
  //   hr - An HRESULT expression that indicates either success (0 or
  // positive), or failure (negative).
  //
  // See Also: _VERIFY, _VERIFYE
  #define _SVERIFY(hr)         ZSucceeded(hr)
  #define _SVERIFYE(hr)        ZSucceeded(hr) // {partof:_SVERIFY}

  ///////////////////////////////////////////////////////////////////////////
  // This macro provides a shorthand way to evaluate an expression only under
  // _DEBUG builds. Under release builds the expression is *not* evaluated.
  //
  // Declaration:
  // #ifdef _DEBUG
  //   #define DEBUG_ONLY(f)          (f)
  // #else
  //   #define DEBUG_ONLY(f)          ((void)0)
  // #endif
  //
  // Parameters:
  //   f - An expression that will be evaluated only under _DEBUG builds.
  #define DEBUG_ONLY(f)          (f)

  void _cdecl TCTrace(LPCTSTR lpszFormat, ...);

  #ifdef _DOCJET_ONLY
  ///////////////////////////////////////////////////////////////////////////
  // Macro Group: The _TRACE Macros
  //
  // These macros provide similar functionality to the printf function by
  // sending a formatted string to a debug monitor. Like printf for console
  // programs, these macros are a convenient way to track the value of
  // variables as your program executes. In the Debug environment, the macro
  // output goes to the debug monitor, if any. Under release builds, they do
  // nothing.
  //
  // For sending output to the debug monitor under both _DEBUG *and* release
  // builds, see the TCERRLOG macros.
  //
  // Parameters:
  //   sz - A format string literal as used in the run-time function
  // *printf*.
  //   p1_thru_p5 - Parameters which are referenced by the '%' placeholders
  // in the /sz/ format string.
  //
  // Declaration:
  // #ifdef _DEBUG
  //   #define _TRACE0(sz)                  TCTrace(TEXT(sz))
  //   #define _TRACE1(sz, p1)              TCTrace(TEXT(sz), p1)
  //   #define _TRACE2(sz, p1,p2)           TCTrace(TEXT(sz), p1,p2)
  //   #define _TRACE3(sz, p1,p2,p3)        TCTrace(TEXT(sz), p1,p2,p3)
  //   #define _TRACE4(sz, p1,p2,p3,p4)     TCTrace(TEXT(sz), p1,p2,p3,p4)
  //   #define _TRACE5(sz, p1,p2,p3,p4,p5)  TCTrace(TEXT(sz), p1,p2,p3,p4,p5)
  // #else
  //   #define _TRACE0(sz)
  //   #define _TRACE1(sz, p1)
  //   #define _TRACE2(sz, p1,p2)
  //   #define _TRACE3(sz, p1,p2,p3)
  //   #define _TRACE4(sz, p1,p2,p3,p4)
  //   #define _TRACE5(sz, p1,p2,p3,p4,p5)
  // #endif
  //
  // See Also: TCTrace, TCERRLOG macros
  #define _TRACE
  #endif // _DOCJET_ONLY

  // {partof:_TRACE}
  #define _TRACE0(sz)                  TCTrace(TEXT(sz))
  // {partof:_TRACE}
  #define _TRACE1(sz, p1)              TCTrace(TEXT(sz), p1)
  // {partof:_TRACE}
  #define _TRACE2(sz, p1,p2)           TCTrace(TEXT(sz), p1,p2)
  // {partof:_TRACE}
  #define _TRACE3(sz, p1,p2,p3)        TCTrace(TEXT(sz), p1,p2,p3)
  // {partof:_TRACE}
  #define _TRACE4(sz, p1,p2,p3,p4)     TCTrace(TEXT(sz), p1,p2,p3,p4)
  // {partof:_TRACE}
  #define _TRACE5(sz, p1,p2,p3,p4,p5)  TCTrace(TEXT(sz), p1,p2,p3,p4,p5)

  #define _TRACE_BEGIN_SIZE(cchMax)                                         \
    {                                                                       \
      TCHAR* _trace_szMsg = (LPTSTR)_alloca(cchMax * sizeof(TCHAR));        \
      int    _trace_nIndex = 0;

  #define _TRACE_BEGIN                                                      \
    _TRACE_BEGIN_SIZE(_MAX_PATH)

  #define _TRACE_PART0(sz)                                                  \
      _trace_nIndex +=                                                      \
        _stprintf(_trace_szMsg + _trace_nIndex, _T(sz))

  #define _TRACE_PART1(sz, p1)                                              \
      _trace_nIndex +=                                                      \
        _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1)

  #define _TRACE_PART2(sz, p1,p2)                                           \
      _trace_nIndex +=                                                      \
        _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2)

  #define _TRACE_PART3(sz, p1,p2,p3)                                        \
      _trace_nIndex +=                                                      \
        _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2,p3)

  #define _TRACE_PART4(sz, p1,p2,p3,p4)                                     \
      _trace_nIndex +=                                                      \
        _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2,p3,p4)

  #define _TRACE_PART5(sz, p1,p2,p3,p4,p5)                                  \
      _trace_nIndex +=                                                      \
        _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2,p3,p4,p5)

  #define _TRACE_END                                                        \
      USES_CONVERSION;                                                      \
      ZDebugOutputImpl(T2CA(_trace_szMsg));                                 \
    }

  #ifndef UNUSED
    /////////////////////////////////////////////////////////////////////////
    // This macro references the specified parameter so that the compiler
    // will not generate a warning to indicate that the parameter is not
    // referenced.
    //
    // The macro only references the specified parameter under release
    // builds. To reference an unused parameter under both _DEBUG
    // *and* release builds, see UNUSED_ALWAYS.
    //
    // Declaration:
    // #ifndef UNUSED
    //   #ifdef _DEBUG
    //     #define UNUSED(x)
    //   #else
    //     #define UNUSED(x) x
    //   #endif
    // #endif
    //
    // Parameters:
    //   x - The parameter of the current function that is otherwise not
    // referenced in the function.
    //
    // See Also: UNUSED_ALWAYS
    #define UNUSED(x)
  #endif // !UNUSED

#else // _DEBUG

  #define _VERIFY(f)               ((void)(f))
  #define _VERIFYE(f)              ((void)(f))
  #define _SVERIFY(f)              _VERIFY(f)
  #define _SVERIFYE(f)             _VERIFYE(f)
  #define DEBUG_ONLY(f)            ((void)0)

  #define _TRACE0(sz)
  #define _TRACE1(sz, p1)
  #define _TRACE2(sz, p1,p2)
  #define _TRACE3(sz, p1,p2,p3)
  #define _TRACE4(sz, p1,p2,p3,p4)
  #define _TRACE5(sz, p1,p2,p3,p4,p5)

  #define _TRACE_BEGIN_SIZE(cchMax)   {
  #define _TRACE_BEGIN                {
  #define _TRACE_PART0(sz)
  #define _TRACE_PART1(sz, p1)
  #define _TRACE_PART2(sz, p1,p2)
  #define _TRACE_PART3(sz, p1,p2,p3)
  #define _TRACE_PART4(sz, p1,p2,p3,p4)
  #define _TRACE_PART5(sz, p1,p2,p3,p4,p5)
  #define _TRACE_END                  }

  #ifndef UNUSED
    #define UNUSED(x) x
  #endif // !UNUSED

#endif // _DEBUG

#ifndef UNUSED_ALWAYS
    /////////////////////////////////////////////////////////////////////////
    // This macro references the specified parameter so that the compiler
    // will not generate a warning to indicate that the parameter is not
    // referenced.
    //
    // The macros references the specified parameter under both _DEBUG and
    // release builds. To reference an unused parameter under *only* release
    // builds, see UNUSED.
    //
    // Declaration:
    // #ifndef UNUSED_ALWAYS
    //   #define UNUSED_ALWAYS(x) x
    // #endif
    //
    // Parameters:
    //   x - The parameter of the current function that is otherwise not
    // referenced in the function.
    //
    // See Also: UNUSED
  #define UNUSED_ALWAYS(x) x
#endif // !UNUSED_ALWAYS

/////////////////////////////////////////////////////////////////////////////
// Evaluates an HRESULT expression and throws the HRESULT as an exception if
// it indicates a failure.
//
// Parameters:
//   hr - The HRESULT expression to be evaluated.
//
// Return Value: If the specified /hr/ parameter indicates success, it is
// returned as specified. If the /hr/ parameter indicates failure, the
// function does not return normally, but instead throws the /hr/ as a
// C++ exception.
//
// See Also: ThrowErrorFAILED, RETURN_FAILED, RETURN_FAILED_VOID
inline HRESULT ThrowFAILED(HRESULT hr)
{
  if (FAILED(hr))
    throw hr;
  return hr;
}

/////////////////////////////////////////////////////////////////////////////
// Evaluates an HRESULT expression and throws a _com_error exception if it
// indicates a failure.
//
// Parameters:
//   hr - The HRESULT expression to be evaluated.
//
// Return Value: If the specified /hr/ parameter indicates success, it is
// returned as specified. If the /hr/ parameter indicates failure, the
// function does not return normally, but instead throws a _com_error
// C++ exception, containing the failed /hr/ and any current IErrorInfo.
//
// See Also: ThrowError, RETURN_FAILED, RETURN_FAILED_VOID
inline HRESULT ThrowErrorFAILED(HRESULT hr)
{
  if (FAILED(hr))
  {
    IErrorInfo* pei = NULL;
    ::GetErrorInfo(NULL, &pei);
    ::SetErrorInfo(NULL, pei);
    throw _com_error(hr, pei);
  }
  return hr;
}

/////////////////////////////////////////////////////////////////////////////
// Evaluates an HRESULT expression and returns it if it indicates a failure.
// This should only be used from a function that has an HRESULT (or
// compatible) return type.
//
// Parameters:
//   exp - The HRESULT expression to be evaluated.
//
// See Also: RETURN_FAILED_VOID, ThrowError, ThrowErrorFAILED
#define RETURN_FAILED(exp)  \
{                           \
  HRESULT _hr = exp;        \
  if (FAILED(_hr))          \
    return _hr;             \
}

/////////////////////////////////////////////////////////////////////////////
// Evaluates an HRESULT expression and returns if it indicates a failure.
// This should only be used from a function that has a void return type.
//
// Parameters:
//   exp - The HRESULT expression to be evaluated.
//
// See Also: RETURN_FAILED, ThrowError, ThrowErrorFAILED
#define RETURN_FAILED_VOID(exp)  \
{                                \
  if (FAILED(exp))               \
    return;                      \
}

/////////////////////////////////////////////////////////////////////////////
// Formats and reports the specified COM error.
//
// Parameters:
//   szFn - A string specifying the function where the error occurred or the
// function that is reporting the error.
//   sz2 - A string specifying what the function is doing with the error.
// Usually, this would be "Returning" or "Caught!".
//   e - A _com_error reference that indicates the error that occurred.
//
// Return Value: The HRESULT of the error that occurred.
//
// See Also: TCReportHR, _TRACE4
inline HRESULT TCReportComErr(LPCSTR szFn, LPCSTR sz2, _com_error& e)
{
  _TRACE4("%hs: %hs 0x%08X - %s\n", szFn, sz2, e.Error(), e.ErrorMessage());
  IErrorInfoPtr pei(e.ErrorInfo());
  ::SetErrorInfo(NULL, pei);
  return e.Error();
}

/////////////////////////////////////////////////////////////////////////////
// Formats and reports the specified COM error.
//
// Parameters:
//   szFn - A string specifying the function where the error occurred or the
// function that is reporting the error.
//   sz2 - A string specifying what the function is doing with the error.
// Usually, this would be "Returning" or "Caught!".
//   hr - The HRESULT of the error that occurred.
//
// Return Value: The HRESULT of the error that occurred.
//
// See Also: TCReportComErr
inline HRESULT TCReportHR(LPCSTR szFn, LPCSTR sz2, HRESULT hr)
{
  IErrorInfo* pei = NULL;
  ::GetErrorInfo(NULL, &pei);
  ::SetErrorInfo(NULL, pei);
  return TCReportComErr(szFn, sz2, _com_error(hr, pei));
}

#ifdef _CPPUNWIND

  /////////////////////////////////////////////////////////////////////////////
  // Defines a *catch* block that catches C++ exceptions of type _com_error&.
  // The *catch* block simply calls TCReportComErr with the specified function
  // name string, the string literal "Returning", and the _com_error exception
  // that was caught.
  //
  // Parameters:
  //   szFn - A string specifying the function where the error occurred or the
  // function that is reporting the error.
  //
  // See Also: TCReportComErr, TC_CATCH_HR, TC_CATCH_ALL, TC_CATCH_ALL_VOID
  #define TC_CATCH_COM_ERR(szFn) catch (_com_error& e) \
    {return TCReportComErr(szFn, "Returning", e);}

  /////////////////////////////////////////////////////////////////////////////
  // Defines a *catch* block that catches C++ exceptions of type HRESULT. The
  // *catch* block simply calls TCReportHR with the specified function name
  // string, the string literal "Returning", and the HRESULT exception that was
  // caught.
  //
  // Parameters:
  //   szFn - A string specifying the function where the error occurred or the
  // function that is reporting the error.
  //
  // See Also: TCReportHR, TC_CATCH_COM_ERR, TC_CATCH_ALL, TC_CATCH_ALL_VOID
  #define TC_CATCH_HR(szFn) catch (HRESULT _hr_) \
    {return TCReportHR(szFn, "Returning", _hr_);}

  /////////////////////////////////////////////////////////////////////////////
  // Defines a *catch* block that catches all C++ exceptions not already
  // caught. The *catch* block simply calls _TRACE1, specifying that the
  // function "Caught an unknonwn exception" and returns the specified return
  // value.
  //
  // Parameters:
  //   szFn - A string specifying the function that is reporting the error.
  //   result - The return value to return from the *catch* block.
  //
  // See Also: TC_CATCH_ALL_VOID, _TRACE1
  #define TC_CATCH_ALL(szFn, result) catch (...) \
    {_TRACE1("%hs: Caught an unknown exception\n", szFn); return result;}

  /////////////////////////////////////////////////////////////////////////////
  // Defines a *catch* block that catches all C++ exceptions not already
  // caught. The *catch* block simply calls _TRACE1, specifying that the
  // function "Caught an unknonwn exception". The *catch* block does
  // *not* return.
  //
  // TODO: This should probably be changed to end the *catch* block with a void
  // return statement. Then, another macro, TC_CATCH_ALL_NR, could be coded to
  // act as this one currently does.
  //
  // Parameters:
  //   szFn - A string specifying the function that is reporting the error.
  //
  // See Also: TC_CATCH_ALL, _TRACE1
  #define TC_CATCH_ALL_VOID(szFn) catch (...) \
    {_TRACE1("%hs: Caught an unknown exception\n", szFn);}

#endif // _CPPUNWIND

/////////////////////////////////////////////////////////////////////////////
// Eliminates from the specified type's name, any of the specified prefixes.
// This is useful when using a type's name in a debug string, and a
// particular set of type prefixes are unnecessary in the debug output, such
// as "class" and "struct".
//
// Parameters:
//   ti - A constant reference to a *type_info* class, as returned from the
// C++ *typeid* operator.
//   nCount - The number of prefix strings specified the variable argument
// list.
//   argptr - A va_list containing the standardized representation of the
// variable argument list.
//
// Return Value: A pointer to the first character after one of the specified
// prefixes in the type name. If the type name has none of the prefixes, the
// return value is a pointer to the first character of the entire type name.
//
// See Also: TCTypeName
inline LPCSTR TCTypeName_EatPrefixesV(const type_info& ti, long nCount,
  va_list argptr)
{
  LPCSTR pszName = ti.name();
  for (int i = 0; i < nCount; i++)
  {
    LPCSTR pszPrefix = va_arg(argptr, LPCSTR);
    size_t nSize = strlen(pszPrefix);
    if (0 == strncmp(pszName, pszPrefix, nSize))
      pszName += nSize;
  }
  return pszName;
}

// {partof:TCTypeName_EatPrefixesV}
inline LPCSTR TCTypeName_EatPrefixes(const type_info& ti, long nCount, ...)
{
  va_list argptr;
  va_start(argptr, nCount);
  LPCSTR psz = TCTypeName_EatPrefixesV(ti, nCount, argptr);
  va_end(argptr);
  return psz;
}

/////////////////////////////////////////////////////////////////////////////
// This macro simply calls *typeid* with the specified type and removes the
// prefixes "class " and "struct ".
//
// Parameters:
//   T - The type for which a modified name will be generated.
//
// See Also: TCTypeName_EatPrefixes
#ifdef _CPPRTTI
  #define TCTypeName(T) \
    TCTypeName_EatPrefixes(typeid(T), 2, "class ", "struct ")
#else
  #define TCTypeName(T) ""
#endif


/////////////////////////////////////////////////////////////////////////////
// Component Category Registration

HRESULT RegisterComponentCategory(REFGUID catid, LPCOLESTR pszDesc);
HRESULT RegisterComponentCategory(LPCOLESTR pszCLSID, LPCOLESTR pszDesc);
HRESULT UnregisterComponentCategory(REFGUID catid);
HRESULT UnregisterComponentCategory(LPCOLESTR pszCLSID);


/////////////////////////////////////////////////////////////////////////////
// Date and Time Retrieval / Conversion

DATE GetVariantDateTime();
DATE GetVariantDate();
DATE GetVariantTime();
DATE GetLocalVariantDateTime();
DATE GetLocalVariantDate();
DATE GetLocalVariantTime();
bool TCSystemTimeToTzSpecificLocalTime(TIME_ZONE_INFORMATION* pTimeZone,
  SYSTEMTIME* pUniversalTime, SYSTEMTIME* pLocalTime);
bool VariantTimeToLocalTime(DATE date, SYSTEMTIME* psystime);
bool FileTimeToVariantTime(FILETIME* pft, DATE* pdate);
int FormatDateTime(SYSTEMTIME* psystime, LPTSTR psz, int nLength);
int FormatVariantDateTimeAsLocal(DATE date, LPTSTR psz, int nLength);
int FormatDate(SYSTEMTIME* psystime, LPTSTR psz, int nLength);
int FormatVariantDateAsLocal(DATE date, LPTSTR psz, int nLength);
int FormatTime(SYSTEMTIME* psystime, LPTSTR psz, int nLength);
int FormatVariantTimeAsLocal(DATE date, LPTSTR psz, int nLength);
int FormatTimeSpan(SYSTEMTIME* psystime, LPTSTR psz, int nLength);
int FormatVariantTimeSpan(DATE date, LPTSTR psz, int nLength);


/////////////////////////////////////////////////////////////////////////////
// MFC Date and Time Wrappers

#ifdef _AFX

  CString FormatDateTime(SYSTEMTIME* psystime);
  BOOL FormatDateTime(SYSTEMTIME* psystime, CString& str);

  CString FormatVariantDateTimeAsLocal(DATE date);
  BOOL FormatVariantDateTimeAsLocal(DATE date, CString& str);

  CString FormatDate(SYSTEMTIME* psystime);
  BOOL FormatDate(SYSTEMTIME* psystime, CString& str);

  CString FormatVariantDateAsLocal(DATE date);
  BOOL FormatVariantDateAsLocal(DATE date, CString& str);

  CString FormatTime(SYSTEMTIME* psystime);
  BOOL FormatTime(SYSTEMTIME* psystime, CString& str);

  CString FormatVariantTimeAsLocal(DATE date);
  BOOL FormatVariantTimeAsLocal(DATE date, CString& str);

  CString FormatTimeSpan(SYSTEMTIME* psystime);
  BOOL FormatTimeSpan(SYSTEMTIME* psystime, CString& str);

  CString FormatVariantTimeSpan(DATE date);
  BOOL FormatVariantTimeSpan(DATE date, CString& str);

#endif // _AFX


/////////////////////////////////////////////////////////////////////////////
// Object Persistence

HRESULT SaveObjectToNewStream(IUnknown* punk, IStream** ppStream);
HRESULT LoadObjectFromStream(IUnknown* punk, IStream* pStream);
HRESULT CopyObjectThruStream(IUnknown* punkDest, IUnknown* punkSrc);
HRESULT CreateCopyObjectThruStream(IUnknown* punk, REFIID riid, void** ppv);
HRESULT CompareObjectsThruStream(IUnknown* punk1, IUnknown* punk2);
HRESULT CompareObjectsThruThisStream(IStream* pstm1, IUnknown* punk2);
HRESULT CompareObjectCLSIDs(IUnknown* punk1, IUnknown* punk2);


/////////////////////////////////////////////////////////////////////////////
// User Interface Helpers

bool FixSliderThumbSize(HWND hwndSlider);

#if defined(_AFX)
  bool FixSliderThumbSize(CWnd* pwndSlider);
#endif // _AFX


/////////////////////////////////////////////////////////////////////////////
// GUID Comparison Operators

/////////////////////////////////////////////////////////////////////////////
// Compares two GUID's to determine if /guid1/ is less than /guid2/. The
// less-than comparison is made using the memcmp runtime library function.
//
// Return Value: true if /guid1/ is less than /guid2/, otherwise false.
//
// Parameters:
//   guid1 - A constant GUID reference. Since CLSID and IID are just type
// definitions of GUID, they can also be used here.
//   guid2 - A constant GUID reference. Since CLSID and IID are just type
// definitions of GUID, they can also be used here.
inline bool operator < (const GUID& guid1, const GUID& guid2)
{
  return memcmp(&guid1, &guid2, sizeof(guid1)) < 0;
}


/////////////////////////////////////////////////////////////////////////////
// GUID Conversion

/////////////////////////////////////////////////////////////////////////////
// Generates the string representation of the specified GUID. The conversion
// is done by calling the Win32 StringFromGUID2 API.
//
// Parameters:
//   guid - A constant GUID reference. Since CLSID and IID are just type
// definitions of GUID, they can also be used here.
//
// Return Value: A _bstr_t instance that contains the string representation
// of the specified GUID.
inline _bstr_t BSTRFromGUID(REFGUID guid)
{
  OLECHAR szGUID[48];
  StringFromGUID2(guid, szGUID, sizeofArray(szGUID));
  return _bstr_t(szGUID);
}


/////////////////////////////////////////////////////////////////////////////
// Variant Helpers

/////////////////////////////////////////////////////////////////////////////
// Determines if the specified VARIANT contains an object reference.
//
// Parameters:
//   v - A pointer to the VARIANT to be tested.
//
// Return Value: true if the type of the specified VARIANT is either
// VT_UNKNOWN or VT_DISPATCH; false otherwise.
//
// See Also: V_OBJECT
#define V_ISOBJECT(v)    (VT_UNKNOWN == V_VT(v) || VT_DISPATCH == V_VT(v))

/////////////////////////////////////////////////////////////////////////////
// Returns the IUnknown* of the object reference contained by the specified
// VARIANT, if the VARIANT does contain an object reference.
//
// Parameters:
//   v - A pointer to the VARIANT to be tested.
//
// Return Value: The IUnknown* of the object reference contained by the
// specified VARIANT, if it does contain an object reference; NULL otherwise.
//
// See Also: V_ISOBJECT
#define V_OBJECT(v)                                                         \
  (V_ISOBJECT(v) ? (IUnknown*)IUnknownPtr(V_DISPATCH(v)) : (IUnknown*)NULL)  


/////////////////////////////////////////////////////////////////////////////
// BSTR Helpers


/////////////////////////////////////////////////////////////////////////////
// Safely determines the character length of a BSTR.
//
// This function first checks the specified BSTR for the NULL value and
// returns zero if it is. Otherwise, it passes the BSTR to the SysStringLen
// API.
//
// Parameters:
//   bstr - A BSTR value for which the length is to be retrieved.
//
// Return Value: The length of the BSTR, in characters.
//
inline UINT BSTRLen(BSTR bstr)
{
  return bstr ? ::SysStringLen(bstr) : 0;
}


/////////////////////////////////////////////////////////////////////////////
// Determine if we're running on Windows 9x or not.
//
// Return Value: *true* if the operation system is Windows 95 or Windows 98.
// *false* if the operating system is Windows NT or Windows 2000.
//
inline bool IsWin9x()
{
  OSVERSIONINFO osvi = {sizeof(osvi)};
  _VERIFYE(GetVersionEx(&osvi));
  return !(VER_PLATFORM_WIN32_NT & osvi.dwPlatformId);
}


/////////////////////////////////////////////////////////////////////////////
// Determine if we're running on Windows NT or not.
//
// Return Value: *true* if the operation system is Windows NT or
// Windows 2000. *false* if the operating system is Windows 95 or Windows 98.
//
inline bool IsWinNT()
{
  OSVERSIONINFO osvi = {sizeof(osvi)};
  _VERIFYE(GetVersionEx(&osvi));
  return !!(VER_PLATFORM_WIN32_NT & osvi.dwPlatformId);
}


/////////////////////////////////////////////////////////////////////////////
//
HRESULT LockAndLoadResource(HINSTANCE hinst, LPCTSTR pszType,
  LPCTSTR pszName, void** ppvData, DWORD* pcbData);


/////////////////////////////////////////////////////////////////////////////
//
bool IsInteractiveDesktop();


/////////////////////////////////////////////////////////////////////////////
// String Loading

int TCLoadStringW(HINSTANCE hInstance, UINT wID, LPWSTR wzBuf, int cchBuf);
HRESULT TCLoadBSTR(HINSTANCE hInstance, UINT wID, BSTR* pbstrOut);


/////////////////////////////////////////////////////////////////////////////
// Exception Filtering

#define TCDebugBreakExceptionFilter()                                       \
  ((EXCEPTION_BREAKPOINT == GetExceptionCode()) ?                           \
    EXCEPTION_CONTINUE_SEARCH : EXCEPTION_EXECUTE_HANDLER)


/////////////////////////////////////////////////////////////////////////////
// Error Reporting Functions/Macros
// Note: These methods will output even in RELEASE builds!

void _cdecl TCErrLog(LPCTSTR lpszFormat, ...);

/////////////////////////////////////////////////////////////////////////////
// Macro Group: The TCERRLOG Macros
//
// These macros provide similar functionality to the printf function by
// sending a formatted string to a debug monitor. Like printf for console
// programs, these macros are a convenient way to track the value of
// variables as your program executes. These macros work the same under
// *both* _DEBUG and release builds.
//
// For sending output to the debug monitor under *only* _DEBUG builds, see
// the _TRACE macros.
//
// Parameters:
//   sz - A format string literal as used in the run-time function
// *printf*.
//   p1_thru_p5 - Parameters which are referenced by the '%' placeholders
// in the /sz/ format string.
//
// Declaration:
// #define TCERRLOG                       TCErrLog
// #define TCERRLOG0(sz)                  TCERRLOG(_T(sz))
// #define TCERRLOG1(sz, p1)              TCERRLOG(_T(sz), p1)
// #define TCERRLOG2(sz, p1,p2)           TCERRLOG(_T(sz), p1,p2)
// #define TCERRLOG3(sz, p1,p2,p3)        TCERRLOG(_T(sz), p1,p2,p3)
// #define TCERRLOG4(sz, p1,p2,p3,p4)     TCERRLOG(_T(sz), p1,p2,p3,p4)
// #define TCERRLOG5(sz, p1,p2,p3,p4,p5)  TCERRLOG(_T(sz), p1,p2,p3,p4,p5)
//
// See Also: TCErrLog, _TRACE macros
#define TCERRLOG                       TCErrLog
// {partof:TCERRLOG}
#define TCERRLOG0(sz)                  TCERRLOG(_T(sz))
// {partof:TCERRLOG}
#define TCERRLOG1(sz, p1)              TCERRLOG(_T(sz), p1)
// {partof:TCERRLOG}
#define TCERRLOG2(sz, p1,p2)           TCERRLOG(_T(sz), p1,p2)
// {partof:TCERRLOG}
#define TCERRLOG3(sz, p1,p2,p3)        TCERRLOG(_T(sz), p1,p2,p3)
// {partof:TCERRLOG}
#define TCERRLOG4(sz, p1,p2,p3,p4)     TCERRLOG(_T(sz), p1,p2,p3,p4)
// {partof:TCERRLOG}
#define TCERRLOG5(sz, p1,p2,p3,p4,p5)  TCERRLOG(_T(sz), p1,p2,p3,p4,p5)

#define TCERRLOG_BEGIN_SIZE(cchMax)                                         \
  {                                                                         \
    TCHAR* _trace_szMsg = (LPTSTR)_alloca(cchMax * sizeof(TCHAR));          \
    int    _trace_nIndex = 0;

#define TCERRLOG_BEGIN                                                      \
  TCERRLOG_BEGIN_SIZE(_MAX_PATH)

#define TCERRLOG_PART0(sz)                                                  \
    _trace_nIndex +=                                                        \
      _stprintf(_trace_szMsg + _trace_nIndex, _T(sz))

#define TCERRLOG_PART1(sz, p1)                                              \
    _trace_nIndex +=                                                        \
      _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1)

#define TCERRLOG_PART2(sz, p1,p2)                                           \
    _trace_nIndex +=                                                        \
      _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2)

#define TCERRLOG_PART3(sz, p1,p2,p3)                                        \
    _trace_nIndex +=                                                        \
      _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2,p3)

#define TCERRLOG_PART4(sz, p1,p2,p3,p4)                                     \
    _trace_nIndex +=                                                        \
      _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2,p3,p4)

#define TCERRLOG_PART5(sz, p1,p2,p3,p4,p5)                                  \
    _trace_nIndex +=                                                        \
      _stprintf(_trace_szMsg + _trace_nIndex, _T(sz), p1,p2,p3,p4,p5)

#ifdef _DEBUG
  #define TCERRLOG_END                                                      \
    ZDebugOutputImpl(_trace_szMsg);                                         \
  }
#else // _DEBUG
  #define TCERRLOG_END                                                      \
    OutputDebugString(_trace_szMsg);                                        \
  }
#endif // _DEBUG


///////////////////////////////////////////////////////////////////////////
//
#ifdef _DEBUG
  #define PRIVATE_ASSERTE(expr)                                           \
    do { if (!(expr) &&                                                   \
      (1 == _CrtDbgReport(_CRT_ASSERT, __FILE__, __LINE__, NULL, #expr))) \
      _CrtDbgBreak(); } while (0)
  #define PRIVATE_VERIFY(expr)               PRIVATE_ASSERTE(expr)
  #define PRIVATE_VERIFYE(expr)              PRIVATE_ASSERTE(expr)
#else // _DEBUG
  #define PRIVATE_ASSERTE(expr)              ((void)(0))
  #define PRIVATE_VERIFY(expr)               ((void)(expr))
  #define PRIVATE_VERIFYE(expr)              ((void)(expr))
#endif // _DEBUG


/////////////////////////////////////////////////////////////////////////////

#endif // !__TCLib_h__