//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1998 - 1998 // // File: dbg_.cpp // //-------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////// // debug helpers #if defined(_USE_MTFRMWK_TRACE) || defined(_USE_MTFRMWK_ASSERT) #ifndef _MTFRMWK_INI_FILE #define _MTFRMWK_INI_FILE (L"\\system32\\mtfrmwk.ini") #endif UINT GetInfoFromIniFile(LPCWSTR lpszSection, LPCWSTR lpszKey, INT nDefault = 0) { static LPCWSTR lpszFile = _MTFRMWK_INI_FILE; WCHAR szFilePath[2*MAX_PATH]; UINT nLen = ::GetSystemWindowsDirectory(szFilePath, 2*MAX_PATH); if (nLen == 0) return nDefault; wcscat(szFilePath, lpszFile); return ::GetPrivateProfileInt(lpszSection, lpszKey, nDefault, szFilePath); } #endif // defined(_USE_MTFRMWK_TRACE) || defined(_USE_MTFRMWK_ASSERT) #if defined(_USE_MTFRMWK_TRACE) DWORD g_dwTrace = ::GetInfoFromIniFile(L"Debug", L"Trace"); void MtFrmwkTrace(LPCTSTR lpszFormat, ...) { if (g_dwTrace == 0) return; va_list args; va_start(args, lpszFormat); int nBuf; WCHAR szBuffer[512]; nBuf = _vsnwprintf(szBuffer, sizeof(szBuffer)/sizeof(WCHAR), lpszFormat, args); // was there an error? was the expanded string too long? ASSERT(nBuf >= 0); ::OutputDebugString(szBuffer); va_end(args); } #endif #if defined(DBG) void MtFrmwkLogFile(LPCTSTR lpszFormat, ...) { va_list args; va_start(args, lpszFormat); int nBuf; WCHAR szBuffer[512]; nBuf = _vsnwprintf(szBuffer, sizeof(szBuffer)/sizeof(WCHAR), lpszFormat, args); CLogFile* _dlog = CLogFile::GetInstance(); if (_dlog) { _dlog->writeln(szBuffer); } va_end(args); } void MtFrmwkLogFileIfLog(BOOL bLog, LPCTSTR lpszFormat, ...) { if (bLog) { va_list args; va_start(args, lpszFormat); int nBuf; WCHAR szBuffer[512]; nBuf = _vsnwprintf(szBuffer, sizeof(szBuffer)/sizeof(WCHAR), lpszFormat, args); CLogFile* _dlog = CLogFile::GetInstance(); if (_dlog) { _dlog->writeln(szBuffer); } va_end(args); } } #endif // // Copied and modified from burnslib on 12-07-1999 by JeffJon // Needed file logging on DnsSetup call from DCPromo. // I wanted it to behave like the DCPromo log but including all of // burnslib required too many alterations in the debugging behavior // already in place. // extern CString LOGFILE_NAME = _T(""); static CLogFile* log_instance = 0; // // # of spaces per indentation level // static const int TAB = 2; static int margin = 0; // // index to Thread Local Storage slot where the per-thread debug state is // kept. Initialized in Startup // static DWORD tls_index = 0; CLogFile* CLogFile::GetInstance() { if (!log_instance && !LOGFILE_NAME.IsEmpty()) { log_instance = new CLogFile(LOGFILE_NAME); } return log_instance; } void CLogFile::KillInstance() { delete log_instance; log_instance = 0; } BOOL PathExists(PCWSTR pszPath) { DWORD attrs = GetFileAttributes(pszPath); if (attrs != 0xFFFFFFFF) { return TRUE; } return FALSE; } HANDLE OpenFile(PCWSTR pszPath) { // // remove the last element of the path to form the parent directory // HANDLE handle = ::CreateFile(pszPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); return handle; } PCWSTR GetSystemRootDirectory() { static CString SYSTEMROOT; WCHAR buf[MAX_PATH + 1]; DWORD result = ::GetWindowsDirectory(buf, MAX_PATH + 1); ASSERT(result != 0 && result <= MAX_PATH); if (result == 0 || result > MAX_PATH) { return NULL; } SYSTEMROOT = buf; return (PCWSTR)SYSTEMROOT; } // locate the log file with the highest-numbered extension, then add 1 and // return the result. int DetermineNextLogNumber(PCWSTR logDir, PCWSTR logBaseName) { ASSERT(logDir != NULL); ASSERT(logBaseName != NULL); int largest = 0; CString filespec = CString(logDir) + L"\\" + CString(logBaseName) + L".*.log"; WIN32_FIND_DATA findData; HANDLE ff = ::FindFirstFile(filespec, &findData); if (ff != INVALID_HANDLE_VALUE) { for (;;) { CString current = findData.cFileName; // grab the text between the dots: "nnn" in foo.nnn.ext // first dot int pos = current.Find(L"."); if (pos == -1) { continue; } CString extension = current.Right(current.GetLength() - pos - 1); // second dot pos = extension.Find(L"."); if (pos == -1) { continue; } extension = extension.Left(pos); long i = 0; i = wcstol(extension, L'\0', 10); largest = max(i, largest); if (!::FindNextFile(ff, &findData)) { ::FindClose(ff); break; } } } // roll over after 255 return (++largest & 0xFF); } // Determine the name of the log file. If a log file of that name already // exists, rename the existing file to a numbered backup. Create the new // log file, return a handle to it. // HANDLE OpenNewLogFile(PCWSTR pszLogBaseName, CString& logName) { CString logDir = CString(GetSystemRootDirectory()) + L"\\debug"; int i = DetermineNextLogNumber(logDir, pszLogBaseName); CString szCount; szCount.Format(L"%d", i); logName = logDir + L"\\" + pszLogBaseName + L"." + szCount + L".log"; HANDLE result = OpenFile(logName); return result; } // Create a new log. // // logBaseName - base name of the log. If logging-to-file is active, then a // file in the %windir%\debug folder will be created/used. The name of the // file is of the form %windir%\debug\logBaseName.log. If a file by that name // already exists, then the existing file will be renamed // %windir%\debug\logBaseName.xxx.log, where xxx is an integer 1 greater than // the last so-numbered file in that directory. CLogFile::CLogFile(PCWSTR pszLogBaseName) : szBase_name(pszLogBaseName), file_handle(INVALID_HANDLE_VALUE), trace_line_number(0) { ASSERT(pszLogBaseName != NULL); if (pszLogBaseName != NULL) { CString logName; file_handle = OpenNewLogFile(pszLogBaseName, logName); if (file_handle != INVALID_HANDLE_VALUE) { CString szOpeningFile; szOpeningFile.Format(L"opening log file %ws", logName); writeln(szOpeningFile); } } SYSTEMTIME localtime; ::GetLocalTime(&localtime); CString szTime; szTime.Format(L"%d/%d/%d %d:%d:%d.%d", localtime.wMonth, localtime.wDay, localtime.wYear, localtime.wHour, localtime.wMinute, localtime.wSecond, localtime.wMilliseconds); writeln(szTime); } CLogFile::~CLogFile() { if (IsOpen()) { writeln(L"closing log file"); ::CloseHandle(file_handle); file_handle = INVALID_HANDLE_VALUE; } } // guarded by caller void CLogFile::indent() { // // indent by adding to the margin // margin += TAB; } BOOL CLogFile::IsOpen() const { return file_handle != INVALID_HANDLE_VALUE; } // guarded by caller void CLogFile::outdent() { // // outdent by subtracting from the margin // ASSERT(margin >= TAB); margin = max(0, margin - TAB); } void ConvertStringToANSI(PCWSTR pszWide, PSTR* ppAnsi) { // // determine the size of the buffer required to hold the ANSI string // int bufsize = ::WideCharToMultiByte(CP_ACP, 0, pszWide, static_cast(wcslen(pszWide)), 0, 0, 0, 0); if (bufsize > 0) { *ppAnsi = new CHAR[bufsize + 1]; if (*ppAnsi == NULL) { return; } memset(*ppAnsi, 0, bufsize + 1); size_t result = ::WideCharToMultiByte(CP_ACP, 0, pszWide, static_cast(wcslen(pszWide)), *ppAnsi, bufsize + 1, 0, 0); ASSERT(result); if (!result) { *ppAnsi = NULL; } } } // // Spews output to the log according to the current logging type and // output options in effect. // // type - log output type of this output spewage. // // text - the spewage. This is prefaced with the log name, thread id, spewage // line number, and current indentation. // void CLogFile::writeln(PCWSTR pszText) { CString white(L' ',margin); CString t; t.Format(L"%ws t:0x%x %3d %ws%ws\r\n", LOGFILE_NAME, ::GetCurrentThreadId(), trace_line_number, white, pszText); if (IsOpen()) { ASSERT(file_handle != INVALID_HANDLE_VALUE); ASSERT(!t.IsEmpty()); PSTR pAnsi; ConvertStringToANSI(t, &pAnsi); size_t bytesToWrite = sizeof(CHAR) * strlen(pAnsi); DWORD bytes_written = 0; BOOL success =::WriteFile(file_handle, pAnsi, static_cast(bytesToWrite), &bytes_written, 0); ASSERT(success); ASSERT(bytes_written == bytesToWrite); delete[] pAnsi; } trace_line_number++; } CScopeTracer::CScopeTracer(BOOL bLog, PCWSTR pszMessage_) : szMessage(pszMessage_), m_bLog(bLog) { // build this string once, instead of using the string literal in the // below expression (which would implicitly build the string on each // evaluation of that expression) as a slight performance gain. static const CString ENTER(L"Enter "); if (m_bLog) { CLogFile* li = CLogFile::GetInstance(); li->writeln(ENTER + szMessage); li->indent(); } } CScopeTracer::~CScopeTracer() { // build this string once, instead of using the string literal in the // below expression (which would implicitly build the string on each // evaluation of that expression) as a slight performance gain. static const CString EXIT(L"Exit "); if (m_bLog) { CLogFile* li = CLogFile::GetInstance(); li->outdent(); li->writeln(EXIT + szMessage); } } #if defined(_USE_MTFRMWK_ASSERT) DWORD g_dwAssert = ::GetInfoFromIniFile(L"Debug", L"Assert"); BOOL MtFrmwkAssertFailedLine(LPCSTR lpszFileName, int nLine) { if (g_dwAssert == 0) return FALSE; WCHAR szMessage[_MAX_PATH*2]; // assume the debugger or auxiliary port wsprintf(szMessage, _T("Assertion Failed: File %hs, Line %d\n"), lpszFileName, nLine); OutputDebugString(szMessage); // display the assert int nCode = ::MessageBox(NULL, szMessage, _T("Assertion Failed!"), MB_TASKMODAL|MB_ICONHAND|MB_ABORTRETRYIGNORE|MB_SETFOREGROUND); OutputDebugString(L"after message box\n"); if (nCode == IDIGNORE) { return FALSE; // ignore } if (nCode == IDRETRY) { return TRUE; // will cause DebugBreak } abort(); // should not return return TRUE; } #endif // _USE_MTFRMWK_ASSERT