/*++ Copyright (c) 1999 Microsoft Corporation Module Name: vs_debug.hxx Abstract: Various defines for general usage Author: Adi Oltean [aoltean] 07/09/1999 Revision History: Name Date Comments aoltean 07/09/1999 Created aoltean 08/11/1999 Adding throw specification aoltean 09/03/1999 Adding DuplicateXXX functions aoltean 09/09/1999 dss -> vss aoltean 09/20/1999 Adding ThrowIf, Err, Msg, MsgNoCR, etc. --*/ #ifndef __VSS_DEBUG_HXX__ #define __VSS_DEBUG_HXX__ #if _MSC_VER > 1000 #pragma once #endif //////////////////////////////////////////////////////////////////////// // Standard foo for file name aliasing. This code block must be after // all includes of VSS header files. // #ifdef VSS_FILE_ALIAS #undef VSS_FILE_ALIAS #endif #define VSS_FILE_ALIAS "INCDEBGH" // //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // Global constants // // @const x_nVssMsgBufferSize | Maximum buffer size in Tracer operations const x_nVssMsgBufferSize = 512; const x_nVssNumericBufferSize = 30; const x_nVssGuidBufferSize = 60; const x_nVssMaxDebugArgs = 10; const WCHAR x_wszEventLogVssSourceName[] = L"VSS"; ///////////////////////////////////////////////////////////////////////////// // Declared classes // // // @class CVssFunctionTracer | This structure is used for tracing, debugging, treating HRESULTS // in a C++ function, especially a COM server method. // struct CVssDebugInfo { // Constructors& destructors private: CVssDebugInfo(); public: CVssDebugInfo( LPCWSTR wszFile, LPCSTR szFileAlias, // Five character alias, see file drivers\volsnap\filealias.xls for mappings ULONG ulLine, DWORD dwLevel, DWORD dwIndent = 0 ); CVssDebugInfo( const CVssDebugInfo& original ); ~CVssDebugInfo() ; // Operations public: // This is used to add an numeric-type argument CVssDebugInfo operator << ( IN INT nArgument ); // This is used to add an numeric-type argument. // It will be represented in hexadecimal format. CVssDebugInfo operator << ( IN HRESULT hrArgument ); // This is used to add an numeric-type argument. // It will be represented in hexadecimal format. CVssDebugInfo operator << ( IN LONGLONG llArgument ); // This is used to add an numeric-type argument. CVssDebugInfo operator << ( IN GUID hrArgument ); // This is used to add an string-type argument CVssDebugInfo operator << ( IN LPCWSTR wszArgument ); public: LPCWSTR m_wszFile; LPCSTR m_szFileAlias; ULONG m_ulLine; DWORD m_dwLevel; DWORD m_dwIndent; // Optional argument list LPWSTR m_ppwszStrings[x_nVssMaxDebugArgs]; WORD m_wNumStrings; bool m_bOutOfMemoryOccured; mutable bool m_bStringsOwnership; }; #define VSSDBG_COORD CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_COORD, 0) #define VSSDBG_SWPRV CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_SWPRV, 0) #define VSSDBG_TESTPRV CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_TESTPRV, 0) #define VSSDBG_VSSTEST CVssDebugInfo(__WFILE__, "TST--", __LINE__, DEBUG_TRACE_VSS_TEST, 0) #define VSSDBG_VSSDEMO CVssDebugInfo(__WFILE__, "TSD--", __LINE__, DEBUG_TRACE_VSS_DEMO, 0) #define VSSDBG_EXCEPT CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_CATCH_EXCEPTIONS, 0) #define VSSDBG_GEN CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_GEN, 0) #define VSSDBG_XML CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_XML, 0) #define VSSDBG_WRITER CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_WRITER, 0) #define VSSDBG_IOCTL CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_IOCTL, 0) #define VSSDBG_SHIM CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_SHIM, 0) #define VSSDBG_SQLLIB CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_SQLLIB, 0) #define VSSDBG_VSSADMIN CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_VSS_ADMIN, 0) #define VSSDBG_STSWRITER CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_STSWRITER, 0) #define VSSDBG_SQLWRITER CVssDebugInfo(__WFILE__, VSS_FILE_ALIAS, __LINE__, DEBUG_TRACE_SQLWRITER, 0) #define VSS_STANDARD_CATCH(FT) \ catch( HRESULT CaughtHr ) { \ FT.hr = CaughtHr; \ FT.Trace( VSSDBG_EXCEPT, L"HRESULT EXCEPTION CAUGHT: hr: 0x%x", FT.hr ); \ } \ catch( ... ) { \ FT.hr = E_UNEXPECTED; \ FT.Trace( VSSDBG_EXCEPT, L"UNKNOWN EXCEPTION CAUGHT, returning hr: 0x%x", FT.hr ); \ } // // @class CVssFunctionTracer | This class is used for tracing, debugging, treating HRESULTS // in a C++ function, especially a COM server method. // class CVssFunctionTracer { class CVssOutputBuffer { private: // Must be under 512 in order to make // the lookaside allocation algortihm work efficiently! enum { nVssMsgBufferSize = 400 }; WCHAR* m_pwszBuffer; public: CVssOutputBuffer() { m_pwszBuffer = new WCHAR[nVssMsgBufferSize + 1]; } ~CVssOutputBuffer() { delete m_pwszBuffer; } INT GetBufferSize() { return nVssMsgBufferSize; }; WCHAR * GetBuffer() { return m_pwszBuffer; }; bool IsBufferValid() { return (m_pwszBuffer != NULL); }; }; // Constructors and destructors private: CVssFunctionTracer(); CVssFunctionTracer(const CVssFunctionTracer&); public: CVssFunctionTracer( IN CVssDebugInfo dbgInfo, IN const WCHAR* pwszFunctionName ); ~CVssFunctionTracer(); // Attributes public: _declspec(property(get=GetHr,put=SetHr)) HRESULT hr; // Implementation inline HRESULT GetHr() { return m_hr; }; inline void SetHr( HRESULT hr ) { m_hr = hr; }; inline bool HrFailed() { return FAILED(m_hr); }; inline bool HrSucceeded() { return SUCCEEDED(m_hr); }; // Operations public: // // Traces. // void inline __cdecl Trace( CVssDebugInfo dbgInfo, const WCHAR* pwszMsgFormat, ...); void inline TraceBuffer( CVssDebugInfo dbgInfo, DWORD dwBufferSize, PBYTE pbBuffer ); void inline __cdecl Throw( CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Message that will be displayed if throw needed ... // Additional arguments ) throw(HRESULT); void inline __cdecl ThrowIf( BOOL bThrowNeeded, // Throw an HR if and only if bThrowNeeded is TRUE. CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Message that will be displayed if throw needed ... // Additional arguments ) throw(HRESULT); // // Messages displayed at the console (only for test and demo purpose). // void __cdecl Msg( const WCHAR* pwszMsgFormat, // Message that will be displayed ... ); void __cdecl MsgNoCR( const WCHAR* pwszMsgFormat, // Message that will be displayed ... ); void __cdecl Err( CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Error message that will be displayed ... ); // // Informative message boxes (only for test and demo purpose). // void __cdecl MsgBox( const WCHAR* pwszMsgTitle, // Title of the Message box const WCHAR* pwszMsgFormat, // Message that will be displayed ... ); void __cdecl ErrBox( CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Error message that will be displayed ... ); // Trace the corresponding COM error void TraceComError(); // Log the attempt of starting VSS void LogVssStartupAttempt(); // Put a new entry in the Error Log. void __cdecl LogError( IN DWORD dwEventID, // The ID of the event IN CVssDebugInfo dbgInfo, // Caller debugging info IN WORD wType = EVENTLOG_ERROR_TYPE // Entry type ); void __cdecl TranslateError( IN CVssDebugInfo dbgInfo, IN HRESULT hr, IN LPCWSTR wszRoutine ); void inline __cdecl CheckForError( IN CVssDebugInfo dbgInfo, IN LPCWSTR wszRoutine ) { if (HrFailed()) TranslateError(dbgInfo, m_hr, wszRoutine); } // check for an error on an internal call. Do not log E_UNEXPECTED // errors void inline __cdecl CheckForErrorInternal( IN CVssDebugInfo dbgInfo, IN LPCWSTR wszRoutine ) { if (HrFailed()) { if (m_hr == E_UNEXPECTED) throw E_UNEXPECTED; else TranslateError(dbgInfo, m_hr, wszRoutine); } } void __cdecl TranslateGenericError ( IN CVssDebugInfo dbgInfo, // Caller debugging info IN HRESULT hr, IN LPCWSTR wszErrorTextFormat, IN ... ); void __cdecl LogGenericWarning ( IN CVssDebugInfo dbgInfo, // Caller debugging info IN LPCWSTR wszErrorTextFormat, IN ... ); BOOL IsDuringSetup() { return g_cDbgTrace.IsDuringSetup(); }; BOOL IsInSoftwareProvider() { return m_dwLevel == DEBUG_TRACE_VSS_SWPRV; }; // // Calls CoCreate in a cycle and performs special logging // void CoCreateInstanceWithLog( IN CVssDebugInfo dbgInfo, // Caller debugging info IN CLSID ServerClassID, IN LPCWSTR ServerName, IN DWORD dwContext, IN IID InterfaceID, OUT IUnknown ** ppUnknown ); // Attributes public: HRESULT m_hr; // Internal Data private: const WCHAR* m_pwszFunctionName; LPCSTR m_szFileAlias; ULONG m_ulLine; DWORD m_dwLevel; // At entry time DWORD m_dwIndent; // At entry time }; /////////////////////////////////////////////////////////////////////////////////////// // Class implementations // /////////////////////////////////////////////////////////////////////////////////////// // CVssDebugInfo inline CVssDebugInfo::CVssDebugInfo( LPCWSTR wszFile, LPCSTR szFileAlias, ULONG ulLine, DWORD dwLevel, DWORD dwIndent ): m_wszFile(wszFile), m_szFileAlias(szFileAlias), m_ulLine(ulLine), m_dwIndent(dwIndent), m_dwLevel(dwLevel), m_wNumStrings(0), m_bOutOfMemoryOccured(false), m_bStringsOwnership(true) { for (WORD wIndex = 0; wIndex < x_nVssMaxDebugArgs; wIndex++) m_ppwszStrings[wIndex] = NULL; } inline CVssDebugInfo::CVssDebugInfo( const CVssDebugInfo& original ) { m_wszFile = original.m_wszFile; // We suppose that this is a constant string. m_szFileAlias = original.m_szFileAlias; // We suppose that this is a constant string. m_ulLine = original.m_ulLine; m_dwIndent = original.m_dwIndent; m_dwLevel = original.m_dwLevel; m_wNumStrings = original.m_wNumStrings; m_bOutOfMemoryOccured = original.m_bOutOfMemoryOccured; // Transfer the strings ownership. // This will work even if the ownership is already transferred m_bStringsOwnership = original.m_bStringsOwnership; original.m_bStringsOwnership = false; // Transfer the strings for (WORD wIndex = 0; wIndex < x_nVssMaxDebugArgs; wIndex++) m_ppwszStrings[wIndex] = original.m_ppwszStrings[wIndex]; } inline CVssDebugInfo::~CVssDebugInfo() { if (m_bStringsOwnership) { for (WORD wIndex = 0; wIndex < x_nVssMaxDebugArgs; wIndex++) { if ( m_ppwszStrings[wIndex] != NULL ) { ::CoTaskMemFree(m_ppwszStrings[wIndex]); m_ppwszStrings[wIndex] = NULL; } } } } // This is used to add an numeric-type argument inline CVssDebugInfo CVssDebugInfo::operator << ( IN INT nArgument ) { // Converting the number into a string WCHAR wszBuffer[x_nVssNumericBufferSize + 1]; ::_snwprintf(wszBuffer, x_nVssNumericBufferSize, L"%d", nArgument); return (*this) << wszBuffer; } // This is used to add an HRESULT-type argument inline CVssDebugInfo CVssDebugInfo::operator << ( IN HRESULT hrArgument ) { // Converting the number into a string WCHAR wszBuffer[x_nVssNumericBufferSize + 1]; ::_snwprintf(wszBuffer, x_nVssNumericBufferSize, L"0x%08lx", hrArgument); return (*this) << wszBuffer; } // This is used to add an LONGLONG-type argument inline CVssDebugInfo CVssDebugInfo::operator << ( IN LONGLONG llArgument ) { // Converting the number into a string WCHAR wszBuffer[x_nVssNumericBufferSize + 1]; ::_snwprintf(wszBuffer, x_nVssNumericBufferSize, WSTR_LONGLONG_FMT, LONGLONG_PRINTF_ARG(llArgument) ); return (*this) << wszBuffer; } // This is used to add an GUID-type argument inline CVssDebugInfo CVssDebugInfo::operator << ( IN GUID guidArgument ) { // Converting the number into a string WCHAR wszBuffer[x_nVssGuidBufferSize + 1]; ::_snwprintf(wszBuffer, x_nVssGuidBufferSize, WSTR_GUID_FMT, GUID_PRINTF_ARG(guidArgument)); return (*this) << wszBuffer; } // This is used to add an string-type argument inline CVssDebugInfo CVssDebugInfo::operator << ( IN LPCWSTR wszArgument ) { if (wszArgument == NULL) { BS_ASSERT(false); return (*this); } if (m_bOutOfMemoryOccured) return (*this); // We cannot add more strings if we do not have the ownership... if (!m_bStringsOwnership) { BS_ASSERT(false); return (*this); } if (m_wNumStrings >= x_nVssMaxDebugArgs) { BS_ASSERT(false); // Improper usage (putting too many arguments) return (*this); } // Storing into the actual list of arguments. We might have an allocation error here. LPWSTR wszFormattedValue = (LPWSTR)::CoTaskMemAlloc(sizeof(WCHAR)*(1 + ::wcslen(wszArgument))); if (wszFormattedValue == NULL) { m_bOutOfMemoryOccured = true; return (*this); } ::wcscpy(wszFormattedValue, wszArgument); m_ppwszStrings[m_wNumStrings++] = wszFormattedValue; return (*this); } /////////////////////////////////////////////////////////////////////////////////////// // CVssFunctionTracer inline CVssFunctionTracer::CVssFunctionTracer( IN CVssDebugInfo dbgInfo, IN const WCHAR* pwszFunctionName ) { m_hr = S_OK; m_pwszFunctionName = pwszFunctionName; m_szFileAlias = dbgInfo.m_szFileAlias; m_ulLine = dbgInfo.m_ulLine; m_dwLevel = dbgInfo.m_dwLevel; m_dwIndent = dbgInfo.m_dwIndent; if ( g_cDbgTrace.IsTracingEnabled() && g_cDbgTrace.GetTraceEnterExit() ) { g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, m_dwIndent, m_dwLevel, m_pwszFunctionName, TRUE ); // The reason that I not allow putting here custom-defined arguments is that // if the caller put wrong references it can easily generate an AV. // And, at this point, there is no NT exceptions treatment at the caller side. g_cDbgTrace.PrintEnterExit(L""); g_cDbgTrace.PostPrint( m_dwIndent ); } } inline CVssFunctionTracer::~CVssFunctionTracer() { if ( g_cDbgTrace.IsTracingEnabled() && g_cDbgTrace.GetTraceEnterExit() ) { g_cDbgTrace.PrePrint( L"", 0UL, m_dwIndent, m_dwLevel, m_pwszFunctionName, FALSE ); g_cDbgTrace.PrintEnterExit(L"hr: 0x%08x", m_hr); g_cDbgTrace.PostPrint( m_dwIndent ); } } void inline __cdecl CVssFunctionTracer::Trace( CVssDebugInfo dbgInfo, const WCHAR* pwszMsgFormat, ...) // WARNING: This function do NOT clear the ft.hr field! { if ( !g_cDbgTrace.IsTracingEnabled() ) return; CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel ); g_cDbgTrace.Print( L"%s: %s", m_pwszFunctionName, buffer.GetBuffer() ); g_cDbgTrace.PostPrint( dbgInfo.m_dwIndent ); } }; void inline CVssFunctionTracer::TraceBuffer( CVssDebugInfo dbgInfo, DWORD dwBufferSize, PBYTE pbBuffer ) { // WARNING: This function do NOT clear the ft.hr field! if ( !g_cDbgTrace.IsTracingEnabled() ) return; const nBytesPerSubgroup = 4; const nSubgroupsPerGroup = 2; const nGroupsCount = 2; const nSubgroupSize = 3*nBytesPerSubgroup + 1; // Add a space between subgroups const nGroupSize = nSubgroupSize*nSubgroupsPerGroup + 1; // Add a space between groups const nLineSize = nGroupSize*nGroupsCount + 1; // Add the zero character. WCHAR wszPrintedBufferLine[nLineSize]; WCHAR wszPrintedBufferLineInChr[nLineSize]; WCHAR wszDigits[] = L"0123456789ABCDEF"; // Print each line for (DWORD dwBufferOffset = 0; dwBufferOffset < dwBufferSize; ) { int nLineOffset = 0; int nLineOffsetChr = 0; // Print each group in the line for (int nGroupIndex = 0; nGroupIndex < nGroupsCount; nGroupIndex++) { // Print each subgroup in the group for (int nSubgroupIndex = 0; nSubgroupIndex < nSubgroupsPerGroup; nSubgroupIndex++) { // Print each byte in the subgroup for (int nByteIndex = 0; nByteIndex < nBytesPerSubgroup; nByteIndex++) { if (dwBufferOffset < dwBufferSize) { BYTE bChar = pbBuffer[dwBufferOffset]; wszPrintedBufferLineInChr[nLineOffsetChr++] = ( bChar >= 0x20 && bChar < 0x7F )? (WCHAR)bChar: L'.'; wszPrintedBufferLine[nLineOffset++] = wszDigits[pbBuffer[dwBufferOffset] / 0x10]; wszPrintedBufferLine[nLineOffset++] = wszDigits[pbBuffer[dwBufferOffset++] % 0x10]; wszPrintedBufferLine[nLineOffset++] = L' '; // Print an additional space after each group } else { wszPrintedBufferLineInChr[nLineOffsetChr++] = L'#'; wszPrintedBufferLine[nLineOffset++] = L'#'; wszPrintedBufferLine[nLineOffset++] = L'#'; wszPrintedBufferLine[nLineOffset++] = L' '; // Print an additional space after each group } } wszPrintedBufferLine[nLineOffset++] = L' '; // Print a space after each subgroup } wszPrintedBufferLine[nLineOffset++] = L' '; // Print an additional space after each group } // Put hte termination characters wszPrintedBufferLineInChr[nLineOffsetChr++] = L'\0'; wszPrintedBufferLine[nLineOffset++] = L'\0'; BS_ASSERT( nLineOffset == nLineSize ); g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel ); g_cDbgTrace.Print( L"%s %s", wszPrintedBufferLine, wszPrintedBufferLineInChr ); g_cDbgTrace.PostPrint( dbgInfo.m_dwIndent ); } }; void inline __cdecl CVssFunctionTracer::Throw( CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Message that will be displayed if throw needed ... // Additional arguments ) throw(HRESULT) { if ( g_cDbgTrace.IsTracingEnabled() ) { if (pwszMsgFormat != NULL) { CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel ); g_cDbgTrace.Print( L"%s: %s", m_pwszFunctionName, buffer.GetBuffer() ); g_cDbgTrace.PostPrint( dbgInfo.m_dwIndent ); } } g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, DEBUG_TRACE_CATCH_EXCEPTIONS ); g_cDbgTrace.Print( L"%s: Throwing HRESULT code 0x%08lx. " L"Previous HRESULT code = 0x%08lx", m_pwszFunctionName, hrToBeThrown, m_hr ); g_cDbgTrace.PostPrint( DEBUG_TRACE_CATCH_EXCEPTIONS ); } m_hr = hrToBeThrown; throw( ( HRESULT )m_hr ); }; void inline __cdecl CVssFunctionTracer::ThrowIf( BOOL bThrowNeeded, // Throw an HR if and only if bThrowNeeded is TRUE. CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Message that will be displayed if throw needed ... // Additional arguments ) throw(HRESULT) // WARNING: This function clears the ft.hr field if no errors ! // Also if bThrowNeeded == false then the m_hr is reset to S_OK ! { if (!bThrowNeeded) { m_hr = S_OK; return; } if ( g_cDbgTrace.IsTracingEnabled() ) { if (pwszMsgFormat != NULL) { CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel ); g_cDbgTrace.Print( L"%s: %s", m_pwszFunctionName, buffer.GetBuffer() ); g_cDbgTrace.PostPrint( dbgInfo.m_dwIndent ); } } g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, DEBUG_TRACE_CATCH_EXCEPTIONS ); g_cDbgTrace.Print( L"%s: %s HRESULT code 0x%08lx. " L"Previous HRESULT code = 0x%08lx", bThrowNeeded? L"Throwing": L"Tracing", m_pwszFunctionName, hrToBeThrown, m_hr ); g_cDbgTrace.PostPrint( DEBUG_TRACE_CATCH_EXCEPTIONS ); } m_hr = hrToBeThrown; throw( ( HRESULT )m_hr ); }; // // Messages displayed at the console (only for test and demo purpose). // void inline __cdecl CVssFunctionTracer::Msg( const WCHAR* pwszMsgFormat, // Message that will be displayed ... ) { USES_CONVERSION; _ASSERTE(pwszMsgFormat); CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); wprintf( L"%s\n", W2CT(buffer.GetBuffer()) ); } } void inline __cdecl CVssFunctionTracer::MsgNoCR( const WCHAR* pwszMsgFormat, // Message that will be displayed ... ) { USES_CONVERSION; _ASSERTE(pwszMsgFormat); CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); wprintf( L"%s", W2CT(buffer.GetBuffer()) ); } } void inline __cdecl CVssFunctionTracer::Err( CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Error message that will be displayed ... ) { USES_CONVERSION; _ASSERTE(pwszMsgFormat); CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); // Message box CVssOutputBuffer buffer2; if (!buffer2.IsBufferValid()) throw(E_OUTOFMEMORY); _snwprintf(buffer2.GetBuffer(), buffer2.GetBufferSize(), L"%s(%ld)\n%s @ [%08lx,%08lx], PID=%ld, TID=%ld\n%s", dbgInfo.m_wszFile, dbgInfo.m_ulLine, m_pwszFunctionName, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel, GetCurrentProcessId(), GetCurrentThreadId(), buffer.GetBuffer() ); ::MessageBoxW( NULL, buffer2.GetBuffer(), L"Error", MB_OK); if ( g_cDbgTrace.IsTracingEnabled() ) { g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel ); g_cDbgTrace.Print( L"%s: %s", m_pwszFunctionName, buffer2.GetBuffer() ); g_cDbgTrace.PostPrint( dbgInfo.m_dwIndent ); } wprintf( L"Error: %s\n", W2CT(buffer2.GetBuffer()) ); } if (hrToBeThrown) { m_hr = hrToBeThrown; throw( ( HRESULT )m_hr ); } } // // Informative message boxes (only for test and demo purpose). // void inline __cdecl CVssFunctionTracer::MsgBox( const WCHAR* pwszMsgTitle, // Title of the Message box const WCHAR* pwszMsgFormat, // Message that will be displayed ... ) { USES_CONVERSION; _ASSERTE(pwszMsgTitle); _ASSERTE(pwszMsgFormat); CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); ::MessageBox(NULL, W2CT(buffer.GetBuffer()), W2CT(pwszMsgTitle), MB_OK); } } void inline __cdecl CVssFunctionTracer::ErrBox( CVssDebugInfo dbgInfo, // Caller debugging info HRESULT hrToBeThrown, // The new HR to be thrown const WCHAR* pwszMsgFormat, // Error message that will be displayed ... ) { USES_CONVERSION; _ASSERTE(pwszMsgFormat); CVssOutputBuffer buffer; if (buffer.IsBufferValid()) { va_list marker; va_start( marker, pwszMsgFormat ); _vsnwprintf( buffer.GetBuffer(), buffer.GetBufferSize(), pwszMsgFormat, marker ); va_end( marker ); if ( g_cDbgTrace.IsTracingEnabled() ) { g_cDbgTrace.PrePrint( dbgInfo.m_wszFile, dbgInfo.m_ulLine, dbgInfo.m_dwIndent, dbgInfo.m_dwLevel ); g_cDbgTrace.Print( L"%s: %s", m_pwszFunctionName, buffer.GetBuffer() ); g_cDbgTrace.PostPrint( dbgInfo.m_dwIndent ); } ::MessageBox(NULL, W2CT(buffer.GetBuffer()), L"Error", MB_OK); } if (hrToBeThrown) { m_hr = hrToBeThrown; throw( ( HRESULT )m_hr ); } } /*++ Routine Description: Traces the COM error description. Can be called after automation COM calls (for example XML or COM+ related) --*/ void inline CVssFunctionTracer::TraceComError() { CVssFunctionTracer ft(VSSDBG_GEN, L"CVssFunctionTracer::TraceComError"); CComPtr pErrorInfo; HRESULT hr2 = ::GetErrorInfo(0, &pErrorInfo); if (SUCCEEDED(hr2)) { if (pErrorInfo != NULL) { BSTR bstrDescription; HRESULT hr3 = pErrorInfo->GetDescription(&bstrDescription); if (SUCCEEDED(hr3)) { ft.Trace(VSSDBG_GEN, L"Error info description: %s", bstrDescription); ::SysFreeString(bstrDescription); } else ft.Trace(VSSDBG_GEN, L"Warning: Error getting error description = 0x%08lx", hr3); } } else ft.Trace(VSSDBG_GEN, L"Warning: Error getting error info = 0x%08lx", hr2); } void inline __cdecl CVssFunctionTracer::LogError( IN DWORD dwEventID, // The ID of the event IN CVssDebugInfo dbgInfo, // Caller debugging info IN WORD wType // By default it is EVENTLOG_ERROR_TYPE ) { CVssFunctionTracer ft(VSSDBG_GEN, L"CVssFunctionTracer::LogError"); try { LPCWSTR ppwszStrings[x_nVssMaxDebugArgs]; CHAR szFileLine[32]; CHAR szTemp[34]; // Max length of string from _ultoa() // Create the four lines of binary data output in the data section of the event log message. // First the Caller debugging info: ::strncpy( szFileLine, dbgInfo.m_szFileAlias, 8 ); ::_ultoa( dbgInfo.m_ulLine, szTemp, 10 ); ::strncpy( szFileLine + 8, szTemp, 8 ); // strncpy has the nice property of zeroing out unuse chars. // Now info on the ft at point of construction: ::strncpy( szFileLine + 16, m_szFileAlias, 8 ); ::_ultoa( m_ulLine, szTemp, 10 ); ::strncpy( szFileLine + 24, szTemp, 8 ); // strncpy has the nice property of zeroing out unuse chars. // Fill out the strings that are given as arguments WORD wNumStrings = dbgInfo.m_wNumStrings; for( WORD wIndex = 0; wIndex < dbgInfo.m_wNumStrings; wIndex++ ) ppwszStrings[wIndex] = const_cast(dbgInfo.m_ppwszStrings[wIndex]); // Get a handle to use with ReportEvent() HANDLE hEventSource = ::RegisterEventSourceW( NULL, // IN LPCWSTR lpUNCServerName, x_wszEventLogVssSourceName // IN LPCWSTR lpSourceName ); if (hEventSource == NULL) ft.Throw( VSSDBG_SHIM, E_UNEXPECTED, L"Error on RegisterEventSourceW 0x%08lx", GetLastError()); // Write to event log. BOOL bRet = ::ReportEventW( hEventSource, // IN HANDLE hEventLog, wType, // IN WORD wType, 0, // IN WORD wCategory, dwEventID, // IN DWORD dwEventID, NULL, // IN PSID lpUserSid, wNumStrings, // IN WORD wNumStrings, sizeof( szFileLine ), // IN DWORD dwDataSize, ppwszStrings, // IN LPCWSTR *lpStrings, szFileLine // IN LPVOID lpRawData ); if ( !bRet ) ft.Trace( VSSDBG_SHIM, L"Error on ReportEventW 0x%08lx", GetLastError()); // Close the handle to the event log bRet = ::DeregisterEventSource( hEventSource ); if ( !bRet ) ft.Throw( VSSDBG_SHIM, E_UNEXPECTED, L"Error on DeregisterEventSource 0x%08lx", GetLastError()); } VSS_STANDARD_CATCH(ft) } // // Set the file alias to unknown in case a module doesn't set it's alias. // #undef VSS_FILE_ALIAS #define VSS_FILE_ALIAS "UNKNOWN" #endif // __VSS_DEBUG_HXX__