DotNet程序的调试,是DotNet程序员必备的技能之一,开发出稳定的程序、解决程序的疑难杂症都需要很强大的调试能力。DotNet调试有很多方法和技巧。现在本文就介绍一下借助DebugView工具进行调试的方法,以及由DebugView引申出来的知识点。
DebugView是一个查看调试信息的非常棒的工具,支持Debug、Release模式编译的程序,甚至支持内核程序,而且能够定制各种过滤条件,让你只看到关心的输出信息,而且可以定制高亮显示的内容等等,非常方便。
捕捉Release模式的Win32程序输出的调试信息,需要选中Capture Global Win32选项:
可以通过include、exclude设置过滤条件,包含指定字符串的输出信息将会被过滤。还可以通过exclude条件过滤掉对应进程ID的调试信息。多个条件使用“;”分隔,而且支持“*”通配符。
DebugView支持远程捕捉调试信息。首先在远程机器上通过如下命令启动DebugView:
DebugView.exe /a /t /g /s
这样,DebugView就会以服务的方式运行,如下图:
然后在本地机器上启动DebugView,并通过Connect连接到远程机器的DebugView,当远程机器中有调试信息输出时,本地就会捕获到,并展示出来:
DebugView的一些功能是不是让你心动了呢。俗话说心动不如行动,但是在行动之前,首先要知道C#如何将调试信息输出到DebugView中。
通过编程输出一些调试信息到DebugView中,一共有三种方式:
通过Debug.WriteLine可以将调试信息写入到DebugView中,如下:
Debug.WriteLine("这是调试信息");
效果如下:
不过此方式只能在Debug模式下有效。具体原因下面会详细介绍。
Debug.WriteLine已经很好用了,唯一的缺点就是在Release模式下无效。那么在Release模式下就可以使用Debugger.Log方法,示例如下:
Debugger.Log(0, null, "这是Debugger.Log输出的调试信息");
做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能做的事情,我用log4Net,NLog等日志框架也能做,为什么要用DebugView呢?
问的好,那么我就根据平时使用DebugView的经验来给你一个用DebugView的理由:
这些理由应该足以让你使用DebugView了吧。使用DebugView的理由肯定还不止这些,如果你有更好的理由,还请分享出来。
当然,DebugView与日志框架,每个都有每个的用途。通过DebugView的方式,只适合短暂的调试,而正式发布的网站或者软件,需要一套记录程序长期以来的运行状态的工具,那么就非日志框架莫属了。所以DebugView与日志框架,要在合适的地方,发挥他们最大的功效。
Log4Net等日志框架,功能足够强大,也足够丰富,相信上面说到的DebugView的功能,也可以通过日志框架来实现。但是和DebugView比较起来,会相对复杂一些。所以上面说到的使用DebugView的理由是基于方便性的比较,DebugView有足够的方便性来让你选择使用他。
说到调试,那么肯定有开发人员遇到这种情况,开发产品的时候,遇到一些问题,就在代码中加入了大量的调试输出信息,比如通过Console.WriteLine、MessageBox.Show或者通过Ilog.Log记录日志,甚至临时改变逻辑来验证逻辑的正确性等。经过这些调试信息的帮助,终于解决了产品的问题。但此时又遇到了新的问题,产品最终发布的时候,肯定是不能有这些调试信息的,可是已经加了这么多调试信息,难道要全部删除掉吗。这显然不是一个好办法,这么多代码,手一抖,很容易就删除了不相关的代码,造成不可预估的后果。
做过C/C++开发的,可以从各种跨平台的开源库中看到,一堆一堆的#if....#else....#endif,这就是条件编译,这也是C/C++跨平台所依赖的最基本的东西,在linux系统,编译这段代码,在Windows系统又编译那段代码,最终实现了代码级别的跨平台。
那么C#中有没有类似的功能呢,答案当然是有,而且有两种:
下面是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模式下无效的原因了。
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 和 RELEASE两种情况呢,如果是这种情况的话,那也就是说DEBUG和RELEASE两种情况是定义好了的,俗话说就是“做死了”,这是作死的节奏啊。不作死就不会死,至少VS在这点上还没有作死。
让我们来一步步揭开DEBUG的面纱。
既然是条件编译,那么就应该和编译选项有关。我们知道C#项目,有一个属性页,可以设置很多编译的选项,如下:
从图中看到,条件编译是用的DEBUG常量,或者称为DEBUG条件编译符号,是在这个编译生成选项中定义的,如果去掉这个定义,那么编译后的HasPermission方法就会根据用户名进行权限检查,程序中通过Debug.WriteLine输出的调试信息也会输出到DebugView中,也就相当于Release模式下的效果。
其实DEBUG常量与Debug、Release模式并无太大的关系,唯一的关系就是,VS生成的项目中,Debug模式下,默认会选中“定义DEBUG常量”,而Release模式下,默认不会选中。也就是说,Debug模式,默认会定义DEBUG常量,而Release不会,如下图:
既然DEBUG常量与Debug模式无本质上的关联,那么为什么说到Debug,就认为DEBUG呢。道理其实很简单,世上本无路,走的人多了,便成了路。本来这个DEBUG常量只是Debug模式下的默认预定义的常量,只是因为大家习惯了,并且对它的这种预定义还比较认可,时间久了,就自然而然认为DEBUG就代表Debug模式。
虽然我们可以通过去掉DEBUG常量,来使条件编译在Debug模式下达到Release模式的效果,但是建议最好不要这样做,因为这就像是大家普遍都认可的一个约定,如果你一反常态,不遵守这个约定,对于程序,编译没有问题,但是后续维护肯定会相当麻烦,所以还请大侠手下留情。
DEBUG常量作为一种普遍的约定,最好不要打破。如果有除DEBUG外的条件编译需要,可以使用自定义的编译常量。
自定义编译常量有两种方法:
新闻热点
疑难解答