/*++ Copyright (c) 1999 Microsoft Corporation Module Name: Debugex.cpp Abstract: Implementation of the CDebug class. Author: Eran Yariv (EranY) Jul, 1999 Revision History: --*/ #include #include #include #include #include #include "debugex.h" #include // We're reading the default mask from HKLM\REGKEY_FAX_CLIENT\REGVAL_DBGLEVEL_EX #include // For the DEBUG_FAX_TAPI_MSG, DEBUG_VER_MSG, DEBUG_WRN_MSG, and DEBUG_ERR_MSG constants #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif #ifdef ENABLE_FRE_LOGGING #define ENABLE_LOGGING #endif #ifdef DEBUG #define ENABLE_LOGGING #endif #ifdef ENABLE_LOGGING ////////////////////////////////////////////////////////////////////// // Static variables ////////////////////////////////////////////////////////////////////// HANDLE CDebug::m_shLogFile = INVALID_HANDLE_VALUE; LONG CDebug::m_sdwIndent = 0; DWORD CDebug::m_sDbgMask = DEFAULT_DEBUG_MASK; DWORD CDebug::m_sFmtMask = DEFAULT_FORMAT_MASK; BOOL CDebug::m_sbMaskReadFromReg = FALSE; BOOL CDebug::m_sbRegistryExist = FALSE; BOOL CDebug::m_sbFlush = TRUE; ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// CDebug::~CDebug() /*++ Routine name : CDebug::~CDebug Routine description: Destructor Author: Eran Yariv (EranY), Jul, 1999 Arguments: None. Return Value: None. --*/ { DWORD dwLastError = GetLastError (); Unindent (); switch (m_ReturnType) { case DBG_FUNC_RET_UNKNOWN: DbgPrint (FUNC_TRACE, NULL, 0, TEXT("}")); break; case DBG_FUNC_RET_HR: // // We have the return HRESULT // if (NOERROR == *m_phr) { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (NOERROR)")); } else if (S_FALSE == *m_phr) { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (S_FALSE)")); } else { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (0x%08X)"), *m_phr); } break; case DBG_FUNC_RET_DWORD: // // We have the return DWORD // if (ERROR_SUCCESS == *m_pDword) { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (ERROR_SUCCESS)")); } else { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (%ld)"), *m_pDword); } break; case DBG_FUNC_RET_BOOL: // // We have the return BOOL // if (*m_pBool) { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (TRUE)")); } else { DbgPrint (FUNC_TRACE, NULL, 0, TEXT("} (FALSE)")); } break; default: DbgPrint (ASSERTION_FAILED, TEXT(__FILE__), __LINE__, TEXT("ASSERTION FAILURE!!!")); { DebugBreak(); } break; } SetLastError (dwLastError); } ////////////////////////////////////////////////////////////////////// // Implementation ////////////////////////////////////////////////////////////////////// void CDebug::EnterModuleWithParams ( LPCTSTR lpctstrModule, LPCTSTR lpctstrFormat, va_list arg_ptr ) { DWORD dwLastError = GetLastError (); if (!m_sbMaskReadFromReg) { ReadMaskFromReg (); } lstrcpyn (m_tszModuleName, lpctstrModule, ARR_SIZE (m_tszModuleName) - 1); m_tszModuleName[ARR_SIZE(m_tszModuleName)-1] = TEXT('\0'); TCHAR szArgs[1024] = {0}; _vsntprintf(szArgs, ARR_SIZE(szArgs)-1, lpctstrFormat, arg_ptr); TCHAR szBuf[1024] = {0}; _sntprintf (szBuf, ARR_SIZE(szBuf) - 1, TEXT("%s (%s)"), m_tszModuleName, szArgs); DbgPrint (FUNC_TRACE, NULL, 0, szBuf); DbgPrint (FUNC_TRACE, NULL, 0, TEXT("{")); Indent (); SetLastError (dwLastError); } void CDebug::EnterModule ( LPCTSTR lpctstrModule ) { DWORD dwLastError = GetLastError (); if (!m_sbMaskReadFromReg) { ReadMaskFromReg (); } lstrcpyn (m_tszModuleName, lpctstrModule, ARR_SIZE (m_tszModuleName) - 1); m_tszModuleName[ARR_SIZE(m_tszModuleName)-1] = TEXT('\0'); DbgPrint (FUNC_TRACE, NULL, 0, m_tszModuleName); DbgPrint (FUNC_TRACE, NULL, 0, TEXT("{")); Indent (); SetLastError (dwLastError); } //***************************************************************************** //* Name: OpenLogFile //* Author: Mooly Beery (MoolyB), May, 2000 //***************************************************************************** //* DESCRIPTION: //* Creates a log file which accepts the debug output //* //* PARAMETERS: //* [IN] LPCTSTR lpctstrFilename: //* the filename which will be created in the temporary folder //* //* RETURN VALUE: //* FALSE if the operation failed. //* TRUE is succeeded. //* Comments: //* this function should be used together with CloseLogFile() //***************************************************************************** BOOL CDebug::OpenLogFile(LPCTSTR lpctstrFilename) { TCHAR szFilename[MAX_PATH] = {0}; TCHAR szTempFolder[MAX_PATH] = {0}; TCHAR szPathToFile[MAX_PATH] = {0}; if (!lpctstrFilename) { DbgPrint (ASSERTION_FAILED, TEXT(__FILE__), __LINE__, TEXT("Internat error - bad Filename")); DebugBreak(); return FALSE; } // first expand the filename if (ExpandEnvironmentStrings(lpctstrFilename,szFilename,MAX_PATH)==0) { return FALSE; } // is this is a file description or a complete path to file if (_tcschr(szFilename,_T('\\'))==NULL) { // this is just the file's name, need to add the temp folder to it. if (GetTempPath(MAX_PATH,szTempFolder)==0) { return FALSE; } _tcsncpy(szPathToFile,szTempFolder,MAX_PATH-1); _tcsncat(szPathToFile,szFilename,MAX_PATH-_tcslen(szPathToFile)-1); } else { // this is the full path to the log file, use it. _tcsncpy(szPathToFile,szFilename,MAX_PATH-1); } m_shLogFile = ::SafeCreateFile( szPathToFile, GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL); if (m_shLogFile==INVALID_HANDLE_VALUE) { return FALSE; } SetLogFile(m_shLogFile); // no sense to open the file and not enable printing to it. m_sFmtMask |= DBG_PRNT_TO_FILE; return TRUE; } //***************************************************************************** //* Name: CloseLogFile //* Author: Mooly Beery (MoolyB), May, 2000 //***************************************************************************** //* DESCRIPTION: //* Closes the log file which accepts the debug output //* //* PARAMETERS: //* //* RETURN VALUE: //* //* Comments: //* this function should be used together with OpenLogFile() //***************************************************************************** void CDebug::CloseLogFile() { if (m_shLogFile!=INVALID_HANDLE_VALUE) { ::CloseHandle(m_shLogFile); m_shLogFile = INVALID_HANDLE_VALUE; } } //***************************************************************************** //* Name: SetLogFile //* Author: Mooly Beery (MoolyB), May, 2000 //***************************************************************************** //* DESCRIPTION: //* redirects the debug output to the file whose handle is given //* //* PARAMETERS: //* [IN] HANDLE hFile: //* the handle of the file which will accept the debug output //* //* RETURN VALUE: //* the previous handle //* //* Comments: //* this function should be used only in cases the OpenLogFile is //* insufficient and a different file location/type is required //* otherwise, don't manipulate the handle yourself, just use the //* pair OpenLogFile / CloseLogFile //***************************************************************************** HANDLE CDebug::SetLogFile(HANDLE hFile) { HANDLE OldHandle = m_shLogFile; m_shLogFile = hFile; return OldHandle; } void CDebug::DbgPrint ( DbgMsgType type, LPCTSTR lpctstrFileName, DWORD dwLine, LPCTSTR lpctstrFormat, ... ) /*++ Routine name : CDebug::DbgPrint Routine description: Print to debug (with file and line number) Author: Eran Yariv (EranY), Jul, 1999 Arguments: type [in] - Type of message lpctstrFileName [in] - Location of caller dwLine [in] - Line of caller lpctstrFormat [in] - printf format string ... [in] - optional parameters Return Value: None. --*/ { va_list arg_ptr; va_start(arg_ptr, lpctstrFormat); Print (type, lpctstrFileName, dwLine, lpctstrFormat, arg_ptr); va_end (arg_ptr); } void CDebug::Trace ( DbgMsgType type, LPCTSTR lpctstrFormat, ... ) /*++ Routine name : CDebug::DbgPrint Routine description: Trace to debug Author: Eran Yariv (EranY), Jul, 1999 Arguments: type [in] - Type of message lpctstrFormat [in] - printf format string ... [in] - optional parameters Return Value: None. --*/ { va_list arg_ptr; va_start(arg_ptr, lpctstrFormat); Print (type, NULL, 0, lpctstrFormat, arg_ptr); va_end (arg_ptr); } void CDebug::Print ( DbgMsgType type, LPCTSTR lpctstrFileName, DWORD dwLine, LPCTSTR lpctstrFormat, va_list arg_ptr ) /*++ Routine name : CDebug::Print Routine description: Print to debug Author: Eran Yariv (EranY), Jul, 1999 Mooly Beery (MoolyB), Jun, 2000 Arguments: type [in] - Type of message lpctstrFileName [in] - Location of caller dwLine [in] - Line of caller lpctstrFormat [in] - printf format string arg_ptr [in] - optional parameters list Return Value: None. --*/ { if (!(type & m_sDbgMask)) { // // This type of debug message is masked out // return; } TCHAR szMsg [2000]={0}; TCHAR szBuf [1000]={0}; TCHAR szTimeBuff[10]={0}; TCHAR szDateBuff[10]={0}; DWORD dwLastError = GetLastError(); DWORD dwInd = 0; // Time stamps if (m_sFmtMask & DBG_PRNT_TIME_STAMP) { dwInd += _stprintf(&szMsg[dwInd], TEXT("[%-8s %-8s] "), _tstrdate(szDateBuff), _tstrtime(szTimeBuff)); } // Thread ID if (m_sFmtMask & DBG_PRNT_THREAD_ID) { dwInd += _stprintf(&szMsg[dwInd], TEXT("[0x%04x] "), GetCurrentThreadId()); } // Message type if (m_sFmtMask & DBG_PRNT_MSG_TYPE) { dwInd += _stprintf(&szMsg[dwInd], TEXT("[%s] "), GetMsgTypeString(type)); } // Now comes the actual message _vsntprintf(szBuf, ARR_SIZE(szBuf)-1, lpctstrFormat, arg_ptr); int i; i = _sntprintf( &szMsg[dwInd], ARR_SIZE(szMsg) - dwInd - 1, TEXT("%*c%s "), m_sdwIndent * _DEBUG_INDENT_SIZE, TEXT(' '), szBuf); if (i > 0) { dwInd += i; } else { // // No more room // szMsg[ARR_SIZE(szMsg)-1]=TEXT('\0'); goto OutputString; } // filename & line number if (m_sFmtMask & DBG_PRNT_FILE_LINE) { if (lpctstrFileName && dwLine) { // // Protect from overrun. // i = _sntprintf( &szMsg[dwInd], ARR_SIZE(szMsg)-dwInd-2, // 2 for '\n' and '\0' TEXT("(%s %ld)"), lpctstrFileName, dwLine); if (i > 0) { dwInd += i; } else { // // No more room // szMsg[ARR_SIZE(szMsg)-1]=TEXT('\0'); goto OutputString; } } } _sntprintf( &szMsg[dwInd], ARR_SIZE(szMsg)-dwInd-1, TEXT("\n")); szMsg[ARR_SIZE(szMsg)-1]=TEXT('\0'); OutputString: // standard output? if (m_sFmtMask & DBG_PRNT_TO_STD) { OutputDebugString(szMsg); } // file output? if (m_sFmtMask & DBG_PRNT_TO_FILE) { if (m_shLogFile!=INVALID_HANDLE_VALUE) { OutputFileString(szMsg); } } SetLastError (dwLastError); } // CDebug::Print void CDebug::Unindent() /*++ Routine name : CDebug::Unindent Routine description: Move indention one step backwards Author: Eran Yariv (EranY), Jul, 1999 Arguments: Return Value: None. --*/ { if (InterlockedDecrement(&m_sdwIndent)<0) { DbgPrint (ASSERTION_FAILED, TEXT(__FILE__), __LINE__, TEXT("Internat error - bad indent")); DebugBreak(); } } // CDebug::Unindent void CDebug::SetDebugMask(DWORD dwMask) { m_sbMaskReadFromReg = TRUE; m_sDbgMask = dwMask; } void CDebug::SetFormatMask(DWORD dwMask) { m_sbMaskReadFromReg = TRUE; m_sFmtMask = dwMask; } DWORD CDebug::ModifyDebugMask(DWORD dwAdd,DWORD dwRemove) { if (!m_sbMaskReadFromReg) { // first let's read the requested debug mask & format ReadMaskFromReg(); } m_sDbgMask |= dwAdd; m_sDbgMask &= ~dwRemove; return m_sDbgMask; } DWORD CDebug::ModifyFormatMask(DWORD dwAdd,DWORD dwRemove) { if (!m_sbMaskReadFromReg) { // first let's read the requested debug mask & format ReadMaskFromReg(); } m_sFmtMask |= dwAdd; m_sFmtMask &= ~dwRemove; return m_sFmtMask; } void CDebug::SetDebugFlush(BOOL fFlush) { m_sbFlush = fFlush; } BOOL CDebug::DebugFromRegistry() { if (!m_sbMaskReadFromReg) { // first let's read the requested debug mask & format ReadMaskFromReg(); } return m_sbRegistryExist; } BOOL CDebug::ReadMaskFromReg() { BOOL bRes = FALSE; HKEY hkey = NULL; DWORD dwRegValue; DWORD dwRegType; DWORD dwRes; DWORD dwRegSize = sizeof (dwRegValue); if (m_sbMaskReadFromReg) { // // Debug & Format mask already read. // goto end; } m_sbMaskReadFromReg = TRUE; m_sDbgMask = DEFAULT_DEBUG_MASK; m_sFmtMask = DEFAULT_FORMAT_MASK; dwRes = RegOpenKeyEx (HKEY_LOCAL_MACHINE, REGKEY_FAX_CLIENT, 0, KEY_READ, &hkey); if (ERROR_SUCCESS != dwRes) { goto end; } dwRes = RegQueryValueEx (hkey, REGVAL_DBGLEVEL_EX, NULL, &dwRegType, (LPBYTE)&dwRegValue, &dwRegSize); if (ERROR_SUCCESS != dwRes) { goto end; } if (REG_DWORD != dwRegType) { // // Expecting only a DWORD value // goto end; } m_sDbgMask = dwRegValue; dwRes = RegQueryValueEx (hkey, REGVAL_DBGFORMAT_EX, NULL, &dwRegType, (LPBYTE)&dwRegValue, &dwRegSize); if (ERROR_SUCCESS != dwRes) { goto end; } if (REG_DWORD != dwRegType) { // // Expecting only a DWORD value // goto end; } m_sFmtMask = dwRegValue; bRes = TRUE; m_sbRegistryExist = TRUE; end: if (hkey) { RegCloseKey (hkey); } return bRes; } // CDebug::ReadMaskFromReg BOOL CDebug::OutputFileString(LPCTSTR szMsg) { BOOL bRes = FALSE; // // Attempt to add the line to a log file // #ifdef UNICODE char sFileMsg[2000]; int Count = WideCharToMultiByte( CP_ACP, 0, szMsg, -1, sFileMsg, sizeof(sFileMsg)/sizeof(sFileMsg[0]), NULL, NULL ); if (Count==0) { return bRes; } #else const char* sFileMsg = szMsg; #endif DWORD dwFilePointer = ::SetFilePointer(m_shLogFile,0,NULL,FILE_END); if (dwFilePointer==INVALID_SET_FILE_POINTER) { return bRes; } DWORD dwNumBytesWritten = 0; DWORD dwNumOfBytesToWrite = strlen(sFileMsg); if (!::WriteFile(m_shLogFile,sFileMsg,dwNumOfBytesToWrite,&dwNumBytesWritten,NULL)) { return bRes; } if (dwNumBytesWritten!=dwNumOfBytesToWrite) { return bRes; } if (m_sbFlush) { if (!::FlushFileBuffers(m_shLogFile)) { return bRes; } } bRes = TRUE; return bRes; } LPCTSTR CDebug::GetMsgTypeString(DWORD dwMask) { switch (dwMask) { case ASSERTION_FAILED: return _T("ERR"); case DBG_MSG: case FUNC_TRACE: return _T(" "); case DBG_WARNING: return _T("WRN"); case MEM_ERR: case COM_ERR: case RESOURCE_ERR: case STARTUP_ERR: case GENERAL_ERR: case EXCEPTION_ERR: case RPC_ERR: case WINDOW_ERR: case FILE_ERR: case SECURITY_ERR: case REGISTRY_ERR: case PRINT_ERR: case SETUP_ERR: case NET_ERR: return _T("ERR"); default: return _T("???"); } } #endif