Err object being reset and returning err.number = 0

T

TC

Hello,

I am experiencing an odd and intermittent problem with a COM addin that I've built. I have seen this problem once before in a Word global template.

Imagine code structured like the following pseudo-code:

Sub Something
On Error Goto ErrorHandler

' routine goes here

ErrorHandler:

If err.number <> 0 then
ErrorMessage err
end if
End Sub

Sub ErrorMessage(byval objErr as ErrObject)
msgbox "Error: " & objErr.Number & vbcr & "Description: " & objErr.Description
End Sub

If the above routine causes an error, the message box returned reveals "Error: 0", i.e. no error, but this is not possible because otherwise, the ErrorMessage routine would not be called.

I resolved this once before by passing in the err.number & err.description values as opposed to the err object itself. It was as if the OS was losing its thread to the err object.

Has anyone else experienced this?

Regards,

TC
 
J

Joseph Geretz

Hi TC,

I've done quite a bit of work with VB error handling. I've seen the symptom
you describe when an error handler calls a method which has it's own error
trap (On Error statement) . For this reason we do very little inside our own
error handlers aside from calling a few of our own methods which are
specifically designed for use inside an error handler. (Similar in concept
to what you are doing. I see nothing wrong with this approach per se.)

That being said, I see the commented On Error trap in your error handler
method, along with the error handler in that method which is ostensibly
never being called. I'm confused. Why comment out the error trap, and retain
a 'dead' error handler? Are you sure that the source code is absolutely
faithful to the binary in which you are seeing these symptoms?

Second, you seem to be missing an Exit Sub before your error handlers. I see
what you are trying to do by trapping for Err.Number = 0 in your error
handlers however typical code construction would prevent entry into the
handler to begin with unless an error has occurred. an Exit Sub just before
your handler will go a long way toward code clarity in this respect.

Another interesting thing you are doing is passing the Err object as a
parameter. I wonder what effect that is having. In our error handling
methods we pass the Err properties we are interested in as separate
parameters (e.g. Err.Number, Err.Description, etc.).

Hope this helps,

- Joe Geretz -

Hey Jezebel,

I don't think that you under stood my question.

For what it's worth, your error handler does not need to be that complicated
nor do you need to repeat all of that code in all of your routines.

See concrete example below:

' This routine removes all references, etc. and ends the current user
session
Public Sub QuitSession()

Const constrROUTINE_NAME As String = "QuitSession"

On Error GoTo ErrorHandler

' If all objects have been destroyed, exit the routine
If g_objThisAddIn Is Nothing Then
GoTo ErrorHandler
End If

If Not g_objThisAddIn.Soap Is Nothing Then
With g_objThisAddIn.Soap
If .IsConnected = True Then
.SoapMethod = EndSession
.SoapMethodName = g_constrEND_SESSION
'disconnect the session!
.CallSoapMethod
.CompleteSoapCall
End If
End With
End If

' Save settings
SaveToolbarSettings

' Destroy addin instance
Set g_objThisAddIn = Nothing

ErrorHandler:

Select Case Err.Number

Case 0 ' Do Nothing

' Unexpected error has occurred
Case Else
Call AppMessageBoxes(g_constrMODULE_AND_METHOD &
m_constrMODULE_NAME & g_constrDOUBLE_SPACE & constrROUTINE_NAME, _
vbExclamation + vbOKOnly +
vbMsgBoxSetForeground, g_constrERROR, , , Err)

End Select

End Sub
Below is a generic errorhandler and messagebox handler for an entire
application:
Public Sub AppMessageBoxes(ByVal strPrompt As String, _
ByVal lngButtons As Long, _
ByVal strTitle As String, _
Optional ByVal strHelpfile As String, _
Optional ByVal lngContext As Long, _
Optional ByVal objErr As Object, _
Optional ByRef intVal As Integer)

Const constrROUTINE_NAME As String = "AppMessageBoxes"

' There is no error trapping in this routine because
' error trapping will clear the error object that
' is passed to this error handling routine upon entrance
' On Error GoTo ErrorHandler

If Not objErr Is Nothing Then
Call MsgBox(g_constrERROR & objErr.Number & vbCr &
objErr.Description & vbCr & vbCr & strPrompt, _
lngButtons, strTitle)

' Log the error
If g_objThisAddIn.CurrentUser.PrintErrorLog = True Then
LogError strPrompt, objErr.Number, objErr.Description
End If
ElseIf intVal = 0 Then
intVal = MsgBox(strPrompt, lngButtons, strTitle)
Else
Call MsgBox(strPrompt, lngButtons, strTitle)
End If

ErrorHandler:

Select Case Err.Number

Case 0 ' Do Nothing

' Unexpected error has occurred
Case Else

End Select

' Clear any unhandled errors
If Err.Number <> 0 Then
Err.Clear
End If

End Sub
Lastly, my actual question was not answered which was:

Has anyone seen or does anyone know the cause of an err object's Number
property being reset when it is passed into a handling routine?

Technically, this should not happen because the object has not been released
from memory within the original calling routine.

Thanks & Regards,

Todd
 
T

TC

Hey Jezebel,

Actually, it does work with exception of a couple of machines.

Secondly, I wasn't interested in getting into a p _ _ _ sing match over code
constructs.

Lastly, you'll have significant repeated code is what I'm offering to you.
You should also note that you don't have any type of logging in your code
either (i.e. when it is implemented you can add that in about a 1000+ places
as well).

Hope that's less obtuse.

You still answer the original question.

Regards,

TC
 
T

Tony Proctor

Despite the suggestions people have made here TX, there's nothing
fundamentally wrong with this code. It works fine on my own machine here.

The 'Err' object is a global one, though, and so can be modified by code
elsewhere, say when local objects are destroyed, or methods called on other
objects. Maybe something is resetting the global 'Err' object before you can
report on it. I'd recommend setting 'Break on All errors' in the IDE and
tracing through the exact sequence of events in your example.

Tony Proctor

Hello,

I am experiencing an odd and intermittent problem with a COM addin that I've
built. I have seen this problem once before in a Word global template.

Imagine code structured like the following pseudo-code:

Sub Something
On Error Goto ErrorHandler

' routine goes here

ErrorHandler:

If err.number <> 0 then
ErrorMessage err
end if
End Sub

Sub ErrorMessage(byval objErr as ErrObject)
msgbox "Error: " & objErr.Number & vbcr & "Description: " &
objErr.Description
End Sub

If the above routine causes an error, the message box returned reveals
"Error: 0", i.e. no error, but this is not possible because otherwise, the
ErrorMessage routine would not be called.

I resolved this once before by passing in the err.number & err.description
values as opposed to the err object itself. It was as if the OS was losing
its thread to the err object.

Has anyone else experienced this?

Regards,

TC
 
R

Ralph

TC said:
Hello,

I am experiencing an odd and intermittent problem with a COM addin that I've built. I have seen this
problem once before in a Word global template.

Imagine code structured like the following pseudo-code:

Sub Something
On Error Goto ErrorHandler

' routine goes here

ErrorHandler:

If err.number <> 0 then
ErrorMessage err
end if
End Sub

Sub ErrorMessage(byval objErr as ErrObject)
msgbox "Error: " & objErr.Number & vbcr & "Description: " & objErr.Description
End Sub

If the above routine causes an error, the message box returned reveals
"Error: 0", i.e. no error, but this is not possible because otherwise, the
ErrorMessage routine would not be called.

I resolved this once before by passing in the err.number & err.description
values as opposed to the err object itself. It was as if the OS was losing its
thread to the err object.

Has anyone else experienced this?

Regards,
TC

Although Tony's and Tom's advice is perhaps the better way to go - ie, don't
use an err object in the first place. I ran into this problem and solved it
a bit differently.

I moved the ErrorHandler function into a .bas module and declared it like
this...

Public Sub HandleError(ByRef oErr As Object, Optional ByRef sMsg As Variant
= "")
Dim sTmp As String
' Get VB Error Object's information
If oErr.Number Then
' format
sTmp = " Description : " & oErr.Description & vbCrLf & _
" Note : " & sMsg & vbCrLf & _
" VB Number : " & oErr.Number & vbCrLf & _
"Source/Module : " & oErr.Source
Else
sTmp = "Error Object Reset: " & sMsg
Debug.Assert False
End If
If gbVerbose Then MsgBox sTmp, vbOKOnly + vbCritical, "Error!"
If gbLogError Then LogErrorToDisk sTmp
Exit Sub
End Sub

Why a .bas module worked better than providing a class or form private
routine, or why declaring it as 'Object' instead of 'ErrObject' made a
difference is open to speculation, but within my problem domain the 'Reset'
errors went away.

hth
-ralph
 
T

TC

Nonetheless, you didn't answer my question.

The only question I had was:

"What causes the behavior."

I know how to address the issue.

Secondly, the code was used successfully in a commercial, not corporate,
software solution but the error object was passed out to a consumer.

Do you have an answer to my actual question?
 
T

TC

Hey Joseph,

A couple of final points.

I forgot to mention that the ErrorHandler in that message handler is not
actually dead at all. If a successful error object is passed into the
routine, it clears it. Also, if the "On Error Goto ErrorHandler" is
uncommented, it clears the object being passed in which defeats its purpose.

Also very interesting, I have been able to reproduce the behavior in a demo
app and if I put a breakpoint just prior to displaying the message and view
the objErr object in the Locals window, it displays the appropriate
information about the object. However, if I extract the err.number &
err.description and dump them in variables from within the same routine, it
works.

It only seems to lose its thread when the error message is actually called.

Very odd.

Regards,

Todd
 
T

TC

Hey Ralph, Tom & Tony,

Thanks for the info.

It appears that explanation lay in what all three have suggested (i.e. there
can be only one error object therefore passing it as an errObject may cause
problems).

That's interesting that passing it in as a generic object doesn't clear the
error.

Thanks for the explanation.

Regards,

TC
 
J

Joseph Geretz

Hi TC,
If a successful error object is passed into the routine, it clears it.

Not necessary. If you've gotten to this routine it means an error was
trapped and this function is being called from an Error Handler. The default
VB behavior is to clear the error when exiting from an Error Handler. You
don't need to do this at all. If you want to handle an error and then
reraise the error, then you need an Err.Raise in your Error Handler. But you
don't need an Err.Clear to suppress the error. That happens automatically
when you exit the Error Handler.

- Joe Geretz -
 
M

Martin Seelhofer

Hey TC
That's interesting that passing it in as a generic object doesn't clear
the error.

Well, apart from passing it in as of type Object there's another - from my
point of view more significant - difference in Ralph's code and in yours.
While you pass the err object ByVal and therefore make a copy of the
global reference err, Ralph did that ByRef and therefore passes in the
global reference err itself (internally by passing a pointer to the
reference
err).

It seems to me that the implementation of VBA error handling and the
global err-object does not to like competition ;-)


Cheers,
Martin
 
M

Martin Seelhofer

Hi Tony
ByVal passes an object body by reference, and ByRef passes an object
reference by reference Martin. Hence, the same object is being passed in
both instances. See [...]

Thanx for trying to clear things up (and thanx for the link). However,
you might still agree with me that, to be picky, there's a difference
between duplicating the object reference (pointer, ByVal) and passing
in a pointer to the object reference (pointer to pointer, ByRef). Depending
on the internal object realization (reference counting etc.), there might
actually be quite a difference (from my point of view that is)...


Cheers,
Martin
 
T

Tony Proctor

I agree that something is odd is going on if the 'Object' declaration works,
although I don't profess to understand why. As to whether a *.bas/*.cls
makes a difference, or Object/ErrObject, or ByVal/ByRef, I can't say since I
still don't have a reproducible case.

However, since the Err object is global to each module (EXE/DLL/OCX - but
not the whole project or process), I tend to avoid this situation anyway.

In general, I make a copy of my Err object before attempting any error
cleanup or reporting. I then use my copy in the knowledge that any
subsequent error is free to use the prevailing Err object.

Tony Proctor

Martin Seelhofer said:
Hi Tony
ByVal passes an object body by reference, and ByRef passes an object
reference by reference Martin. Hence, the same object is being passed in
both instances. See [...]

Thanx for trying to clear things up (and thanx for the link). However,
you might still agree with me that, to be picky, there's a difference
between duplicating the object reference (pointer, ByVal) and passing
in a pointer to the object reference (pointer to pointer, ByRef). Depending
on the internal object realization (reference counting etc.), there might
actually be quite a difference (from my point of view that is)...


Cheers,
Martin
 
G

gmaxey

Jezebel,

There are many weak links in the chain of my understanding of VBA.
Error handling is one of the weakest. Do you have or could you provide
an example of the techinque that you are advocating here? I am not
smart enough to know whether it is excessive or not. I would just like
to be able to step through it and then hopefully understand just what
is going on.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top