利用线程局部存储保存栈深实现函数间的缩进,输出使用 OutputDebugString API 便于用 DebugView 查看,这样本机和远程(DebugView可远程)都可看到调用栈了。 #ifndef TraceEx_H #define TraceEx_H ////////////////////////////////////////////////////////////////////////// #include <Windows.h> ////////////////////////////////////////////////////////////////////////// class CTraceEx { public: CTraceEx(LPCTSTR pszFunction, LPCTSTR pszThread = NULL); ~CTraceEx(); static VOID Log(LPCTSTR pszFormat, ...); static VOID SetFlag(LONG lFlag); protected: CTraceEx(); CTraceEx(CTraceEx &); CTraceEx & operator = (CTraceEx &); private: DWORD m_dwTickCount; LPCTSTR m_pszFunction; }; ////////////////////////////////////////////////////////////////////////// #endif #include "TraceEx.h" ////////////////////////////////////////////////////////////////////////// enum { THREAD_NAME = 24, BUFFER_SIZE = 4 * 1024, }; ////////////////////////////////////////////////////////////////////////// #ifndef __ITEMCOUNT #define __ITEMCOUNT(Array) sizeof((Array))/sizeof((Array)[0]) #endif ////////////////////////////////////////////////////////////////////////// const TCHAR szSpace[] = TEXT(" "); const TCHAR szNewLine[] = TEXT("/r/n"); ////////////////////////////////////////////////////////////////////////// static LONG s_lCallDeep = -1; static LONG s_lThreadName = -1; ////////////////////////////////////////////////////////////////////////// static LONG g_lTraceFlag = 1; ////////////////////////////////////////////////////////////////////////// VOID CTraceEx::SetFlag(LONG lFlag) { g_lTraceFlag = lFlag; } ////////////////////////////////////////////////////////////////////////// CTraceEx::CTraceEx(LPCTSTR pszFunction, LPCTSTR pszThread) : m_dwTickCount(0), m_pszFunction(pszFunction) { if (g_lTraceFlag == 0) { return; } LPTSTR pszThreadName = (LPTSTR) TlsGetValue(s_lThreadName); LONG lCallDeep = (LONG) TlsGetValue(s_lCallDeep); TCHAR szBuffer[BUFFER_SIZE] = { 0 }; if (pszThread != NULL && pszThreadName == NULL) { pszThreadName = new TCHAR[THREAD_NAME]; lstrcpy(pszThreadName, pszThread); TlsSetValue(s_lThreadName, pszThreadName); } m_dwTickCount = GetTickCount(); if (m_pszFunction != NULL) { TCHAR szMessage[] = TEXT(" -> "); DWORD dwWritten = 0; if (pszThreadName != NULL) { lstrcpy(&szBuffer[dwWritten], pszThreadName); dwWritten += lstrlen(pszThreadName); } for (LONG i=0; i<lCallDeep; ++i) { lstrcpy(&szBuffer[dwWritten], szSpace); dwWritten += __ITEMCOUNT(szSpace) - 1; } lstrcpy(&szBuffer[dwWritten], szMessage); dwWritten += __ITEMCOUNT(szMessage) - 1; lstrcpy(&szBuffer[dwWritten], m_pszFunction); dwWritten += lstrlen(m_pszFunction); lstrcpy(&szBuffer[dwWritten], szNewLine); dwWritten += __ITEMCOUNT(szNewLine) - 1; szBuffer[dwWritten] = TEXT('/0'); OutputDebugString(szBuffer); } TlsSetValue(s_lCallDeep, (LPVOID) (++lCallDeep)); } CTraceEx::~CTraceEx() { if (g_lTraceFlag == 0) { return; } LPTSTR pszThreadName = (LPTSTR) TlsGetValue(s_lThreadName); LONG lCallDeep = (LONG) TlsGetValue(s_lCallDeep); TlsSetValue(s_lCallDeep, (LPVOID) (--lCallDeep)); TCHAR szBuffer[BUFFER_SIZE] = { 0 }; if (m_pszFunction != NULL) { TCHAR szMessage[] = TEXT(" <- "); DWORD dwWritten = 0; if (pszThreadName != NULL) { lstrcpy(&szBuffer[dwWritten], pszThreadName); dwWritten += lstrlen(pszThreadName); } for (LONG i=0; i<lCallDeep; ++i) { lstrcpy(&szBuffer[dwWritten], szSpace); dwWritten += __ITEMCOUNT(szSpace) - 1; } lstrcpy(&szBuffer[dwWritten], szMessage); dwWritten += __ITEMCOUNT(szMessage) - 1; lstrcpy(&szBuffer[dwWritten], m_pszFunction); dwWritten += lstrlen(m_pszFunction); dwWritten += wsprintf(&szBuffer[dwWritten], TEXT(" - %d Milliseconds"), GetTickCount() - m_dwTickCount); lstrcpy(&szBuffer[dwWritten], szNewLine); dwWritten += __ITEMCOUNT(szNewLine) - 1; szBuffer[dwWritten] = TEXT('/0'); OutputDebugString(szBuffer); } if (lCallDeep == 0) { TlsSetValue(s_lThreadName, NULL); delete []pszThreadName; pszThreadName = NULL; } } ////////////////////////////////////////////////////////////////////////// VOID CTraceEx::Log(LPCTSTR pszFormat, ...) { if (g_lTraceFlag == 0 || pszFormat == NULL) { return; } LONG lCallDeep = (LONG) TlsGetValue(s_lCallDeep); LPTSTR pszThreadName = (LPTSTR) TlsGetValue(s_lThreadName); va_list param_list; va_start(param_list, pszFormat); TCHAR szBuffer[BUFFER_SIZE] = { 0 }; DWORD dwWritten = 0; if (pszThreadName != NULL) { lstrcpy(&szBuffer[dwWritten], pszThreadName); dwWritten += lstrlen(pszThreadName); } for (LONG i=0; i<lCallDeep; ++i) { lstrcpy(&szBuffer[dwWritten], szSpace); dwWritten += __ITEMCOUNT(szSpace) - 1; } dwWritten += wvsprintf(&szBuffer[dwWritten], pszFormat, param_list); va_end(param_list); lstrcpy(&szBuffer[dwWritten], szNewLine); dwWritten += __ITEMCOUNT(szNewLine) - 1; szBuffer[dwWritten] = TEXT('/0'); OutputDebugString(szBuffer); } ////////////////////////////////////////////////////////////////////////// struct __InitializeTLS { __InitializeTLS() { s_lCallDeep = TlsAlloc(); s_lThreadName = TlsAlloc(); TlsSetValue(s_lCallDeep, 0); TlsSetValue(s_lThreadName, NULL); } ~__InitializeTLS() { TlsFree(s_lCallDeep); TlsFree(s_lThreadName); s_lCallDeep = -1; s_lThreadName = -1; } } __gInitializeTLS;