蔚姓名人肖像:VFP的错误,急求解决方法!

来源:百度文库 编辑:神马品牌网 时间:2024/05/05 11:57:12
我有一个用VFP编写的软件(录入系统),经常遇到打完表之后修改错误出现“函数参数的值、类型或数目无效”,单击取消就退出录入系统,单击忽略就出现“表没有设置排序索引”,请问用VFP6.0如何修改叫这个表能正常使用?

这个我不懂,:(这里有篇资料,希望会有参考作用。
vfp中的错误处理: http://www.vfp80.com/ReadNews.asp?NewsID=1170

与FoxPro 2.x相比,Visual FoxPro对错误的处理更为灵活但也更为复杂。当对象具有Error方法来处理局部错误时,怎样为你的应用程序提供公共的、全局错误处理服务?当发生错误时如何恢复?这里提供一种经证明是行之有效的方法来实现Visual FoxPro应用程序的错误处理 – 开始于单独的控件,结束于一个全局的错误处理对象。
错误处理基础
在错误处理中有许多困难的问题:设置错误处理器,检查错误的情况,提示用户发生了什么情况(并可能将其写入一个文件供以后分析),并解决问题(试着再次执行命令,继续出错的语句的下一条语句,退出系统等等)。
设置错误处理
与FoxPro 2.x中的设置错误处理相同,VFP中要设置全局错误处理仍然使用on error命令。举例如下:

on error do ERR_PROC with error(), sys(16), lineno()

这些参数告诉错误处理程序:错误号,发生错误的程序的名字,行号。你可以按你的需要传递任意参数到错误处理程序。
VFP以各对象的ERROR事件的方式,提供了对全局错误处理的能力。在VFP中每一个对象的事件模块都具有Error事件。当然,并非每一个对象都有Error方法。如果你不清楚这种差别,记住,事件是被用户或系统的某些动作触发的(击键,鼠标单击,或者一些Visual FoxPro认为是错误的东西),当事件发生时方法编码被执行。当一个消息传递到一个对象通知它执行方法时,方法代码也会被执行。在很多事件中,如鼠标单击,如果对象的方法中没有代码,事件被忽略或执行默认的动作。可是,当错误出现时,将会发生什么取决于一系列的事情。
当一个对象调用另一对象或一个非对象程序(如PRG文件)时出现错误、且该对象存在Error方法时,对象的Error方法将被调用。如果该对象没有Error方法将会发生什么情况呢?当我第一次使用VFP时,我假设该对象的父容器(如Form)的Error方法将被调用。然而,情况并非如此。实际情况是,如果任何在调用堆栈上的对象存在着Error方法,该方法将被调用;如果没有,on error例程(如果有的话)被调用。如果没有on error例程,Visual FoxPro执行它自己的错误处理(就是那个声名狼籍的取消/忽略对话框),然后程序崩溃。
检查错误情况
FoxPro 2.x中的一系列的函数和VFP都有助于检查错误发生的原因,包括error(),message(),lineno(),sys(16),和sys(2018)。VFP还提供一个aerror()函数,用于提供最近发生的错误的信息并将这些信息存入一个数组。虽然该数组中的一些信息也可从其它函数获得,对于某些类型的错误(如OLE,OBDC,和触发器),aerror()其它函数则不能得到错误信息(例如何种触发器造成了错误)。
提示用户发生了什么
坦率地告诉用户发生了错误,并允许用户决定如何处理错误。主要涉及到要告诉用户什么、并提供给用户什么样的选择。对要显示的错误信息进行适当修改,并以尽可能平静的方式措词,以防止惊慌失措的用户同时按下“Ctrl-Alt-Delete”。它也应提供如何处理错误的信息。例如,一些简单的错误如打印机未联接可以询问用户确信打印机正确地联接并装好了纸。这样用户可以在打印和取消打印之间作出选择。如果一个用户试图修改一个被别的用户锁定的记录,你可以告诉用户当前正有另一用户在修改该记录,并给出再试一次或取消修改的选择。
在VFP中可能出现的错误多达600种以上,你可能会令人心悸地想为每一个错误给出有意义的信息。幸运的是,如果你查看VFP的帮助中关于错误信息的主题,你会发现它们中的大多数仅归类于下列少数的种类:
· 决不会发生
· 如果程序员在写程序前未喝酒,就不会发生
· 一个以上的用户同时使用该系统时会发生
· 需要个别处理的错误
在第一种类别中,你的程序除了退出之外没有别的选择。第二种错误可以在测试中被发现,但若未发现,你的应用程序将报告并登录该错误,然后关闭。第三种类型的错误也可以在测试中被发现,但是仅出现一个简单的类似于“你现在不能操作”的错误信息。真正需要你处理的只是第四种类型的错误。这种类型错误的例子是字段或表验证规则失败,主关键字失败(如果你使用了系统指定的代理关键字,这种情况很少发生),触发器失败,和文件未找到。
在显示错误信息给用户之前,许多开发者喜欢登录错误到一个错误登录文件。这已被证明了是极有价值的,因为用户常常会含糊的向你报告错误发生的细节。错误登录文件可以是一个文本文件,但是我使用一个表中的字段来保存错误发生的日期和时间(可以是VFP中的DateTime字段),用户名,错误号和信息,行号和错误出现处的代码,一个备注型字段用于包含当前的内存变量。
解决错误
解决错误是复杂的事情。retry命令试着再次执行发生错误的命令,但是对于很多错误而言,由于用户自己处理错误的能力所限,该命令对用户处理错误没有多大帮助。return则继续执行发生错误的命令的后面一行程序,但由于造成错误的程序行并没有执行,所以因此而造成的二级错误信息可能会接踵而至,如变量没有建立(毕竟,如果可以忽略造成错误的程序且不会带来任何问题的话,它是做什么用的呢?)。cancel不是一种现实的选择,因为它终止应用程序,这样就没有机会来适当地进行退出前的系统清理。以被约束的方式关闭应用程序是一个正确的选项,因为很多错误是由于编程人员或操作系统造成的,在问题被解决前没有更多的选择。作为一个开发者,你可能也愿意选择取消应用程序并返回到命令窗口。这样做的代码将尽可能地清除环境。
另一个选项是使用return to master或return to 来退出发生错误的程序而返回到主控程序或指定的程序。要小心处理这种选项,因为这样会使系统进入一种混乱状态:表单和窗口仍然存在,表或游标任然是打开的,等等。在使用return to命令前,尽可能地清除这些东西是一个好的办法。
设计错误处理方案
全局Error 处理和对象的Error方法代表了一个事物的两个方面:
· 当Error方法是造成错误的对象的一部分时,全局错误处理器与发生错误的根源相距较远。这样,Error方法知道它自己所处的环境,那些错误可能会发生,如何解决错误。例如,CommonDialogs ActiveX控件(显示文件,打印,色彩和打印机的对话框)在用户选择取消时,将会造致错误。它会无声地让全局错误处理器处理错误,由于它只知道那是一个某种类型的OLE错误并不知道怎样去处理它。只能在CommonDialogs控件的Error方法中放入代码来处理用户选择取消的情况。
· 全局错误处理器在VFP的事件处理器之外,用FoxPro的老的“ON”事件方法进行调用(该方案仍被用于菜单和on key label命令)。这意味着,你不能使用象Thisform一样的对象语法,或发布象private datasessions这样的会使错误处理变得复杂的命令。
· 全局错误处理器可以有效的把错误处理服务放入同一个地方(如错误登录和显示)。但在你的应用程序中的每一个对象的Error方法中,它处理多种不可预期的错误时确是低效率的(如网络连接断线)。
让我们看看设计一个将两个世界进行最好的一体化的错误处理方案。我们希望以尽可能有效的方式处理错误,同时还提供个别对象处理它们自己特殊错误的能力。这里是我们使用的策略:
· 就象一个玩笑中所说的那样,一个对象比其父类更清楚它正在做什么,因此一个对象的Error方法将处理它自己所能处理的一切错误。并将它所不能处理的部分用dodefault()命令传递给它的父类。在该类层次中的各个子类都这样做。如果一个子类或一个类的实例不需要处理任何特殊的错误,则其Error方法中不会放置任何代码,以促成其父类代码自动执行。这样,SFDeepSubClassTextBox.Error调用SFSubClassTextBox.Error再调用SFTextBox.Error。
· 对象的最顶层父类的Error方法将处理任何它能处理的错误。它用This.Parent.Error传递那些经不能处理错误到它的容器。
· 由于容器类与控件一样工作(它们传递不可处理的错误到它们的父类,最顶层父类传递错误到它们的容器类),实际效果是我们上移一个类层次而进入到容器类的错误处理器。
· 最顶层父类的最外部容器的Error方法将处理它可处理的任何错误。它会传递那些它不能处理的错误到全局错误处理器。
以下是一个设计流程图:在链中的每一个对象处理错误或将它传递到链中的下一个对象。在这个多层方案中,当你从对象到全局错误处理器时,错误处理获取较少的细节和较多的一般信息,允许错误在各个不同等级进行处理。图 1 表明了这种策略。

图 1. 错误处理策略。
我们在应用程序启动时从SFErrorMgr实例化一个对象到一个全局变量oError。它的一个方法(ErrorHandler)既可以象上述一样被对象直接调用也可以用on error命令调用。错误处理对象有一个简单的接口(对编程而言),因此SFErrorMgr接受与Error方法相同的参数(错误号,方法名,行号)并返回一个说明用户选择的串给用户(或对象)以便处理错误。错误对象处于处理链的末端,因此它不知道调用它的地方的环境(可能经历了一系列的传递,在不同的数据工作期中等)。作为一个结果,它不能真正地作更多的“处理”(应该是:解决)。它的用途是显示一信息给用户,登录错误以备后用,并决定下一步的行动(在可靠的可预知条件下)或更多的,可靠的,将怎样做的询问。这样,错误处理对象将真正地用于处理你没有预料到的可预知的错误(一但它们出现,你会修改造成错误的对象,类,或过程)和不可预知错误(真正的bugs或未预料的环境条件)。
全局错误处理器自己会作出一个全局性的决定(调出VFP的Debugger或关闭应用程序)或允许对象引发错误来作出最后的决定。要允许后者,在错误处理链中的每一步返回一个决定码到上一级。为简便起见,我决定返回一个串来说明用户选择了那一决定:“重试”重新执行引起错误的命令,“继续”则从引发错误的程序的下一行继续运行程序,或“关闭表单”用于关闭当前起控制作用的表单。各对象依返回的信息执行相应的行动。由于容器类的Error方法可能已经被其成员对象或它自己的某个方法调用,容器类必须决定是向上传递返回信息还是由它自己处理。在稍后,我们将会看见这些代码。
该方案有一个问题:控件置于VFP的Page,Column,或其它没有Error方法代码的容器基类上时,由于它们调用一个空的方法,所以它们实质上没有错误捕捉!解决办法是在类层次中向上移动直到找到一个具有Error方法代码的父类。如果我们不能找到这样的父类,则显示一个一般性的错误信息(这并不可靠,由于所有的表单类都是以SFForm类为基类,而SFForm没有Error方法代码)。
要注意的事是完整的错误处理链必须是你的应用程序中最没有Bug的(经彻底调试的)部份,因为在错误状态下再次出现错误时,唯一可依靠的就是取消/忽略对话框了。幸运的是,由于你可以将错误处理代码放入你的大多数工作框架中,一但你使它们工作,它们不会受到更多的关注(虽然与众不同的环境条件仍会使错误处理器自身失败)。不要试着在错误处理对象中建立一个错误处理方法:当错误处理器自身出错时,它不会被调用。
Error方法
让我们注意错误处理策略的更多的细节。当一个对象发生错误时开始点是在该对象所具有的Error方法,因此我们从这里开始。
在我的应用程序的基类中,大多数类的Error方法代码,包含在SFCTRLS.VCX中,清单如下(包括ccMSG_RETRY定义在ERRORS.H中,包含于SFCTRLS.H中,每一个类的include文件)。我说“大多数类”,是因为顶级容器如form和toolbar的工作方式有一小点不同。这是我的少数的希望VFP支持多重继承的理由之一;在这和情况下,你需要象在VB中那样,将相同的代码放入所有类的Error方法中(采用复制-粘贴的方法)。

lparameters tnError, ;
tcMethod, ;
tnLine
local loParent, ;
lcMethod, ;
lcReturn, ;
lcError

* 向上移动一个层次直到我们找到一个具有Error 方法的父类

if type(‘Thisform‘) = ‘O‘
loParent = iif(pemstatus(Thisform, ‘FindErrorHandler‘, ;
5), Thisform.FindErrorHandler(This), .NULL.)
else
loParent = .NULL.
endif type(‘Thisform‘) = ‘O‘
lcMethod = This.Name + ‘.‘ + tcMethod
do case

* 我们有一个可以处理错误的父类。

case not isnull(loParent)
lcReturn = loParent.Error(tnError, lcMethod, tnLine)

* 我们有一个错误处理对象, 因此调用它的 ErrorHandler()方法。

case type(‘oError‘) = ‘O‘ and not isnull(oError)
lcReturn = oError.ErrorHandler(tnError, lcMethod, ;
tnLine)

* 一个全局错误处理器在起作用, 因此让我们传递错误信息给它。
* 以适当的值替换可靠的参数以传递到错误处理器(程序名, 错误号以及行号)。

case not empty(on(‘ERROR‘))
lcError = strtran(strtran(strtran(strtran(upper(on(‘ERROR‘)), ;
‘SYS(16)‘, lcMethod), ;
‘PROGRAM()‘, lcMethod), ;
‘ERROR()‘, ‘tnError‘), ;
‘LINENO()‘, ‘tnLine‘)

* 如果错误处理器是以 DO 命令调用, 进行宏扩展并假设返回值是"继续"。如果错误处理器是
* 被一个函数调用 (比如一个对象的方法), 调用它并获得返回值。

if left(lcError, 3) = ‘DO ‘
&lcError
lcReturn = ccMSG_CONTINUE
else
lcReturn = &lcError
endif left(lcError, 3) = ‘DO ‘

* 显示一个一般的对话框。

otherwise
messagebox(‘错误号‘ + ltrim(str(tnError)) + ;
‘对象 ‘ + This.Name + ;
‘方法 ‘+ tcMethod + ;
‘行号 ‘+ ltrim(str(tnLine)), 0, ;
_VFP.Caption)
endcase

* 确信返回信息是可接受的。否则, 假定为
* "继续"。

lcReturn = iif(type(‘lcReturn‘) <> ‘C‘ or ;
empty(lcReturn) or not lcReturn $ ccMSG_CONTINUE + ;
ccMSG_RETRY + ccMSG_CANCEL, ccMSG_CONTINUE, lcReturn)

* 处理返回值。

do case
case ‘.‘ $ tcMethod
return lcReturn
case lcReturn = ccMSG_RETRY
retry
case lcReturn = ccMSG_CANCEL
cancel
otherwise
return
endcase
以上代码检查表单控件是否具有FindErrorHandler方法,如果有,调用它来定位到控件的第一个父容器(不要被这个代码迷惑;你可以在供提供的源代码中查看它们)。这样做防止了这样一种情形:由于Page,Column,或其它容器类没有Error方法代码而致使错误停留在这些类中。如果一个可进行错误处理的父容器找到了,它的Error方法以相同的参数被调用,除非对象的名字被加到了tcMethod,因此我们的错误处理服务程序能够知道错误是发生在那一对象。如果父容器未找到但存在一个全局错误处理器(稍后我们再谈全局错误处理程序),它的ErrorHandler方法被调用。如果一个ON ERROR例程存在,我们将调用它(首先将我们知道的值调整为它可接受的参数),无论它是一个函数还是一个过程。如果我们没有东西可传递,就使用messagebox()来显示一条信息。
错误处理器返回的值将决定怎样处理错误。首先,如果该错误不是发生在本地,我们必须返回决定信息而不是自行处理。我们将用检查发生错误的方法的名字的句点号(.)来检查这一点;如果错误出现在一个类的方法中,VFP只传递方法名,但对象成员传递对象和方法名,这就提供了一种快速的方法用对象自己或对象名来区别错误原因。如果不是这种情况,则是我们的错误,因此我们将‘重试’,‘取消’,或‘返回’。
由于它们是“顶级”容器(我没有使用表单集Formsets),SFForm和SFToolbar类的Error方法与其它对象不同。该方法使用自定义的SetError方法来组织一些自定义属性形成关于错误的信息,并且用HandleError方法来处理错误。最后它处理返回值,它自己要么执行一个行动(比如关闭表单)要么返回到调用这个方法的对象。注意如果对象是DataEnvironment且它自己替换处理这些错误,它将不返回值。你可能想改变这一行为。同时也应注意使用return to;稍后我们将讨论这一问题的更多细节。

lparameters tnError, ;
tcMethod, ;
tnLine
local lcReturn,
lcReturnToOnCancel

* 使用 SetError() 和 HandleError() 来取得错误信息并处理它。

with This
.SetError(tnError, tcMethod, tnLine)
lcReturn = .HandleError()

* 如果用户选择了”取消”,指明到那里去。

do case
case type(‘oError‘) = ‘O‘ and not isnull(oError)
lcReturnToOnCancel = oError.cReturnToOnCancel
case type(‘.oError‘) = ‘O‘ and not ;
isnull(.oError)
lcReturnToOnCancel = .oError.cReturnToOnCancel
otherwise
lcReturnToOnCancel = ‘MASTER‘
endcase
endwith

* 处理返回值, 取决于错误是 "ours" 或来自一个成员.

do case
case lcReturn = ccMSG_CLOSEFORM
This.Release()
return to &lcReturnToOnCancel
case ‘.‘ $ tcMethod and ;
not ‘DATAENVIRONMENT‘ $ upper(tcMethod)
return lcReturn
case lcReturn = ccMSG_RETRY
retry
case lcReturn = ccMSG_CANCEL
return to &lcReturnToOnCancel
otherwise
return
endcase
我们不能仅着眼于SetError 方法(你可以在提供的源代码中自己检验这一点) 但以下是HandleError 方法代码:
local lnError, ;
lcMethod, ;
lnLine, ;
loError, ;
lcMessage, ;
lcReturn
with This
lnError = .aErrorInfo[.nLastError, cnAERR_NUMBER]
lcMethod = .Name + ‘.‘ + ;
.aErrorInfo[.nLastError, cnAERR_METHOD]
lnLine = .aErrorInfo[.nLastError, cnAERR_LINE]

* 如果存在着错误处理对象,取得一个引用到我们的错误处理对象。
* 它可能是表单号也可能是一个全局对象。

do case
case type(‘.oError‘) = ‘O‘ and not isnull(.oError)
loError = .oError
case type(‘oError‘) = ‘O‘ and not isnull(oError)
loError = oError
otherwise
loError = .NULL.
endcase
lcMessage = ccMSG_ERROR_NUM + ltrim(str(lnError)) + ;
ccCR + ccMSG_MESSAGE + ;
.aErrorInfo[.nLastError, cnAERR_MESSAGE] + ccCR + ;
iif(empty(.aErrorInfo[.nLastError, cnAERR_SOURCE]), ;
‘‘, ccMSG_CODE + ;
.aErrorInfo[.nLastError, cnAERR_SOURCE] + ccCR) + ;
iif(lnLine = 0, ‘‘, ccMSG_LINE_NUM + ;
ltrim(str(lnLine)) + ccCR) + ccMSG_METHOD + lcMethod
do case

* 存在着一个错误处理对象, 因此调用它的
* ErrorHandler() 方法。

case not isnull(loError)
lcReturn = loError.ErrorHandler(lnError, lcMethod, ;
lnLine)

* 一个全局错误处理程序正在起作用, 因此我们将错误传递给它。以适当的
* 值为错误处理程序替换可靠的参数(程序名, 错误号,和行号)。

case not empty(on(‘ERROR‘))
lcError = strtran(strtran(strtran(strtran(upper(on(‘ERROR‘)), ;
‘SYS(16)‘, lcMethod), ;
‘PROGRAM()‘, lcMethod), ;
‘ERROR()‘, ‘tnError‘), ;
‘LINENO()‘, ‘tnLine‘)

* If the error handler is called with DO, macro expand it
* and assume the return value is "CONTINUE"。If the error
* handler is called as a function (such as an object
* method), call it and grab the return value if there is
* one。

if left(lcError, 3) = ‘DO ‘
&lcError
lcReturn = ccMSG_CONTINUE
else
lcReturn = &lcError
endif left(lcError, 3) = ‘DO ‘

* We don‘t have an error handling object, so display a
* dialog box。

otherwise
lcReturn = iif(messagebox(lcMessage, ;
MB_ICONEXCLAMATION + MB_OKCANCEL, ;
_screen.Caption) = IDCANCEL, ccMSG_CANCEL, ;
ccMSG_CONTINUE)
endcase
endwith
lcReturn = iif(type(‘lcReturn‘) <> ‘C‘ or empty(lcReturn) or ;
not lcReturn $ ccMSG_CONTINUE + ccMSG_RETRY + ccMSG_CANCEL + ;
ccMSG_CLOSEFORM, ccMSG_CONTINUE, lcReturn)
return lcReturn

HandleError试着通过一个全局错误变量oError或表单的oError属性传递错误到全局错误处理对象。该方案允许你在可能的情况下,拥有一个定制版本的与表单关联的全局错误处理器。如果一个ON ERROR例程存在,我们调用它(首先将我们知道的值调整为它可接受的参数),无论它是一个函数还是一个过程。如果我们没有东西可传递,就使用messagebox()来显示一条信息。然后从错误处理器返回的值回传到Error方法。
全局错误处理
SFErrorMgr是一个派生于SFCustom的非可视类。它包含在SFMGRS.VCX中并使用SFERRORMGR.H包含文件中定义的一些常量。在应用程序启动时,它被实例进全局变量oError中(参见SYSMAIN.PRG)。我们不必查看该类的全部源代码,只需查看对我们的错误处理方案有关的方法代码。你自己可已查看任何其它的方法。
Init方法接受三个参数:当错误出现时显示给用户的对话框的标题(保存于cTitle属性中),一个指明Init是否将保存当前的on error处理器并改变它到它自己的ErrorHandler方法,以及类实例化为对象时的名称(这对于on error命令是必需的,因为我们不能使用类名词This)。
一般说来,Destroy方法会清除已被类改变了的东西;在这种情况下,它重置VFP的错误处理程序到对象被实例化以前。
ErrorHandler方法被错误处理链中最后的对象直接调用,并由于它也是on error错误处理器故也被间接调用。以下是该方法的代码:
lparameters tnError, ;
tcMethod, ;
tnLine
local lcCurrTalk, ;
lcChoice, ;
lcProgram
with This

* Ensure TALK is off。

if set(‘TALK‘) = ‘ON‘
set talk off
lcCurrTalk = ‘ON‘
else
lcCurrTalk = ‘OFF‘
endif set(‘TALK‘) = ‘ON‘

* Put the error into the aErrorInfo array and set the
* lErrorOccurred flag。

.GetErrorInfo(tcMethod, tnLine)

* If errors aren‘t being suppressed, display the error
* and get the user‘s choice of action。

lcChoice = ccMSG_CONTINUE
if not .lSuppressErrors

* Log the error if necessary。

if .lLogErrors
.LogError()
endif .lLogErrors

* Display the error and get the user‘s choice if desired。

if .lDisplayErrors
lcChoice = .DisplayError()
do case

* Cancel or Quit in development environment: remove any
* WAIT window, revert all open cursors and issue a CLEAR
* EVENTS (in the case of Quit), and then return to the
* top-level program。

case lcChoice = ccMSG_CANCEL or ;
(lcChoice = ccMSG_QUIT and version(2) <> 0)
wait clear
if lcChoice = ccMSG_QUIT
.lQuit = .T.
.RevertAllTables()
clear events
endif lcChoice = ccMSG_QUIT
lcProgram = .cReturnToOnCancel
return to &lcProgram

* Display the debugger (development environment): activate
* the Trace and Debug windows。

case lcChoice = ccMSG_DEBUG and version(2) <> 0
activate window debug
set step on

* Retry programmatic code: we must do the retry here,
* since nothing will receive the RETRY message (as is the
* case with an object)。

case lcChoice = ccMSG_RETRY
lcMethod = upper(tcMethod)
if at(‘.‘, lcMethod) = 0 or ;
inlist(right(lcMethod, 4), ‘.FXP‘, ‘.PRG‘, ;
‘.MPR‘, ‘.MPX‘)
if lcCurrTalk = ‘ON‘
set talk on
endif lcCurrTalk = ‘ON‘
retry
endif at(‘.‘, lcMethod) = 0 ...

* Quit: revert all open cursors, then quit。

case lcChoice = ccMSG_QUIT
.lQuit = .T.
.RevertAllTables()
on shutdown
quit
endcase
endif .lDisplayErrors
endif not .lSuppressErrors

* Restore TALK。

if lcCurrTalk = ‘ON‘
set talk on
endif lcCurrTalk = ‘ON‘
endwith
return lcChoice
当错误发生时,三个参数传递到ErrorHandler:错误号,发生错误的程序名,发生错误的行号。ErrorHandler使用GetErrorInfo方法来设置lErrorOccurred属性为.T.并设置关于错误的信息到aErrorInfo属性。如果lSuppressErrors属性值为.T.,不登录错误且不显示错误信息(这用于当你想捕捉错误,但又不想登录和显示错误信息给用户时)。否则,LogError方法被调用来登录错误并且DisplayError方法用于显示关于错误的信息并取得用户的选择。这些选择是:
· 调试:该选项仅当lShowDebug设置为.T.且我们是运行于VFP的开发版中时可选,调出跟踪和调试窗口。仅当开发者使用时,lShowDebug被设置为.T.(这一点可以在用户表中或Windows注册表中检查到)。
· 继续:返回到造成错误的程序的后面一行,并继续执行程序。
· 重试:重新执行出错的程序。这里使用了一小点技巧:如果ErrorHandler已经被明确地调用了(就是说,从一个对象的Error方法),则不能直接发布retry命令,由于那样做会把控制返回到调用它的方法,而不是造成错误的方法。在这种情况下,我们将仅返回一个信息“重试”。然而,如果错误出