您的位置:首页 > 其它

VC 调试 信息输出

2010-08-27 13:52 92 查看
一.前言:
一开始学习C++时,在控制台下写程序做练习,很容易输出程序变量,所以调试很方便。之后学习编写MFC程序,程序运行时要实时查看变量的情况就很麻烦了,虽然有TRACE宏(这个也是后来才知道的),但必须是程序结束后才能查看,而且阅读起来也很费劲。最开始自己写了个简陋的dis()函数(请看本文后面的附录),很简单,几行代码往程序一粘贴,再调用dis函数就可以显示字符串和int变量了。但后面输出的变量会覆盖之前的变量,所以还是不方便。于是一直在寻找一种输出变量更好的方法,偶然的情况下在网上下载了个DebugView.exe下载),试了一下,就觉爱不释手。TRACE输出的变量实时地显示在DebugView上,给调试带来了更多的方便。本来以为是这已经是终极好方法,谁知又在偶然的情况下,看到一位高手写的一段代码,可以很简单的将变量输出到控制台窗口,比起用DebugView的方法是更甚一筹了。之后自己再做了些研究,不断改进,力求简单傻瓜,终于有了一个较为满意的解决法案了。现在拿出来很大家分享。希望对你有所帮助。

二.把trace函数添加到你的工程:
方法一:把下面的红色字体代码保存为头文件Trace.h,然后在StdAfx.h包含它。
方法二:直接把下面的红色字体代码粘贴在StdAfx.h中。(我个人更倾向于这种方法,方便)

#include <afxwin.h> // MFC core and standard components
#include <afxext.h> // MFC extensions
#include <afxdisp.h> // MFC Automation classes
#include <afxdtctl.h> // MFC support for Internet Explorer 4 Common Controls
#ifndef _AFX_NO_AFXCMN_SUPPORT
#include <afxcmn.h> // MFC support for Windows Common Controls
#endif // _AFX_NO_AFXCMN_SUPPORT

/****************************************************************************
trace function
功能:输出调试变量
操作:在StdAfx.h加入以下代码(或者放在一个独立的头文件中),之后在需要的地方调用trace()函数就可以监视变量
说明:
1.如果想在Release版本中使用trace(),用"TRACE_WINDOW"代替"#ifdef _DEBUG"中的"_DEBUG"
2.可调用 SetDebugWndPos(HWND_TOPMOST,0,0,400,600); 设置输出窗口的位置以及是否置顶窗口
3.所有输出的变量都会保存在工程目录下的"DebugData.txt"文件,以方便查看
4.更详细介绍请访问:http://hi.baidu.com/qiujiejia/blog/item/e43943187fc1f90e34fa4176.html
5.由于在下水平有限,若有错误,还望海涵。若有问题或建议,请email我:qiujiejia@gmail.com
****************************************************************************/

//如果想在Release版本中使用trace(),用"TRACE_WINDOW"代替"#ifdef _DEBUG"中的"_DEBUG"
#define TRACE_WINDOW

#define IndexLength 6 //设置序号的长度

#ifdef _DEBUG

#include <sstream> #include <string>

//设置类成为通用版本
#ifdef _UNICODE
#define tstring wstring
#define tostringstream wostringstream
#define titoa _itow
#else
#define tstring string
#define tostringstream ostringstream
#define titoa _itoa
#endif

class DEBUG_WINDOW
{
private:
TCHAR WindowText[100];
HANDLE hOut; //控制台句柄
FILE* fp; //输出文件句柄

public:
DEBUG_WINDOW(TCHAR* WindowName,char* FileName,int cx,int cy)
{
_tcsncpy(WindowText,WindowName,99);
AllocConsole();//分配
SetConsoleTitle(WindowName); //窗口标题
hOut = GetStdHandle(STD_OUTPUT_HANDLE);//指明句柄为标准输出HANDLE
COORD co = {cx,cy};
SetConsoleScreenBufferSize(hOut, co);//指明缓冲区大小
fp = fopen(FileName,"w");//打开由于保存数据文件DebugData.txt,
}

~DEBUG_WINDOW() { fclose(fp); }

void PrintString(TCHAR* OutputStr,int nMax)
{
DWORD cCharsWritten;
WriteConsole(hOut,OutputStr,nMax,&cCharsWritten, NULL);
#ifdef _UNICODE
//先进行转换
char* buff = new char[nMax*2+1];
memset(buff,0,nMax*2+1);
setlocale(LC_ALL,".936");
int nChars = wcstombs(buff,OutputStr,nMax*2+1);
setlocale(LC_ALL,"C");
fwrite(buff,1,nChars,fp);
delete buff;
#else
fwrite(OutputStr,1,nMax,fp);
#endif
}

void SetWindowPos(HWND hWndInsertAfter=HWND_TOPMOST,int x=0,int y=0,int cx=200,int cy=200)
{
::SetWindowPos(::FindWindow(NULL,WindowText),hWndInsertAfter,x,y,cx,cy,NULL);
}

template<class T> void Trace(TCHAR* ArgName,T a)
{
//ArgName就是trace() 括号里的所有东西,可能是"jacky" or _T("jacky") or TEXT("jacky")
if(*ArgName=='"') //*****************情况为 "jacky"
{
int len=_tcslen(ArgName+1);
TCHAR* p=new TCHAR[len];
_tcsncpy(p,ArgName+1,len-1);
p[len-1]='/0'; //最后一个置0
PrintString(p,len-1);
delete p;
}
else if ( *(ArgName+2)=='(' ) //******情况为 _T("jacky")
{
int len=_tcslen(ArgName+4)-1;
TCHAR* p=new TCHAR[len];
_tcsncpy(p,ArgName+4,len-1);
p[len-1]='/0'; //最后一个置0
PrintString(p,len-1);
delete p;
}
else if ( *(ArgName+4)=='(' ) //******情况为 TEXT("jacky")
{
int len=_tcslen(ArgName+6)-1;
TCHAR* p=new TCHAR[len];
_tcsncpy(p,ArgName+6,len-1);
p[len-1]='/0'; //最后一个置0
PrintString(p,len-1);
delete p;
}
else //是一个变量名称
{
PrintString(ArgName,_tcslen(ArgName)); //变量名没有分号
std::tostringstream FormatString;
FormatString<<TEXT("=")<<a;
std::tstring OutStr=FormatString.str();
PrintString((TCHAR*)OutStr.c_str(),OutStr.length());
}
}
};

//实例化一个对象,要定义为static,否则通不过编译
static DEBUG_WINDOW DebugWnd(_T("Debug Window"),"DebugData.txt",400,400);

static int DebugItemIndex=0;
static TCHAR iBuf[20];
#define AddIndex() {DebugWnd.PrintString(titoa(DebugItemIndex++,iBuf,10),IndexLength);}

#define SetDebugWndPos(X,Y,Z,W,T) {DebugWnd.SetWindowPos(X,Y,Z,W,T);}

#define trace(X) { AddIndex();DebugWnd.Trace(TEXT(#X),X); DebugWnd.PrintString(_T("/n"),1); }
#define trace2(X,Y) { AddIndex();DebugWnd.Trace(TEXT(#X),X); DebugWnd.PrintString(_T(" "),3); /
DebugWnd.Trace(TEXT(#Y),Y); DebugWnd.PrintString(_T("/n"),1); }
#define trace3(X,Y,Z) { AddIndex();DebugWnd.Trace(TEXT(#X),X); DebugWnd.PrintString(_T(" "),3); /
DebugWnd.Trace(TEXT(#Y),Y); DebugWnd.PrintString(_T(" "),3); /
DebugWnd.Trace(TEXT(#Z),Z); DebugWnd.PrintString(_T("/n"),1); }
#define trace4(X,Y,Z,W) { AddIndex();DebugWnd.Trace(TEXT(#X),X); DebugWnd.PrintString(_T(" "),3); /
DebugWnd.Trace(TEXT(#Y),Y); DebugWnd.PrintString(_T(" "),3); /
DebugWnd.Trace(TEXT(#Z),Z); DebugWnd.PrintString(_T(" "),3); /
DebugWnd.Trace(TEXT(#W),W); DebugWnd.PrintString(_T("/n"),1); }
#define TracePoint(P) trace2(P.x,P.y)
#define TraceRect(X) trace4(X.left,X.top,X.right,X.bottom)
#define TraceLastError(){ AddIndex(); DebugWnd.Trace(_T("System Error Codes:"),GetLastError()); DebugWnd.PrintString(_T("/n"),1); }

#else
#define SetDebugWndPos(X,Y,Z,W,T)
#define trace(X)
#define trace2(X,Y)
#define trace3(X,Y,Z)
#define trace4(X,Y,Z,W)
#define TracePoint(P)
#define TraceRect(X)
#define TraceLastError()
#endif

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__EE32DF26_4082_4291_A7C6_3A35739DAD97__INCLUDED_)

三.使用示例

void CDemoDlg::OnButton1()
{
int a=45; trace(a); //输出int型
double b=67.45; trace(b); //输出double型
TCHAR buf[20]=_T("jacky"); trace(buf); //输出字符串
trace("测试");
trace(_T("测试"));
}

void CDemoDlg::OnButton2()
{
int a=45; double b=67.45;
trace2(a,b);
}

void CDemoDlg::OnButton3()
{
POINT point;
GetCursorPos(&point);
TracePoint(point);
}

void CDemoDlg::OnButton4()
{
RECT rect;
::GetWindowRect(m_hWnd,&rect);
TraceRect(rect);
}

void CDemoDlg::OnButton5()
{
TraceLastError();
}

四.几点说明

1.输出函数有以下几个:
trace(X) //输出1个变量
trace2(X,Y) //输出2个变量
trace3(X,Y,Z) //输出3个变量
trace4(X,Y,Z,W) //输出4个变量
TracePoint(P) //输出坐标型POINT的数据
TraceRect(X) //输出RECT型的数据
TraceLastError()//输出上次错误的代码号

2.几个trace函数都具有自动识别数据类型的功能。
如:
int a=45; trace(a); //输出int型
char str[10]="jacky"; trace(str); //输出字符串
double b=67.45; trace(b); //输出double型

3.用trace()函数直接输出字符串时,可以不添加_T() 或 TEXT() 宏
trace("my test");
trace(_T("my test"));
trace(TEXT("my test"));
以上三个函数输出的都是 “my test”;

4.设置输出窗口的位置和大小。
在OnInitDialog()里添加下面代码:
SetDebugWndPos(HWND_TOPMOST,0,0,400,600);

5.如果想在Release版本中使用trace(),请用"TRACE_WINDOW"代替"#ifdef _DEBUG"中的"_DEBUG"就可以了

6.上面的代码支持Unicode,在VC6和VS2008中编译通过

五.图片预览



六.示例代码下载
trace_Demo.zip (在VC6下编译通过,支持unicode)(请不要直接使用迅雷下载)
trace_Demo_for_VS2008 (在vs2008中编译通过,支持unicode)

七.写应用程序日志

以下这个ftrace() 函数跟上面提到的trace() 函数的用法非常相似,不同点:

1.trace()监视的变量会显示在窗口并且保存在"DebugData.txt"文件中,

而ftrace() 直接保存变量在AppLog.txt中。

2.ftrace()是以追加的方式存入到原有的文件,而trace()每次都会删除原来

DebugData.txt中的数据后再重新保存

3.用法和上面描述的一样,把下面的代码粘贴在StdAfx.h中

/****************************************************************************
ftrace() 函数
功能:将要监视的状况写入应用程序事件日志,方便了解程序的运行状况。
操作:在StdAfx.h加入以下代码(或者放在一个独立的头文件中),之后在需要的地方调用ftrace()函数就可以监视变量
说明:
1.希望所有的ftrace函数无效,只需要注释掉“#define WRITE_DATA” 就可以了。
2.所有输出的数据都会保存在程序目录下的"AppLog.txt"文件,数据以追加的方式存入文件。
3.更详细介绍请访问:http://hi.baidu.com/qiujiejia/blog/item/e43943187fc1f90e34fa4176.html
4.由于在下水平有限,若有错误,还望海涵。若有问题,请email我:qiujiejia@gmail.com
****************************************************************************/

//注释掉这一句会使所有ftrace()函数无效
#define WRITE_DATA

#ifdef WRITE_DATA

#include <sstream>
#include <string>

//设置类成为通用版本
#ifdef _UNICODE
#define tstring wstring
#define tostringstream wostringstream
#else
#define tstring string
#define tostringstream ostringstream
#endif

class WRITE_DATA_TO_FILE
{
private:
FILE* fp;
public:
WRITE_DATA_TO_FILE(char* FileName) {fp=fopen(FileName,"a+");}
~WRITE_DATA_TO_FILE(){ fclose(fp); }

void PrintString(TCHAR* OutputStr,int nMax)
{
#ifdef _UNICODE
//先进行转换
char* buff = new char[nMax*2+1];
memset(buff,0,nMax*2+1);
setlocale(LC_ALL,".936");
int nChars = wcstombs(buff,OutputStr,nMax*2+1);
setlocale(LC_ALL,"C");
fwrite(buff,1,nChars,fp);
delete buff;
#else
fwrite(OutputStr,1,nMax,fp);
#endif
}

template<class T> void Trace(TCHAR* ArgName,T a)
{
//ArgName就是trace() 括号里的所有东西,可能是"jacky" or _T("jacky") or TEXT("jacky")
if(*ArgName=='"') //*****************情况为 "jacky"
{
int len=_tcslen(ArgName+1);
TCHAR* p=new TCHAR[len];
_tcsncpy(p,ArgName+1,len-1);
p[len-1]='/0'; //最后一个置0
PrintString(p,len-1);
delete p;
}
else if ( *(ArgName+2)=='(' )  //******情况为  _T("jacky")
{
int len=_tcslen(ArgName+4)-1;
TCHAR* p=new TCHAR[len];
_tcsncpy(p,ArgName+4,len-1);
p[len-1]='/0'; //最后一个置0
PrintString(p,len-1);
delete p;
}
else if ( *(ArgName+4)=='(' )  //******情况为  TEXT("jacky")
{
int len=_tcslen(ArgName+6)-1;
TCHAR* p=new TCHAR[len];
_tcsncpy(p,ArgName+6,len-1);
p[len-1]='/0'; //最后一个置0
PrintString(p,len-1);
delete p;
}
else //是一个变量名称
{
PrintString(ArgName,_tcslen(ArgName));    //变量名没有分号
std::tostringstream FormatString;
FormatString<<TEXT("=")<<a;
std::tstring OutStr=FormatString.str();
PrintString((TCHAR*)OutStr.c_str(),OutStr.length());
}
}
};

//实例化一个对象
static WRITE_DATA_TO_FILE obj("AppLog.txt");

#define ftrace(X)    { obj.Trace(TEXT(#X),X); obj.PrintString(_T("/n"),1);    }
#define ftrace2(X,Y) { obj.Trace(TEXT(#X),X); obj.PrintString(_T("     "),3); /
obj.Trace(TEXT(#Y),Y); obj.PrintString(_T("/n"),1);    }
#define ftrace3(X,Y,Z) { obj.Trace(TEXT(#X),X); obj.PrintString(_T("     "),3); /
obj.Trace(TEXT(#Y),Y); obj.PrintString(_T("     "),3);     /
obj.Trace(TEXT(#Z),Z); obj.PrintString(_T("/n"),1);     }
#define ftrace4(X,Y,Z,W) { obj.Trace(TEXT(#X),X); obj.PrintString(_T("     "),3); /
obj.Trace(TEXT(#Y),Y); obj.PrintString(_T("     "),3);     /
obj.Trace(TEXT(#Z),Z); obj.PrintString(_T("     "),3);    /
obj.Trace(TEXT(#W),W); obj.PrintString(_T("/n"),1);    }
#define fTracePoint(P)    ftrace2(P.x,P.y)
#define fTraceRect(X)    ftrace4(X.left,X.top,X.right,X.bottom)
#define fTraceLastError(){ obj.Trace(_T("System Error Codes:"),GetLastError()); obj.PrintString(_T("/n"),1); }

#else
#define ftrace(X)
#define ftrace2(X,Y)
#define ftrace3(X,Y,Z)
#define ftrace4(X,Y,Z,W)
#define fTracePoint(P)
#define fTraceRect(X)
#define fTraceLastError()
#endif

八.另一个监视变量的工具
它是高手Paul DiLascia写的,相信会很不错。
http://www.dilascia.com/TraceWin.htm

其他:

vc 调试信息输出
文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/c++/cppjs/20090303/157190.html

1.CDumpContext

该类没有基类。
这个类支持面向流的诊断输出,以人能够阅读的文本。
该类重载了<<操作符。

afxDump是一个预声明的CDumpContext对象,可以方便使用。
该对象只在MFC的Debug版中有效。
可以将调式信息输出到调试输出窗口或调试终端。

// example for afxDump
CPerson myPerson = new CPerson;
// set some fields of the CPerson object...
//..
// now dump the contents
#ifdef _DEBUG
afxDump << "Dumping myPerson:/n";
myPerson->Dump( afxDump );
afxDump << "/n";
#endif

如果想建立一个制定的输出,比如一个制定的errlog文件。
我们可以自己生成一个CDumpContext对象。
方法如下:

CFile f;
if( !f.Open( "dump.txt", CFile::modeCreate | CFile::modeWrite ) ) {
afxDump << "Unable to open file" << "/n";
exit( 1 );
}
CDumpContext dc( &f );

2.TRACE

这个宏可以在DEBUG过程中,方便的跟踪程序中的变量的值。
在Debug环境中,TRACE宏输出到afxDump对象中。
在Release环境中,它不起作用。
TRACE一次限制512个字符,包括结束的NULL字符。
如果超过将引发ASSERT。

例:
int i = 1;
char sz[] = "one";
TRACE( "Integer = %d, String = %s/n", i, sz );
// Output: 'Integer = 1, String = one'

同时,还有TRACE0,TRACE1,TRACE2,TRACE3等宏。
数字代表宏中的参数数。

// example for TRACE0
TRACE0( "Start Dump of MyClass members:" );

// example for TRACE1
int i = 1;
TRACE1( "Integer = %d/n", i );
// Output: 'Integer = 1'

// example for TRACE2
int i = 1;
char sz[] = "one";
TRACE2( "Integer = %d, String = %s/n", i, sz );
// Output: 'Integer = 1, String = one'

3.void AfxDump( const CObject* pOb )
该函数调用对象的Dump成员函数,将信息输出到afxDump制定的位置。
最好不要在程序中调用该函数,而使用对象的Dump函数。

4.virtual void Dump( CDumpContext& dc ) const;
是CObjec的成员函数,将对象的内容输出到一个CDumpContext对象。
写自定义类的时候,应该重写Dump函数,来提供诊断服务。
重写的Dump函数中一般会先调用基类的Dump函数,后输出数据成员。
CObject::Dump输出类名,如果你的类用了IMPLEMENT_DYNAMIC或IMPLEMENT_SERIAL宏。
例:

class CPerson : public CObject
{
public:
//声明
#ifdef _DEBUG
virtual void Dump( CDumpContext& dc ) const;
#endif

CString m_firstName;
CString m_lastName;
// etc. ...
};
//实现
#ifdef _DEBUG
void CPerson::Dump( CDumpContext& dc ) const
{
// call base class function first
CObject::Dump( dc );

// now do the stuff for our specific class
dc << "last name: " << m_lastName << "/n"
<< "first name: " << m_firstName << "/n";
}
#endif

//调用
CPerson person;
#ifdef _DEBUG
CFile f;
if( !f.Open( "c://dump.txt", CFile::modeCreate | CFile::modeWrite ) ) {
afxDump << "Unable to open file" << "/n";
exit( 1 );
}
CDumpContext dc( &f );

person.Dump(dc);
dc<<"test dump output";
#endif

在较复杂的程序中,我们可以采用上述方法,

在调试程序的过程中,输出自己想要的数据和信息。

还是较为方便和简单的方法的。

文章出处:飞诺网(www.firnow.com):http://dev.firnow.com/course/3_program/c++/cppjs/20090303/157190.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: