如果你手里有一个现成的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

如何用VS调试不属于解决方案的EXE和DLL程序的更多相关文章

  1. Safari 前端开发调试 iOS 完美解决方案

    转http://www.2cto.com/kf/201403/283404.html afari 前端开发调试 iOS 完美解决方案 2014-03-05      0个评论    来源:Safari ...

  2. vscode在执行 npm任务的时候,会先执行package的name@version 然后命令名 加 当前路径,问题是我的引入路径e是小写的,会导致调试错误,解决方案:没找到,先手书吧

    vscode在执行 npm任务的时候,会先执行package的name@version 然后命令名 加 当前路径,问题是我的引入路径e是小写的,会导致调试错误,解决方案:没找到 Executing t ...

  3. 如何用Visual Studio 2013 (vs2013)编写C语言程序

    如何用Visual Studio 2013 (vs2013)编写C语言程序 (2014-05-16 10:58:15)   Visual Studio 2013是一个很强大的软件,但是刚开始用Visu ...

  4. 如何用Baas快速在腾讯云上开发小程序-系列4:实现客户侧商品列表、商品详情页程序

    版权声明:本文由贺嘉 原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/431172001487671163 来源:腾云阁 h ...

  5. 调试技巧 —— 如何利用windbg + dump + map分析程序异常

    调试技巧 —— 如何利用windbg + dump + map分析程序异常 逗比汪星人2011-09-04上传   调试技巧 —— 如何利用windbg + dump + map分析程序异常 http ...

  6. vs2013中$(TargetPath)与Link.OutputFile不同,导致调试debug找不到exe

    之前把VS2008项目升级为VS2013项目后,出现了VS2013调试debug找不到exe文件的现象,如:http://blog.sina.com.cn/s/blog_6c617ee301013xt ...

  7. 转:使用IDA动态调试WanaCrypt0r中的tasksche.exe

    逆向分析——使用IDA动态调试WanaCrypt0r中的tasksche.exe 转:http://www.4hou.com/technology/4832.html 2017年5月19日发布 导语: ...

  8. C# 最基本的涉及模式(单例模式) C#种死锁:事务(进程 ID 112)与另一个进程被死锁在 锁 | 通信缓冲区 资源上,并且已被选作死锁牺牲品。请重新运行该事务,解决方案: C#关闭应用程序时如何关闭子线程 C#中 ThreadStart和ParameterizedThreadStart区别

    C# 最基本的涉及模式(单例模式) //密封,保证不能继承 public sealed class Xiaohouye    { //私有的构造函数,保证外部不能实例化        private  ...

  9. 通过Windows Visual Studio远程调试WSL2中的.NET Core Linux应用程序

    最近两天在Linux中调试.NET Core应用程序,同时我发现在Linux中调试.NET Core应用程序并不容易.一直习惯在Visual Studio中进行编码和调试.现在我想的是可以简单快速的测 ...

随机推荐

  1. switch parser.p4源码

    /* Copyright 2013-present Barefoot Networks, Inc. Licensed under the Apache License, Version 2.0 (th ...

  2. Nginx 笔记与总结(16)nginx 负载均衡

    nginx 反向代理时,如果后端有多台服务器,就可以实现负载均衡. 实现原理:把多台服务器用 upstream 绑定在一起并起一个组名,然后 proxy_pass 指向该组. ngx_http_ups ...

  3. HTML: margin詳解

    margin:10px; 設置塊元素的上,右,下,左方向的值同爲10px margin:10px 30px; 設置塊元素的上和下爲10px,左和右爲30px; margin:10px 20px 30p ...

  4. processor, memory, I/O

    COMPUTER ORGANIZATION AND ARCHITECTURE DESIGNING FOR PERFORMANCE NINTH EDITION 3.3 INTERCONNECTION S ...

  5. Machine Learning in Action -- Logistic regression

    这个系列,重点关注如何实现,至于算法基础,参考Andrew的公开课 相较于线性回归,logistic回归更适合用于分类 因为他使用Sigmoid函数,因为分类的取值是0,1 对于分类,最完美和自然的函 ...

  6. Windows 下 Nginx + PHP + Xdebug + PHPStorm 调试环境配置

    前期条件:安装好 Nginx.PHP.PHPStorm,使得可以正常访问 一.为 PHP 安装 Xdebug 到 Xdebug 的官网(http://xdebug.org/download.php)下 ...

  7. 蓝牙4.0(包含BLE)简介

    1. BLE   (低功耗蓝牙)简介 国际蓝牙联盟( BT-SIG,TI  是 企业成员之一)通过的一个标准蓝牙无线协议. 主要的新特性是在蓝牙标准版本上添加了4.0 蓝牙规范 (2010 年6 月 ...

  8. mysql源码重启

    1.通过rpm包安装的MySQL service mysqld restart /etc/inint.d/mysqld start 2.从源码包安装的MySQL // linux关闭MySQL的命令 ...

  9. iftop ifstat

    ifstat 介绍 ifstat工具是个网络接口监测工具,比较简单看网络流量 实例 默认使用 #ifstat eth0 eth1 KB/s in KB/s out KB/s in KB/s out 0 ...

  10. Mysql 按行dump出数据

    mysqldump -u${user} -p${passwd} --skip-extended-insert --database ${dbname} --table ${tablename} > ...