首页 > 学院 > 开发设计 > 正文

C#调试入门篇

2019-11-17 03:16:33
字体:
来源:转载
供稿:网友

C#调试入门篇

DotNet程序的调试,是DotNet程序员必备的技能之一,开发出稳定的程序、解决程序的疑难杂症都需要很强大的调试能力。DotNet调试有很多方法和技巧。现在本文就介绍一下借助DebugView工具进行调试的方法,以及由DebugView引申出来的知识点。

DebugView

DebugView是一个查看调试信息的非常棒的工具,支持Debug、Release模式编译的程序,甚至支持内核程序,而且能够定制各种过滤条件,让你只看到关心的输出信息,而且可以定制高亮显示的内容等等,非常方便。

debugview

捕捉Release模式的Win32程序输出的调试信息,需要选中Capture Global Win32选项:

debugview_release

过滤与高亮功能

debugview_过滤与高亮

可以通过include、exclude设置过滤条件,包含指定字符串的输出信息将会被过滤。还可以通过exclude条件过滤掉对应进程ID的调试信息。多个条件使用“;”分隔,而且支持“*”通配符。

远程调试

DebugView支持远程捕捉调试信息。首先在远程机器上通过如下命令启动DebugView:

DebugView.exe /a /t /g /s

这样,DebugView就会以服务的方式运行,如下图:

debugview服务

然后在本地机器上启动DebugView,并通过Connect连接到远程机器的DebugView,当远程机器中有调试信息输出时,本地就会捕获到,并展示出来:

debugview_connect

输出信息到DebugView的几种方式

DebugView的一些功能是不是让你心动了呢。俗话说心动不如行动,但是在行动之前,首先要知道C#如何将调试信息输出到DebugView中。

通过编程输出一些调试信息到DebugView中,一共有三种方式:

  • Debug.WriteLine
  • Debugger.Log
  • Kernal32.dll中的OutputDebugString方法

一、Debug.WriteLine

通过Debug.WriteLine可以将调试信息写入到DebugView中,如下:

Debug.WriteLine("这是调试信息");

效果如下:

Debug_DebugView

不过此方式只能在Debug模式下有效。具体原因下面会详细介绍。

二、Debugger.Log

Debug.WriteLine已经很好用了,唯一的缺点就是在Release模式下无效。那么在Release模式下就可以使用Debugger.Log方法,示例如下:

Debugger.Log(0, null, "这是Debugger.Log输出的调试信息");

三、Kernel32.dll中的OutputDebugString方法

做C++开发的应该知道可以通过OutputDebugString这个API开实现输出调试信息到DebugView中吧。那么C++能做的,C#也能做。可以通过PInvoke的方式引入此方法,这个API属于Kernel32.dll,如下声明:

[DllImport("kernel32.dll", CharSet=CharSet.Auto)]public static extern void OutputDebugString(string message);

然后就可以通过调用此方法,将调试信息输出到DebugView中。

DebugView与日志框架比较

可能有人会说,DebugView能做的事情,我用log4Net,NLog等日志框架也能做,为什么要用DebugView呢?

问的好,那么我就根据平时使用DebugView的经验来给你一个用DebugView的理由:

  • DebugView使用非常方便。相比于日志框架庞大的体系,DebugView的使用可谓是十分的简单方便。DebugView只有几百K的大小,占用空间几乎可以忽略不计。从官网下载后,直接运行exe,几乎不需要任何配置就可以正常使用。而相比于DebugView,日志框架可以算的上庞然大物。而当你从官网获得log4Net后,需要进行各种繁杂的配置。甚至你要花上几天时间专门学习一下这套框架。由此可以看出DebugView的使用实在是方便的不能再方便。
  • DebugView是可视化工具,支持各种过滤和高亮。DebugView可以通过过滤条件来过滤不关心的信息,只显示相关的调试信息。而日志框架输出的是文件等文本信息,这些信息会包含程序运行过程中的所有信息,虽然可以通过配置文件来指定只输出哪一类信息,但是不如DebugView来的方便简单。
  • DebugView可以实时监视。DebugView中有“自动滚动”的功能,程序中输出的调试信息,基本上瞬间就会在DebugView中展示出来,当由于大量信息导致DebugView中的文本框满了后,DebugView可以通过自动滚动滚动条,让你随时都可以看到最新的一条信息,达到类似监视的效果。而日志框架由于其写文本的特性,很难达到这种效果,即使能达到,相信也是需要对日志框架相当清楚了解,才能完成这个效果。

这些理由应该足以让你使用DebugView了吧。使用DebugView的理由肯定还不止这些,如果你有更好的理由,还请分享出来。

当然,DebugView与日志框架,每个都有每个的用途。通过DebugView的方式,只适合短暂的调试,而正式发布的网站或者软件,需要一套记录程序长期以来的运行状态的工具,那么就非日志框架莫属了。所以DebugView与日志框架,要在合适的地方,发挥他们最大的功效。

声明

Log4Net等日志框架,功能足够强大,也足够丰富,相信上面说到的DebugView的功能,也可以通过日志框架来实现。但是和DebugView比较起来,会相对复杂一些。所以上面说到的使用DebugView的理由是基于方便性的比较,DebugView有足够的方便性来让你选择使用他。

ConditionalAttribute详解与条件编译

说到调试,那么肯定有开发人员遇到这种情况,开发产品的时候,遇到一些问题,就在代码中加入了大量的调试输出信息,比如通过Console.WriteLine、MessageBox.Show或者通过Ilog.Log记录日志,甚至临时改变逻辑来验证逻辑的正确性等。经过这些调试信息的帮助,终于解决了产品的问题。但此时又遇到了新的问题,产品最终发布的时候,肯定是不能有这些调试信息的,可是已经加了这么多调试信息,难道要全部删除掉吗。这显然不是一个好办法,这么多代码,手一抖,很容易就删除了不相关的代码,造成不可预估的后果。

做过C/C++开发的,可以从各种跨平台的开源库中看到,一堆一堆的#if....#else....#endif,这就是条件编译,这也是C/C++跨平台所依赖的最基本的东西,在linux系统,编译这段代码,在Windows系统又编译那段代码,最终实现了代码级别的跨平台。

那么C#中有没有类似的功能呢,答案当然是有,而且有两种:

  • 通过给方法加上ConditionalAttribute特性
  • 使用#if..#else..#endif,来控制代码的编译

ConditionalAttribute特性

下面是ConditionalAttribute的构造函数:

public ConditionalAttribute(    string conditionString)

构造函数中的参数conditionString,是一个区分大小写的条件编译符号的字符串。

上面提到Debug.WriteLine时,说到这个功能只在Debug模式下才有用,Release下就不起作用了。

我们从MSDN中看一下Debug.WriteLine的说明:

[ConditionalAttribute("DEBUG")]public static void WriteLine(    string message)

由此也就明白了Debug.WriteLine只能在Debug模式下使用,而在Release模式下无效的原因了。

条件编译#if..#else..#endif

C/C++中有#if..#else..#endif,C#中也有这些,他们都被称为预处理器。通过预定义的条件编译符号,来控制编译时编译哪部分代码。如下:

public bool Haspermission(string userName){#if DEBUG    //Debug模式下,不需要做权限判断,直接返回true    return true;#else    //Release模式下,只有sa用户才有权限    if (!string.IsNullOrEmpty(userName) && userName == "sa")    {        return true;    }    else    {        return false;    }#endif}

预定义的Debug宏在什么地方

说到条件编译,是不是只有DEBUG 和 RELEASE两种情况呢,如果是这种情况的话,那也就是说DEBUG和RELEASE两种情况是定义好了的,俗话说就是“做死了”,这是作死的节奏啊。不作死就不会死,至少VS在这点上还没有作死。

让我们来一步步揭开DEBUG的面纱。

既然是条件编译,那么就应该和编译选项有关。我们知道C#项目,有一个属性页,可以设置很多编译的选项,如下:

VS项目生成属性页

从图中看到,条件编译是用的DEBUG常量,或者称为DEBUG条件编译符号,是在这个编译生成选项中定义的,如果去掉这个定义,那么编译后的HasPermission方法就会根据用户名进行权限检查,程序中通过Debug.WriteLine输出的调试信息也会输出到DebugView中,也就相当于Release模式下的效果。

其实DEBUG常量与Debug、Release模式并无太大的关系,唯一的关系就是,VS生成的项目中,Debug模式下,默认会选中“定义DEBUG常量”,而Release模式下,默认不会选中。也就是说,Debug模式,默认会定义DEBUG常量,而Release不会,如下图:

Debug_Release默认配置

既然DEBUG常量与Debug模式无本质上的关联,那么为什么说到Debug,就认为DEBUG呢。道理其实很简单,世上本无路,走的人多了,便成了路。本来这个DEBUG常量只是Debug模式下的默认预定义的常量,只是因为大家习惯了,并且对它的这种预定义还比较认可,时间久了,就自然而然认为DEBUG就代表Debug模式。

虽然我们可以通过去掉DEBUG常量,来使条件编译在Debug模式下达到Release模式的效果,但是建议最好不要这样做,因为这就像是大家普遍都认可的一个约定,如果你一反常态,不遵守这个约定,对于程序,编译没有问题,但是后续维护肯定会相当麻烦,所以还请大侠手下留情。

使用自定义的编译常量

DEBUG常量作为一种普遍的约定,最好不要打破。如果有除DEBUG外的条件编译需要,可以使用自定义的编译常量。

自定义编译常量有两种方法:

  • 通过编译生成属性页中的条件编译输入框,定义自己的编译常量。
  • 在代码中使用#define预处理,来定义编译常量
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表