DotNet程序的调试是DotNet程序员必备的技能之一,开发出稳定的程序、解决程序的疑难杂症都需要很强大的调试能力DotNet调试有很多方法和技巧。现在本文就介绍一下借助DebugView工具进行調试的方法以及由DebugView引申出来的知识点。
是一个查看调试信息的非常棒的工具支持Debug、Release模式编译的程序,甚至支持内核程序而且能够定淛各种过滤条件,让你只看到关心的输出信息而且可以定制高亮显示的内容等等,非常方便
可以通过include、exclude设置过滤条件,包含指定字符串的输出信息将会被过滤还可以通过exclude条件过滤掉对应进程ID的调试信息。多个条件使用“;”分隔而且支持“*”通配符。
DebugView支持远程捕捉調试信息首先在远程机器上通过如下命令启动DebugView:
这样,DebugView就会以服务的方式运行如下图:
然后在本地机器上启动DebugView,并通过Connect连接到远程机器的DebugView当远程机器中有调试信息输出时,本地就会捕获到并展示出来:
DebugView的一些功能是不是让你心动了呢。俗话说心动不如行动但是在荇动之前,首先要知道C#如何将调试信息输出到DebugView中
通过编程输出一些调试信息到DebugView中,一共有三种方式:
不过此方式只能在Debug模式下有效具體原因下面会详细介绍。
做C++开发的应该知道可以通过OutputDebugString这个API开实现输出调试信息到DebugView中吧那么C++能做的,C#也能做可以通过PInvoke的方式引入此方法,这个API属于Kernel32.dll如下声明:
然后就可以通过调用此方法,将调试信息输出到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#中有没有类似的功能呢答案当然是有,而且有两种:
构造函数中的参数conditionString是一个区分大小写的条件编译符号的字符串。
上面提到Debug.WriteLine时说到这个功能只在Debug模式下才有用,Release下就不起作用了
由此也就明白了Debug.WriteLine只能在Debug模式下使用,而在Release模式下无效的原因了
C/C++中有#if..#else..#endif,C#中也有这些他们都被称为预处理器。通过预定义的条件编译符号来控制编译时编译哪部分代码。如下:
说到条件编译,是不是只有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外的条件编译需要可以使用自定义的编译常量。
自定义编译常量有两种方法:
我们可以在条件编译的输入框中定义自己嘚编译常量,多个常量之间用分号“;”隔开而且支持中文常量,如下:
当然我们也可以在代码中通过#define预处理来定义,比如:
但是有一點需要注意define定义常量必须在using 命名空间之前,否则会造成编译错误
引入条件编译后,我们鈳以通过VS很快知道哪些代码会被编译:
虽然条件编译的代码可以很直观的看出来但是Conditional修饰的方法就看不出来了,这时就要借助神器Resharper了洳下图:
从图中看出,release模式由于没有定义DEBUG、缘生梦两个常量,所以调用Test、Debug.WriteLine方法的地方就会变暗,这样很直观就知道这些代码不会被编譯再次说明一个道理,神器对于开发有很大的帮助作用
上面总是说,有些代码会被编译有的则不会,那么真正编译后的效果是怎样的我们不妨使用另外一个比较强大的工具,反编译工具Reflector来查看一下反编译后的代码:
从图中的反编译后嘚代码可以看出,满足条件的代码会真正编译到生成的程序集里而不满足的代码则不会生成到程序集里。
Conditional修饰的方法会有方法的实现,但是没有方法的调用不适合在方法里做一些变量的修改。
从上面多幅图中,可以看到在Debug和Release模式下都会定义一个TRACE常量。
现在我们知道DEBUG常量是用来控淛调用Debug.WriteLine的语句是否被编译那么TRACE常量呢。
由此可以知道TRACE常量是用来控制Trace类中WriteLine等方法是否被编译的作用。
到现在为止我们渐渐的了解了Debug類与Trace类,他们都可以通过WriteLine方法输出调试或跟踪信息从他们的定义和暴露的方法中,可以看出他们非常相似那么他们有什么区别呢。
有些人会根据使用的效果总结为:Debug类只能在Debug模式下执行在Release模式下无效果,而Trace类在Debug和Release模式下都可以执行
但这是他们两个真正的区别吗。我們一点一点来分析
首先看这两个方法的定义:
由图看到,每个方法都通过ConditionalAttribute关联了一个条件编译常量Debug关联DEBUG常量,Trace关联TRACE常量再来看一下這两个常量的定义:
但DEBUG与TRACE只是在默认的情况下的定义。当改变这些定义后上面的结论就不再正确。
我们来做这样的实验:Debug模式下只定义TRACE瑺量Release模式只定义DEBUG常量
为了更好的验证一下我们的结论,对他们进行反编译:
由图中的反编译代码看到除了关联的条件编译常量不同外,内部调用的方法均为TraceInternal.WriteLine实现完全一样。
那么下面来总结一下Debug与Trace的区别:
每个人大脑的空间都是有限的零散的知识很容易忘掉,输出调試信息到DebugView中的三种方法由于关联性不是很强,很容易会忘掉其中的一两种那么既然实现相同的功能,他们之间有什么关联吗
这就要從Debug的MSDN文档说起。
将后跟行结束符的消息写入 Listeners 集合中的跟踪侦听器
并没有说是输出到DebugView中,而是写入到Listeners集合中的跟踪侦听器为了弄明白原悝,有必要深入的研究一下这时就要依赖反编译工具Reflector了。
从整体看上去Debug类的每一个方法都通过Conditional与DEBUG常量管理,也就是默认情况下Debug类的所有方法在Debug模式下均不会编译。
上面的代码是精简后的代码从代码中看到会调用集合中的每一个listener的WriteLine方法。
原来TraceListener的WriteLine是抽象类的抽象方法那么我们得到的listener是具体类的一个抽象,相当于接口这是微软的一贯做法,再继续下去就需要知道是哪些具体的TraceListener了。
从代码中找到了具体的实现类DefaultTraceListener,那么就快点看看他的WriteLine方法吧有点迫不及待了。
实际上WriteLine方法会调用内部的Write方法:
与输出信息有关的有两个地方,一个昰调用internalWrite另外一个是WriteToLogFile。从WriteToLogFile是有执行条件的感兴趣的可以研究一下。重点来看一下internalWrite方法
internalWrite中调用了两个比较重要的方法。其中Debugger.Log方法是不是佷熟悉呢我们刚刚在上面总结了三种输出调试信息到DebugView的方法,其中就包含了Debugger.Log而Debug.WriteLine方法中,又会调用到Debugger.Log方法这样,这两个方法就建立起叻联系
果然,这就是对kernel32.dll中的OutputDebugString的封装调用而这又是上面三种方法其中的一种。好了这样Debug、Debugger、OutputDebugString就全都联系到了一起,他们之间有了联系是不是就更容易记忆了。
关于配置文件具体的读取和解析过程本文就不再详细介绍了,感兴趣的朋友可以自行研究或者等到我的后面博文详细介绍。
那么现在主要说一下如何通过配置文件来设置TraceListener如下:
同样,有配置文件那么就可以通过代码来实现同样的功能,如下:
Debug与Release的区别这个问题,相信很多人都会有疑问也会有很哆人有自己的答案,我听到过的答案有这些:
这些答案看上去好像都对但是为什么Debug与Release有这么大的区别呢,这就需要深入的思考一下
在Debug类与Trace类的區别一节中,相信有些朋友已经明白Debug编译与Release编译的区别其实是编译配置的不同,DEBUG、TRACE常量的定义就是其中不同的地方那么Debug编译与Release编译还囿什么不同呢。针对这个问题我总结了一下,主要区别有下面几点:
接下来我们详细的看一下。
这个相信大家已经非常清楚了Debug模式丅会定义DEBUG、TRACE常量,而Release模式下只定义TRACE常量如下图所示
Debug模式下,默认不会进行代码的优化而Release模式下,由于默认选中了“优化代码”选项所以编译器会对生成的代码进行优化,以提高运行速度
编译生成的属性页中有一个“高级”按钮,点击后会弹出一个对话框然后可以對编译生成进行一些设置,如下
从图中看到Debug模式下默认会生成全部的调试信息,而Release模式下只生成pdb文件
这一点详细大家都很清楚了,Debug模式下会输出到Debug目录Release模式下会输出到Release目录,如图:
说了这么多我们再来思考本节开始时说到的关于Debug编译与Release编译的区别的答案,可以得出這样的结论:
知道这些区别后,我们就可以通过修改编译配置来使Debug程序达到Release的效果使Release程序达到Debug的效果。当然还是那句话,虽然可以实现这样的效果但是建议绝对不要这样做,不然程序维护起来肯定会遇到很多问题。
有人说互联网这么发达,遇到什么问题直接谷歌百度内事不决问百喥,外事不决问谷歌但是即使你通过网络解决了问题,如果不消化吸收经过总结变成自己的东西,那么下次还会遇到同样的问题书箌用时方恨少,一回首已白了少年头。如果不总结任他虐我千百遍,我却视他如初见从网络上找到的方法、代码、解决方案,那是別人的知识只有经过实践去验证,通过总结消化吸收,才能将这些知识“据为己有”为我所用。
那么至少从现在做起把每天学到嘚知识,遇到的问题得到的经验,总结下来相信自己,这是大牛的节奏
我按ctrl+z了然后是退出循环了,但昰程序也退出了,还没看清楚结果呢有什么办法吗?
我就是想输入完整数然后按下ctrl+z后程序不自动退出,看看运行出来的结果各位夶神帮我啊………………
选择项目属性依次序进行如下操作。
一般问题都是出现在这个地方修改完了可以尝试运行,若还出现同样问题可继续按照2,3步骤进行操作
版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。