一,各宏定义如下:
#ifdef _DEBUG
#define TRACE ::AfxTrace
#define TRACE0(sz) ::AfxTrace(_T("%s"), _T(sz))
#define TRACE1(sz, p1) ::AfxTrace(_T(sz), p1)
#define TRACE2(sz, p1, p2) ::AfxTrace(_T(sz), p1, p2)
#define TRACE3(sz, p1, p2, p3) ::AfxTrace(_T(sz), p1, p2, p3)
#else
inline void AFX_CDECL AfxTrace(LPCTSTR, ...) { }
#define TRACE 1 ? (void)0 : ::AfxTrace
#define TRACE0(sz)
#define TRACE1(sz, p1)
#define TRACE2(sz, p1, p2)
#define TRACE3(sz, p1, p2, p3)
#endif
二. 调试输出的核心函数
MFC调试输出的核心函数是AfxTrace,其代码如下:
#ifdef _DEBUG // entire file
#ifdef AFX_AUX_SEG
#pragma code_seg(AFX_AUX_SEG)
#endif
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/
// Helper routines that can be called from debugger
void AFXAPI AfxDump(const CObject* pOb)
{
afxDump << pOb;
}
/
// Diagnostic Trace
void AFX_CDECL AfxTrace(LPCTSTR lpszFormat, ...)
{
#ifdef _DEBUG // all AfxTrace output is controlled by afxTraceEnabled
if (!afxTraceEnabled)
return;
#endif
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512];
nBuf = _vsntprintf(szBuffer, _countof(szBuffer), lpszFormat, args);
// was there an error? was the expanded string too long?
ASSERT(nBuf >= 0);
if ((afxTraceFlags & traceMultiApp) && (AfxGetApp() != NULL))
afxDump << AfxGetApp()->m_pszExeName << ": ";
afxDump << szBuffer;
va_end(args);
}
#endif //_DEBUG
三 全局DumpDC
AFX_DATADEF CDumpContext afxDump;
四. CDumpContext类接口
class CDumpContext
{
public:
CDumpContext(CFile* pFile = NULL);
// Attributes
int GetDepth() const; // 0 => this object, 1 => children objects
void SetDepth(int nNewDepth);
// Operations
CDumpContext& operator<<(LPCTSTR lpsz);
#ifdef _UNICODE
CDumpContext& operator<<(LPCSTR lpsz); // automatically widened
#else
CDumpContext& operator<<(LPCWSTR lpsz); // automatically thinned
#endif
CDumpContext& operator<<(const void* lp);
CDumpContext& operator<<(const CObject* pOb);
CDumpContext& operator<<(const CObject& ob);
CDumpContext& operator<<(BYTE by);
CDumpContext& operator<<(WORD w);
CDumpContext& operator<<(UINT u);
CDumpContext& operator<<(LONG l);
CDumpContext& operator<<(DWORD dw);
CDumpContext& operator<<(float f);
CDumpContext& operator<<(double d);
CDumpContext& operator<<(int n);
void HexDump(LPCTSTR lpszLine, BYTE* pby, int nBytes, int nWidth);
void Flush();
// Implementation
protected:
// dump context objects cannot be copied or assigned
CDumpContext(const CDumpContext& dcSrc);
void operator=(const CDumpContext& dcSrc);
void OutputString(LPCTSTR lpsz);
int m_nDepth;
public:
CFile* m_pFile;
};
void CDumpContext::HexDump(LPCTSTR lpszLine, BYTE* pby,
int nBytes, int nWidth)
// do a simple hex-dump (8 per line) to a CDumpContext
// the "lpszLine" is a string to print at the start of each line
// (%lx should be used to expand the current address)
{
ASSERT(nBytes > 0);
ASSERT(nWidth > 0);
ASSERT(AfxIsValidString(lpszLine));
ASSERT(AfxIsValidAddress(pby, nBytes, FALSE));
#ifdef _DEBUG // all CDumpContext output is controlled by afxTraceEnabled
if (!afxTraceEnabled)
return;
#endif //_DEBUG
int nRow = 0;
TCHAR szBuffer[32];
while (nBytes--)
{
if (nRow == 0)
{
wsprintf(szBuffer, lpszLine, pby);
*this << szBuffer;
}
wsprintf(szBuffer, _T(" %02X"), *pby++);
*this << szBuffer;
if (++nRow >= nWidth)
{
*this << _T("/n");
nRow = 0;
}
}
if (nRow != 0)
*this << _T("/n");
}
/
#ifdef _UNICODE
// special version for ANSI characters
CDumpContext& CDumpContext::operator<<(LPCSTR lpsz)
{
if (lpsz == NULL)
{
OutputString(L"(NULL)");
return *this;
}
#ifdef _DEBUG // all CDumpContext output is controlled by afxTraceEnabled
if (!afxTraceEnabled)
return *this;
#endif //_DEBUG
// limited length
TCHAR szBuffer[512];
_mbstowcsz(szBuffer, lpsz, _countof(szBuffer));
return *this << szBuffer;
}
CDumpContext& CDumpContext::operator<<(LPCTSTR lpsz)
{
if (lpsz == NULL)
{
OutputString(_T("(NULL)"));
return *this;
}
#ifdef _DEBUG // all CDumpContext output is controlled by afxTraceEnabled
if (!afxTraceEnabled)
return *this;
#endif //_DEBUG
if (m_pFile == NULL)
{
TCHAR szBuffer[512];
LPTSTR lpBuf = szBuffer;
while (*lpsz != '/0')
{
if (lpBuf > szBuffer + _countof(szBuffer) - 3)
{
*lpBuf = '/0';
OutputString(szBuffer);
lpBuf = szBuffer;
}
if (*lpsz == '/n')
*lpBuf++ = '/r';
*lpBuf++ = *lpsz++;
}
*lpBuf = '/0';
OutputString(szBuffer);
return *this;
}
m_pFile->Write(lpsz, lstrlen(lpsz)*sizeof(TCHAR));
return *this;
}
void CDumpContext::Flush()
{
if (m_pFile)
m_pFile->Flush();
}
void CDumpContext::OutputString(LPCTSTR lpsz)
{
#ifdef _DEBUG
// all CDumpContext output is controlled by afxTraceEnabled
if (!afxTraceEnabled)
return;
#endif
// use C-runtime/OutputDebugString when m_pFile is NULL
if (m_pFile == NULL)
{
AfxOutputDebugString(lpsz);
return;
}
// otherwise, write the string to the file
m_pFile->Write(lpsz, lstrlen(lpsz)*sizeof(TCHAR));
}
五 AfxOutputDebugString宏
#ifdef _UNICODE
#define AfxOutputDebugString(lpsz) /
do /
{ /
USES_CONVERSION; /
_RPT0(_CRT_WARN, W2CA(lpsz)); /
} while (0)
#else
#define AfxOutputDebugString(lpsz) _RPT0(_CRT_WARN, lpsz)
#endif
#ifdef _DEBUG
#define _RPT0(rptno, msg) /
do { if ((1 == _CrtDbgReport(rptno, NULL, 0, NULL, "%s", msg))) /
_CrtDbgBreak(); } while (0)
#endif
六 进入CRT
_CRTIMP int __cdecl _CrtDbgReport(
int nRptType,
const char * szFile,
int nLine,
const char * szModule,
const char * szFormat,
...
)
{
int retval;
va_list arglist;
char szLineMessage[MAX_MSG] = {0};
char szOutMessage[MAX_MSG] = {0};
char szUserMessage[MAX_MSG] = {0};
#define ASSERTINTRO1 "Assertion failed: "
#define ASSERTINTRO2 "Assertion failed!"
va_start(arglist, szFormat);
if (nRptType < 0 || nRptType >= _CRT_ERRCNT)
return -1;
/*
* handle the (hopefully rare) case of
*
* 1) ASSERT while already dealing with an ASSERT
* or
* 2) two threads asserting at the same time
*/
if
(_CRT_ASSERT == nRptType && _CrtInterlockedIncrement(&_crtAssertBusy) > 0)
{
/* use only 'safe' functions -- must not assert in here! */
#ifdef _WIN32
static int (APIENTRY *pfnwsprintfA)(LPSTR, LPCSTR, ...) = NULL;
if (NULL == pfnwsprintfA)
{
HANDLE hlib = LoadLibrary("user32.dll");
if (NULL == hlib || NULL == (pfnwsprintfA =
(int (APIENTRY *)(LPSTR, LPCSTR, ...))
GetProcAddress(hlib, "wsprintfA")))
return -1;
}
(*pfnwsprintfA)( szOutMessage,
"Second Chance Assertion Failed: File %s, Line %d/n",
szFile, nLine);
OutputDebugString(szOutMessage);
#else /* _WIN32 */
strcpy(szOutMessage, "Second Chance Assertion Failed: File ");
strcat(szOutMessage, szFile);
strcat(szOutMessage, ", Line ");
numtostring(nLine, &szOutMessage[strlen(szOutMessage)]);
strcat(szOutMessage, "/n");
_CrtOutputDebugString(szOutMessage);
#endif /* _WIN32 */
_CrtInterlockedDecrement(&_crtAssertBusy);
_CrtDbgBreak();
return -1;
}
if (szFormat && _vsnprintf(szUserMessage,
MAX_MSG-max(sizeof(ASSERTINTRO1),sizeof(ASSERTINTRO2)),
szFormat,
arglist) < 0)
strcpy(szUserMessage, TOOLONGMSG);
if (_CRT_ASSERT == nRptType)
strcpy(szLineMessage, szFormat ? ASSERTINTRO1 : ASSERTINTRO2);
strcat(szLineMessage, szUserMessage);
if (_CRT_ASSERT == nRptType)
{
if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
strcat(szLineMessage, "/r");
strcat(szLineMessage, "/n");
}
if (szFile)
{
if (_snprintf(szOutMessage, MAX_MSG, "%s(%d) : %s",
szFile, nLine, szLineMessage) < 0)
strcpy(szOutMessage, TOOLONGMSG);
}
else
strcpy(szOutMessage, szLineMessage);
/* user hook may handle report */
if (_pfnReportHook && (*_pfnReportHook)(nRptType, szOutMessage, &retval))
{
if (_CRT_ASSERT == nRptType)
_CrtInterlockedDecrement(&_crtAssertBusy);
return retval;
}
if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_FILE)
{
if (_CrtDbgFile[nRptType] != _CRTDBG_INVALID_HFILE)
{
#ifdef _WIN32
DWORD written;
WriteFile(_CrtDbgFile[nRptType], szOutMessage, strlen(szOutMessage), &written, NULL);
#else /* _WIN32 */
long written = strlen(szOutMessage);
FSWrite((short)_CrtDbgFile[nRptType], &written, szOutMessage);
#endif /* _WIN32 */
}
}
if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_DEBUG)
{
#ifdef _WIN32
OutputDebugString(szOutMessage);
#else /* _WIN32 */
_CrtOutputDebugString(szOutMessage);
#endif /* _WIN32 */
}
if (_CrtDbgMode[nRptType] & _CRTDBG_MODE_WNDW)
{
char szLine[20];
retval = CrtMessageWindow(nRptType, szFile, nLine ? _itoa(nLine, szLine, 10) : NULL, szModule, szUserMessage);
if (_CRT_ASSERT == nRptType)
_CrtInterlockedDecrement(&_crtAssertBusy);
return retval;
}
if (_CRT_ASSERT == nRptType)
_CrtInterlockedDecrement(&_crtAssertBusy);
/* ignore */
return FALSE;
}
七 REACTOS中的OutputDebugStringA
VOID
WINAPI
OutputDebugStringA(LPCSTR _OutputString)
{
_SEH2_TRY
{
ULONG_PTR a_nArgs[2];
a_nArgs[0] = (ULONG_PTR)(strlen(_OutputString) + 1);
a_nArgs[1] = (ULONG_PTR)_OutputString;
/* send the string to the user-mode debugger */
RaiseException(DBG_PRINTEXCEPTION_C, 0, 2, a_nArgs);
}
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
{
/* no user-mode debugger: try the systemwide debug message monitor, or the
kernel debugger as a last resort */
/* mutex used to synchronize invocations of OutputDebugString */
static HANDLE s_hDBMonMutex = NULL;
/* true if we already attempted to open/create the mutex */
static BOOL s_bDBMonMutexTriedOpen = FALSE;
/* local copy of the mutex handle */
volatile HANDLE hDBMonMutex = s_hDBMonMutex;
/* handle to the Section of the shared buffer */
volatile HANDLE hDBMonBuffer = NULL;
/* pointer to the mapped view of the shared buffer. It consist of the current
process id followed by the message string */
struct { DWORD ProcessId; CHAR Buffer[1]; } * pDBMonBuffer = NULL;
/* event: signaled by the debug message monitor when OutputDebugString can write
to the shared buffer */
volatile HANDLE hDBMonBufferReady = NULL;
/* event: to be signaled by OutputDebugString when it's done writing to the
shared buffer */
volatile HANDLE hDBMonDataReady = NULL;
/* mutex not opened, and no previous attempts to open/create it */
if(hDBMonMutex == NULL && !s_bDBMonMutexTriedOpen)
{
/* open/create the mutex */
hDBMonMutex = K32CreateDBMonMutex();
/* store the handle */
s_hDBMonMutex = hDBMonMutex;
}
_SEH2_TRY
{
volatile PCHAR a_cBuffer = NULL;
/* opening the mutex failed */
if(hDBMonMutex == NULL)
{
/* remember next time */
s_bDBMonMutexTriedOpen = TRUE;
}
/* opening the mutex succeeded */
else
{
do
{
/* synchronize with other invocations of OutputDebugString */
WaitForSingleObject(hDBMonMutex, INFINITE);
/* buffer of the system-wide debug message monitor */
hDBMonBuffer = OpenFileMappingW(SECTION_MAP_WRITE, FALSE, L"DBWIN_BUFFER");
/* couldn't open the buffer: send the string to the kernel debugger */
if(hDBMonBuffer == NULL) break;
/* map the buffer */
pDBMonBuffer = MapViewOfFile(hDBMonBuffer,
SECTION_MAP_READ | SECTION_MAP_WRITE,
0,
0,
0);
/* couldn't map the buffer: send the string to the kernel debugger */
if(pDBMonBuffer == NULL) break;
/* open the event signaling that the buffer can be accessed */
hDBMonBufferReady = OpenEventW(SYNCHRONIZE, FALSE, L"DBWIN_BUFFER_READY");
/* couldn't open the event: send the string to the kernel debugger */
if(hDBMonBufferReady == NULL) break;
/* open the event to be signaled when the buffer has been filled */
hDBMonDataReady = OpenEventW(EVENT_MODIFY_STATE, FALSE, L"DBWIN_DATA_READY");
}
while(0);
/* we couldn't connect to the system-wide debug message monitor: send the
string to the kernel debugger */
if(hDBMonDataReady == NULL) ReleaseMutex(hDBMonMutex);
}
_SEH2_TRY
{
/* size of the current output block */
volatile SIZE_T nRoundLen;
/* size of the remainder of the string */
volatile SIZE_T nOutputStringLen;
/* output the whole string */
nOutputStringLen = strlen(_OutputString);
do
{
/* we're connected to the debug monitor:
write the current block to the shared buffer */
if(hDBMonDataReady)
{
/* wait a maximum of 10 seconds for the debug monitor
to finish processing the shared buffer */
if(WaitForSingleObject(hDBMonBufferReady, 10000) != WAIT_OBJECT_0)
{
/* timeout or failure: give up */
break;
}
/* write the process id into the buffer */
pDBMonBuffer->ProcessId = GetCurrentProcessId();
/* write only as many bytes as they fit in the buffer */
if(nOutputStringLen > (PAGE_SIZE - sizeof(DWORD) - 1))
nRoundLen = PAGE_SIZE - sizeof(DWORD) - 1;
else
nRoundLen = nOutputStringLen;
/* copy the current block into the buffer */
memcpy(pDBMonBuffer->Buffer, _OutputString, nRoundLen);
/* null-terminate the current block */
pDBMonBuffer->Buffer[nRoundLen] = 0;
/* signal that the data contains meaningful data and can be read */
SetEvent(hDBMonDataReady);
}
/* else, send the current block to the kernel debugger */
else
{
/* output in blocks of 512 characters */
a_cBuffer = (CHAR*)HeapAlloc(GetProcessHeap(), 0, 512);
if (!a_cBuffer)
{
DbgPrint("OutputDebugStringA: Failed/n");
break;
}
/* write a maximum of 511 bytes */
if(nOutputStringLen > 510)
nRoundLen = 510;
else
nRoundLen = nOutputStringLen;
/* copy the current block */
memcpy(a_cBuffer, _OutputString, nRoundLen);
/* null-terminate the current block */
a_cBuffer[nRoundLen] = 0;
/* send the current block to the kernel debugger */
DbgPrint("%s", a_cBuffer);
if (a_cBuffer)
{
HeapFree(GetProcessHeap(), 0, a_cBuffer);
a_cBuffer = NULL;
}
}
/* move to the next block */
_OutputString += nRoundLen;
nOutputStringLen -= nRoundLen;
}
/* repeat until the string has been fully output */
while (nOutputStringLen > 0);
}
/* ignore access violations and let other exceptions fall through */
_SEH2_EXCEPT((_SEH2_GetExceptionCode() == STATUS_ACCESS_VIOLATION) ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
if (a_cBuffer)
HeapFree(GetProcessHeap(), 0, a_cBuffer);
/* string copied verbatim from Microsoft's kernel32.dll */
DbgPrint("/nOutputDebugString faulted during output/n");
}
_SEH2_END;
}
_SEH2_FINALLY
{
/* close all the still open resources */
if(hDBMonBufferReady) CloseHandle(hDBMonBufferReady);
if(pDBMonBuffer) UnmapViewOfFile(pDBMonBuffer);
if(hDBMonBuffer) CloseHandle(hDBMonBuffer);
if(hDBMonDataReady) CloseHandle(hDBMonDataReady);
/* leave the critical section */
if(hDBMonDataReady != NULL)
ReleaseMutex(hDBMonMutex);
}
_SEH2_END;
}
_SEH2_END;
}