All Stories

使用Windbg定位程序崩溃

  今天突发奇想,看到Edraw的一个软件包里有个CrashReport程序,觉得这个功能真是很值得放到自己的工程中去。想想大概原理就是使用dbghelp.dll库中的Debug API来实现,不过具体细节我是一点都不懂。记起以前在公司网上看到有人提到过FileZilla就有这样的功能,可以在程序崩溃的那一刻记下程序堆栈,函数调用栈,寄存器等有用信息。于是我马上找来FileZilla代码,运气也好,刚好找到2.x版本的,因为3.0以上版本的源代码包中是不带这部分代码了。这部分代码模块独立性做得相当好,只要把那几个文件抠出来加入自己的工程,几乎不用怎么修改就可以工作了。要做试验的话,可以来个除0错误,或者向空指针写入值,然后运行,就会弹出消息框,并生成2个文件,一个是文本格式的简要信息,另一个是二进制格式的core dump。又在公司网上找到一篇用Windbg分析core dump文件的简要介绍,马上拿来试试,还真是好用,设置好symbol目录和source目录,它能自动从网上下载缺少的symbol文件,并能自动打开源代码文件跳转到引起崩溃的那行代码上,真是太爽啦。以后再定位程序莫名崩溃问题,要方便简单多啦。

使用CppUnit进行单元测试

  网上随便一搜就能找到一堆一堆的文章,讲述如何使用xUnit进行单元测试,虽然我这个标题也的是用CppUnit进行单元测试,但我不会重复一遍n多先辈们说过的话,因为即使说了,也肯定不如那些话来得精准、全面和专业。  今天花时间看到一下CppUnit自带的例子,结合以前在书上看到的,我意识到,测试用例确实不能跟项目代码混在同一个工程里。于是我把其中一个例子的代码全都抠出来,把我那些测试用例放到里面,然后生成一个TestRunner,嗯,基本完美解决了,呵呵。原来也就这么回事呀,嘻嘻!

读《重构》ing

  这两天在看Martin Fowler大牛的《重构》,之所以不写是《Refactoring》,是因为我看的是jjhou和gigix的翻译版。《Refactoring》我已经买了两年了,基本上没拿出来瞟过一眼,也幸亏没瞟,前些天偶然拿出来翻了翻,发现里面的用语太偏了,n多不认识的单词,汗颜!不像《C++ Templates》,当年每天早上翻几页,居然也看了十几章。  《重构》一书由jjhou和gigix来翻,质量上基本还是可以保障的,反正感觉比较信得过他们两人,至少应该极少可能会出现翻错的情况吧。也幸亏是看这本中文版,让我对那些个古怪的名称有了比较感性的认识。  Martin Fowler大牛在书中罗列了几十种所谓的坏味道(bad smell),然后简洁地描述了一遍针对各种坏味道可以采取的措施。我现在也才看完这部分,但马上发现,要能在项目进行时重构,在个人技能上需要能嗅探出坏味道,在工具支持上需要有像VAX这样的辅助,在开发方式上还需要有TDD之类的有效质量保障体系支持。第一项,通过对该书的学习,以及之前的积累和以后的长期坚持,应该不成问题。第二项,现在新版的VAX都是有支持的,以前用VAX基本只是用了它的自动完成提示功能,现在才发现这个Refactoring菜单项的功能是多么强大多么好用。第三项我就有点迷茫和动摇了。其实有时候我也想用TDD的,可是一方面总感觉信心不足,写Test case本身就是一件很花时间的事情,我不敢保证这个投入是否值得,另一方面我现在基本全是用C++/MFC写程序,绝大部分代码都紧紧地跟界面耦合在一起了,要用CppUnit来还真不容易,除非先重构一遍,把所有业务逻辑都提取出来,再用CppUnit来进行测试,但是如果真要通过UI来跟用户交互的怎么办呢?这个我暂时不知道应该怎么做,但如果不用TDD来进行重构,感觉有点形似神不似,而且重构后的代码质量也不好保证,所以还是得尽量往那个方向走。

Boost::bind传引用不能修改值

  今天偶然发现,用boost::bind传递了一个std::vector的引用过去,想在那个绑定的函数里修改这个std::vector的内容,结果居然在函数里的好像是另外一个容器,对外面的那个std::vector压根没有影响。幸亏发现得早,但也已经写了好几处这样的代码了,不过总算都改过来了。  也不知道是我哪里用错了,还是boost::bind本来就不能支持,晕!

2008年第1次去健身房

  说起来真是惭愧啊,去年办的一张半年卡,到现在为止5个月了,总共去了不到10次吧,就算是10次,也是很亏啊,¥120/次,真是奢侈啊。今天一天都在屋里度过,室友问我去不去健身房,我想再不去人都要发霉了,于是说吃过晚饭过一会儿再去。  晚饭是在屋里做的,我只负责用电饭锅做饭和生炒了个青菜,很简单的类型,呵呵,吃完饭后就觉得困了,然后就坐在椅子上睡着了,一直睡到8点半,才动身去健身房。  很久没去了,身上肥肉又长厚了一层,跑了15分钟,累得要死。

IDE需要什么功能

  在网上看到一个英文博客,有一篇文章作者谈到他希望一个代码编辑器,或者一个IDE应该有什么样的功能。看了之后感觉还算比较客观,基本没有特别个性化的要求,对于要做这方面工作的人很有参考价值。

扩展性和通用性

  这两天在想一些问题,怎样构建一个有足够扩展性和通用性的框架呢?  对Scintilla的使用有了一点经验后,就想好好利用它来做几个有点实用价值的东东,关键就在于想做的不止一个,但在文本编辑方面由于都是使用Scintilla,所以肯定会想到如果能让代码写得足够通用,所有工程都共享一个代码实现就好了,以后即便有了修改,只要修改一次,其他的最大程度上只要重新编译链接一下就可以了,不用再修改源代码。但这样的通用性要如何实现呢,组件化是一种常见的解决方案,这里Scintilla已经是一个现成成熟的组件,但它只是提供了最基本的能力,如果要把这些能力展现出来,还是自己写代码实现。  有鉴于Impeller项目越来越冗长庞杂的实现,我心里很是不舒服。它确实有那么多事要做,也许有些事不是它的责任,但那也只是极小一部分,但把所有事的实现都放在工程中用C++完成,我就觉得有待商榷了。比如有很经典的一段代码,用于在用户动作触发时,在当前光标所在位置插入日期和时间,这样微不足道的功能,花了好几十行C++代码。其实在大半年前,我还在维护编辑器模块时,我就想通过脚本来支持这些简单功能了,但后来因为这样那样的原因最终没有实现。这只是一个简单的例子,照我的想法,最理想的方案是程序从配置文件中读出相关扩展信息,根据配置在主菜单、工具栏、弹出菜单等用户最习惯的界面上添加可触发控制,当用户触发了这些控制时,程序从配置中读出需要执行的动作,可能是一段脚本,或一个脚本文件,或一个DLL导出函数,甚至是一个COM组件中的一个接口中的方法,总之在被触发前这些东西可是是实际不存在的,也即Eclipse提倡的懒加载规则。这里有几个实现上的难点,如何通过配置文件就动态地实现配置的用户界面;如何通过用户动作立即找到正确的执行动作;如何让主程序和扩展进行交互。一般说来主程序需要提供一些方法供扩展进行调用控制主程序的行为,于是有相应的问题是主程序通过什么方式暴露这些方法,又需要暴露哪些方法。如果这些问题都能解决了,还有另外一个问题,不同的扩展之间是否可以交互,它们的协议应该怎么设计。  总之在动手做那几个东东前,这扩展性和通用性的问题必须要有一个可靠可行的方案,不解以后自己得累死。

从文本编辑转向图形编辑

  看了一点SciTE的代码,因为从SciTE可以看到Scintilla控件的各种使用方法,说起来Impeller也使用的Scintilla,但是总感觉很弱,比如SciTE可以同时直接支持UTF-8和ANSI编码的文件,而Impeller就不行,一个时刻只能支持一种,另一种编码的文件打开时,如果有中文之类的多字节编码符号,就会显示成乱码。  似乎我是不会被调回去做编辑器模块的工作了,可能要我去做图形编辑方面的事了,版本计划中下个大版本需要加入像Rose那样的UML类图编辑功能,不禁要感叹,他们的野心还真大,唉,Visio这样的工具全世界也没有几个呀!  我现在是陷入项目泥潭了,正是有这样大野心的领导,才会让我等小兵异常痛苦,尽提些高难度高技巧的需求,还总以为很简单很容易实现,却并不考虑用户可接受程度。又听到一个新的需求,说是要求所有工具都能自动安装到部门每个员工的电脑上去。那要是人家不愿意呢,又该怎么办!技术上是没有多少难度的,关键是使用者的抵制情绪如何抚平。  想想我转去做图形编辑方面的工作对我自己来讲是件大好事,本来对文本编辑部分已经有了一定的基础,以后就是自己钻研扩展了,现在趁工作的机会再学习图形编辑,对我以后极有好处啊。刚开始听到这个消息时,心里还略有点失落的,现在想起来,觉得这简直是上天对我的恩赐啊,哈哈!

让自己的数据结构支持std::for_each了

  经过几个小时的努力敲打键盘,终于让自己的数据结构支持std::for_each了。昨天我是过高地估计了其实现难度,其实看看for_each的实现代码,非常简单的算法,只要能让iterator支持前置++和提领(这是jjhou的叫法,就是对指针取值)操作符即可。这些内容昨天就知道了,但是自己没有动手做完前,总是心虚的。  实现过程基本上还算顺利,虽然老是编译不过,但VC的提示信息很详细,准确性也很好。在这个任务中,我觉得最好iterator不知道指向的类型,STL就是这样做的,也不是为了什么软件工程或者代码美感方面考虑,而是这样做对于实现者来说似乎更容易理解。  我在中间也犯了个比较严重的错误。因为NodeList已经被封装成一个类了,而我的设计里,iterator需要保存对NodeList的引用,所以我就很习惯成自然地用这个封装类了,另一方面,因为需要,NodeList封装类中也需要这个iterator类型,于是就成交叉引用了,编译都过不了。后来才想到,在iterator中使用封装类并没有得到多少好处,反而似乎增加了实现复杂度,直接用MSXML中的原始类型就行了。  嗯,略有收获,呵呵!