社区应用 最新帖子 精华区 社区服务 会员列表 统计排行 银行

  • 34912阅读
  • 23回复

VC调试技巧大全

级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 10楼 发表于: 2012-03-29
符号文件—VC调试必备
一、何谓符号文件?
  符号文件(Symbol Files)是一个数据信息文件,它包含了应用程序二进制文件(比如:EXE、DLL等)调试信息,专门用来作调试之用,最终生成的可执行文件在运行时并不需要这个符号文件,但你的程序中所有的变量信息都记录在这个文件中。所以调试应用程序时,这个文件是非常重要的。用 Visual C++ 和 WinDbg 调试程序时都要用到这个文件。
  在 Windows 系统中,符号文件以 .pdb 为扩展名,比如:每个 Windows 操作系统下有一个 GDI32.dll 文件,编译器在编译该 DLL 的时候会产生一个 GDI32.pdb 文件,一旦你拥有了这个 PDB 文件,那么便可以用它来调试并跟踪到 GDI32.dll 内部。该文件和二进制文件的编译版本密切相关,比如修改了 DLL 的输出函数,再编译该 DLL,那么原先的 PDB 文件就过时了,不能再用老的 PDB 文件来做调试工作,而必须使用最新的 PDB 文件版本。
  Visual C++ 编译代码后会在 Debug 或者 Release 目录下生成一个 PDB 文件。一般情况下,符号文件包括以下的数据信息:

  1. 全局变量(Global variables);
  2. 局部变量(Local variables);
  3. 函数名和它们的入口地址(Function names and the addresses of their entry points);
  4. FPO 数据(Frame Pointer Omission):Frame Pointer 是一种用来在调用堆栈(Call stack)中找到下一个将要被调用的函数的数据结构源代码的行序号(Source-line numbers);
二、如何得到和安装符号文件?
  1. 先确定你的操作系统(OS)版本;
  2. 到微软网站下载相应的符号文件;
  3. 安装符号文件,对于符号文件的安装位置没有特贝要求,可以安装在任何目录中;
  4. 设置环境变量,使得调试工具(比如:Visual C++、WinDbg、Ntsd、DrWatson 等)能找到符号文件;
安装符号文件的注意事项:

  如果是手动安装符号文件,有一点很重要,那就是宿主机(Hostt Computer)上的符号文件必须与目标机器(Target Computer)上的 Windows 版本相匹配。
  这里所谓的宿主机指的是运行调试会话的机器,在典型的双系统调试会话环境中,宿主机可以是连接到目标机器的任何机器。目标机器指的是发生软件组件、系统服务、应用程序或操作系统运行失败的机器。也即是需要被调试的机器,它是调试会话关注的焦点。目标机器可以近在咫尺,也可以位于完全不同的地方。有时我们也将目标机器称之为——被调试者(debuggee),那么与之对应,宿主机则可以称为调试者(debugger)。

三、在 Visual C++ 使用符号文件的方法
在 Visual C++ 6.0 中的使用方法:
  1. 打开 Visual C++ 6.0 的 Workspace 文件(*.dsw);
  2. 进入 Tools 菜单,选择 Options 菜单项 (Tools->Options);
  3. 单击 Directoties 标签;
  4. 在 “Show directories for”下拉列表中选择 “Executable files”;
  5. 将符号文件的路径添加到 “Directories” 路径列表中;
  6. 单击  OK 完成;
在 Visual C++ .NET 2003 中的使用方法:
  1. 打开 Visual C++ .NET 的项目文件(*.vcproj);
  2. 在解决方案管理器中选中要使用符号文件的项目;
  3. 单击右键进入项目属性对话框;
  4. 选择“配置属性”中的“调试”;
  5. 在与“调试”对应的“操作”选项中有一个“符号路径”,在此添加符号文件的路径即可;
  6. 单击  “确定” 完成;
四、如何产生 Release 版本二进制文件对应的 PDB 文件?
在 Visual C++ 6.0 中的方法:
  1. 打开 Visual C++ 6.0 的 Workspace 文件(*.dsw);
  2. 进入 Project 菜单,选择 Settings 菜单项 (Project->Settings),打开项目设置对话框;
  3. 在 “Settings for”列表中选择项目的 Release 配置;
  4. 单击“C/C++”标签;
  5. 在“Category”下拉列表框中选择“General”选项;
  6. 在“Debug info”下拉列表框中选择调试信息格式(具体选项参见图一),在此不必禁用任何优化选项;
  7. 单击“Link”标签;
  8. 在“Category”下拉列表框中选择“Debug”选项;
  9. 选中“Debug info”复选框,然后选择需要的链接调试类型(具体选项参见图一);
  10. 不要选择“Separate types”复选框;
  11. 在“Project options”编辑框的最后添加如下指令:/opt:ref,icf;
  12. 重新生成(Rebuild)项目;
在 Visual C++ .NET 2003 中的方法:
  1. 打开 Visual C++ .NET 的项目文件(*.vcproj);
  2. 进入 Project 菜单,选择 Settings 菜单项 (Project->Settings),打开项目设置对话框;
  3. 在 “配置”下拉列表中选择项目的 “(活动)Release” 配置;
  4. 选择“配置属性”树型节点中的“C/C++” ==〉“常规”;
  5. 设置右边的“调试信息格式”选项(具体选项参见图一);
  6. 选择“配置属性”树型节点中的“链接器”==〉“调试”;
  7. 设置右边的“生成程序数据库文件”(具体选项参见图一);
  8. 选择“配置属性”树型节点中的“链接器”==〉“命令行”;
  9. 在“附加选项(D)”编辑框中添加如下指令:/opt:ref,icf;
  10. 按“确定”退出;
  11. 重新生成(Rebuild)项目;


图一

五、关于 Free Build(也称 Retail Build)和 Checked Build(也称 Debug Build)
每个基于 NT 操作系统有两种不同的程序生成模式,即:
  • Free Build (或 Retail Build)
  • Checked Build (或 Debug Build)
  Free Build 生成的是最终用户版本,针对生成的二进制文件进行了彻底的优化,禁用了调试断言,并剥离了调试信息。这样一来使可执行程序文件更小,加载更快,使用的内存也更小。
  Checked Build 生成的是测试和调试版本。它包含额外的 Free Build 所没有的错误检查,参数验证和调试信息,Checked Build 有助于隔离和跟踪可能导致不可预见的行为的问题,比如内存溢出,不正确的设备配置。虽然 Checked Build 提供了额外的保护,但与 Free Build 比较,它需要更多的内存开销和磁盘空间。由于可执行程序包含符号调试信息;调试时要执行附加的代码、参数检查和输出调试诊断信息,从而导致性能下降。

六、系统符号文件的更新方法

  系统符号文件指 Windows 操作系统依赖的那几个重要的 DLL/SYS 和可执行文件对应的符号文件,常见的比如:gdi32.dll、Kernel32.dll、Kerberos.dll、psapi.dll、user32.dll等,使用 WinDbg 调试时,你就会发现系统符号文件(PDB)有多重要,这些文件都与本地的 OS 密切相关,比如,Windows 2000 打了SP补丁的话,那么必须更新系统符号文件才能进行相关调试,原来的符号文件与打补丁后的系统就会不匹配,怎么办呢? 可以通过网络来更新!象下面这样在 WinDbg 的 Symbols Path 里面输入路径:

[pre]SRV*D:\Symbols\websymbols*http://msdl.microsoft.com/download/symbols(斜体部分是你在本地保存符号文件的路径)

  如果你不是通过代理上网,那么在你用 WinDbg 打开一个被调试程序后,输入 symchk 回车,WinDbg 就会自动的连到微软的网站根据你的机器的情况更新的 PDB 文件,并将它保存在上面斜体部分指定的本地路径里,这样你就可以确保你的符号文件版本和你机器上的文件版本一致。

如果你是通过代理上网那么你需要配置 IE 的连接设置。具体方法恕不赘言。
 

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 11楼 发表于: 2012-03-29
如何利用.pdb文件去调试
问题需求:
1.需要去调试一个exe,查看内部执行的过程,但是该exe的源代码是孤立的,利用make文件组织进行编译.每天会给出编译包。

问题的分析:
考虑1. 新建一个VS的工程,然后手动添加这些文件,然后编译调试。
考虑2. 既然有make文件,那么之前是否有编译成功的版本,该版本是否保存了调试的符号文件?
针对方案1:手动添加得考虑一下该工程的依赖关系,是否涉及太多的模块,否则将问题更加复杂化就不好了,我的需求中提到我的这个工程牵制的模块非常多,所以这个方案我肯定要放弃的。
针对方案2:我可以获取到每日编译包,打开找了一个,找到了该 *.exe,而且Symbol Files统一放到一个文件下管理,那么我没有理由不用第二种方案.

背景知识:
方案2肯定会用到的知识有符号文件,所以先大致说一下符号文件(Symbol Files)的定义.
符号文件(Symbol Files)是一个数据信息文件,它包含了应用程序二进制文件(比如:EXE、DLL等)调试信息,专门用来作调试之用,最终生成的可执行文件在运行时并不需要这个符号文件,但你的程序中所有的变量信息都记录在这个文件中。所以调试应用程序时,这个文件是非常重要的。用 Visual C++ 和 WinDbg 调试程序时都要用到这个文件。

解决方案;
1.新建一个 Visual C++ Win32 Console Application project.
这个控制台的程序只是一个用来进行调试的宿主程序,所以可以任意的创建,在这里我创建一个默认的Win32 Console Application.

2.选择工程的属性,在Debugging标签下,设置以下几个信息:
    Command: C:\Test\Sample.exe;Command Arguments: test1  test2 (that depends on what you want to debug);Working Directory: C:\Test;
3.In VS, Tools -> Options -> Debugging -> Symbols, 然后指定Symbol file(.pdb) ,将该路径填入即可。

4.打开Sample.exe的源代码文件,将调试中需要用到的文件打开并设置断点,F5即可调试.

总结:
碰到这个问题的时候,我第一反应就是用第一种方案,但是花费很长时间之后还是问题重重之后,我就看是意识到可能这并不是很好的办法,通过分析和研究,选择第二种方案,不到10分钟就可以解决这个问题。
碰到问题一定要先分析一下已有的资源,然后再进行方案的选择。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 12楼 发表于: 2012-03-29
VC中的pdb文件及其作用
VISUAL c+中的pdb文件及其作用

     程序数据库 (PDB) 文件保存着调试和项目状态信息,使用这些信息可以对程序的调试配置进行增量链接。当以 /ZI 或 /Zi(用于 C/C++)生成时,将创建一个 PDB 文件。

     在 Visual C++ 中,/Fd 选项用于命名由编译器创建的 PDB 文件。当使用向导在 Visual Studio 中创建项目时,/Fd 选项被设置为创建一个名为 project.PDB 的 PDB。



    如果使用生成文件创建 C/C++ 应用程序,并指定 /ZI 或 /Zi 而不指定 /Fd 时,则最终将生成两个 PDB 文件:

VC80.PDB (更笼统地说就是 VCx0.PDB,其中 x 表示 Visual C++ 的版本。)该文件存储各个 OBJ 文件的所有调试信息并与项目生成文件驻留在同一个目录中。

project.PDB 该文件存储 .exe 文件的所有调试信息。对于本机 C/C++代码,它驻留在 \debug 子目录中。对于托管代码,它驻留在 \WINDEBUG 子目录中。

每当创建 OBJ 文件时,C/C++ 编译器都将调试信息合并到 VCx0.PDB 中。插入的信息包括类型信息,但不包括函数定义等符号信息。因此,即使每个源文件都包含公共头文件(如 <windows.h>),这些头文件中的 typedef 也只存储一次,而不是在每个 OBJ 文件中都存在。

链接器将创建 project.PDB,它包含项目的 EXE 文件的调试信息。project.PDB 文件包含完整的调试信息(包括函数原型),而不仅仅是在 VCx0.PDB 中找到的类型信息。这两个 PDB 文件都允许增量更新。链接器还在其创建的 .exe 或 .dll 文件中嵌入 .pdb 文件的路径。

Visual Studio 调试器使用 EXE 或 DLL 文件中的 PDB 路径查找 project.PDB 文件。如果调试器在该位置无法找到 PDB 文件或者如果路径无效(例如,如果项目被移动到了另一台计算机上),调试器将搜索包含 EXE 的路径,即在“选项”对话框(“调试”文件夹,“符号”节点)中指定的符号路径。调试器不会加载与所调试的二进制不匹配的 PDB。

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 13楼 发表于: 2012-03-29
开发人员必会的PDB文件
一 什么是PDB文件
大部分的开发人员应该都知道PDB文件是用来帮助软件的调试的。但是他究竟是如何工作的呢,我们可能并不熟悉。本文描述了PDB文件的存储和内容。同时还描 述了debugger如何找到binay相应的PDB文件,以及debugger如何找到与binay对应的源代码文件。本文适用于所有的Native和 Managed的开发人员。

在开始前,我们先定义2个术语:private build, 用来表示在开发人员自己机器上生成的build;public build,表示在公用的build机器上生成的build。private build相对来说比较简单,因为PDB和binay在相同的地方,通常地我们遇到的问题都是关于public build。  

所有的的开发人员需要知道的最重要的事情是”PDB文件跟源代码同样的重要“, 没有PDB文件,你甚至不能debugging。对于public build,需要symbol server存储所有的PDB,然后当用户报告错误的时候,debugger才可以自动地找到binay相应的PDB文件, visual studio 和 windbg都知道如何访问symbol server。在将PDB和binay存储到symbol server前,还需要对PDB运行进行source indexing, source indexing的作用是将PDB和source关联起来。  

接下来的部分假设有已经设置好了symbol server和source server indexing。TFS2010中可以很简单地完成对一个新的build的source indexing 和 symbol server copying。


二 PDB文件的内容
正式开始PDB的内容,PDB不是公开的文件格式,但是Microsoft提供了API来帮助从PDB中获取数据。

Native C++ PDB包含了如下的信息:
* public,private 和static函数地址;
* 全局变量的名字和地址;
* 参数和局部变量的名字和在堆栈的偏移量;
* class,structure 和数据的类型定义;
* Frame Pointer Omission 数据,用来在x86上的native堆栈的遍历;
* 源代码文件的名字和行数;

.NET PDB只包含了2部分信息:
* 源代码文件名字和行数;
* 和局部变量的名字;
* 所有的其他的数据都已经包含在了.NET Metadata中了;  


三 PDB如何工作
当你加载一个模块到进程的地址空间的时候,debugger用2中信息来找到相应的PDB文件。第一个毫无疑问就是文件的名字,如果加载 zzz.dll,debugger则查找zzz.pdb文件。在文件名字相同的情况下debugger还通过嵌入到PDB和binay的GUID来确保 PDB和binay的真正的匹配。 所以即使没有任何的代码修改,昨天的binay和今天的PDB是不能匹配的。可以使用dempbin.exe来查看binary的GUID。

在VisualStudio中的modules窗口的symbol file列可以查看PDB的load顺序。第一个搜索的路径是binary所在的路径,如果不在binary所在的路径,则查找binary中hardcode记录的build目录,例如obj\debug\*.pdb, 如果以上两个路径都没有找到PDB,则根据symbol server的设置,在本地的symbol server的cache中查找,如果在本地的symbol server的cache中没有对应的PDB,则最后才到远程的symbol server中查找。通过上面的查找顺序我们可以看出为什么public build和private build的PDB查找不会冲突。

对于private build有时我们需要在别人的机器上debug的情况,需要将相应的PDB与binary一起拷贝,对于加入GAC的.NET的binary,需要将PDB文件拷贝到C:\Windows\assembly\GAC_MSIL\Example\1.0.0.0__682bc775ff82796a类似的binary所在的目录。另一个变通的方法是定义环境变量DEVPATH,从而代替使用命令GACUTIL将binary放入GAC中。在定义DEVPATH后,只需要将binary和PDB放到DEVPATH的路径,在DEVPATH下的binary相当于在GAC下。使用DEVPATH,首先需要创建目录且对当前build用户有写权限,然后创建环境变量DEVPATH且值为刚才创建的目录,然后在web.config,app.config或machine.config中开启development模式,启动对DEVPATH的使用
<configuration>
   <runtime>
      <developmentMode developerInstallation="true"/>
   </runtime>
</configuration>

在你打开了development模式后,如果DEVPATH没有定义或路径不存在的话会导致程序启动时异常"Invalid value for registry"。而且如果在machine.config中开启DEVPATH的使用会影响其他的所有的程序,所以要慎重使用machine.config。

最后开发人员需要知道的是源代码信息是如何存储在PDB文件中的。对于public builds,在运行source indexing tool后,版本控制工具将代码存储到你设置的代码cache中。对于private builds,只是存储了PDB文件的全路径,例如在c:\foo下的源文件mycode.cpp,在pdb文件中存储的路径为c:\foo\mycode.cpp。对于private builds可以使用虚拟盘来增加PDB对绝对路径的依赖,例如可以使用subst.exe将源代码路径挂载为V:,在别人的机器上debug的时候也挂载V:。

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 14楼 发表于: 2012-03-29
VS2008编译找不到且不能升级vc90.pdb文件解决方法
VS2008编译找不到且不能升级vc90.pdb文件解决方法

  问题描述:
  1>e:/vcomh264net/vcomh/vcomh264net2.0 1.0.25 av/video/videoproc.cpp : error C2471: cannot update program database 'e:/vcomh264net/vcomh264net2.0 1.0.25 2008/vcomh264net2.0 1.0.25 av/debug/vc90.pdb'
  1>e:/vcomh264net/vcomh/vcomh264net2.0 1.0.25 av/video/videoproc.cpp : fatal error C1083: Cannot open program database file: 'e:/vcomh264net/vcomh264net2.0 1.0.25 2008/vcomh264net2.0 1.0.25 av/debug/vc90.pdb': No such file or directory
  为什么我把一个VC6的工程转换为VS2008的工程后,编译总是出现找不到而且不能升级vc90.pdb文件的问题。重新编译了也不行。
  从VC6直接跳到VS2008
  这个vs2008一个著名的bug。详情可以参见https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=309462
  官方现有解决方案如下:
  I have found an alternate way for the time beging to avoid C2471 error but it works only in the case of successful release build.
  for this click Build menu than Configuration manager than create a new setting from release settings. Change following things in your project settings as :
  C/C++ | General | Debug Information format | C7 Compatible (/Z7)
  C/C++ | Code Generation | Enable String Pooling | Yes (/GF)
  Linker | DEBUG | General Debug Info | Yes (/DEBUG)
  after these settings. Rebuild your application. I'm sure you will be able to resolve the pdb updation issue if it is really not hampering your other settings. Hope it will be helpful to you.

    可以解释为要做以下项目属性设置:
  C/C++ | 常规 | 调试信息格式 | C7兼容 (/Z7)
  C/C++ | 代码生成 | 启用字符串池 | 是 (/GF)
  链接器 | 调试 | 生成调试信息 | 是 (/DEBUG)

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 15楼 发表于: 2012-03-29
让Visual Studio载入Symbol(pdb)文件

VC编译工程的编译连接阶段,会产生Symbol文件,也就是常说的 pdb 文件。pdb 的全称为program database,在 MSDN 中的解释为,pdb 文件是一个在编译和链接阶段产生的,包含了类型和调试符号信息的二进制文件。pdb 文件中包含了各个函数的符号表,也是进行 WinDbg 调试必不可少的文件。

既然 pdb 文件是给调试器使用的,那么 Visual Studio 调试器也就可以使用之。通过如下设置,Visual Studio 便可以在调试过程中加载 pdb 文件了。

Tools --> Options --> Debugging

在“Symbol file (.pdb) locations:”下面的控件中输入微软默认 pdb 文件的下载路径“http://msdl.microsoft.com/download/symbols”。

在“Cache symbols from symbol servers to this directory:”下的编辑框中输入本地保存路径,如:D:/WinSymbols/MsDownSymbols

 

下面两个 CheckBox 的意思分别为:

Search the above locations only when symbols are loaded manually”。

调试器仅仅载入手动载入的 Symbol。勾上此选项,调试速度会更快,但 pdb 载入相对较少,调试信息较少,建议不勾选。

 

Load symbols using the updated settings when the dialog is closed”

对话框关闭后,立刻加载符号表。勾上此选项可以立刻看到效果,调试速度当然会稍微慢一点,建议勾选。

 

点击 OK 关闭对话框,设置便生效了。

如图所示:

 

 

 

 

设置过了,当然要看看效果啦。

我们先看看正常情况下 CallStack 的状态,建立一个普通 Win32 程序,在InitInstance入口下个断点,F5开启调试,在 CallStack 窗口如下显示: 

 

 

    我们可以看到,在最下面两行中,信息是灰掉的,kernel32.dll中那个函数调用了我们的wWinMainCRTStartup也看不到,进程最开始由哪个函数创建也看不到。

    现在看看设置了 Symbol  CallStack 的状态,建立一个普通 Win32 程序,在InitInstance入口下个断点,F5开启调试,在 CallStack 窗口如下显示: 

 

    我们可以看到,程序是由kernel32.dll中的导出函数“_BaseProcessStart”开始的,_BaseProcessStart调用了我们的wWinMainCRTStartup函数,然后程序就运行起来了。

当然设置 Symbol 的用处远远不止这些,其余的就等您慢慢发现。

 

注:设置 Symbol 之后,调试速度会慢下来,如果无需知道系统函数是如何调用的,建议调试时不设置 Symbol 

QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 16楼 发表于: 2012-12-11
VC调试器高级应用
要成为一位优秀的软件工程师,调试能力必不可缺。本文将较详细介绍VC6调试器的主要用法。
windows平台的调试器主要分为两大类:
1 用户模式(user-mode)调试器:它们都基于win32 Debugging API,有使用方便的界面,主要用于调试用户模式下的应用程序。这类调试器包括Visual C++调试器、WinDBG、BoundChecker、Borland C++ Builder调试器、NTSD等。
2 内核模式(kernel-mode)调试器:内核调试器位于CPU和操作系统之间,一旦启动,操作系统也会中止运行,主要用于调试驱动程序或用户模式调试器不易调试的程序。这类调试器包括WDEB386、WinDBG和softice等。其中WinDBG和softice也可以调试用户模式代码。
国外一位调试高手曾说,他70%调试时间是在用VC++,其余时间是使用WinDBG和softice。毕竟,调试用户模式代码,VC6调试器的效率是非常高的。因此,我将首先在本篇介绍VC6调试器的主要用法,其他调试器的用法及一些调试技能在后续文章中阐述。
一 位置断点(Location Breakpoint)
大家最常用的断点是普通的位置断点,在源程序的某一行按F9就设置了一个位置断点。但对于很多问题,这种朴素的断点作用有限。譬如下面这段代码:
01.void CForDebugDlg::OnOK()  
02.{
03.for (int i = 0; i < 1000; i++)   //A
04.{
05.int k = i * 10 - 2; //B
06.SendTo(k);      //C
07.int tmp = DoSome(i);    //D
08.int j = i / tmp;    //E
09.}
10.}    


执行此函数,程序崩溃于E行,发现此时tmp为0,假设tmp本不应该为0,怎么这个时候为0呢?所以最好能够跟踪此次循环时DoSome函数是如何运行的,但由于是在循环体内,如果在E行设置断点,可能需要按F5(GO)许多次。这样手要不停的按,很痛苦。使用VC6断点修饰条件就可以轻易解决此问题。步骤如下。
1 Ctrl+B打开断点设置框,如下图:

Figure 1设置高级位置断点
2 然后选择D行所在的断点,然后点击condition按钮,在弹出对话框的最下面一个编辑框中输入一个很大数目,具体视应用而定,这里1000就够了。
3 按F5重新运行程序,程序中断。Ctrl+B打开断点框,发现此断点后跟随一串说明:...487 times remaining。意思是还剩下487次没有执行,那就是说执行到513(1000-487)次时候出错的。因此,我们按步骤2所讲,更改此断点的skip次数,将1000改为513。
4 再次重新运行程序,程序执行了513次循环,然后自动停在断点处。这时,我们就可以仔细查看DoSome是如何返回0的。这样,你就避免了手指的痛苦,节省了时间。
再看位置断点其他修饰条件。如Figure 1所示,在“Enter the expression to be evaluated:”下面,可以输入一些条件,当这些条件满足时,断点才启动。譬如,刚才的程序,我们需要i为100时程序停下来,我们就可以输入在编辑框中输入“i==100”。
另外,如果在此编辑框中如果只输入变量名称,则变量发生改变时,断点才会启动。这对检测一个变量何时被修改很方便,特别对一些大程序。
用好位置断点的修饰条件,可以大大方便解决某些问题。
二 数据断点(Data Breakpoint)
软件调试过程中,有时会发现一些数据会莫名其妙的被修改掉(如一些数组的越界写导致覆盖了另外的变量),找出何处代码导致这块内存被更改是一件棘手的事情(如果没有调试器的帮助)。恰当运用数据断点可以快速帮你定位何时何处这个数据被修改。譬如下面一段程序:
01.#include "stdafx.h"
02.#include
03.
04.int main(int argc, char* argv[])
05.{
06.char szName1[10];
07.char szName2[4];
08.strcpy(szName1,"shenzhen");    
09.printf("%s\n", szName1);        //A
10.
11.strcpy(szName2, "vckbase");     //B
12.printf("%s\n", szName1);
13.printf("%s\n", szName2);
14.
15.return 0;
16.}


这段程序的输出是
1.szName1: shenzhen
2.szName1: ase
3.szName2: vckbase


szName1何时被修改呢?因为没有明显的修改szName1代码。我们可以首先在A行设置普通断点,F5运行程序,程序停在A行。然后我们再设置一个数据断点。如下图:

Figure 2 数据断点
F5继续运行,程序停在B行,说明B处代码修改了szName1。B处明明没有修改szName1呀?但调试器指明是这一行,一般不会错,所以还是静下心来看看程序,哦,你发现了:szName2只有4个字节,而strcpy了7个字节,所以覆写了szName1。
数据断点不只是对变量改变有效,还可以设置变量是否等于某个值。譬如,你可以将Figure 2中红圈处改为条件”szName2[0]==''''y''''“,那么当szName2第一个字符为y时断点就会启动。
可以看出,数据断点相对位置断点一个很大的区别是不用明确指明在哪一行代码设置断点。
三 其他
1 在call stack窗口中设置断点,选择某个函数,按F9设置一个断点。这样可以从深层次的函数调用中迅速返回到需要的函数。
2 Set Next StateMent命令(debug过程中,右键菜单中的命令)
此命令的作用是将程序的指令指针(EIP)指向不同的代码行。譬如,你正在调试上面那段代码,运行在A行,但你不愿意运行B行和C行代码,这时,你就可以在D行,右键,然后“Set Next StateMent”。调试器就不会执行B、C行。只要在同一函数内,此指令就可以随意跳前或跳后执行。灵活使用此功能可以大量节省调试时间。
3 watch窗口
watch窗口支持丰富的数据格式化功能。如输入0x65,u,则在右栏显示101。
实时显示windows API调用的错误:在左栏输入@err,hr。
在watch窗口中调用函数。提醒一下,调用完函数后马上在watch窗口中清除它,否则,单步调试时每一步调试器都会调用此函数。
4 messages断点不怎么实用。基本上可以用前面讲述的断点代替。
总结
调试最重要的还是你要思考,要猜测你的程序可能出错的地方,然后运用你的调试器来证实你的猜测。而熟练使用上面这些技巧无疑会加快这个过程。最后,大家如果有关于调试方面的问题,我乐意参与探讨。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 17楼 发表于: 2013-01-23
10 个Visual Studio调试技巧
我以前关于Visual Studio调试技巧的文章引起了大家很大的兴趣,以至于我决定分享更多调试的知识。以下的列表中你可以看到写原生开发的调试技巧(接着以前的文章来编号)。这些技巧可以应用在VS2005或者更新版本中(当然有一些可以适用于旧版本)。如果你继续,你可以知道每个技巧的详细信息。
数据断点
线程重命名
特定进程中断
大概执行时间
数字格式化
内存数据格式化
系统DLL中断
装载符号表
MFC中内存泄露报告
调试ATL


guoliang
翻译于 16天前
0人顶
顶 翻译的不错哦!
提示11:数据断点
当数据所在内存位置变化时,调试器将会中断。然而,这是唯一可能在一个时间创建4这样的硬件的数据断点。数据断点只能在编译的过程中添加,可以通过菜单(编译>新断点>新数据断点)或者通过断点窗口来添加。



凡程子
翻译于 16天前
0人顶
顶 翻译的不错哦!
您可以使用一个内存地址或地址表达式。即使你能看到堆栈上的两个值,我认为通常当堆上的值被改变时,这项功能才会有用。这对 识别内存损坏是一个很大的帮助。


沙滩哥
翻译于 23天前
0人顶
顶 翻译的不错哦!
在下面的例子中,指针的值已经更改为所指向对象的值。为了能找出什么地方做的更改,我在指针值存储的位置设置了一个断点,如 &ptr (注意这是在指针初始化后发生的)。当数据更改后,以为着某人更改了指针的值,调试器终止,然后能发现哪些代码引起了这个改变。


额外阅读:
How Can I Find Out If My Pointers Corrupt a Memory Address?
How Can I Find Out Where My Pointer Is Getting Changed?


小编辑
翻译于 23天前
0人顶
顶 翻译的不错哦!
提示 12: 线程重命名
当你调试多线程应用是,Threads窗口会显示创建了哪些线程,以及当前的线程。线程越多,你就越难找到你要找的线程(特别是当同一段程序,被多个线程同时执行的时候,你就不知道当前执行的是哪个线程实例)


调试器允许你给线程重新命名。用右键单击一个线程,并重命名。




guoliang
翻译于 16天前
0人顶
顶 翻译的不错哦!
也可以以程式设计方式命名线程,虽然这有点棘手而且线程启动后必须去做的,否则调试器将以它的默认命名规定重新将其初始化,下面的函数显示了如何定义和使用一个线程。
01
typedef struct tagTHREADNAME_INFO
02
{
03
    DWORD dwType;        // 必须是两个字节的长度
04
    LPCSTR szName;       // 指针指向命名 (同一个地址空间)
05
    DWORD dwThreadID;    // 线程ID(-1调用线程)
06
    DWORD dwFlags;       // 保留待用,多数情况下为0
07
} THREADNAME_INFO;
08

09
void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName)
10
{
11
    THREADNAME_INFO info;
12
    info.dwType = 0x1000;
13
    info.szName = szThreadName;
14
    info.dwThreadID = dwThreadID;
15
    info.dwFlags = 0;
16

17
    __try
18
    {
19
        RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);
20
    }
21
    __except (EXCEPTION_CONTINUE_EXECUTION)
22
    {
23
    }
24
}


凡程子
翻译于 16天前
0人顶
顶 翻译的不错哦!
提示13:指定线程设置断点
对于多线程应用程序,另一个有用的技巧是在指定线程、进程甚至计算机设置断点。可以使用断点的Filer命令来实现这种功能。


调试器允许你使用线程名、线程ID、进程名、进程ID和机器名的不同组合(使用AND、OR、NOT连接)。掌握如何设置线程名称也使得这种过滤技术操作更为简单。
辅助阅读:
How to: Specify a Breakpoint Filter
Setting a Breakpoint Filter


Tocy
翻译于 15天前
0人顶
顶 翻译的不错哦!
提示14:(不准确的)定时执行
在我之前的文章中我曾提及在Watch窗口中使用伪变量。其中一个没有提及的是@clk,可显示一个计数器的值,用于获得两个断点之间代码执行所需要的大体时间,该值的单位为毫秒(ms)。但是,这种方法不能用于配置程序执行。你应该使用Visual Studio Profiler或者性能计时器来完成这些配置。


Tocy
翻译于 2天前
0人顶
顶 翻译的不错哦!
通过在Watch窗口或者Immediate窗口中添加@clk=0来重置计时器。因此,若需要计算末段代码执行所需要的时间,做下列处理:
在代码块起始位置设置断点
在代码块结束位置设置断点
在Watch窗口中添加@clk
当第一个断点触发时,在Intermediate窗中中输入@clk=0
运行程序,直到遇到代码块结束位置的断点,并在Watch窗口中查看@clk的值


Tocy
翻译于 2天前
0人顶
顶 翻译的不错哦!
注意网上有技巧说需要在Watch窗口中添加两条表达式:@clk和@clk=0,据说可以每次在断点执行的位置重置计时器。这种技巧只能在较低版本的Visual Studio中使用,但是不能在高版本VS中使用,例如VS2005(作者做过测试,vs2005不支持这种技巧)以及更高版本。
辅助阅读:
Debugging Tips - @CLK


Tocy
翻译于 2天前
0人顶
顶 翻译的不错哦!
提示15:格式化数字
当你使用Watch或者Quick Watch窗口查看变量时,显示这些数值是用默认的预定义可视化格式。当变量是数字时,显示形式按照他们的类型(int、float、double)来的,并且使用十进制显示。然而,你可以设置调试器在显示数字的使用使用不同的类型,或者使用不同的进制。


Tocy
翻译于 14天前
0人顶
顶 翻译的不错哦!
改变变量显示类型可以在变量前添加以下前缀:
by —— unsigned char(unsigned byte)
wo —— unsigned short(unsigned word)
dw —— unsigned long(unsigned double word)
改变变量显示的进制可以在变量前添加以下前缀:
d或i —— 有符号十进制数
u     —— 无符号十进制数
o     —— 无符号八进制数
x     —— 小写十六进制数
X     —— 大写十六进制数


辅助阅读:
C++ Debugger Tips


Tocy
翻译于 14天前
0人顶
顶 翻译的不错哦!
提示16:格式化内存数据
除了数字,debugger还可以在Watch窗口中显示格式化的内存数据,最长为64字节。你可以在表达式(变量或者内存地址)后面添加下面的后缀来格式化数据:
mb或m —— 十六进制显示的16字节数据,后面跟着16个ASCII字符
mw —— 8字(WORD,通常1 WORD = 2 BYTE)数据
md —— 4个双字(DWORD,通常1 DWORD = 4 BYTE)数据
mq —— 2个四字(Quad WORD)数据
ma —— 64个ASCII字符
mu —— 2字节UNICODE字符


附加阅读:
Format Specifiers in C++
Debugging Tips in Developer Studio


Tocy
翻译于 10天前
0人顶
顶 翻译的不错哦!
提示17:在系统DLL调用处暂停
有时在DLL的某个函数被调用时暂停是很有用,特别是系统DLL(比如kernel32.dll、user32.dll)。实现这种暂停需要使用原生debugger提供的上下文运算符。你可以设定断点位置、变量名或者表达式:
{[函数],[源代码],[模块]}断点位置
{[函数],[源代码],[模块]}变量名
{[函数],[源代码],[模块]}表达式
大括号内可以是函数名、源代码及模块的任意组合,但是逗号不能省略。


Tocy
翻译于 昨天(9:31)
0人顶
顶 翻译的不错哦!
举个例子如果我们需要在CreateThread函数调用时暂停。这个函数是从kernel32.dll导出的,因此上下文运算符应该是这样子的:{,,kernel32.dll}CreateThread。然而,这样并不行,因为该运算符需要CreateThread修饰之后的名字。可以使用  DBH.exe来获得一个特定函数的修饰名(编译器编译生成)。


Tocy
翻译于 昨天(12:32)
0人顶
顶 翻译的不错哦!
下面是如何获得CreateThread的修饰名的方法:
01
C:\Program Files (x86)\Debugging Tools for Windows (x86)>dbh.exe -s:srv*C:\Symbo
02
ls*http://msdl.microsoft.com/Download/Symbols -d C:\Windows\SysWOW64\kernel32.dl
03
l enum *CreateThread*
04
Symbol Search Path: srv*C:\Symbols*http://msdl.microsoft.com/Download/Symbols
05

06
index            address     name
07
     1            10b4f65 :   _BaseCreateThreadPoolThread@12
08
     2            102e6b7 :   _CreateThreadpoolWork@12
09
     3            103234c :   _CreateThreadpoolStub@4
10
     4            1011ea8 :   _CreateThreadStub@24
11
     5            1019d40 :   _NtWow64CsrBasepCreateThread@12
12
     6            1019464 :   ??_C@_0BC@PKLIFPAJ@SHCreateThreadRef?$AA@
13
     7            107309c :   ??_C@_0BD@CIEDBPNA@TF_CreateThreadMgr?$AA@
14
     8            102ce87 :   _CreateThreadpoolCleanupGroupStub@0
15
     9            1038fe3 :   _CreateThreadpoolIoStub@16
16
     a            102e6f0 :   _CreateThreadpoolTimer@12
17
     b            102e759 :   _CreateThreadpoolWaitStub@12
18
     c            102ce8e :   _CreateThreadpoolCleanupGroup@0
19
     d            102e6e3 :   _CreateThreadpoolTimerStub@12
20
     e            1038ff0 :   _CreateThreadpoolIo@16
21
     f            102e766 :   _CreateThreadpoolWait@12
22
    10            102e6aa :   _CreateThreadpoolWorkStub@12
23
    11            1032359 :   _CreateThreadpool@4
看起来真实的名字是_CreateThreadStub@24。因此我们可以创建断点,{,,kernel32.dll}_CreateThreadStub@24。
运行程序,当遇到暂停时,直接忽略关于在断点位置无相关源代码的消息提示。

使用调用堆栈窗口来查看调用这个函数的代码。

附加阅读:
How to set breakpoints without source code in Visual Studio 2010
Context Operator (C/C++ Language Expressions)
How to: Set a Function Breakpoint


Tocy
翻译于 昨天(12:37)
0人顶
顶 翻译的不错哦!
提示18:载入符号
当你调试程序的时候,调用堆栈窗口有可能不会显示全部的调用堆栈,其中忽略系统DLL(例如kernel32.dll, user32.dll)的信息。





Tocy
翻译于 9天前
0人顶
顶 翻译的不错哦!
通过加载这些DLL的符号信息,可以获得全部调用堆栈信息,并且在调用堆栈窗口,使用上下文菜单(右键菜单),直接设置这种效果。你可以从预定义的符号路径或者微软的符号服务器(针对系统DLL)下载这些符号。在这些符号下载并导入到debugger中之后,调用堆栈更新如下:
  


Tocy
翻译于 9天前
0人顶
顶 翻译的不错哦!
这些符号也可以从Module窗口导入。


一旦载入之后,这些符号会保存在缓存中,并且可以在Tools>Options>Debugging>Symbols中配置。





Tocy
翻译于 9天前
0人顶
顶 翻译的不错哦!
提示19:在MFC中报告内存泄露
如果你想在MFC应用程序中监测内存泄露,你可以使用宏DEBUG_NEW来重定义new运算符,这是new运算符的一个修改版本,可以记录其分配内存的文件名及行数。在Release版中构建的DEBUG_NEW会解析成原始的new运算符。


Tocy
翻译于 10天前
0人顶
顶 翻译的不错哦!
MFC向导产生的源代码中在#include后米娜包含如下预处理指令:
1
#ifdef _DEBUG
2
#define new DEBUG_NEW
3
#endif
上面代码就是如何重定义new运算符的方法。




Tocy
翻译于 10天前
0人顶
顶 翻译的不错哦!
很多STL头文件和这里定义的new运算符不兼容。如果你在重新定义运算符new之后包含了<map><vector><list><string>等头文件,会有如下错误(以<vector>为例):
01
1>c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(43) : error C2665: 'operator new' : none of the 5 overloads could convert all the argument types
02
1>        c:\program files\microsoft visual studio 9.0\vc\include\new.h(85): could be 'void *operator new(size_t,const std::nothrow_t &) throw()'
03
1>        c:\program files\microsoft visual studio 9.0\vc\include\new.h(93): or       'void *operator new(size_t,void *)'
04
1>        while trying to match the argument list '(const char [70], int)'
05
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(145) : see reference to function template instantiation '_Ty *std::_Allocate<char>(size_t,_Ty *)' being compiled
06
1>        with
07
1>        [
08
1>            _Ty=char
09
1>        ]
10
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(144) : while compiling class template member function 'char *std::allocator<_Ty>::allocate(std::allocator<_Ty>::size_type)'
11
1>        with
12
1>        [
13
1>            _Ty=char
14
1>        ]
15
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\xstring(2216) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
16
1>        with
17
1>        [
18
1>            _Ty=char
19
1>        ]
解决方法是在包含这些STL文件之后再使用DEBUG_NEW重定义new运算符。
附加阅读:
DEBUG_NEW


Tocy
翻译于 10天前
0人顶
顶 翻译的不错哦!
提示20:调试ATL
当你开发ATL COM组件时你可以在debugger中查看你所开发的COM对象的QueryInterface、AddRef和Release的调用情况。默认情况下并不支持这些调用的产看,你需要在预处理定义或者预编译头文件中定义两个宏。这两个宏定义之后,关于这些函数的调用会显示在输出(Output)窗口中。


Tocy
翻译于 7天前
0人顶
顶 翻译的不错哦!
这两个宏是:
_ATL_DEBUG_QI,显示每个被查询接口的名字。必须在atlcom.h头文件被包含之前定义。
_ATL_DEBUG_INTERFACES,每当AddRef或Release被调用时显示当前接口的引用次数以及类名、接口名等信息。必须在atlbase.h包含之前定义。
辅助阅读:
Debugging Tips
ATL Debugging Techniques
How does _ATL_DEBUG_INTERFACES work?


Tocy
翻译于 7天前
0人顶
顶 翻译的不错哦!
结论
在本篇文章及上一篇文章中提到的tips,尽管没有包含全部调试技巧,但是可以帮助你解决你遇到的多数原生应用程序的问题。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 18楼 发表于: 2013-03-25
windbg调试句柄泄露
同事介绍了一篇调试句柄泄露的blog文章,今天有空看了一下,这家伙用视频的方式录下整个调试的过程,学习一目了然,真是有心。鉴于学习的过程总结一下能加深记忆,所以我这里做个记录,感兴趣的朋友可以看这里:http://blogs.msdn.com/ntdebugging/archive/2007/09/14/talkbackvideo-understanding-handle-leaks-and-how-to-use-htrace-to-find-them.aspx
    如果你不喜欢英文,那就可以看下面我蹩脚的解释了。
1、用c++写一个句柄泄露的样例程序:

#include "stdafx.h"
#include <windows.h>
void fun1(void);
void fun2(void);
void fun3(void);
void fun4(void);
int main(int argc, char* argv[])
{
      while(1)
      {
            fun1();
            fun2();
            Sleep(100);
      }
      return 0;
}
void fun1(void)
{
      fun3();
}
void fun2(void)
{
      fun4();
}
void fun3(void)
{
      HANDLE hEvent;
      hEvent = CreateEvent(NULL,TRUE,TRUE,NULL);
      CloseHandle(hEvent);
}
void fun4(void)
{
      HANDLE hEvent2;
      hEvent2 = CreateEvent(NULL,TRUE,TRUE,NULL);//这里只打开但是没关闭句柄
}

代码非常简单,明眼人一看就能看出哪里有问题,那么程序编译后用windbg怎么调出来呢?
2、windbg调试
1)找到windbgs安装目录下的gflags.exe工具,该工具可用来打开windows自带的一些调试选项,具体gflags.exe的详细使用可以查看windbg帮助;

这里我们设置勾上application verifiwer,该工具主要可用来对程序做一些稳定性的检测,本次调试主要用于保存栈的相关信息。同时设置stack backtrace即栈的大小为10.

2)运行windbg,打开第一步编译的程序,并使其跑起来;此时你查看任务管理器中的句柄信息,会发行相应进程句柄一直在增加。

3)windbg用ctrl+break命令中断进程运行,用!htrace -enable命令开启句柄检测;htrace提供了进行句柄相关检测的命令,可查看windbg帮助。

同时用g命令让程序运行。

4)再次中断进程,使用!htrace -snapshot命令,获得此时进程句柄的镜像。并再次让程序运行。

5)第三次中断进程运行,我们再使用!htrace -diff命令获得当前句柄状态与第4步 snapshot镜像句柄的差异;

我们可以发现:新增很多打开的句柄,平常情况下这些打开的句柄有可能不是泄露,需要具体分析,但是本次示例程序太简单,所以刚好所有打开的句柄都属于泄露的。
6)我们使用lsa 传递指定位置对应的代码,lsa  handlew2!fun4+0x0000002e

到这里,我们就找到了泄露句柄的函数,真是神奇啊。
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
级别: 管理员
发帖
8532
金币
2762
威望
3231
贡献值
0
元宝
0
只看该作者 19楼 发表于: 2013-03-25
Win32 API资源分配释放速查,防止资源泄露
文档对防止资源泄露有很大帮助。抱歉一贴上就串了。
***********************************************************************
资源                     分配函数                                   清除函数              需要手动清除?
======================================================================

Accelerators       LoadAccelerators                         N/A                  No

Atoms               GlobalAddAtom                        GlobalFreeAtom       Yes

Bitmaps                CreateBitmap                      DeleteObject             Yes *
                CreateBitmapIndirect                   DeleteObject             Yes *
                CreateCompatibleBitmap           DeleteObject                Yes *
                CreateDIBitmap                   DeleteObject       Yes *
                CreateDiscardableBitmap               DeleteObject                Yes *
                LoadBitmap                       DeleteObject                Yes *

Brushes         CreateBrushIndirect                   DeleteObject         Yes
                CreateDIBPatternBrush            DeleteObject         Yes
                CreateHatchBrush                 DeleteObject         Yes
                CreatePatternBrush                   DeleteObject         Yes
                CreateSolidBrush                 DeleteObject         Yes

Carets                 CreateCaret                      DestroyCaret         No  #

Clipboard              OpenClipboard                    CloseClipboard       Yes

Comms Ports     OpenComm                  CloseComm          Yes

Cursor                 CreateCursor                     DestroyCursor           Yes
                LoadCursor                       N/A                 No

DC             CreateDC                         DeleteDC             Yes
                CreateCompatibleDC               DeleteDC             Yes @
                GetDC                            ReleaseDC            Yes
                GetWindowDC                      ReleaseDC            Yes
                BeginPaint                       EndPaint             Yes

Dialog Box             CreateDialog                     DestroyWindow        No
                CreateDialogIndirect                  DestroyWindow        No
                CreateDialogIndirectParam             DestroyWindow        No
                CreateDialogParam                DestroyWindow        No

File                   OpenFile                         _lclose                     No

Fonts                  CreateFont                       DeleteObject         Yes
                CreateFontIndirect                   DeleteObject                Yes
                AddFontResource                  RemoveFontResource         Yes

GDI Stock Obj          GetStockObject                   N/A                  No  &

Hook                  SetWindowsHook                   UnhookWindowsHook    Yes

IC              CreateIC                         DeleteDC             Yes

Icon            CreateIcon                       DestroyIcon          Yes
                LoadIcon                         N/A                  No

Library                LoadLibrary                      FreeLibrary          Yes

Memory         AllocDStoCSAlias                 FreeSelector         Yes
                AllocSelector                           FreeSelector         Yes
                GlobalAlloc                      GlobalFree           No
                GlobalDOSAlloc                   GlobalDOSFr          No
                GlobalFix                        GlobalUnfix          No
                GlobalLock                       GlobalUnlock         No
                GlobalPageLock                   GlobalPageUnlock     No
                GlobalRealloc                    GlobalFree           No
                GlobalWire                       GlobalUnwire         No
                LocalAlloc                       LocalFree            No
                LocalLock                        LocalUnlock          No
                LocalRealloc                     LocalFree            No

Menu           CreateMenu                       DestroyMenu          No  %
                CreatePopupMenu                  DestroyMenu          No  %
                LoadMenu                         DestroyMenu          No  %
                LoadMenuIndirect                 DestroyMenu          No  %
                GetSystemMenu                    N/A                  No

MetaFile               CreateMetaFile                   DeleteMetaFile       Yes

Palette                CreatePalette                           DeleteObject         Yes

Pen             CreatePen                        DeleteObject         Yes
                CreatePenIndirect                   DeleteObject         Yes

Regions         CreateEllipticRgn                    DeleteObject         Yes
                CreateEllipticRgnIndirect               DeleteObject         Yes
                CreatePolygonRgn                 DeleteObject         Yes
                CreatePolyPolygonRgn             DeleteObject         Yes
                CreateRectRgn                    DeleteObject         Yes
                CreateRectRgnIndirect               DeleteObject         Yes
                CreateRoundRectRgn               DeleteObject         Yes

Resource               AllocResource                    FreeResource         No
                FindResource                     N/A                  No
                LoadResource                     FreeResource         No
                LockResource                     UnlockResource       No

Sound                 OpenSound                        CloseSound           Yes

String                 LoadString                       N/A                  No

Thunk                 MakeProcInstance                 FreeProcInstance     No

Timer           SetTimer                         KillTimer            No

Window         CreateWindow                     DestroyWindow        No
                CreateWindowEx                   DestroyWindow        No


Notes:
------
*   位图资源在释放的时候必须从选中它的DC中脱离。
%   当菜单失去焦点和窗口不再关联或者应用程序结束的时候占用资源必须被释放。
@   DC被释放前必须和它关联(选中)的位图取消联系
#   如果脱字符指定了位图,位图必须被释放
&   对DeleteObject的调用不会销毁常规的StockObject(系统内定资源)
QQ: 378890364 微信:wwtree(省短信费) 紧急事宜发短信到0061432027638  本站微博:http://t.qq.com/wwtree QQ群:122538123
描述
快速回复

您目前还是游客,请 登录注册
如果您在写长篇帖子又不马上发表,建议存为草稿