使用的环境是VS2008 + sp1.个人觉得这个版本的vs是M$的巅峰之作。功能全、运行速度快、不吃太多的内存。vs10就太慢了,不过vs12还是蛮清爽的,虽然也因为提示功能被多吃了200M+的内存。这个系列的文章主要是讲解Vc++的工程属性。不涉及任何编码技术。其中有些内容出自个人理解,难免有谬误,欢迎拍砖。
第一篇就介绍一下最普通的两个属性页内容。

---------------------------------------------------------------------------------------------------------------------
常规:1.jpg

  • 输出目录:

     项目生成最终文件的保存位置,用宏和相对路径来配置,保证工程的可拷贝。

  • 中间目录:

     项目编译生成临时文件(.obj等)所保存的位置。

  • 清除时要删除的扩展名:

     删除项目生成时的符号表文件、Obj文件等中间文件和输出文件。

  • 生成日志文件:

     更详细的项目编译链接信息,包括命令行执行情况、输出情况和最终结果。

  • 继承的项目属性表:

     当一个解决方案中有多个项目,并且项目互相有多个相同的属性(比如附加包含目录、附加库目录等),当某一附加目录的名称或位置发生变化时,需要重新修改多个项目的相关属性。使用继承的项目属性表,可以将多个工程的属性配置信息单独提出成一个配置文件,各个项目共享此配置文件。
     这样就解决了上述问题。

  • 启用托管增量生成:

     启用托管增量生成属性允许您指定是否要使用增量生成。如果没有增量生成,代码必须重新编译每次更改时引用的程序集。出现此现象,甚至当更改进行内在化处理,例如,当您添加的注释。如果您启用启用托管增量生成属性时,编译器将确定对程序集的任何更改是否会影响依赖于该程序集的项目。仅当所做的更改会影响它所依赖的项目将被重建。     这个是MSDN上对这个选项的介绍。我做了一些尝试,企图发现他们的不同,最后没有找到。可能是代码结构比较简答的缘故吧。

  • 配置类型:

     会根据配置类型进行一些生成文件类型的配置。比如.exe时,会查找有没有入口函数,没有就会报错等。
MFC的使用:
     如果项目是MFC项目,或者其他项目中使用到了MFC库,可以使用此选项来配置如何处理对MFC库的依赖关系。
     “在共享DLL中使用MFC”————在安装vc分发包(vc_redist)的系统中,会查找MFC的依赖DLL并加载他们。所以项目生成的文件比较小,但部署就比较麻烦了。
     “在静态库中使用MFC”————即使在没有安装对应版本的分发包的系统中也能正确使用。部署简单,但生成文件比较大。
     上述两个选项的区别就在于是在链接时使用MFC库(LIB)还是在运行时使用MFC库(DLL)。
     这个选项不是告诉用户选择上面两个选项就可以直接使用MFC了。还需要包含afxwin.h的。
     另外这个选项是只适用于MFC的,有的时候即使适用静态库,还是会出现“找不到XXXX.dll”的错误。这个就和/MT、/MD、/MTd、/MDd选项有关了。这几个开关的意思,会在以后的文章中介绍。

  • ATL的使用:

     同“MFC的使用“。

  • 字符集:

     使用多字节字符集还是Unicode字符集。使用不同字符集,每个字符占多少字节不一样,更多关于字符集的知识请百度。
     我们在使用MessageBox这样的函数时,MessageBox这个函数名实际上并不是user32.dll的导出函数。user32.dll实际导出的是MessageBoxA和MessageBoxW
     这样的函数。
     在winuser.h里有关于MessageBox的这样的一个宏:
     #if defined(_M_CEE)
     #undef MessageBox
     __inline
     int
     MessageBox(
          HWND hWnd,
          LPCTSTR lpText,
          LPCTSTR lpCaption,
          UINT uType
          )
     {
     #ifdef UNICODE
          return MessageBoxW(
     #else
          return MessageBoxA(
     #endif
               hWnd,
          lpText,
          lpCaption,
          uType
               );
     }
     #endif  /* _M_CEE */
    
     使用Unicode时,实际上是使用MessageBoxW,使用MBCS,使用的是MessageBoxA。其他API、MFC库函数都类似。

  • 公共语言运行时支持:

     这个是.net支持的选项。
     .net支持的都是托管类代码。所谓托管类代码就是项目生成的文件并非是本地代码,而是一种中间代码。运行时需要进一步转换成本地代码。

  • 全程序优化:

     不太懂。只知道Debug时不要使用这个选项,否则会跟错断点。Release版本使用这个选项,程序执行会效率高点,体积也小点。


调试
这个主要不讲调试属性页内各个属性设置的意思,只讲讲调试本身。“运行->出错->关闭->下断点->再运行->命中断点”,我们平时都在这样调试。
“其实调试是一门技术,更是一门艺术。学习调试,会让菜鸟了解系统,从而变得更Geek,更Hack。”我们公司一位大牛说的,大概意思是这样,具体怎么说的忘了...
不知道大家遇到过这种情况没:
(1)程序一运行就崩,但下断点后单步运行后没有任何问题。
(2)跟踪windows消息时,对分发函数设断点,启动后总会意外的命中,F5继续,再意外命中。比如跟踪WM_PAINT?
以上两种情况,在本机调试是比较麻烦的。因为有时候命中断点进入调试器会导致线程上下文的切换,一些多线程的问题很难调出来。或者不想在第一次命中断点时就中断(可以加命中次数或数据断点,但命中次数未知,变量又是临时变量呢?)
那么就用远程调试吧。

Image.png

远程调试时,线程的上下文就比较清晰了,能发现一些不能在本地发现的问题。

F5运行,程序蹦了怎么办?
先不要急着关闭,尝试用调试器附加到进程,这样就能看到崩溃时的堆栈调用等信息。如果有pdb和源码,就能直接看到在哪个函数的什么位置失败了。这个方法本地调试和远程调试都很有用。

远程调试器很好配置,把remote debug tools拷贝到远程机,做点配置就可以了。在VS里选择远程调试器,再按F5就开始在远程执行了。调试方法和本地调试一样。

另外调试不要拘泥在VS里面。WinDbg调试、Log日志分析调试,调试方法有很多很多。


我是软件开发工程师,又不是测试工程师,为什么要学这么多调试?
测试:发现问题
调试:发现问题,定位问题,明确原因,解决问题。
调试本来就是软件开发工程师的活儿。

推荐一本外国大牛的书《windows高级调试》