调试Windows程序是一件繁琐而又复杂的事情,掌握必要的调试策略却可以使这些工作变得轻松起来。本书精选了进行Windows调试所需的基础调试技巧,共分为三个大部分。第一部分介绍调试策略,帮助理解调试过程,以及如何使用C++语言、断言、跟踪和异常来预防、揭示、诊断和消除错误。第二部分介绍C++和Windows中的调试工具。第三部分介绍调试技术,帮助读者充分利用Visual C++的调试工具,并特别论述了与调试内存相关的问题、多线程问题和COM问题。本书将重点放在调试概念上,而不是简单地介绍工具,因此具有很强的实用性,是您在程序开发时的最佳选择。 本书送合有一定Visual C++基础知识的程序开发员及计算机爱好者阅读。
常见问题\r\n\r\n前言\r\n\r\n简介\r\n\r\n第一部分调试策略\r\n\r\n第1章 调试的过程\r\n\r\n1.1 错误的调试五步曲\r\n1.2 正确的调试五步曲\r\n1.3 确定错误的存在\r\n1.4 收集错误信息\r\n1.5 分析错误信息\r\n1.6 消除错误\r\n1.7 修改的验证\r\n1.8 巧妙地而不是艰苦地调试\r\n1.9 推荐阅读\r\n\r\n第2章 编写便于调试的C++代码\r\n\r\n2.1 设计\r\n2.2 C++编程风格\r\n2.3 C++语言\r\n2.4 Visual C++编译器\r\n2.5 推荐阅读\r\n\r\n第3章 使用断言\r\n\r\n3.1 断言的局限性\r\n3.2 断言的类型\r\n3.3 更多的MFC断言宏\r\n3.4 自定义断言\r\n3.5 可移植的断言(Portable Assertion)\r\n3.6 使用断言的策略\r\n3.7 不变关系\r\n3.8 断言模式\r\n3.9 为你的断言书写文档注释\r\n3.10 实现AssertValid\r\n3.11 防御性的编程(Defensive Programming)\r\n3.12 错误处理\r\n3.13 各种各样的提示\r\n3.14 推荐阅读\r\n\r\n第4章 使用跟踪语句\r\n\r\n4.1 跟踪语句的类型\r\n4.2 自定义的跟踪语句\r\n4.3 跟踪语句策略\r\n4.4 各种技巧\r\n4.5 推荐阅读\r\n\r\n第5章 使用异常和返回值\r\n\r\n5.1 不正确的错误处理结果\r\n5.2 策略的需要\r\n5.3 使用异常\r\n5.4 使用返回值\r\n5.5 异常和错误\r\n5.6 C++异常和Windows结构异常处理比较\r\n5.7 将结构异常转化为C++异常\r\n5.8 异常的性能\r\n5.9 异常策略\r\n5.10 使用异常的防御性编程\r\n5.11 调试异常\r\n5.12 各种技巧\r\n5.13 推荐阅读\r\n\r\n第二部分调试工具\r\n\r\n第6章 在Windows中调试\r\n\r\n6.1 事后调试\r\n6.2 Windows API错误码\r\n6.3 Windows异常基础知识\r\n6.4 可移植的可执行文件基础知识\r\n6.5 DLL重定位\r\n6.6 汇编语言基础知识\r\n6.7 使用映射文件调试\r\n6.8 使用PDB文件调试\r\n6.9 使用Windows 98崩溃对话框调试\r\n6.10 使用Dr.Watson调试\r\n6.11 各种技巧\r\n6.12 推荐阅读\r\n\r\n第7章 使用Visua C++调试器调试\r\n\r\n7.1 编译与链接选项\r\n7.2 调试版本与发布版本\r\n7.3 调试发布版本\r\n7.4 测试版本\r\n7.5 调试符号\r\n7.6 调试窗日\r\n7.7 查看表达式\r\n7.8 数据标签表达式\r\n7.9 寄存器和伪寄存器\r\n7.10 观察窗口的格式化符号\r\n7.11 使用 Autoexp.dat\r\n7.12 使用断点调试\r\n7.13 即时调试\r\n7.14 远程调试\r\n7.15 编辑继续调试\r\n7.16 推荐阅读\r\n\r\n第三部分调试技术\r\n\r\n第8章 基本调试技术\r\n\r\n8.1 普通调试技术\r\n8.2 VisualC++调试器技术\r\n8.3 Windows调试技术\r\n8.4 MFC调试技术\r\n8.5 推荐阅读\r\n\r\n第9章 内存调试\r\n\r\n9.1 内存泄漏为什么不可接受\r\n9.2 内存调试的类型\r\n9.3 使用调试堆\r\n9.4 调试堆是如何工作的\r\n9.5 查看Windows内存地址\r\n9.6 调试内存破坏\r\n9.7 调试内存泄漏\r\n9.8 调试Windows资源泄漏\r\n9.9 调试 Windows线程难栈\r\n9.10 各种技巧\r\n9.11 推荐阅读\r\n\r\n第10章 调试多线程程序\r\n\r\n10.1 什么是多线程\r\n10.2 多线程程序的几个要点\r\n10.3 书写线程安全的代码\r\n10.4 线程的创建和终止\r\n10.5 理解调试器\r\n10.6 调试技术\r\n10.7 推荐阅读\r\n\r\n第11章 COM调试\r\n\r\n11.1 本章基础\r\n11.2 防御性的COM编程实战经验\r\n11.3 调试基COM DLL\r\n11.4 调试基COM EXE\r\n11.5 调试配定组件\r\n11.6 调试被ASP调用的基COM DLL\r\n11.7 推荐阅读\r\n\r\n第12章 非常规策略\r\n\r\n12.1 检查简单的东西\r\n12.2 开动你的脑筋\r\n12.3 重新检查你的假设\r\n12.4 检查明显的事物\r\n12.5 检查代码\r\n12.6 检查系统\r\n12.7 再次检查文档\r\n12.8 依靠其他人\r\n12.9 使用新闻组\r\n12.10 结束危险的生活
调试Windows程序是一项浩大、复杂的工程。一些介绍有关Windows调试的所有方面的书,动辄就能达到这本书的两倍厚。但是全面介绍的书籍有一个缺点,就是它太厚了,令人望之生畏,恐怕很少有人想读它。所以,这本书中,我们圈定了一个范围,将重点放在Windows调试的某些方面。下面介绍这个范围是如何挑选的。
本书最基本的动机建立在这样的信念上:如果程序员能更好地获取调试知识,他们就可以提高调试技能。虽然有大量调试知识,但它们目前仍然非常零散,不利于程序员阅读并掌握。这些知识常常是含糊的,不完整的,要么就是把重点放在调试工具而不是调试概念上。最终的调试工具永远是程序员的头脑,但这个工具常常被忽略了。掌握Windows调试的基础概念,有助于预防错误,同时也可以有效地发现残留的错误。
本书精选了进行Windows调试所需的基础调试技巧。读完每一章,读者都应该将该章所介绍的主题与已有的知识朕系起来。本书第一部分介绍了调试策略以帮助理解调试过程,以及如何使用C++语言、断言、 跟踪和异常来预防、揭示、诊断和消除错误。第二部分介绍了C++和Windows中的调试工具。第三部分介绍了调试技术,帮助读者充分利用Visual C++的调试工具,并特别论述了与调试内存相关的问题。多线程问题和COM问题。
这里介绍的有些问题跨越了编程技术和调试技术的界限。虽然错误预防更可能是属于调试中的部分,但是为了避免犯错,也需要懂得编程常犯的错误。很多编程文章里没有提到调试和错误预防的内容,只有在这里介绍了。
很容易看出有些内容我们没有提及。我们基本上没有涉及到任何不属于Visual C++或Windows的第三方调试工具,如WinDbg。这样做有几个原因。最明显的原因就是我们把重点放在介绍调试概念上,而不是介绍工具。另一个重要的原因是,Visual C++程序员需要较好的知识,来利用已有的工具。最后,我们怀疑,我们所能介绍的那点东西能不能比厂商介绍的对大家更有帮助。
可我用的是Bounds Checker呀……
有些读者会想了:“可我用的是Bounds Checker呀,为什么要读这本书呢?”问得好。调试工具,例如 Compuware NuMega公司的BoundsChecker和Ratlonale Sottware公司的Purify,可以很好地发现各种运行期错误,如不良指针和句柄、内存破坏(corruption)和泄漏(leak)、错误的Windows API参数,等等。但是它们做不到的是帮助理解调试过程,包括如何使用C++语言。断言。跟踪和异常来预防和消除错误,如何充分利用 Visual C++和Windows自带的调试工具来调试多线程程序和COM程序。这些本书全部都做到了。
而且,这些工具一定不能检测到所有的错误,并且不能用于预防错误。如果完全依赖调试工具,当遇到工具找不到的错误时,你就会很无助。另外,使用这些工具需要执行额外的开发步骤,它们对性能影响也很大,但是本书中介绍的错误检测技术在调试版本时自动进行,对性能的影响也比较小。调试是个很复杂的解谜过程,这些工具往往只能给与片面的帮助。
如何阅读本书
虽然我们希望读者能够一页页地阅读本书,但我们也了解读者通常都很忙,一般没有时间通读。所以,每章本身都是完备的,你可以根据需要只读某些章。完备,意味着可能有些冗余的部分,虽然我们尽力缩小了这些冗余,但它们仍然不只出现在一章之中。我们希望你能赞同这种方式,它使得本书成为一个更好用的手册。
在目录后,有一个常见问题的列表,可以帮助你很快地找到问题的答案。第8章是用同样的方式组织的。这一章和常见问题列表都能帮助你很快地找到某个调试问题的解决方法。
Windows程序有很多种,所以也有许多不同的调试技术。我们将从Windows API。MFC和ATL应用程序基本结构的观点来介绍调试。我们会清楚地标出那些用于MFC和ATL的调试技术,所以当你不用到这些框架时,如果时间很紧,就不用看了。如果有时间,当然最好阅读一下,因为你可以从中了解到其他观点的调试技术。
最后,每章都有一个关键的调试建议,我们把它用这种形式表示:
这些提示突出了调试概念中最有用的地方,很容易看到,这可以帮助你快速找到重要的调试主题。
Windows版本和硬件
为了简化陈述,我们只选用了当前版本的Windows,在我写书的时候,主流操作系统是Windows2000和 Windows 98。这里针对Windows2000的内容也适用于Windows NT 4.0,针对Window 98的内容也适用于Windows 95。我们在说Windows NT 4.0和Windows 95时,仅仅指的是该版本的Windows。
为了简化陈述,我们假设你在一个Intel X86的CPU上使用 Windows。虽然这本书大部分与CPU无关,但是在阅读16进制转储信息和在汇编级别调试时,CPU就是一个必须考虑的因素了。对那些在非Intel平台上进行汇编级别的调试的程序员,我们只能说抱歉了。
“我”是谁
虽然封皮上只有两个名字,但每章都是用第一人称介绍的。Mike Woodring写了第 10章“调试多线程程序”和第11章“ COM调试”,Everett McKay写了其他部分。所以,在第10章和第11章的“我”指的就是 Mike Woodring,而其他章节的“我”就是 Everett McKay。
更新信息
这本书是在Microsoft Visual C++ 6.0的基础上写的。如果你使用更新的版本,这本书的大部分还能适用,只有编译器和调试器的一些细节会有些改变。为了让这本书的知识能够及时更新,我们在www.windebug.com下会贴出更新和更正信息。如果你发现了错误,或过时信息,请通知我们,发邮件给corrections@windebug.com。
致谢
这是一本很难写的书,并不是因为没有关于调试的知识,而是知识点太多了,并且非常分散。从很大程度来说,我们的工作就是收集、吸收,然后有条理地描述出来。本书中有不少原创思想,但只是少数部分。我很想感谢每一位对本书中涉及的调试思想有贡献的人,但是很难知道他们是谁。
如果不对 Windows和C++调试器有很深的了解,是不能写出这本书的。我应该感谢John Robbins、Matt Pietrek和 Paul Dilascia,他们在《Microsoft Systems Journal》中很多优秀的Windows调试文章。我还应该感谢 Sieve Mapuire在《Wnting Solid Code: Microsoft’s Techniques for Developing Bug-Free C Programs》中的灵感,以及 Sieve McConnell在《Code Complete: A Pracitical Handbook of Software Construction》中的思想。感谢 Scott Meyers、Tom Cargill和 Bjarne Stroustrup关于如何正确使用C++语言的著作。
我应该感谢调试的先驱:Glenford J.Myers、Brian W.Kernighan和P.J.Plauger,他们早期的工作成果至今仍对我们非常有帮助。最后,感谢 Rebort M.Pirsis,他的《Zen and the Art of Motorcycle Maintenance: An Inquiry into Values》对我的质量的概念确立有很大帮助。创造高质量的软件是本书的基本观点。
在构思这本书时,我花了很长时间研究各种新闻组论坛上关于调试的讨论,特别是microsoft.public.vc.debugger。我应该感谢那些新闻组的参与者,他们在很多调试主题上给我提供了细节信息,而他们有的可能还不知道。感谢Jay Bazuzi、Bruce Dawson、Raj Rangarajan、Scott McPhillips、Tomas Restrepo、Katy Mulvey、Doug Harison和Joseph Newcomer,他们常常发布一些很有价值的文章。
非常感谢那些相信我能写出这本书的人们。感谢前任策划编辑Gmp Clarke和他的助手Rebecca Bence,他们最终促成这个项目。特别感谢Kristin Erickson,另一个编辑,感谢他在这个项目中从开始到结束的卓越工作。同样感谢Shetri Dietrich、Connie Leavittof Bookwrlghts、Joan Kocsls、Marilyn Rash、Angela Stone和Judy Strakalatitis,感谢他们杰出的编辑。制作和排版的工作。
如果没有从读者那里得到的有见解的反馈,这本书也不算什么。感谢Mike Woodring、Eugene Kain、 Christophe Nasarre、David Schmitt和Robert Ward。
写一本书需要做上干个的决定,我最成功的决定就是使Mike Woodring成为合作作者。Mike写了第10章“调试多线程程序”和第11章“COM调试”。他比我更加了解这些专业技术的调试知识。
最后,特别感谢我的妻子Marie和两个孩子, Phillppe和 Michele,他们在我写作这本书的几个月里默默奉献,如果没有他们的帮助我是很难完成这个工作的。