如何用VS调试不属于解决方案的EXE和DLL程序

如果你手里有一个现成的EXE, 以及EXE相关联PDB文件, 还有相关联的CPP文件和H文件. 你如何用VS调试? (当然你可以选择WinDbg.不过这里就讨论VS)

你或许想问我干嘛不从一开始就用VS写代码?

设想, 有一个人喜欢用 NMAKE 和 MAKEFILE 创建工程. 他已经编译链接好了. 得到了 EXE. PDB. 现在他叫你帮他调试一下. 你又喜欢用VS调试, 怎么办? 对. 这就是下面要说的.

 

首先推荐阅读这个链接作为热身:

https://msdn.microsoft.com/zh-cn/library/0bxe8ytt.aspx

 

总的来说, 有2种办法

1. 附加到进程

2. 创建调试EXE的解决方案

 

无论你选择哪种, 步骤是类似的:

 

首先的首先, 准备工作: 确保PDB文件和CPP文件以及H文件的相对路径不发生变化.

因为PDB中保存了CPP文件和H文件的相对路径, 这样调试器才知道去哪里找源文件和头文件. 如果你挪动PDB或者CPP文件或者H文件导致它们之间的相对路径变了的话, 调试器就找不到符号了. 你就没法打断点. 后续的调试工作也就无法进行.

 

1.打开VS

2.把源文件组织为一个项目:文件->新建->从现有代码创建项目->选择项目文件位置, 比如VS_DEBUG\ClientSrc, 项目名称比如说ClientSrc, 将文件从这些文件夹添加到项目中, 选择比如说C:\TestProject, 确保所有你写的CPP和H文件都在这个目录(这些源代码文件只是会被引入项目, 但不会拷贝到VS_DEBUG\ClientSrc目录下, 也就意味着你仍然可以随时修改代码并用NMAKE构建而不必担心代码在VS中更新的问题)

##########################################################

# 注意这一步非常重要, 如果你只是单纯地用VS打开某个源文件而不是把所有的源文件组织到一个解决方案里的话,

# 你是无法使用"转到定义", "转到声明", "查看定义", "查找所有引用"这些功能的, 你查看代码的时候会很麻烦.

##

设置源文件搜索路径:

然后右击解决方案->属性->通用属性->调试源文件->设置为C:\TestProject, 这样调试器知道在这个目录可以搜索到你的源文件.

3.文件->打开->项目/解决方案->在右下角选择"可执行项目文件(.exe)"->选中"添入解决方案"->打开你想调试的EXE文件即可, 比如 Client.exe

4.现在你可以看见解决方案中有2个项目, 一个是只有代码的项目名为ClientSrc. 一个是只有EXE的项目名为Client. 

5.把Client设为启动项目

6.设置PDB的位置: 选中Client项目, 在工具栏点击:调试->选项和设置->符号->把PDB文件的路径添加进去并打勾即可

7.设置命令行参数, 工作目录:选中Client项目, 右击, 点属性, 就可以看到设置命令行参数和工作目录的地方

8.在ClientSrc中打上断点, 调试Client即可. 或者你用附加到进程也行.

 

附上几张截图:

 

 

参考资料:

https://msdn.microsoft.com/zh-cn/library/ms241613.aspx

https://msdn.microsoft.com/zh-cn/library/0bxe8ytt.aspx

 

后记:

 

我写这篇BLOG的原因是学习Inside COM 第10章代码的时候遇到的问题.

作者用的MAKEFILE来完成了 IDL的编译, 组件的编译, DLL的生成(Server.dll), EXE的生成(Server.exe, Client.exe).

Server.dll会被加载到Client.exe的进程中从而Client.exe可以使用Server.dll中的组件.

Client.exe也可以与Server.exe使用COM的LPC(Local Procedure Call, 即单机版的Remote Procedure Call, RPC)来完成进程间通信, 从而Client.exe也可以使用Server.exe中实现的组件.

如果要把作者的代码在VS中建立一个DLL工程生成Server.dll, 两个EXE工程分别生成(Server.exe)与Client.exe也不是不行. 只不过没有MAKEFILE来得那么顺畅. (因为IDL文件的编译会生成几个.c文件和.h文件, 而且这些文件要用到后面DLL与EXE的生成, 但目前看来, 我不知道怎么让VS的解决方案中也能把这个过程也自动化起来.只能依靠MAKEFILE要方便一些.一个nmake命令就够了.)

 

当然我可以用WinDbg来调试这个项目. 但是我想尝试一下是否用VS也能完成. 因为以前没试过. 摸索了一段时间想了个这个办法出来.

 

这里要补充2个比较重要的问题.

 

1.

调试Server.dll中的代码, 打上断点提示"当前不会命中断点, 没有为该文档加载任何符号"

这是因为Client.exe在没有加载Server.dll之前. Server.dll对应的Server.pdb也没加载.当Server.dll加载之后就好了.可以通过如下步骤验证.

1.1

在Client.cpp的main函数打上断点.

在CMPNT1.CPP中任意一个地方,比如CA::FxStringIn,打上断点(提示"当前不会命中断点...")

1.2

按F5调试, 点击工具栏的调试->窗口->模块(这个窗口只有在调试已经启动的情况下能勾选, 没按F5启动调试是无法打开这个窗口的)

你可以看到Client.exe已经加载, 但是找不到Server.dll, 此时CMPNT1.CPP中的断点仍然提示"当前不会命中..."

单步执行main函数(并用命令告诉Client.exe加载Server.dll而不是去找Server.exe), 执行了CoCreateInstance之后, 再去查看模块窗口, 发现Server.dll已经加载, 此时CMPNT1.CPP中的断点变为可以命中的断点了

2.

按照上面所述的步骤1.1, 1.2做了, 看到Server.dll也加载了. 但是CMPNT1.CPP中的断点仍然提示"当前不会命中..."

2.1

原因是pdb与dll不匹配.

2.2

这是因为此时的Server.pdb对应的是Server.exe而不是Server.dll. 把MAKEFILE中"nmake -f make-one OUTPROC=1"这一行注释掉即可.

MAKEFILE大概是按如下顺序生成项目的.

编译IDL, 生成几个.C文件和.H文件.

编译所有的.C和.CPP等等文件

链接生成Client.exe, 生成Client.pdb

链接生成Server.dll, 生成Server.pdb

链接生成Server.exe, 生成Server.pdb(把Server.dll对应的pdb给覆盖了)

所以不生成Server.exe即可.

3. 

调试Server.exe中的代码, 打上断点提示"当前不会命中断点, 没有为该文档加载任何符号"

Server.exe是一个独立的进程, 不会加载到Client.exe的进程空间中. 所以调试Client.exe的时候不会命中Server.exe的代码断点.

 

首先, 把MAKEFILE的"nmake -f make-one"注释掉, 不生成Server.dll, 只生成Server.exe.

 

3.1 不可行的办法

把Server.exe也添加到解决方案中调试.(就跟添加Client.exe的方法一样)

这种办法是不行的. 因为一个调试器同一时刻只能调试一个进程.

选中解决方案资源管理器中的Server, 按Delete键移除.

3.2 第一种可行的办法

调试Client.exe 在 Client.cpp的 main函数打上断点. 单步调试. 执行到CoCreateInstance的时候

打开任务管理器, 你会发现启动了一个Server.exe

点击VS的工具栏:调试->附加到进程->选Server.exe->按F5继续执行->

你会发现CMPNT1.CPP中Server.exe的代码断点命中了.->再按F5继续执行就会跳转到Client.exe的下一个断点(如果有的话, 没有就运行到程序结束)

用这种方法, 只要2个进程中的任意一个后续代码还有断点, 调试器就会切换到相应的进程命中断点. 当2个进程后续的代码都没有断点的话, 你按F5就会让2个进程都一直运行到结束. 如果你希望用F10单步, 确保不漏掉每个进程中的所有代码的话, 进程间跳转/通信的时候又会有很多没有源码的代码.(当然你可以边调试边给后面的代码打断点, 直接按F5跳过进程切换的代码.) . 总的来说有方便的地方也有不方便的地方(你需要在调试开始的时候选择附加的进程. 以及随时去添加断点以便用F5跳过进程切换的代码.).

 

举个例子:

 

现在你在 Client.cpp 只给 main打了一个断点, 在 CMPNT1.CPP 中 有2个断点, 一个在 CA::FxStringIn上 还有一个在 CA::FxStringOut 上

 

按F5开始调试, 命中 main函数, F10单步, 调用CoCreateInstance之后, Server.exe启动, 选择附加到进程Server.exe, 然后按F5, 命中Server.exe的CA::FxStringIn, F10单步, CA::FxStringIn快执行完了之后进入rpcrt4.dll的进程通信代码(无源码), 此时不能按F5, 按F5之前先去Client.cpp的CA::FxStringIn后面一行代码打上断点, 然后按F5, 命中Client.cpp中刚刚打的断点, 继续F10, 命中Server.exe中的CA::FxStringOut, 继续F10, 这回我们学聪明点, CA::FxStringOut返回之前我们就去Client.cpp中调用FxStringOut的代码后一行打上断点, 直接F5就跳过了rpcrt4.dll的进程通信代码, ... 反复如此调试即可

 

可以看出, 虽然能用. 但是多少还是有点麻烦.

 

3.3 第二种可行的办法

就像我们创建一个解决方案调试Client.exe一样.再创建一个解决方案调试Server.exe就行啦. 2个调试器分别调试2个EXE, 互不干扰.

这个用起来最方便. 只不过你要创建2个解决方案同时调试2个EXE. 也是它麻烦的地方.

 

总之,发生了上面所说的问题.建议去工具栏的调试->选项和设置->符号 看看PDB的搜索位置是否设置对了. 以及是否正确配置了"对以下模块自动加载符号"(这里面可以配置那些DLL或EXE自动加载符号, 哪些不自动加载.)

 

项目下载:

http://pan.baidu.com/s/1A0FPs

目录:

CHAP10

  SRC // MAKEFILE以及源文件, nmake 生成, nmake clean清理

  VS_DEBUG // 用于调试DLL和EXE的VS2013解决方案, 点击.sln文件即可打开解决方案, 打开之前确保已经用MAKEFILE生成了EXE和DLL

建议:

MAKEFILE中"nmake -f make-one"和"nmake -f make-one OUTPROC=1"只保留一个. 避免Server.exe的PDB把Server.dll的PDB给覆盖了.

Demo.sln, Debug目录, 以及其他的几个就是调试Client.exe建立的VS2013的解决方案.如果下载下来因为路径问题用不了的话, 可以按照上面说的步骤重新建立即可.

使用方法:

管理员权限打开"VS2013 开发人员命令提示", 切换到MAKEFILE的目录.

nmake构建项目.

nmake clean清理项目.

 

参考资料:

http://www.cnblogs.com/MigCoder/p/3368319.html

http://www.cnblogs.com/lidabo/p/3486134.html

https://technet.microsoft.com/zh-cn/magazine/ms241613.aspx#bkmk_find_symbol___pdb__files

 

posted @ 2015-10-29 19:16  rldts  阅读(5698)  评论(0编辑  收藏  举报