Documente online.
Zona de administrare documente. Fisierele tale
Am uitat parola x Creaza cont nou
 HomeExploreaza
upload
Upload




Tip 11: Replace useful intrinsic objects with your own

software


Tip 11: Replace useful intrinsic objects with your own.

Our main ROOS contains a set of alternative standard object classes, TMSErr and TMSApp, for example. These are instantiated as Err and App at application start-up as part of our application template initialization. (All our Visual Basic applications are built on this template.) By creating objects like this, we can add methods, properties, and so on to what looks like one of Visual Basic's own objects.



For example, our error object has extra methods named Push and Pop. These, mostly for historical reasons, are really useful methods because it's not clear in Visual Basic when Err.Clear is actually applied to the Err object-that is, when the outstanding error, which you've been called to handle, is automatically cleared. This can easily result in the reporting of error 0. Watch out for this because you'll see it a lot!

Usually, an error is mistakenly cleared in this way when someone is handling an error and from within the error handler he or she calls some other routine that causes V 18318d314s isual Basic to execute an Err.Clear. All sorts of things can make Visual Basic execute an Err.Clear. The result in this case is that the error is lost! These kinds of mistakes are really hard to find. They're also really easy to put in-lines of code that cause this to happen, that is!

The Help file under Err Object used to include this Caution about losing the error context.

If you set up an error handler using On Error GoTo and that handler calls another procedure, the properties of the Err object may be reset to zero and zero-length strings. To retain values for later use, assign the values of Err properties to variables before calling another procedure, or before executing Resume, On Error, Exit Sub, Exit Function, or Exit Property statements.

Of course, if you do reset Err.Number (perhaps by using On Error GoTo in the called routine), when you return to the calling routine the error will be lost. The answer, of course, is to preserve, or push, the error context onto some kind of error stack. We do this with Err.Push. It's the first line of code in the error handler-always. (By the way, Visual Basic won't do an Err.Clear on the call to Err.Push but only on its return-guaranteed.) Here's an example of how this push and pop method of error handling looks in practice:

Private Sub Command1_Click()

On Error GoTo error_handler:

VBA.Err.Raise 42

Exit Sub

error_handler:

Err.Push
Call SomeFunc
Err.Pop
MsgBox Err.Description
Resume Next

End Sub

Here we're raising an error (42, as it happens) and handling it in our error handler just below. The message box reports the error correctly as being an Application Defined Error. If we were to comment out the Err.Push and Err.Pop routines and rerun the code, the error information would be lost and the message box would be empty (as Err.Number and Err.Description have been reset to some suitable "nothing"), assuming the call to SomeFunc completes successfully. In other words, when we come to show the message box, there's no outstanding error to report! (The call to Err.Push is the first statement in the error handler. This is easy to check for during a code review.)

Note

If we assume that Visual Basic itself raises exceptions by calling Err.Raise and that Err.Raise simply sets other properties of Err, such as Err.Number, our own Err.Number obviously won't be called to set VBA.Err properties (as it would if we simply had a line of code that read, say, Err.Number = 42). This is a pity because if it did call our Err.Number, we could detect (what with our Err.Number being called first before any other routines) that an error was being raised and automatically look after preserving the error context; that is, we could do an Err.Push automatically without having it appear in each error handler.

All sound good to you? Here's a sample implementation of a new Err object that contains Pop and Push methods:

In a class called ErrObject

Private e() As ErrObjectState

Private Type ErrObjectState

Description As String
HelpContext As Long
HelpFile As String
Number As Long

End Type

Public Property Get Description() As String

Description = VBA.Err.Description

End Property

Public Property Let Description(ByVal s As String)

VBA.Err.Description = s

End Property

Public Property Get HelpContext() As Long

HelpContext = VBA.Err.HelpContext

End Property

Public Property Let HelpContext(ByVal l As Long)

VBA.Err.HelpContext = l

End Property

Public Property Get HelpFile() As String

HelpFile = VBA.Err.HelpFile

End Property

Public Property Let HelpFile(ByVal s As String)

VBA.Err.HelpFile = s

End Property

Public Property Get Number() As Long

Number = VBA.Err.Number

End Property

Public Property Let Number(ByVal l As Long)

VBA.Err.Number = l

End Property

Public Property Get Source() As String

Source = VBA.Err.Source

End Property

Public Property Let Source(ByVal s As String)

VBA.Err.Source = s

End Property

Public Sub Clear()

VBA.Err.Clear

Description = VBA.Err.Description
HelpContext = VBA.Err.HelpContext
HelpFile = VBA.Err.HelpFile
Number = VBA.Err.Number

End Sub

Public Sub Push()

ReDim Preserve e(UBound(e) + 1) As ErrObjectState

With e(UBound(e))

.Description = Description
.HelpContext = HelpContext
.HelpFile = HelpFile
.Number = Number

End With

End Sub

Public Sub Pop()

With e(UBound(e))

Description = .Description
HelpContext = .HelpContext
HelpFile = .HelpFile
Number = .Number

End With

If UBound(e) Then
ReDim e(UBound(e) - 1) As ErrObjectState
Else
VBA.Err.Raise Number:=28 ' Out of stack space - underflow
End If

End Sub

Private Sub Class_Initialize()

ReDim e(0) As ErrObjectState

End Sub

Private Sub Class_Terminate()

Erase e()

End Sub

In Sub Main

Set Err = New ErrObject

In Global Module

Public Err As ErrObject

As you can see, our new Err object maintains a stack of a user-defined type (UDT) called ErrObjectState. An instance of this type basically holds information from the last error. In Sub Main we create our only ErrObject-note that it's called Err. This means that calls to methods like Err.Number will be directed to our object. In other words, Err refers to our instance of ErrObject and not the global instance VBA.Err. This means, of course, that we have to provide stubs for all the methods that are normally part of the global Err object: Number, Description, Source, and so on.

Note that we've left LastDLLError off the list. This is because when we pop the stack we'd need to write a value back into VBA.Err.LastDLLError and, unfortunately, this is a read-only property!

Another object we replace is the Debug object. We do this because we sometimes want to see what debug messages might be emitting from a built executable.

As you know, "normal" Debug.Print calls are thrown away by Visual Basic when your application is running as an executable; "special" Debug.Print calls, however, can be captured even when the application is running as an executable. Replacing this object is a little trickier than replacing the Err object because the Debug object name cannot be overloaded; that is,you have to call your new object something like Debugger. This new object can be designed to write to Visual Basic's Immediate window so that it becomes a complete replacement for the Debug object. Chapter 6 shows how you can write your own Assert method so that you can also replace the Debug object's Assert method.


Document Info


Accesari: 951
Apreciat: hand-up

Comenteaza documentul:

Nu esti inregistrat
Trebuie sa fii utilizator inregistrat pentru a putea comenta


Creaza cont nou

A fost util?

Daca documentul a fost util si crezi ca merita
sa adaugi un link catre el la tine in site


in pagina web a site-ului tau.




eCoduri.com - coduri postale, contabile, CAEN sau bancare

Politica de confidentialitate | Termenii si conditii de utilizare




Copyright © Contact (SCRIGROUP Int. 2024 )