打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Windows 程序调试

                        Windows 调试指南       

                                             ---原文作者:段晓冬
             
                             第一章     跟踪
 1.Windows跟踪语句:
(1)TRACE(_T(“Warning (FunctionName):Object %s notfound.\n”),objectName);
跟踪信息输出到输出窗口outputwindow中。[调试版本中使用]
(2) OutputDebugString (_T(“trace  debuginfo!\n”));[调试版本中使用]
如果只想在调试版本中使用OutputDebugString,可以使用下面得宏来实现:
#ifdef  _DEBUG
#define OutputTraceString(text) OutputDebugString(text)
#else
#define OutputTraceString(text)  ((void)(0))
   #endif
2.ANSI C++ 运行时刻函数库跟踪
具体有:C语言的stderr和C++语言的clog流,在Windows程序中没有任何效果!
3.VC++的C运行时刻库函数跟踪语句
  ANSI C运行时刻库函数没有跟踪语句,但是VC++的C运行时刻库函数有。可使用_RPTn或_RPTFn调试报告宏,但是你必须在程序中引用crtdbg.h,并利用C运行时刻函数库链接:
  _RPT0(reportType,format);
  _RPT1(reportType,format,arg1);
  _RPT2(reportType,format,arg1,arg2);

_RPTF0(reportType,format);
  _RPTF1(reportType,format,arg1);
  _RPTF2(reportType,format,arg1,arg2);
例子:
       int a =1000,b=2000;
       int *p =&a;
       int *q = &b;
_RPTF2(_CRT_WARN,"%x ,%x",p,q);//直接在outputwindow中输出
     _RPTF2(_CRT_ERROR,"%x ,%x",p,q); //弹出ERROR对话框
     _RPTF2( _CRT_ASSERT,"%x ,%x",p,q); //弹出AEESRT对话框
reportType:_CRT_WARN _CRT_ERROR _CRT_ASSERT
其中_CRT_WARN用于跟踪语句,_RPTFn宏报告了源码文件名和调用这些宏的行号。[调试版本中使用]
可以使用_CrtSetReportMode函数改变默认输出设置,(比如输出到调试器输出窗口,文件,消息框中),_CrtSetReportFile可以指定将报告输出到哪个文件中。
4.MFC中的跟踪语句
区别:使用TRACE宏时,需要使用_T宏来格式化参数以正确解决Unicode的校正,反之,在TRACEn类型的宏中,不必使用_T宏。
例子1-1:
TRACE(_T("ssss\n"));
     TRACE2("%d r %d l\n",1,2);
5.使用Trace实用程序
使用Tools->MFC Tracer->Enabletracing选项打开跟踪输出功能。如果你选择了“Mainmessagedispatch“,MFC使用下面的代码跟踪所有窗口:

//from Wincore.cpp
#ifdef _DEBUG
  if(afxTraceFlags&traceWinMsg)_AfxTraceMsg(_T(“Wndpro”),&pThreadState->m_lastSentMsg);
#endif

也可以使用”WM_COMMANDdispatch”选项通过MFC命令消息的路线进行跟踪!Tracer只影响MFC跟踪语句的输出!
6.使用AfxOutputDebugString
AfxOutputDebugString宏使用和OutputDebugString一样的语法。
7.使用CObiect::Dump
CObject类有一个转储(dump)虚拟函数,继承于它的所有子类函数都可以重载这个函数,输出它们的值。
例1-2:用下列语句输出CObject派生类pObject的值。afxDump是预定义的全局变量CDumpContext,注意CDumpContext对最一般的内建数据类型及CObject的指针和引用支持插入操作符(<<)。有几个CObject派生的类也有定义的插入操作符,CPoint,CSize,CRect,CString,CTime和CTimeSpan。
#if _DEBUG
AfxDump(pObiect);
pObject->Dump(afxDump);
afxDump<
#endif

例1-3:
afxDump<<_T(“Warning:This objectdoesn’t seem right:\n”)<
8.使用AfxDump
AfxDump是MFC中相当于cerr流的跟踪语句,所以你可以直接向它输出跟踪消息。
TRACE宏由afxDump实现,afxDump由AfxOutputDebugString实现。而AfxOutputDebugString在调试版中由_RPT0宏实现。可以使用下面的方法将afxDump重定向。
#ifdef _DEBUG
CFile dumpFile; //必须为全局变量
   dumpFile.Open(_T("dump.log"),CFile::modeWrite|CFile::modeCreate);
   afxDump.m_pFile = &dumpFile;
#endif
轻轻巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧巧
9.使用AfxDumpStack
可以使用AfxDumpStack函数输出一个调用栈:
void AFXAPI AfxDumpStack(DWORD dwTarget =AFX_STACK_DUMP_TARGET_DEFAULT);
参数:dwTarget决定在调试和发布版本中输出到什么地方,可以输出到TRACE宏,OutputDebugString或到剪贴板。如果使用AFX_STACK_DUMP_TARGET_TRACE,含义是在调试版中输出到TRACE宏,而在发布版本中没有输出!如果你希望在发布版本中输出,可使用AFX_STACK_DUMP_TARGET_ODS选项,而且必须在路径中有Imagehlp.dll文件。在project?settings?Link?Category?DebugBoth formats。
10.ATL跟踪语句 最基本的类型是AtlTrace函数:
inline void_cdecl AtlTrace(LPCTSTR format……);
ATLTRACE (format……);

和MFCTRACE宏一样,它使用一个512字节固定大小的缓冲区,如果它的参数需要一个大于512字节的文本缓冲区,会导致一个出错的断言。实际上,它是使用API函数OutputDebugString实现的,因此它的输出不能改变到其他目标。
ATL跟踪语句的另一个选择AtlTrace2函数:
Inline void _cedecl AtlTrace2(DWORD category,UINT level,LPCTSTRformat,…);
ATLTRACE(category,level,format);
该函数增加了一个参数跟踪类别(category)(例如,atlTraceCOM,atlTraceWindowing和atlTraceControl)和一个参数严格级别。类别值是位掩码,ATL自身使用介于0-2的级别值,0级指最严格的级别。和AtlTrace函数类似,AtlTrace2函数只能用在调试版本中!
11.VC++消息Pragma
Pagama是一个编译时的跟踪语句,可用来警告在预处理过程中发现的潜在的编连(build)问题
12.处理长字符串:[例如处理SQL语句]
(1)MFC下[只能调试版]
TRACE(longString); //assert if_tcslen(longString)>511,最大是512
#ifdef _DEBUG
afxDump<




#endif
(2)ATL下[只能调试版]
ATLTRACE(longString); //assert if_tcslen(longString)>511,最大是512
#ifdef _DEBUG
OutputDebugString(longString); //dosen’t assert for long strings不需要判断
#endif
13.处理大量的跟踪输出。
如果跟踪消息数据产生的速度超过输出窗口处理的速度,那么消息会塞满缓冲区,数据会丢失。
简单方法:在输出大量数据的代码段,例如对象转储函数时候,调用Sleep API函数。例如:Sleep(100).

第二章 异常和返回值
C++程序中可以使用异常和返回值来返回状态信息。C语言返回一个函数的状态的最好方法就是它的返回值
Try
{
//code may fail
}
Catch
{
//handle the failure
}
只有在抛出异常的时候会有开销!Catch块有一些开销,但是try块有很少的开销!
例子2-1只能在调试版中处理异常,并弹出MessageBox,发布版不处理异常,为的就是优化。
try
    {
     int *pInt = 0;
     *pInt =42;
    }
   catch(...)
    {
     MessageBox(_T("Exception was caught!"),_T("Exceptiontest"),MB_OK);
     
    }

第三章   调试
1.当错误发生得时候,调用GetLastError()得到相应得错误码
错误码得位域有固定的格式,,在C:\ProgramFiles\Microsoft VisualStudio\VC98\Include\Winerror.h中有详细的说明:
//
//  Values are 32 bit values layed out asfollows:
//
//   3 3 2 2 2 2 2 2 2 2 2 2 1 11 1 1 1 1 1 1 1
//   1 0 9 8 7 6 5 4 3 2 1 0 9 87 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
// +---+-+-+-----------------------+-------------------------------+
// |Sev|C|R|    Facility                      Code           |
// +---+-+-+-----------------------+-------------------------------+
//
//     Sev - is the severitycode      安全代码
//
//         00 –Success         0-安全  
//         01 –Informational    1-信息
//         10 –Warning        2-警告
//         11 –Error           3-错误
//     C - is the Customer code flag
客户代码:0-Microsoft定义的,1-客户定义的
//     R - is a reserved bit 保留位必须是0 
//     Facility - is the facility code工具-Microsoft 定义的
//     Code - is the facility's status code工具状态代码-Microsoft或客户定义
 工具:更好的查看错误代码的方法,Tools-》ErrorLookup;在VC++调试器的watch窗口中输入@ERR监视GetLastError的返回数值,ERR是调试器用来显示最新错误码的一个虚拟寄存器。还可以用@ERR,hr将错误码转换为文本格式
  
 技巧:在调试中,按F11键,Alt+8显示反汇编窗口,在Edit菜单中选择GoTo命令,在GoTo对话框的Go towhat中选择Address选项,在Enter addressexpression中输入导致崩溃的地址。

创建映射文件的方法:Project?Settings?Link?Generatemapfile.即可在工程的Debug文件下看到。映射文件里面所有的公共符号都使用混合名字。可使用VC++的名字解析工具(Undname)将混合名字转换到原始名字。你可以在“开始”?”运行”?”cmd”?输入
C:\Documents and Settings\zhangzhongping>cdC:\Program Files\Microsoft Visual Studio\Common\Tools
输入:
C:\ProgramFiles\MicrosoftVisualStudio\Common\Tools>Undname?RandException@@YGXHHHH@Z
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp.1981-1998
输出:
>> ?RandException@@YGXHHHH@Z ==RandException

当输入-f时,显示整个函数的原型!查看映射文件的时候,若出现重载函数,名字解析工具很有用!
输入:
C:\ProgramFiles\MicrosoftVisualStudio\Common\Tools>Undname-f?RandException@@YGXHHHH@Z
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp.1981-1998
输出:
>> ?RandException@@YGXHHHH@Z ==void __stdcall RandException(int,int,int,int)

使用MFC和ATL的DEF文件:

MFC的DEF文件在
C:\ProgramFiles\MicrosoftVisualStudio\VC98\MFC\SRC\Intel目录下。
ATL的DEF文件在
C:\Program Files\Microsoft VisualStudio\VC98\ATL\SRC目录下。
如果用户运行自己的程序出现:The ordinal 6880 couldnot be located in the dynamic link LibraryMFC42.dll信息。你可查看MFC42.DEF中对应的6880号函数:?ScreenToClient@CWnd@@QBEXPAUtagRECT@@@Z@ 6880NONAME.然后可以用名字解析工具解析混合名字如下:
输入:
C:\Program Files\Microsoft VisualStudio\Common\Tools>Undname -f ?ScreenToClient@CWnd@@QBEXPAUtagRECT@@@Z@ 6880 NONAME
Microsoft(R) Windows NT(R) Operating System
UNDNAME Version 5.00.1768.1Copyright (C) Microsoft Corp.1981-1998
输出:
>> ?ScreenToClient@CWnd@@QBEXPAUtagRECT@@@Z== public: void __thiscall CWnd::ScreenToClient(struct tagRECT*)const
>> @ == @
>> 6880 == 6880
>> NONAME == NONAME
使用依赖关系浏览工具:
VC++依赖关系浏览工具(COMMON\TOOLS\Depends.exe)

第四章   内存泄漏的调试
在VC中我们使用_CrtDumpMemoryLeaks()来检测内存泄漏。
例子:
void CTRACEDlg::OnButton1()
{
    int *pLeak =new int; //故意造成内存泄漏
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
    //deletepLeak;
    //pLeak =NULL;
   _CrtDumpMemoryLeaks();//调用此函数在outputwindow的Debug窗口中显示内存泄漏的地址的数据。
   
}
Debug窗口中显示:
Detected memory leaks!
Dumping objects ->
C:\ProgramFiles\MicrosoftVisualStudio\MyProjects\TRACE\TRACEDlg.cpp(181): {89} normal block at 0x004213B0, 4 bytes long.
Data:<   > CD CD CD CD
Object dump complete.


注意:0xCD表示已经分配的数据
     0xDD表示已经释放的数据
     0xFD表示被保护的数据

第四章   断言
1.断言的特征:
(1)发现运行时刻错误(用户输入错误,资源分配错误,文件系统错误,硬件错误或其他类型错误)
(2)断言中的布尔表达式显示的是某个对象或者状态的有效性,而不是正确性
(3)断言在条件编译后只存在调试版本里面,特别是,断言只在_DEBUG符号定义后,才能编译!
(4)断言不能包含程序代码,也不能有副作用,不能修改程序变量,也比能调用修改程序变量的函数
(5)断言只是给程序员提供有用信息的
2.断言的类型
(1)ANSI C断言
Voidassert(expression)   包含在assert.h头文件中(最好不用assert)
原因:*当文件名太长的化,对话框显示的路径将会被截至掉!
*函数是由ANSINDEBUG函数驱动的,如果定义了NDEBUG后,断言就被取消!

如果要启用JIT调试(Just-in-time),在Tools?Options?Debug
中选择Just-in-time debugging,默认也会勾选上OLE RPCdebugging
单击“重试(R)”就会显示出错误所在的标记行。

(2)C运行时刻函数库断言
_ASSERT(Boolean Expression)  (crtdbg.h)[不用]

_ASSERTE(Boolean Expression) (crtdbg.h)[经常用这个]

_ASSERTE宏更能给出更多简洁,有用的信息,显示了断言!
(3)MFC库中的断言
ASSERT(Boolean expression)ASSERT宏和_ASSERT宏显示的消息框相同。VERIFY(Booleanexpression) VERIFY中的BOOL表达式在发布版本中被保留了下来。它简化了对返回值的判断!
CString str;
VERIFY(str.LoadString(IDS_STRING));//不要用VERIFY宏
ASSERT_VALID宏,被用来决定一个指向CObject派生类的对象的指针是否有效。ASSERT(pObjectDerivedFromCObject);主要是在使用CObject派生类对象之前调用,检查对象的有效性。
ASSERT_KINDOF(className,pObjectDerivedFromCObject);
ASSERT_POINTER(pointer,pointerType);
ASSERT_NULL_OR_POINTER(pointer,pointerType);
AfxlsValidAddress(const void*memoryAddress,UINT memoryBytes,BOOLisWriteable = TRUE);
BOOL AfxlsValidString (LPCSTR string, int stringLength = -1);
(4)ATL断言
如果你使用ATL,crtdbg.h就包含在atlbase.h中。在任何ATL代码中,ATLASSERT才是你的选择,在atldef.h中你会发现ATLASSERT是_ASSERT的一个别名。
优点:在ATL程序中使用ATLASSERT可以让你使用自己的断言。
(5)考虑使用_ASSERTE(FALSE)来简化防御性的编程和断言的结合,要想得到描述性的断言消息,考虑使用_ASSERTE(“Problemdescription.”==0).
_ASSERTE("This is the object requires the MM_TEXT mapping mode" ==0);
If (!expression)
{
//handle error
_ASSERT(FALSE);

}
If(FAILED(SomeFunction()))
{
//handle error
_ASSERT(FALSE)
}
(6)考虑使用_CrtSetReportMode和_CrtSetReportFile

 

                         

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
MFC中常用类,宏,函数介绍
用WTL构建HTML界面应用程序1
Debugging
Visual C++ 2012 Express 中使用ATL和MFC方法
Activex感知网页关闭事件
Jupyter notebook中debug
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服