我的上一篇文章《Vistual Studio原生开发的10个调试技巧》引起了很多人的兴趣,所以我决定把更多的调试技巧分享给大家。接下来,您可以看到一些对本机应用程序有用的调试技巧(编号在上一篇文章之后)。这些技术需要在VistualStudio2005或更新的版本中应用(当然,有一些适用于旧版本)。如果您可以阅读本文推荐的一些相关文章,您可以了解有关每种技术的更多信息。11.数据断点12.线程重命名13.为特定线程设置断点14.(粗略)估计执行时间15.数字格式化16.(内存)数据格式化17.系统DLL中断18.加载符号表19.MFC中监控内存泄漏20.调试ATL技巧11:数据断点当数据所在的内存位置发生变化时,可以通知调试器断点,但一次只能创建4个字节硬件数据断点。数据断点只能在调试期间通过菜单(调试>新断点>新数据断点)或断点窗口添加。您可以使用内存地址或地址表达式。虽然可以看到栈上和堆上的值,但我觉得这个特性在堆上的值发生变化时更有用。它有助于识别内存损坏。在下面的例子中,指针的值发生了变化,不再是它指向的对象的值。为了找出发生了什么变化,我在存储指针值的地方设置了一个断点,即&ptr(注意必须在指针初始化之后)。数据的变化意味着有人改变了指针的值,调试器中断了,我可以找出是哪段代码导致了变化。更多阅读:1.如何查明指针是否破坏了内存2.如何查明指针在哪里更改技巧12:线程重命名在调试多线程应用程序时,“线程”窗口将显示创建了哪些线程以及当前正在使用哪些线程运行溃败。线程越多,就越难找到自己想要的线程(尤其是当一个程序同时被多个线程执行时,你无法确切地知道哪个是当前正在执行的线程实例)。调试器允许修改线程的名称,可以在线程窗口中使用线程的快捷菜单对线程进行重命名。也可以在程序内命名线程,虽然有点棘手,必须在线程启动后命名,否则调试器将使用默认命名约定重新初始化它。定义一个线程并使用以下函数重命名该线程。typedefstructtagTHREADNAME_INFO{DWORDdwType;//mustbe0x1000LPCSTRszName;//pointertoname(insameaddrspace)DWORDdwThreadID;//threadID(-1callerthread)DWORDdwFlags;//reservedforfutureuse,mostbezero}THREADNAME_INFO;voidSetThreadName(DWORDdwThreadID,LPCSTRszThreadName){THREADNAME_INFOinfo;info.dwType=0x1000;info.szName=szThreadName;info.dwThreadID=dwThreadID;info.dwFlags=0;__try{RaiseException(0x406D1388,0,sizeof(info)/sizeof(DWORD),(DWORD*)&info);}__except(EXCEPTION_CONTINUE_EXECUTION){}}更多阅读更多:设置线程名称(非托管)#p#技巧13:为指定线程设置断点对于多线程应用程序,另一个有用的技术是在线程、进程甚至计算机中指定断点设置过滤。这个功能可以通过断点的Filter命令来实现。调试器允许您指定线程名称、线程ID、进程名称、进程ID和机器名称的任意组合(使用AND、OR、NOT)来设置过滤器。知道如何设置线程名称也使这种过滤更加容易。阅读更多:Howtospecifyabreakpointfilter设置断点过滤器Tip14:(Roughly)EstimatingExecutionTime在上一篇文章中,我写了关于Watch窗口中的伪变量,而我没有提到的一个是@clk,用来显示计数器的值,可以粗略计算出两个断点之间代码的执行时间,单位为微秒(μS)。但是,不要用这种方法来分析程序的执行效率,应该使用VisualStudio分析工具或者性能定时器来分析。您可以通过在Watch窗口或即时窗口中添加@clk=0来重置计时器。因此,如果你想估计执行一段代码需要多长时间,可以按照以下步骤进行:在代码块的开头设置断点在代码块的末尾设置断点在Watchwindow程序进入第一个断点,在立即窗口中输入@clk=0运行程序,直到执行进入代码块末尾的断点,在Watchwindow中查看@clk的值。注意网上有一些提示在Watch窗口中添加了两个表达式:@clk和@clk=0,每次执行断点都需要重新设置定时器。这种用法只适用于旧版本的VisualStudio,VS2005及以上不再适用。阅读更多:调试技巧-@CLK技巧15:数字格式当您在Watch或QuickWatch窗口中查看变量时,值会显示在默认的预定义可视化中。对于数字,根据数据类型(整数、浮点数、双精度)以十进制形式显示。但是您可以使用调试器以不同类型或基数显示数字。如果要改变显示类型,可以在变量前加上如下前缀:by–unsignedchar(也称unsignedbyte)wo–unsignedshot(也称unsignedword)dw–unsignedlong(也称unsigned双字)要更改显示,请使用以下前缀为变量添加前缀:d或i-带符号的十进制u-无符号的十进制o-无符号的八进制x-小写的十六进制X-大写的十六进制阅读更多:C++调试技巧#p#技巧16:(内存Data)Formatting除数字外,调试器还可以在Watch窗口中显示格式化的内存数据,最多64个字节。您可以通过将以下说明符作为后缀添加到表达式(变量或内存地址)来格式化数据:mb或m–16字节的十六进制数据,后跟16个ASCII字符mw–8个字md–4个双字mq-2quad-wordsma-64个ASCII字符mu-2个字节的UNICODE字符更多阅读:C++中的格式说明符DeveloperStudio调试技巧技巧17:系统DLL中断有时在DLL中在调用函数时中断很有用,例如系统DLL(例如Kernel32.dll或user32.dll)。要实现此中断,您需要使用本机调试器提供的上下文运算符。可以设置断点位置、变量名或表达式:1.{[function],[sourcecode],[module]}位置2.[function],[sourcecode],[module]}变量名3.[function]],[sourcecode],[module]}表达式花括号可以是函数名、源代码和模块的任意组合,但不能省略逗号。我们假设我们想在调用CreateThread函数时中断。这个函数是从kernel32.dll导出的,所以上下文操作符应该是:{,,kernel32.dll}CreateThread。但是,这是不可行的,因为上下文操作符需要CreatThread的修饰符,可以使用DBH.exe获取具体函数的修饰符。下面是获取CreateThread函数修饰符的方法:C:\ProgramFiles(x86)\DebuggingToolsforWindows(x86)>dbh.exe-s:srv*C:\Symbols*http://msdl.microsoft.com/Download/符号-dC:\Windows\SysWOW64\kernel32.dllenum*CreateThread*SymbolSearchPath:srv*C:\Symbols*http://msdl.microsoft.com/Download/Symbolsindexaddressname110b4f65:_BaseCreateThreadPoolThread@122102e6b7:_CreateThreadpoolWork@12310_34f65:_BaseCreateThreadPoolThread@122102e6b7:_CreateThreadpoolWork@12310_344:CreateThreadpoolWork@12310_348:1_CreateThreadStub@2451019d40:_NtWow64CsrBasepCreateThread@1261019464:??_C@_0BC@PKLIFPAJ@SHCreateThreadRef?$AA@7107309c:??_C@_0BD@CIEDBPNA@TF_CreateThreadMgr?$AA@8102ce87:_CreateThreadpoolCleanupGroupStub@091038fe3:_CreateThreadpoolIoStub@16a102e6f0:_CreateThreadpoolTimer@12b102e759:_CreateThreadpoolWaitStub@12c102ce8e:_CreateThreadpoolCleanupGroup@0d102e6e3:_CreateThreadpoolTimerStub@12e1038ff0:_CreateThreadpoolIo@16f102e766:_CreateThreadpoolWait@1210102e6aa:_CreateThreadpoolWorkStub@12111032359:_CreateThreadpool@4看上去实际名字应该是_CreateThreadStub@24,这样我们就可以创建断点,{,,kernel32.dll}_CreateThreadStub@24来运行程序,当中断发生时,会提示断点处没有相关源码,无视即可。使用“调用堆栈”窗口查看调用函数的代码。更多阅读:1.在VisualStudio2010中,如何在没有源代码的情况下设置断点2.上下文运算符(C/C++语言表达式)3.如何为函数设置断点#p#技巧18:在调试a时加载符号表程序,调用堆栈窗口不会显示完整的调用堆栈,跳过系统DLL(如kernel32.dll和user32.dll)的信息。完整的调用堆栈信息可以通过加载这些DLL的符号表来获得,这可以直接使用调用堆栈窗口中的快捷菜单来完成。您可以从预先指定的符号路径或从Microsoft的符号服务器(对于系统DLL)下载符号。符号下载完成后,直接导入调试器,调用栈就会更新。这些符号也可以从“模块”窗口导入。下载后,符号将保存在缓存中,可在“工具”>“选项”>“调试”>“符号”中进行配置。技巧19:监视MFC中的内存泄漏如果要检测MFC应用程序中的内存泄漏,需要使用宏DEBUG_NEW重新定义new运算符。这是new运算符的修改版本,它记录每个对象的内存分配。文件名和行号。DEBUG_NEW将在发布版本中解析为new运算符。向导生成的MFC源文件在#includes后包含如下预处理指令:#ifdef_DEBUG#definenewDEBUG_NEW#endifnew运算符就是这样重新定义的。但是,许多STL头文件与重新定义的新运算符和版本不兼容。如果重新定义new操作符,包含
