调试技巧

很多错误只有在运行的时候,才会发现。
虽然对于运行中的错误,系统会明确提示是哪一个对象的哪一个事件发生了错误,以及错误的原因。

例如通过下图的错误提示,我们可以知道错误代码位于订单表的DataColChanged事件:

虽然错误提示会告诉我们是那一个事件的代码引发了错误,但是要具体定位是哪一行代码有问题,往往是一件艰巨的工作,特别是代码很长,逻辑较为复杂的时候。

用MessageBox跟踪

我们可以在代码段中插入MessageBox.Show(x),将代码分割成多段,x是一个数字,例如:

代码段一
MessageBox.Show(1)
代码段二
MessageBox.Show(2)
代码段三
MessageBox.Show(3)
代码段四
MessageBox.Show(4)
代码段五
MessageBox.Show(5)
...

这样每执行一段代码,就会显示一个数字,在上面的例子中,如果显示2后发生了错误,那么说明问题发生在第三段代码,这样我们就可以重点分析该段代码了。

调试结束后,删除所有用于调试的MessageBox.Show语句即可。

在调试代码的时候,我们还可以利用MessageBox.Show来显示关键变量或数据,这对于分析错误原因,会有很大的帮助。
例如下面的代码用于加载雇员为当前登录用户的订单:

Dim fl As String
fl =
" 雇员 = " & User.Name
DataTables
("订单").LoadFilter = fl
DataTables(
"订单").Load()

执行上面的代码,会提示错误,显然最大的可能是我们合成的加载条件语法有误,为了验证,我们先屏蔽Load语句,暂时用MessageBox.Show显示一下合成的加载条件:

Dim fl As String
fl =
" 雇员 = " & User.Name
DataTables
("订单").LoadFilter = fl
MessageBox.Show(fl)
'DataTables("订单").Load()

当登录用户为EP01的时候,执行上述代码,可以发现合成的条件为:雇员 = EP01。
显然正确的条件应该为:雇员 = 'EP01',出错的原因在于表达式中的字符串没有用单引号括起来。
既然 知道了错误原因,我们就能写出正确代码:

Dim fl As String
fl =
" 雇员 = '" & User.Name & "'"
DataTables
("订单").LoadFilter = fl
DataTables("订单").Load()

在MessaageBox显示的对话框中,按"Ctrl+C"键,可以复制其显示的内容:

这在动态合成一些复杂的SQL语句或表达式时的时候很实用,例如我们可以从MessageBox中复制合成好的SQL语句,粘贴到SQL执行窗口进行执行调试,找出此SQL语句的问题后,再回头调整代码。
同样如果合成的是复杂的条件表达式,可以粘贴到对应表的表达式筛选窗口,根据此条件语句进行筛选测试,找出问题所在,然后回头调整代码。

可以在代码中多处嵌入MessaageBox.Show语句,在任何时候显示任何需要跟踪的信息,对代码执行结果进行全方位的跟踪。

用命令窗口跟踪

使用MessageBox跟踪简单直观,缺点是每次只能显示一个数据,无法查看之前显示的数据,不便于进行一些复杂的跟踪调试;而且MessageBox语句如果出现在一些特定的事件中,还可能会导致死循环,例如表事件PrepareEdit。

如果不想采用MessageBox跟踪错误代码位置,或者由于特殊原因,无法使用MessageBox,可以改用命令窗口来调试分析,例如:

Output.Clear() '清除命令窗口显示区现有内容
代码段一

Output.Show(1)
代码段二
Output.Show(2)
代码段三
Output.Show(3)
代码段四
Output.Show(4)
代码段五
Output.Show(5)
...

需要注意的是,如果要调试的是事件代码,必须先打开命令窗口,然后再调试。
命令窗口是非模式的,打开后可以继续进行任何操作,例如编辑数据、执行菜单命令等等,所以很适合调试代码。
需要的话,可以在调试过程中将命令窗口最小化,调试结束后再还原窗口,查看显示结果。
Output的Clear方法我们是首次接触,此方法用于清除命令窗口信息显示区的内容。

同样,在调试代码的时候,我们可以利用Output.Show来显示关键变量或数据,这对于分析错误原因,会有很大的帮助 ,例如:

Dim dt As DataTable
Dim
cmd As New SQLCommand
cmd
.CommandText = "SELECT * From {订单} Where 日期 = #" & Date.Today & "#"
Output
.show(cmd.CommandText) '显示合成的SQL语句
dt
= cmd.ExecuteReader
Output
.show(dt.DataRows.Count) '显示查询得到的行数

使用命令窗口跟踪的好处是不会打断程序的执行,而且历次显示的调试数据全部可查,但是执行前必须先打开命令窗口,也不够MessageBox来得简单直观。

代码是前后关联的

代码是前后关联的,有的时候某处代码执行时发生错误,并不是该处代码有问题,而是前面的代码造成的,例如:

Dim dr As DataRow
dr =
DataTables("产品").Find("编号 = '03'") '找出编号为03的产品
dr("单价") = 0.1

如果产品表中不存在编号为03的记录,那么上面的代码执行到第三行:

dr("单价") = 0.1

将出现错误提示,但是显然该行代码本身并没有问题,我们需要的是加上判断语句:

Dim dr As DataRow
dr =
DataTables("产品").Find("编号 = '03'") '找出编号为03的产品
If dr IsNot Nothing Then '如果找到的话
   
dr("单价") = 0.1
End If

初学者对于错误代码位置的判断往往过于直接,我曾经遇到一个用户,他贴出了一段类似下面的代码:

'新加入的一段代码
Dim
dr As DataRow
dr =
DataTables("产品").Find("编号 = '03'") '找出编号为03的产品
dr(
"单价") =
0.1

然后问我什么会出错,我告诉他应该是最后这三行有问题,最好判断一下是否存在符合条件的行,也就是dr是否为Nothing。
可该用户并不这么认为,因为整段代码之前运行无误,在加入前面这一段代码后,运行才出错的,所以错误肯定是在前面这段新加入的代码。
他调试了一下午,最终没有找出问题,只好直接将项目发给我,我如下修改代码后,运行一切正常:

'新加入的一段代码
Dim
dr As DataRow
dr =
DataTables("产品").Find("编号 = '03'") '找出编号为03的产品
If dr IsNot Nothing Then '如果找到的话
   
dr("单价") = 0.1
End If

此用户觉得很惊讶,明明是加入了前面这段代码后,运行才出错,为啥错误代码却在后面呢?
其实很好理解,因为前面新加入的代码可能修改了数据,导致产品表中不再存在编号为03的行,这样变量dr就等于Nothing,从而导致运行出错。

 


本页地址:http://www.foxtable.com/webhelp/topics/1485.htm