//+------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1992 - 1999 // // File: mmcerror.cpp // // Contents: Class definitions for mmc error support code. // // History: 15-Jul-99 VivekJ Created // //-------------------------------------------------------------------------- #pragma once #ifndef _MMCERROR_H #define _MMCERROR_H #include "baseapi.h" // for MMCBASE_API #include "stddbg.h" // for ASSERT, COMPILETIME_ASSERT /*+-------------------------------------------------------------------------* * WHY NAMESPACES ? * We had problems trying to use "modified" SC when implementing * com classes supporting ISupportErrorInfo. * we had: * [global version] - class CS * [local version ] - a template class _SC, derived from SC and typedef'ed to SC. * That was not only confusing to us - IDE debugger was also confused and crashing. * * The solution for that was to separate real types used for implementing. * Thus to have typedef'ed definitions both in global and local scope. * Plus (to avoid dealing with _SC and __SC and have better IDE support) * we have used namespaces mmcerror and comerror, so we endup with this: * - mmcerror::SC defining main functionality * - comerror::SC (derived from mmcerror::SC) defining modified functionality * - global SC - typedef of mmcerror::SC * - local SC - typedef of comerror::SC *+-------------------------------------------------------------------------*/ namespace mmcerror { /*+-------------------------------------------------------------------------* * class SC * * PURPOSE: The definition of a status code. Contains two members, a facility * and an error code. This is a class rather * than a typedef to avoid accidental casts to and from HRESULTS. * * SC's hold information about an error: The source of the error, * and the error code itself. These are stored in * different bit fields within the SC. * * NOTE: Do not add any virtual functions or member variables to this class. * This could potentially wreak havoc on MMC performance. * *+-------------------------------------------------------------------------*/ class MMCBASE_API SC { public: typedef long value_type; private: enum facility_type { FACILITY_WIN = 1, // Defined by the system FACILITY_MMC = 2, // these map directly to an UINT. FACILITY_HRESULT = 3, // these map directly to an HRESULT }; public: /* * Constructor. Default copy construction and assignment are sufficient. * If they are ever insufficient, that is a clear indication that this * class has become heavier than is acceptable for its pervasive pass-by- * value usage. */ SC (HRESULT hr = S_OK); // equality operators bool operator==(const SC &rhs) const; bool operator==(HRESULT hr) const; bool operator!=(const SC &rhs) const; bool operator!=(HRESULT hr) const; SC& operator= (HRESULT hr) {MakeSc(FACILITY_HRESULT, hr); return (*this);} SC& FromWin32(value_type value) {MakeSc(FACILITY_WIN, value); return (*this);} SC& FromMMC(value_type value) {MakeSc(FACILITY_MMC, value); return (*this);} void Clear() {MakeSc(FACILITY_HRESULT, S_OK); } HRESULT ToHr() const; value_type GetCode() const {return m_value;} // get the error message in a preallocated buffer void GetErrorMessage(UINT maxLength, /*[OUT]*/ LPTSTR szMessage) const; static void SetHinst(HINSTANCE hInst); static void SetHWnd(HWND hWnd); static DWORD GetMainThreadID() {return s_dwMainThreadID;} static void SetMainThreadID(DWORD dwThreadID); operator bool() const; operator ! () const; bool IsError() const {return operator bool();} static HINSTANCE GetHinst() {ASSERT(s_hInst); return s_hInst;} static HWND GetHWnd() {return s_hWnd;} DWORD GetHelpID(); static LPCTSTR GetHelpFile(); void Throw() throw(SC); void Throw(HRESULT hr) throw(); void FatalError() const; // ends the application. SC& FromLastError(); // does the same trace like in ~SC(); does not change contents. void Trace_() const; void TraceAndClear() { Trace_(); Clear(); } private: void MakeSc(facility_type facility, value_type value){m_facility = facility, m_value = value;} // accessor functions facility_type GetFacility() const {return m_facility;} private: operator HRESULT() const; // this is to prevent automatic conversions to HRESULTs by way of bool's. private: facility_type m_facility; value_type m_value; // the error code. static HINSTANCE s_hInst; // the module that contains all error messages. static HWND s_hWnd; // the parent HWnd for the error boxes. static DWORD s_dwMainThreadID; // The main thread ID of MMC. // debug specific behavior #ifdef DBG // Debug SC's hold a pointer to the name of the function they are declared in. public: void SetFunctionName(LPCTSTR szFunctionName); LPCTSTR GetFunctionName() const; void SetSnapinName (LPCTSTR szSnapinName) { m_szSnapinName = szSnapinName;} LPCTSTR GetSnapinName() const { return m_szSnapinName;} void CheckCallingThreadID(); ~SC(); // SC shouldn't pass the function name around - it's something personal. // These will prevent doing so: SC& operator = (const SC& other); SC(const SC& other); private: LPCTSTR m_szFunctionName; LPCTSTR m_szSnapinName; static UINT s_CallDepth; #endif // DBG }; } // namespace mmcerror // see "WHY NAMESPACES ?" comment at the top of file typedef mmcerror::SC SC; //############################################################################ //############################################################################ // // the module that contains all the localized strings // //############################################################################ //############################################################################ MMCBASE_API HINSTANCE GetStringModule(); //############################################################################ //############################################################################ // // Functions to format and display an error // //############################################################################ //############################################################################ // // Functions to get an error string from a given SC // void MMCBASE_API FormatErrorIds( UINT idsOperation, SC sc, UINT maxLength, /*[OUT]*/ LPTSTR szMessage); void MMCBASE_API FormatErrorString(LPCTSTR szOperation, SC sc, UINT maxLength, /*[OUT]*/ LPTSTR szMessage, BOOL fShort = FALSE); void MMCBASE_API FormatErrorShort(SC sc, UINT maxLength, /*[OUT]*/ LPTSTR szMessage); // // Error Boxes - These will eventually allow to user to suppress more error messages // int MMCBASE_API MMCErrorBox(UINT idsOperation, UINT fuStyle = MB_ICONSTOP | MB_OK); int MMCBASE_API MMCErrorBox(UINT idsOperation, SC sc, UINT fuStyle = MB_ICONSTOP | MB_OK); int MMCBASE_API MMCErrorBox(LPCTSTR szOperation, SC sc, UINT fuStyle = MB_ICONSTOP | MB_OK); int MMCBASE_API MMCErrorBox( SC sc, UINT fuStyle = MB_ICONSTOP | MB_OK); int MMCBASE_API MMCErrorBox(LPCTSTR szMessage, UINT fuStyle = MB_ICONSTOP | MB_OK); // // Message Boxes - These cannot be suppressed // // This #define eventually will change so that MessageBox's are different and cannot be suppressed #define MMCMessageBox MMCErrorBox //############################################################################ //############################################################################ // // Debug macros // //############################################################################ //############################################################################ #ifdef DBG MMCBASE_API void TraceError(LPCTSTR sz, const SC& sc); MMCBASE_API void TraceErrorMsg(LPCTSTR szFormat, ...); MMCBASE_API void TraceSnapinError(LPCTSTR szError, const SC& sc); #define DECLARE_SC(_sc, _func) SC _sc; sc.SetFunctionName(_func); // This define is used only within the SC class #define INCREMENT_CALL_DEPTH() ++s_CallDepth #define DECREMENT_CALL_DEPTH() --s_CallDepth /////////////////////////////////////////////////////////////////////// // MMC public interfaces (for snapins) should use this macro as this // // does some initial error checks and more can be added later. // /////////////////////////////////////////////////////////////////////// #define DECLARE_SC_FOR_PUBLIC_INTERFACE(_sc, _func) SC _sc;\ sc.SetFunctionName(_func);\ sc.SetSnapinName(GetSnapinName());\ sc.CheckCallingThreadID(); #define IMPLEMENTS_SNAPIN_NAME_FOR_DEBUG() tstring _szSnapinNameForDebug;\ LPCTSTR GetSnapinName()\ {\ return _szSnapinNameForDebug.data();\ };\ void SetSnapinName(LPCTSTR sz)\ {\ _szSnapinNameForDebug = sz;\ }; #else #define TraceError ;/##/ #define TraceSnapinError ;/##/ #define DECLARE_SC(_sc, _func) SC _sc; // This define is used only within the SC class #define INCREMENT_CALL_DEPTH() #define DECREMENT_CALL_DEPTH() #define DECLARE_SC_FOR_PUBLIC_INTERFACE(_sc, _func) SC _sc; #define IMPLEMENTS_SNAPIN_NAME_FOR_DEBUG() #endif //############################################################################ //############################################################################ // // Parameter validation // //############################################################################ //############################################################################ /*+-------------------------------------------------------------------------* * * ScCheckPointers * * PURPOSE: Checks to make sure that all specified parameters are non-NULL * * PARAMETERS: * const void * pv1 : * * RETURNS: * inline SC: S_OK if no error, E_INVALIDARG if any of the pointers are NULL * * * NOTE: Do not replace with a single function and optional parameters; that * is inefficient. *+-------------------------------------------------------------------------*/ inline SC ScCheckPointers(const void * pv1, HRESULT err = E_INVALIDARG) { return (NULL == pv1) ? err : S_OK; } inline SC ScCheckPointers(const void * pv1, const void *pv2, HRESULT err = E_INVALIDARG) { return ( (NULL == pv1) || (NULL == pv2) ) ? err : S_OK; } inline SC ScCheckPointers(const void * pv1, const void * pv2, const void * pv3, HRESULT err = E_INVALIDARG) { return ( (NULL == pv1) || (NULL == pv2) || (NULL == pv3) ) ? err : S_OK; } inline SC ScCheckPointers(const void * pv1, const void * pv2, const void * pv3, const void * pv4, HRESULT err = E_INVALIDARG) { return ( (NULL == pv1) || (NULL == pv2) || (NULL == pv3) || (NULL == pv4) ) ? err : S_OK; } inline SC ScCheckPointers(const void * pv1, const void * pv2, const void * pv3, const void * pv4, const void * pv5, HRESULT err = E_INVALIDARG) { return ( (NULL == pv1) || (NULL == pv2) || (NULL == pv3) || (NULL == pv4) || (NULL == pv5) ) ? err : S_OK; } inline SC ScCheckPointers(const void * pv1, const void * pv2, const void * pv3, const void * pv4, const void * pv5, const void* pv6, HRESULT err = E_INVALIDARG) { return ( (NULL == pv1) || (NULL == pv2) || (NULL == pv3) || (NULL == pv4) || (NULL == pv5) || (NULL == pv6)) ? err : S_OK; } // see "WHY NAMESPACES ?" comment at the top of file namespace mmcerror { /*+-------------------------------------------------------------------------* * SC::SC * * Constructor for SC. * * Default copy construction and assignment are sufficient. If they are * ever insufficient, that is a clear indication that this class has become * heavier than is acceptable for its pervasive pass-by-value usage. *--------------------------------------------------------------------------*/ inline SC::SC (HRESULT hr /* =S_OK */) #ifdef DBG : m_szFunctionName(NULL), m_szSnapinName(NULL) #endif // DBG { /* * This assert will fail if SC's ever derive from a non-trivial base * class (i.e. one that has members or virtual functions), or defines * virtual functions of its own. Don't do that! SC's must remain * extremely lightweight. */ COMPILETIME_ASSERT (offsetof (SC, m_facility) == 0); INCREMENT_CALL_DEPTH(); MakeSc (FACILITY_HRESULT, hr); } /*+-------------------------------------------------------------------------* * SC::operator== * * * PURPOSE: Determines whether two SC's are equivalent. * *+-------------------------------------------------------------------------*/ inline bool SC::operator==(const SC &rhs) const { return ( (m_facility == rhs.m_facility) && (m_value == rhs.m_value) ); } inline bool SC::operator==(HRESULT hr) const { return ( (m_facility == FACILITY_HRESULT) && (m_value == hr) ); } inline bool SC::operator!=(const SC &rhs) const { return !operator==( rhs ); } inline bool SC::operator!=(HRESULT hr) const { return !operator==( hr ); } // this version compares an hr to an SC. inline operator == (HRESULT hr, const SC & sc) { return (sc == hr); } #ifdef DBG /*+-------------------------------------------------------------------------* * * SC::GetFunctionName * * PURPOSE: Sets the debug function name to the supplied string. * * PARAMETERS: * * RETURNS: * LPCTSTR The function name. * *+-------------------------------------------------------------------------*/ inline LPCTSTR SC::GetFunctionName() const { return m_szFunctionName; } /*+-------------------------------------------------------------------------* * * SC::CheckCallingThreadID * * PURPOSE: Check if the method was called on main thread. * * PARAMETERS: * * RETURNS: * inline void * *+-------------------------------------------------------------------------*/ inline void SC::CheckCallingThreadID() { ASSERT(-1 != GetMainThreadID()); if (GetMainThreadID() == ::GetCurrentThreadId()) return; TraceSnapinError(_T(", method called from wrong thread"), (*this)); return; } /*+-------------------------------------------------------------------------* * * SC::~SC * * PURPOSE: Destructor - Debug mode only. Does a trace if an error occurred. * *+-------------------------------------------------------------------------*/ inline SC::~SC() { DECREMENT_CALL_DEPTH(); Trace_(); } #endif // DBG /*+-------------------------------------------------------------------------* * * SC::Trace_() * * PURPOSE: Does a trace if an error occurred. Does nothing in release mode * It is very convenient when we want to register, but ignore the error - * Simply doing sc.Trace_(); sc.Clear(); does all we need. * *+-------------------------------------------------------------------------*/ inline void SC::Trace_() const { #ifdef DBG if (IsError()) { // Distinguish between snapin error & MMC error using the // snapin name variable. if (m_szSnapinName != NULL) { TraceSnapinError(_T(""), *this); } else if (m_szFunctionName != NULL) { TraceError(m_szFunctionName, *this); } } #endif // DBG } /*+-------------------------------------------------------------------------* * * SC::operator bool * * PURPOSE: Returns a value indicating whether the SC holds an error code * * PARAMETERS: None * * RETURNS: * bool : true if error, else false * *+-------------------------------------------------------------------------*/ inline SC::operator bool() const { if(GetCode()==0) return false; // quick exit if no error return (GetFacility()==FACILITY_HRESULT) ? FAILED(GetCode()) : true; } inline SC::operator !() const { return (!operator bool()); } } // namespace mmcerror /*+-------------------------------------------------------------------------* * * ScFromWin32 * * PURPOSE: Creates an SC with the facility set to Win32. * * PARAMETERS: * SC::value_type code : * * RETURNS: * inline SC * *+-------------------------------------------------------------------------*/ inline SC ScFromWin32(SC::value_type code) { SC sc; sc.FromWin32(code); return sc; } /*+-------------------------------------------------------------------------* * * ScFromMMC * * PURPOSE: Creates an SC with the facility set to MMC. * * PARAMETERS: * SC::value_type code : * * RETURNS: * inline SC * *+-------------------------------------------------------------------------*/ MMCBASE_API inline SC ScFromMMC(SC::value_type code) { SC sc; sc.FromMMC(code); return sc; } /*+-------------------------------------------------------------------------* * * HrFromSc * * PURPOSE: Converts a status code (SC) to an HRESULT. Use sparingly, as this * loses information in the conversion. * * PARAMETERS: * SC &sc: The SC to convert * * RETURNS: * inline HRESULT: The converted value. * *+-------------------------------------------------------------------------*/ MMCBASE_API inline HRESULT HrFromSc(const SC &sc) { return sc.ToHr(); } /*+-------------------------------------------------------------------------* * * SCODEFromSc * * PURPOSE: Converts a status code (SC) to an SCODE. Use sparingly, as this * loses information in the conversion. * On 32bit machine SCODE is same as HRESULT. * * PARAMETERS: * SC &sc: The SC to convert * * RETURNS: * inline SCODE: The converted value. * *+-------------------------------------------------------------------------*/ MMCBASE_API inline SCODE SCODEFromSc(const SC &sc) { return (SCODE)sc.ToHr(); } #endif //_MMCERROR_H