输出框便会输出“KCisafucker”。
我们现在来看看afxDump的定义代码。
源代码在AFX.H中
#ifdef_DEBUG
externAFX_DATACDumpContextafxDump; //LookAtHere!
externAFX_DATABOOLafxTraceEnabled; //这个变量和afxTraceFlags同为调式输出的开关标志
//不过MS在新版本的MFC中被废除了
#endif
然后我们再来看看CDumpContext辅助类的定义。
源代码同样也在AFX.H中
classCDumpContext
{
public:
CDumpContext(CFile*pFile=NULL);
//Attributes
intGetDepth()const; //0=>thisobject,1=>childrenobjects
voidSetDepth(intnNewDepth);
//Operations
CDumpContext&operator<<(LPCTSTRlpsz);
#ifdef_UNICODE
CDumpContext&operator<<(LPCSTRlpsz); //automaticallywidened
#else
CDumpContext&operator<<(LPCWSTRlpsz);//automaticallythinned
#endif
CDumpContext&operator<<(constvoid*lp);
CDumpContext&operator<<(constCObject*pOb);
CDumpContext&operator<<(constCObject&ob);
CDumpContext&operator<<(BYTEby);
CDumpContext&operator<<(WORDw);
CDumpContext&DumpAsHex(BYTEb);
CDumpContext&DumpAsHex(WORDw);
#ifdef_WIN64
CDumpContext&operator<<(LONGl);
CDumpContext&operator<<(DWORDdw);
CDumpContext&operator<<(intn);
CDumpContext&operator<<(UINTu);
CDumpContext&DumpAsHex(LONGl);
CDumpContext&DumpAsHex(DWORDdw);
CDumpContext&DumpAsHex(intn);
CDumpContext&DumpAsHex(UINTu);
#else
CDumpContext&operator<<(LONG_PTRl);
CDumpContext&operator<<(DWORD_PTRdw);
CDumpContext&operator<<(INT_PTRn);
CDumpContext&operator<<(UINT_PTRu);
CDumpContext&DumpAsHex(LONG_PTRl);
CDumpContext&DumpAsHex(DWORD_PTRdw);
CDumpContext&DumpAsHex(INT_PTRn);
CDumpContext&DumpAsHex(UINT_PTRu);
#endif
CDumpContext&operator<<(floatf);
CDumpContext&operator<<(doubled);
CDumpContext&operator<<(LONGLONGn);
CDumpContext&operator<<(ULONGLONGn);
CDumpContext&DumpAsHex(LONGLONGn);
CDumpContext&DumpAsHex(ULONGLONGn);
CDumpContext&operator<<(HWNDh);
CDumpContext&operator<<(HDCh);
CDumpContext&operator<<(HMENUh);
CDumpContext&operator<<(HACCELh);
CDumpContext&operator<<(HFONTh);
voidHexDump(LPCTSTRlpszLine,BYTE*pby,intnBytes,intnWidth);
voidFlush();
//Implementation
protected:
//dumpcontextobjectscannotbecopiedorassigned
CDumpContext(constCDumpContext&dcSrc);
voidoperator=(constCDumpContext&dcSrc);
voidOutputString(LPCTSTRlpsz);
intm_nDepth;
public:
CFile*m_pFile;
};
CDumpContext只有一个构造函数,而且默认把m_pFile设置成了NULL,这点很关键,我们在后面马上会看到~
这里可能会有点疑问,为什么会存在一个CFile*类型的Public成员变量?
我也不知道,KC个人的猜测是,CDumpContext不仅能够往输出框输出信息,应该还能够往外写文件。
而下面的m_pFile->Write也能够支持我的猜测。
另一个亮点是,CDumpContext中存在多个<<重载运算符,这样便于afxDump进行不同类型的<<运算。
不过这里有一个插曲,CDumpContext的上述代码中有几行比较有意思:
//Operations
CDumpContext&operator<<(LPCTSTRlpsz);
#ifdef_UNICODE
CDumpContext&operator<<(LPCSTRlpsz); //automaticallywidened
#else
CDumpContext&operator<<(LPCWSTRlpsz);//automaticallythinned
#endif
之前我一直不明白这段的用意,后来经D大提醒,幡然醒悟。
这段宏的作用大致是:
在UNICODE下,遇到MBCS字符串自动做扩大处理;在MBCS下,遇到UNICODE字符串自动做缩小处理。
相应的实现代码如下:
#ifdef_UNICODE
//specialversionforANSIcharacters
CDumpContext&CDumpContext:
:
operator<<(LPCSTRlpsz)
{
if(lpsz==NULL)
{
OutputString(L"(NULL)");
return*this;
}
//limitedlength
TCHARszBuffer[512];
_mbstowcsz(szBuffer,lpsz,_countof(szBuffer));
szBuffer[511]=0;
return*this<}
#else //_UNICODE
//specialversionforWIDEcharacters
CDumpContext&CDumpContext:
:
operator<<(LPCWSTRlpsz)
{
if(lpsz==NULL)
{
OutputString("(NULL)");
return*this;
}
//limitedlength
charszBuffer[512];
_wcstombsz(szBuffer,lpsz,_countof(szBuffer));
szBuffer[511]=0;
return*this<}
#endif //!
_UNICODE
/////////////////////////////////////////////////////////////////////////////
接下来我们重点看CDumpContext对<<的实现。
虽然<<的重载很多,但是从本质上,可以分成对String和数值类型的两类。
那么我们先来看看对于数值类型的处理,额,随便挑一个~当~当~当~当~
CDumpContext&CDumpContext:
:
operator<<(WORDw)
{
TCHARszBuffer[32];
wsprintf(szBuffer,_T("%u"),(UINT)w);
OutputString(szBuffer);
return*this;
}
因为是数值类型,所以算上64Bit的大整数,也长不到哪里去。
所以这里分配的缓冲区数组的下标只有32.
然后利用wsprintf把数字格式化,最后用OutputString输出。
wsprintf详细信息请参考附录
至于对String的处理,代码如下:
CDumpContext&CDumpContext:
:
operator<<(LPCTSTRlpsz)
{
if(lpsz==NULL)
{
OutputString(_T("NULL"));
return*this;
}
ASSERT(lpsz!
=NULL);
if(lpsz==NULL)
AfxThrowUserException();
if(m_pFile==NULL)
{
TCHARszBuffer[512];
LPTSTRlpBuf=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;
}
做<<前,先对参数进行合法性检查,然后在m_pFile为NULL的情况下,分配缓冲区(由于是字符串,所以下标为512),然后逐一的复制字符,最后相同的用OutputString转出。
比较上面两种<<的运算实现,我们可以很明显的看出,最后的数据都被传递到了OutputString里,所以我们还必须跟进OutputString。
1.4.OutputString
我们现在跳到OutputString的实现源代码上:
voidCDumpContext:
:
OutputString(LPCTSTRlpsz)
{
//useC-runtime/OutputDebugStringwhenm_pFileisNULL
if(m_pFile==NULL)
{
TRACE(traceDumpContext,0,lpsz);
return;
}
ASSERT(lpsz!
=NULL);
if(lpsz==NULL)
AfxThrowUserException();
//otherwise,writethestringtothefile
m_pFile->Write(lpsz,lstrlen(lpsz)*sizeof(TCHAR));
}
因为前面说过,m_pFile的值为NULL(我们没有给他传值,构造函数又自动给他NULL掉了),所以OutputString应该会执行下面的代码:
//useC-runtime/OutputDebugStringwhenm_pFileisNULL
if(m_pFile==NULL)
{
TRACE(traceDumpContext,0,lpsz);
return;
}
很奇怪,很神奇,很……囧……又回到了TRACE……
更何况,上面注释写着useC-runtime/OutputDebugString的字眼呢,多大个的字啊……
无奈中,我去翻了下MSDN,又去Google,结果得到了惊人的发现!
在MSDN,对于CDumpContext有这么一段的描述:
UndertheWindowsenvironment,theoutputfromthepredefinedafxDumpobject,conceptuallysimilartothecerrstream,isroutedtothedebuggerviatheWindowsfunctionOutputDebugString.
换句话说,转储的东西的的确确会经过底层的C运行时库函数或者OutputDebugString这个API。
此时我想起了之前出现的一个关于TRACE的BUG:
在UNICODE下无法输出中文。
当时我通过F9/F10/F11不断的跟进,但是单语句调试到TRACE(traceDumpContext,0,lpsz)这里时,却提示没有可显示的语句。
所以,有可能转储的东西跑到了某个C底层函数去(如果是OutputDebugString,中文也应输出)。
于是我把目光瞄准了traceDumpContext,发现这个是个宏(很奇怪,是ATL系列的),经过多次进进出出的跟进后,发现了一个叫做CTrace的类,而且在里面还发现如下代码:
classCTrace
{
public:
typedefint(__cdecl*fnCrtDbgReport_t)(int,constchar*,int,constchar*,constchar*,...);
private:
CTrace(
#ifdef_ATL_NO_DEBUG_CRT
fnCrtDbgReport_tpfnCrtDbgReport=NULL)
#else
fnCrtDbgReport_tpfnCrtDbgReport=_CrtDbgReport)
#endif
我很敏感的关注了_CtrDbgReport这个函数,去MSDN翻了下,得到的结果很惊人!
Generatesareportwithadebuggingmessageandsendsthereporttothreepossibledestinations(debugversiononly).
而且,Remark上还有这么一段(具体请参考附录):
InVisualC++2005,_CrtDbgReportWisthewide-characterversionof_CrtDbgReport.Allitsoutputandstringparametersareinwide-characterstrings;otherwiseitisidenticaltothesingle-bytecharacterversion.
_CrtDbgReportand_CrtDbgReportWcreatetheusermessageforthedebugreportbysubstitutingtheargument[n]argumentsintotheformatstring,usingthesamerulesdefinedbytheprintforwprintffunctions.Thesefunctionsthengeneratethedebugreportanddeterminethedestinationordestinations,basedonthecurrentreportmodesandfiledefinedforreportType.Whenth