Josip Zohil, Koper, Slovenija, January, 2015
When we bind two events using a Visual FoxPro (VFP) BINDEVENT command, the event that happens first is removed from a Call Stack. In cases we have an error in the second function (delegate), we don't know from where it was called. We would "enrich" the BINDEVENT function and extend its functionality to report the whole chain of function calls.
For example, we have a form with a button and its Click event. In it is an error.
Define Class mform As Form
Add Object btn As CommandButton
Local T, ex
T=z && error, z is not defined
Catch To ex
Local oForm As Form, ex1, ot, ox
Try && centralized error catch
* limited error call trace
*centralized error catch
*non centralized error catch
Catch To ex1
We run this program and click on a button. VFP reports an error in a usual way.
Uncomment this line: bindevent(ot,"timer",oForm.btn,"Click",1) and run the program again. It reports the same error as in the first example.
We don't know from where the btn.click event was raised: from a timer or from a mouse click? There is no evidence or an indication of the former presence or existence of an action that initiate the Click event.
Let us define a class BindTracer that will attach (bind) additional information to the eventual Exception object. This class has a method bindtrace. In it, we add some information to the eventual Exception object.
Define Class BindTracer As Custom
oObj=Null && first parameter of a bindevent
ofun="" && second parameter of a bindevent
producer=Null && object to be bound, third parameter of a bindevent
tobindfun="" && function to be bound, fourth parameter of a bindevent
fail="" && eventual function to report errors
Function Init(ot, ft, oprod, fun, ffail)
Bindevent(This.oObj,This.ofun,This,"bindtrace",1) && bind a first function to the function bindtrace
Function bindTrace() && ofun as parameter
Local ex As Exception, la As String, res, lof, ox
res=Evaluate(lof) && evaluate the delegate function (second)
Catch To ex
* like aevents function
la=this.oObj.Name+" "+this.ofun+" "+Program()+" "+ This.producer.Name+" "+ This.tobindfun
If Vartype(This.fail)$"NL" Or Len(Trim(This.fail))=0 && fail function is present ?
Evaluate(This.fail+"()") && evaluate the fail function
We evaluate the second function of a BINDEVENT command. In case it throws an exception, we create a new exception object and add it the information about the previous event handler (the event source). After that we throw a new exception in an alternative way: If the Bindtrace object has a fail function, we pass the exception to this function, otherwise we throw it.
Behind the scene, in the init procedure, we bind the timer's timer event to the bindtrace function. Implicitly, when a timer event happens, it activates the function bindtrace and the last evaluates the cmd.Click event handler (the delegate). We have wrapped the function click inside a function bindtrace.
The class bindtrace accepts four parameters, we usually pass to the bindevent function; arbitrary we can pass also the fifth parameter, a function to present the eventual exception information. It accepts " as a parameter" the exception object.
In the previous example comment the line * bindevent(ot,"timer",oForm.btn,"Click",1)
and uncomment the line
This command creates the object bindTrace. Its parameters are similar to a bindevent function.
We run the modified program from the EXAMPLE PROGRAM. It reports an error. In it are included also the information about the first function of a bindevent.
All the complexity of error management is hidden inside the object bindTrace. We use this object in a similar way as a BINDEVENT function.
The object bindTrace accepts also the fifth parameter, a string, a name of an error function, that is a member of an exception object. For example, the next function ffail print the error message on the screen.
?"Non centralized error catch:"
?.Message, .LineContents, .Details, .Lineno, .Procedure, Program()
We uncomment this line: obt=Createobject("bindTracer",ot,"timer",oForm.btn,"Click","ffail") of the EXAMPLE PROGRAM. This command has also a fifth parameter "ffail". We shall catch the eventual errors of a bindevent function using a ffail function. Errors will not be propagated to the central error catch construction. It will be printed on a screen.
Using the object bindTrace we can bind events in a similar way as using a bindevent function. The bindTrace object has the additional capabilities to manage eventual errors inside the delegate (second) function of a bindevent command. We can use a centralized or non centralized way to manage errors.
BINDEVENT behaves as an asynchronous function; It fires a first event (put in a call stack the first event handler) and after that remove it and put the delegate function in a call stack. When this second function executes it has not information about the previous function on a call stack; there is a "break" between the two. We must "help" a VFP debugger.
The delegate function (a second function, in our case a cmd.click) is a callback function; it reacts as a consequence of a first event (in our case a timer event). It is without a callback in case of eventual error. The function ffail is a callback for an eventual error. We have two execution path:
· a normal - CMD.CLICK,
· an error - ffail.