Correctly handling error in instantiating class / stopping main program execution (1 Viewer)

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
A thought occurred to me today to leverage some prior learnings in order to determine if a Class had an error completing its Class_Initialize() Subroutine which does not support a return code. Learnings were as follows:

"How to raise error in class method such that calling code will be notified of err"
http://www.access-programmers.co.uk/forums/showthread.php?t=234958#post1198646

So I coded up an error within Class_Initialize() as follows:

Code:
Private Sub Class_Initialize()

  'Clear the class attributes
  Me.Clear

End Sub

'This is a generic API that clears all of the field attributes
Public Sub Clear()
On Error GoTo Err_Clear

  [B][COLOR=Red]Dim intA As Integer[/COLOR][/B]

  Me.id = 0
  Me.authid = 0
  Me.authusername = vbNullString
  Me.logtimestamp = vbNullString
  Me.title = vbNullString
  Me.budget = 0
  Me.rptactiveflg = False
  Me.rpttitle = vbNullString

  strFETempTableName = "tmptblqry_projects"

  [B][COLOR=Red]intA = 1 + "A"[/COLOR][/B]

Exit_Clear:
  Exit Sub

Err_Clear:
  Call errorhandler_MsgBox("Class: clsObjProjectsTbl, Subroutine: Clear()")
  'Disable further error handling, so that the code which is using this object will handle the error
  On Error GoTo 0
  'Raise the error to the caller program
  Err.Raise -1[B][COLOR=Blue], "Class: clsObjProjectsTbl, Subroutine: Clear()"[/COLOR][/B]
  Resume Exit_Clear

End Sub
I have marked the bug I planted.

So I tested it, indeed program execution was halted as the parent's error handler correctly received error notification that the class object could not be created. As follows:

Code:
Date: 20121109 Time: 10:58:16 UserID: c_mlueck
AppErrorMsg: Class: clsObjProjectsTbl, Subroutine: Clear()
Error Source: Fandango_FE
Error Number: 13
Error Description: Type mismatch
MessageText: The data is invalid.


Date: 20121109 Time: 10:58:32 UserID: c_mlueck
AppErrorMsg: Form: Form_main, Subroutine: Form_Load()
Error Source: [null]
Error Number: 0
Error Description: [null]
MessageText: The operation completed successfully.
Now, in the Form_main \ Form_Load() error report, I do not see the error information which was escalated to it. It is as if Form_Load() has no idea why it is diverting execution into its own error handler. I would have expected the blue text to show up somewhere, no?

Did I code my Err.Raise incorrectly then?
 

MarkK

bit cruncher
Local time
Today, 04:46
Joined
Mar 17, 2004
Messages
8,179
What's the code that calls this demo?

It seems to me that you don't need both the call to "errorhandler_MsgBox" AND the err.raise, but that's also not what your question is.

I would just do ...
Code:
Public Sub Clear()
On Error GoTo Err_Clear

  Dim intA As Integer
  intA = 1 + "A"
  Exit Sub

Err_Clear:
  Err.Raise err, err.source & " in clsObjProjectsTbl.Clear()"
  Resume Exit_Clear  [COLOR="Green"]'This never executes.  An error exits the sub immediately[/COLOR]
End Sub
You can just re-raise the actual error, tagging on the additional source information.

I would only want this call to occur ...
Code:
Call errorhandler_MsgBox("Class: clsObjProjectsTbl, Subroutine: Clear()")
... for the last error handler in the stack, so I would never call the messagebox and re-raise the error in the same handler.
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
What's the code that calls this demo?
An instance of the global object being created by the object being defined in the top of a module. That is how I instantiate global class objects in VBA applications. Object creation is forced by the Main() code encountering a first reference to said object.

It seems to me that you don't need both the call to "errorhandler_MsgBox" AND the err.raise, but that's also not what your question is.

Fair enough, I clarified my test case code here.

Code:
Private Sub Class_Initialize()
On Error GoTo Err_Class_Initialize

  Dim intA As Integer

  'Clear the class attributes
  Me.Clear

  intA = 1 + "A"

Exit_Class_Initialize:
  Exit Sub

Err_Class_Initialize:
  [B][COLOR=Blue]Call errorhandler_MsgBox("Class: clsObjProjectsTbl, Subroutine: Class_Initialize()")[/COLOR][/B]
  'Disable further error handling, so that the code which is using this object will handle the error
  On Error GoTo 0
  'Raise the error to the caller program
  [B][COLOR=Red]Err.Raise Err, "Class: clsObjProjectsTbl, Subroutine: Class_Initialize(), Error: Failed to instantiate class instance"[/COLOR][/B]
  Resume Exit_Class_Initialize

End Sub
Thank you for pointing out that I may simply pass Err along when I Err.Raise.

Actually I wanted to trap an instantiation error, not merely a call to the Clear() class method. Clearly since Class_Initialize() only calls Me.Clear there never was much to fail in there. However, should something fail in Clear() then I wanted to bubble that error condition up to the code trying to create the object and alert that code to the fact that "something went wrong while instantiating this class object, best not expect the object to be 100% intact." As was, nothing would alert the calling code that something had gone wrong, so that is what I am working towards rolling out throughout the application.

Still, the lack of receiving information about the error which occurred persists in Form: Form_main, Subroutine: Form_Load()'s error handler.

Code:
Date: 20121109 Time: 12:28:55 UserID: c_mlueck
AppErrorMsg: Class: clsObjProjectsTbl, Subroutine: Class_Initialize()
Error Source: Fandango_FE
Error Number: 13
Error Description: Type mismatch
MessageText: The data is invalid.


Date: 20121109 Time: 12:29:11 UserID: c_mlueck
AppErrorMsg: Form: Form_main, Subroutine: Form_Load()
Error Source: [null]
Error Number: 0
Error Description: [null]
MessageText: The operation completed successfully.
I was still expecting details from the Err.Raise to appear in Main()'s handling of the error... the second error log. There is no details about why the error handler was evoked. Thus, I am hesitant to disable the additional MsgBox at this time as if I did so users would really have no clue why the application just died!

Do you have suggestions where the Error Number / Error Description end up going missing?

Aaahhh..... the blue LOC ends up clearing the error!!

So I simplified the Err_Class_Initialize as follows:

Code:
Err_Class_Initialize:
  Err.Raise Err, "Class: clsObjProjectsTbl, Subroutine: Class_Initialize(), Error: Failed to instantiate class instance"
  Resume Exit_Class_Initialize
and now I receive from Main()

Code:
Date: 20121109 Time: 12:57:16 UserID: c_mlueck
AppErrorMsg: Form: Form_main, Subroutine: Form_Load()
Error Source: Fandango_FE
Error Number: 440
Error Description: Automation error
MessageText: Error not found.
I have a Global watch on Err.... upon Err.Raise the original error is lost from the ErrObject. That is when Automation Error replaces the error.

Suggestions?


Thank you!!
 

MarkK

bit cruncher
Local time
Today, 04:46
Joined
Mar 17, 2004
Messages
8,179
Here's the pattern I use much more commonly, which avoids the use of Class_Initialize() for the reasons you observe--that Class_Initialize() is a sort of isolated routine and--that almost always what I might do there is more effectively done like this ...

Code:
[COLOR="Green"]'********************************************************************
'*                                                        Nov 9 2012
'*       This is a class called Class1>
'*
'********************************************************************[/COLOR]
Property Get Greetings() As String
    Greetings = "Hello World"
End Property

Function Load() As Class1
On Error GoTo handler
    Debug.Print 1 / 0
    Set Load = Me
    Exit Function

handler:
    err.Raise err, err.Source & " in Class1.Load()"

End Function
... so rather than use Class_Initialize, I roll my own constructor called Load().
Consuming code might look like ...
Code:
Private Sub Test183647()
On Error GoTo handler
    Dim tmp As New Class1
    Debug.Print tmp.Load.Greetings
    Exit Sub
    
handler:
    Debug.Print err & vbCrLf & err.Source & vbCrLf & err.Description
    
End Sub
... so that does all of what you want, I think, and avoids all of what you want to avoid.

I use Class_Terminate way more commonly to do clean up, but Class_Initalize() is amost useless, and in .NET you can see that Microsoft figured that out with the Object object implicitly declaring a Public Sub New(), and every object necessarily inherits from object. And in .NET you can return a new instance of a class in one line ...
Code:
Dim tmp As New cMyClass(SomeInitData)  [COLOR="Green"]'leverages Public Sub New()[/COLOR]
... whereas in VBA, with your own load function, you need two lines ...
Code:
Dim tmp As New cMyClass
tmp.Load SomeInitData   [COLOR="Green"]'leverages Public Function Load[/COLOR]
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
Wowsers!!! I commented out my Sub Class_Initialize(), added your suggested:

Code:
Public Function Load() As clsObjProjectsTbl
On Error GoTo Err_Load

  'Clear the class attributes
  Me.Clear

  'Return an instance of this class
  Set Load = Me

Exit_Load:
  Exit Function

Err_Load:
  Err.Raise Err.Number, "Class: clsObjProjectsTbl, Function: Load()", "Error: Failed to instantiate class instance"
  Resume Exit_Load

End Function
BANG!!! Access crashes so hard a "Please report this problem to MS" dialog comes up.

I had never heard of Load being a known/core class method.
 

MarkK

bit cruncher
Local time
Today, 04:46
Joined
Mar 17, 2004
Messages
8,179
There's nothing special about "Load." You could call it ...
Code:
Function GetMyNewInstance(SomeID As Long) As cYourClass
End Function
It's weird Access would crash over that. Maybe you need to /decompile??? Compact & Repair???
 

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
A few things…

1.
On Error GoTo 0
Clears the Error object

2.
A discussion about handling errors in Class modules should contain information about the status of ‘Break in Class Module’ or ‘Break on Unhandled Errors’ and I can’t see it mentioned anywhere in the thread.

3.
Can you reduce the requirement to its simplest test?


Chris.
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
A few things…
1.
On Error GoTo 0
Clears the Error object

I saw that during my long post where I reduced the error handler to simply raising the error back to the parent caller.

I had a global watch on the Err object, and the INSTANT the class raised the error back to the caller, the Err object lost the correct details and they were replaced with "Automation Error"

2.
A discussion about handling errors in Class modules should contain information about the status of ‘Break in Class Module’ or ‘Break on Unhandled Errors’ and I can’t see it mentioned anywhere in the thread.

Are you concerned about the text of the custom error message? Or something else?

What I am point out is I am unable to send custom error text back.

3.
Can you reduce the requirement to its simplest test?

What do you think I should simplify?

1) Cause an error in class initialization
2) Raise the error back to the caller
3) Contents of Err object get destroyed the moment the error is raised to the caller

Never mind the fact that I am testing in my entire 68,000+ LOC application... that is the man behind the curtain. :cool:
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
There's nothing special about "Load."

I just had never seen documented that is a supported / automatic class method. Always I have seen Class_Initialize()

It's weird Access would crash over that. Maybe you need to /decompile??? Compact & Repair???

I ended up stitching up the application for the weekend leaving it having two MsgBox's pop, the second one will be "Automation Error" and then the program will exit. That is at least much closer to what I was hoping for.

And I reverted back to no error handling within Class_Initialize() and the Clear() method is what raises the error back to the caller. That at least works as desired, just the "Automation Error" is more cryptic than desired. I would really like my custom error text to survive the journey being raised.
 

MarkK

bit cruncher
Local time
Today, 04:46
Joined
Mar 17, 2004
Messages
8,179
Elaboration on Chris' Number 2).
If you go to a CodeWindow->Menu->Tools->Options->GeneralTab->ErrorTrappingSection you'll notice there are three possible settings, Break on All Errors, Break in Class Module, and Break on Unhandled Errors.

By default VBA "Break on Unhandled Errors," but in this state the debugger never breaks inside a class module, but only ever halts execution in the code that consumes the members of a class. When working with classes, change that setting to "Break in Class Module" and your debugger behaviour will be far more useful and intuitive.

I just had never seen documented that [Load()] is a supported / automatic class method. Always I have seen Class_Initialize()
Load() is not a supported / automatic class method. Load() is strictly a convention of mine that I use as a "constructor" name in VBA to return an instance of a class. My more general point is that I believe Class_Initialize() is not a very useful method when dealing with classes because it does not return a value. Add to that your observation that Class_Initialize() does not return rich error information, and those are enough reasons for me to not use it almost ever.

H0p3 th4t h3lps,

Here's an examples of how I do use Class_Initialize(), in a library class that encapsulates the tricky bits of making right-click popup menus ...
Code:
[COLOR="Green"]'********************************************************************
'*                                                        Sep 5 2006
'*         Class Init and Terminate Ops>
'*
'********************************************************************
[/COLOR]Private Sub Class_Initialize()
[COLOR="Green"]'   On Initialization, this routine attempts to create a popup command bar named "LibPopup"
'   - If the object already exists it is deleted and recreated.[/COLOR]
On Error GoTo handler
    Set m_popup = Application.CommandBars.Add("LibPopup", msoBarPopup, , True)
    Exit Sub
    
handler:
    If err.Number = 5 Then  [COLOR="Green"]'Invalid Procedure call or argument -- Popup already exists[/COLOR]
        Application.CommandBars("LibPopup").Delete
        Resume
    Else
        err.Raise err, err.source & " in cPopup.Class_Initialize()"
    End If

End Sub

Private Sub Class_Terminate()
[COLOR="Green"]'   On terminate, this class deletes the Command Bar that it created.[/COLOR]
    Application.CommandBars("LibPopup").Delete
End Sub
... but by design it only creates an ancillary object that must be preset for the class to function properly when the initialization data is actually received.

Interesting thread,
Cheers,
 

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
Let’s see if named arguments help…

Behind the Form:-
Code:
Option Explicit
Option Compare Text


Private Const conBreakOnUnhandledErrors As Long = 2
Private Const conBreakInClassModule     As Long = 1

Private XYZ As clsTest


Private Sub Form_Load()

    On Error GoTo ErrorHandler
    
    Application.SetOption "Error Trapping", conBreakOnUnhandledErrors
    
    Set XYZ = New clsTest
    
ExitProcedure:
    Exit Sub

ErrorHandler:
    MsgBox "Error:  " & Err.Number & vbNewLine & _
           "Description:  " & Err.Description & vbNewLine & _
           "Source:  " & Err.Source
           
    Resume ExitProcedure
    
End Sub


In the Class Module:-
Code:
Option Explicit
Option Compare Text


Private Sub Class_Initialize()
    
    Me.Clear

End Sub


Public Sub Clear()
    Dim intA As Integer
    
    On Error GoTo ErrorHandler

    intA = 1 / 0

ExitProcedure:
    Exit Sub

ErrorHandler:
    MsgBox "Error:  " & Err.Number & "  " & Err.Description & "  in Class"
    
    Err.Raise Number:=-1, _
              Description:=Err.Description, _
              Source:="Class: clsObjProjectsTbl, Subroutine: Clear()"

    Resume ExitProcedure

End Sub

Chris.
 

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
Note:

Application.SetOption "Error Trapping", conBreakOnUnhandledErrors

is only required in an MDB file. Since an MDE file has no source code an MDE file can not ‘Break in Class Module’ or ‘Break on All Errors’ and so an MDE file will ignore any instruction to do either.

Therefore, an MDE file must use ‘Break on Unhandled Errors’.

If an MDE file does not have an error handler to handle the error then a generic Access error should be displayed or Access might shut down.

Web folklore, which is often quoted, states; “All procedures must have error handling”.
Due to the ambiguity of the quoted statement, that is rubbish. The ambiguity arrises by the person repeating the folklore not stating where the error handling must be done. Like a lot of other stuff on the www, it gets repeated by people who read but do not understand.

Let’s try to remove some of that ambiguity…

Should every procedure have internal error handling? No.
Should every procedure have error handling? Yes, but that does not mean internal to that procedure.

Case in point…
An MDB file with ‘Break on Unhandled Errors’ selected or in an MDE file by default:-

External Object Class Module named clsTest:-
Code:
Option Explicit
Option Compare Text


Private Sub Class_Initialize()
    Dim X As Integer
    
    X = 1 / 0

End Sub


Called from:-
Code:
Option Explicit
Option Compare Text

Private XYZ As clsTest


Private Sub Form_Load()

    On Error GoTo ErrorHandler
    
    Set XYZ = New clsTest
    
ExitProcedure:
    Exit Sub

ErrorHandler:
    MsgBox "Error:  " & Err.Number & vbNewLine & _
           "Description:  " & Err.Description & vbNewLine & _
           "Source:  " & Err.Source
           
    Resume ExitProcedure
    
End Sub

Does the above actually mean “All procedures must have error handling”? I think not.

In fact Private Sub Class_Initialize() does not have error handling but instead passes the error back to the caller. And, in an MDE file that happens by default.

That then goes most of the way to explaining why a lot of external Object Class Modules we see do not have error handling within them. They are designed to pass back errors to the caller and that happens, in an MDE file, by default. And since an MDE is the preferred run time file format it must be tested that way.

So why do the options to ‘Break in Class Module’ or ‘Break on All Errors’ exist at all?
My estimation is that they are only there for testing MDB files during a debug session. They both produce a ‘run to crash’ but each does it to a varying degree.

‘Break in Class Module’ will take us to the fault line even in an external Object Class Module but it will comply with an On Error Resume Next line of code. ‘Break on All Errors’ will not comply with any error handling at all not even On Error Resume Next.

So to recap…
‘Break in Class Module’ and ‘Break on All Errors’ exist only in an MDB file for debug to go into source code. An MDE file will default to ‘Break on Unhandled Errors’ because it has no source code to break into.

Therefore, an MDB file must also be tested under ‘Break on Unhandled Errors’ before it is converted to an MDE file. Once it is converted to an MDE file there is no source code to modify for testing.

Chris.
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
Greetings All,

Thank you for the most useful feedback and input on VBA error handling.

I just stumbled upon a break through methodically testing various conditions and capturing screen shots.

I tried the suggestion...

Elaboration on Chris' Number 2).
If you go to a CodeWindow->Menu->Tools->Options->GeneralTab->ErrorTrappingSection you'll notice there are three possible settings, Break on All Errors, Break in Class Module, and Break on Unhandled Errors.

By default VBA "Break on Unhandled Errors," but in this state the debugger never breaks inside a class module, but only ever halts execution in the code that consumes the members of a class. When working with classes, change that setting to "Break in Class Module" and your debugger behaviour will be far more useful and intuitive.


And that has a nasty behavior that it refuses to raise the error to the caller... keeps looping popping the same box / returning to the Err.Raise LOC over and over and not using my VBA error handler code. Please see: VBAFailedInstantiateClassInstanceErrorVBAEditor.jpg

So I put VBA setting back to the default so that VBA would handle the error.

I changed my code as follows, and made a discovery...

Code:
Private Sub Class_Initialize()
On Error GoTo Err_Class_Initialize

  'Clear the class attributes
  If Not Me.Clear Then
    GoTo Err_Class_Initialize
  End If

Exit_Class_Initialize:
  Exit Sub

Err_Class_Initialize:
[B][COLOR=Blue]  'Disable further error handling. Since the code was already handling an error, if we raised the error without first
  'turning it off, that would result in an "Automation Error" aka Double Trap
  On Error GoTo 0
[/COLOR][/B]  'Raise the error to the caller program
  Err.Raise vbObjectError + 1, "Class: clsObjProjectsTbl, Subroutine: Class_Initialize()", "Error: Failed to instantiate class instance."
  Resume Exit_Class_Initialize

End Sub

'This is a generic API that clears all of the field attributes
Public Function Clear() As Boolean
On Error GoTo Err_Clear

  Dim intA As Integer

  Me.id = 0
  Me.authid = 0
  Me.authusername = vbNullString
  Me.logtimestamp = vbNullString
  Me.title = vbNullString
  Me.budget = 0
  Me.rptactiveflg = False
  Me.rpttitle = vbNullString

  strFETempTableName = "tmptblqry_projects"

  intA = 1 + "A"

  'Good return code
  Clear = True

Exit_Clear:
  Exit Function

Err_Clear:
  Call errorhandler_MsgBox("Class: clsObjProjectsTbl, Subroutine: Clear()")
  Clear = False
  Resume Exit_Clear

End Function
I still receive two MsgBox'es, one with the fault details, the other stating the error instantiating the class instance, and then Main closes the entire application. No more cryptic "Automation Error"!!! :D

I think this a workable scenario. 1) I get the specific error details as best as VBA can provide, and 2) Now I am correctly handling class instantiation errors.

Code:
Date: 20121112 Time: 09:13:50 UserID: c_mlueck
AppErrorMsg: Class: clsObjProjectsTbl, Subroutine: Clear()
Error Source: Fandango_FE
Error Number: 13
Error Description: Type mismatch
MessageText: The data is invalid.


Date: 20121112 Time: 09:14:04 UserID: c_mlueck
AppErrorMsg: Form: Form_main, Subroutine: Form_Load()
Error Source: Class: clsObjProjectsTbl, Subroutine: Class_Initialize()
Error Number: -2147221503
Error Description: Error: Failed to instantiate class instance.
MessageText: Error not found.
Any quick suggested enhancements? I am now poised to take a tour d'code and propagate the new design.

Thank you all!
 

Attachments

  • VBAFailedInstantiateClassInstanceErrorVBAEditor.jpg
    VBAFailedInstantiateClassInstanceErrorVBAEditor.jpg
    93.5 KB · Views: 188

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
>>Any quick suggested enhancements?<<

Yes, clean up your code. :D

This functions the same as you posted without the need to call Clear as a function and pass back a Boolean:-

Code:
Private Sub Class_Initialize()
   
   Me.Clear

End Sub


Public Sub Clear()

    On Error GoTo Err_Clear

    Dim intA As Integer

    intA = 1 + "A"

Exit_Clear:
  Exit Sub

Err_Clear:
  MsgBox "Class: clsObjProjectsTbl, Subroutine: Clear()"
  
  Err.Raise vbObjectError + 1, "Class: clsObjProjectsTbl, Subroutine: Class_Initialize()", "Error: Failed to instantiate class instance."

  Resume Exit_Clear

End Sub

With ‘Break on Unhandled Errors’ selected in an MDB file or in an MDE file with any one of the 3 break methods selected it should work. What’s happening is that, with ‘Break on Unhandled Errors’ selected, a Class module will not break on a Raised error in Sub Clear() but will pass the raised error back to Class_Initialize(). Since Class_Initialize() has no error handling the error gets passed back to the caller which in this case is the Form Load() event.

You should not have to re-invent the wheel with this stuff; it’s all been done before, at least since Access 97.

First test it before propagating the code throughout the entire project then convert it to an MDE file and test it again.
(Use your errorhandler_MsgBox instead of a simple MsgBox if you like.)

The primary method of debugging is to simplify, reduce, and derive the essence of the problem not adding more stuff to the problem.

Divide and conquer.


Chris.
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
>>Any quick suggested enhancements?<<

Yes, clean up your code. :D
ssssiiiiggghhhh.....

This functions the same as you posted without the need to call Clear as a function and pass back a Boolean:

Production use of my classes uses the Clear() method to reset the class to all defaults. So code itself calling Clear() would have its own error handling present, creating an instance of the object / Clear() getting in trouble, THAT I need to bomb out of the application for.

Also, my errorhandler_MsgBox() Subroutine collects up various debug information, logs it to disk and pops a MsgBox. I have shown examples of the log file capture in this thread already.

The primary method of debugging is to simplify, reduce, and derive the essence of the problem not adding more stuff to the problem.

I hardly see how calling a shared code / well tested error handler is "adding more stuff to the problem".

I have tried allowing the Err.Raise to simply send the error back to the procedural Form code. With a Global watch on the Err object, the SECOND the Err.Raise executes, it turns it into a meaningless "Automation Error"... the details are thus destroyed.

Even if I do no errorhandler_MsgBox() and simply Err.Raise...

Even if I removed error handling all together in Class_Initialize() and allow the error from the Class to be handled by the Form.

The only way I found THUS FAR to capture the exact error is to handle it in the class, then to facilitate shutdown of the application upon failure to create a class instance, there I use Err.Raise after having turned off error handling in the class.

If Error handling is enabled in the class code, Err.Raise always results in "Automation Error" being captured by the Form error handler. So I do not do that anywhere... all code processes its own errors.

This tracking down of failed object instantiation is the only thing I see needing addressing. The rest of error handling works well and is well tested.
 

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
>>I hardly see how calling a shared code / well tested error handler is "adding more stuff to the problem".<<

Michael, this is the thing as I see it…

You are asking for help on a subject but you are posting so much extranious stuff that people can not test it. We should not have to build things like your errorhandler_MsgBox() subroutine just in order to test what you post.

And this sort of stuff:-
Me.id = 0
Me.authid = 0
Me.authusername = vbNullString
Me.logtimestamp = vbNullString
Me.title = vbNullString
Me.budget = 0
Me.rptactiveflg = False
Me.rpttitle = vbNullString

strFETempTableName = "tmptblqry_projects"

It is absolutly meaningless to the error handling problem so why leave it there? It may mean something to you but it is meaningless junk to the people trying to help you. So by posting all that meaningless stuff you are creating more work for the people trying to help.

Look at the difference between the code you post and the code posted by lagbolt and me. I can state that another way. Look at the difference between the code posted by the person wanting help and the code posted by the people trying to help.

Can you not see that the code posted by lagbolt and me is reduced to the essence? That doesn’t just happen by chance, it takes a lot of work. If you too learn to reduce your code to the essence of the problem then you may be better able to solve your own problems.

And while I’m at it I tell you something else. I’m not good enough to simple write code to site without first testing it. I need to create a test case and test it, at least to some degree. The code you see from me is copy pasted after testing.

The code I put in post #14 took some time to test and yet you didn’t even bother to say if it behaves the same as the code you posted in post #13. You went on to say this and that about your percieved ultimate needs and how good your other code is but you did not even say if you tested the code in #14. Did you?

So this is the main problem as I see it…
You are posting a technical question in a technical forum, so far so good. But things like "Never mind the fact that I am testing in my entire 68,000+ LOC application... that is the man behind the curtain. :cool: " from post #8 inevitably seems to creep into your posts.

That sort of thing indicates to me that there is a fair amount of extraneous social communication added to your posts. Social communication is fine in the Watercooler or on Twitter or on Facebook but not in a technical forum. It indicates to me a lack of focus on the technical question. That same lack of focus also seems to spill over into your code.

To me, it is that lack of focus on the job at hand which is actually causing you the problems. Focus and reduce the problem to the essence. Failure to do that only prolongs the problem and creates more work for others trying to help.

That is what I mean by “Yes, clean up your code” in post #14.

In a technical forum, try to focus on the essence of the problem.

Chris.
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
Chris, you are asking me to come up with mockup isolated test cases rather than focus on one tiny detail of class architecture which makes a production application tick. I observed odd behavior in the application at a specific spot and desired to get that resolved once and for all. I tried to communicate what I observed, and yes never mind the entire rest of the program. Others do not post their entire application to ask one specific question.

The architecture I developed for this client double class wraps the UI to the SQL BE DB.

One layer is the DB classes. (Example of one such class all along this thread.) It has attributes for all of the table columns, a consistently named string holds the FE temp table name. The DB class is able to perform DB work such as RefreshFETempTable, LocateByID, Insert, and Update. These all wrap Stored Procedures.

The other standard class is what I call Validation classes. The Forms create an instance of them when they open. They understand the wiring between DB classes and the Form UI controls. Methods include Populate / Validate / Insert / Update.

All class methods leverage the same standardized error handling. The error handler resides off in a common module. Serves Forms and Reports and modules and and and... as well.

I would think it rare to have such an elaborate architecture and standardized coding practices in a MS Access application. I strive for perfection in "Computing That Just Plain Works!" rather than getting by with the least effort exerted. YMMV. The client specified Access and SQL Server. I endeavored to do my best within the canvas I was told to use.

In a technical forum, try to focus on the essence of the problem.

Which is exactly why I endeavored to isolate out exactly what I saw the problem as, hiding the rest of the application.

Several posts in, I discovered that once the error was raised from the Class to the calling Form, the error details were getting lost. I needed a way to prevent that and to actually receive the error details I was attempting to send. I eventually found that... #13 post resulted, which I posted as "I am happy with the way this code behaves, anyone see a problem doing XYZ?" and showed what XYZ I meant, and noted the one lingering poison pill... "Will pop two separate MsgBox'es."

From what I can see, that is about as good as it is going to get with Access / VBA.
 

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
Michael.

Post #17 is exactly what I mean, apart from one important thing:-
>> Chris, you are asking me to come up with mockup isolated test cases…<<

That isolated mock-up is the way to start testing complex theories.
I had to, and probably lagbolt had to as well, do exactly that. I (we) had to reduce it to essence of the problem.

Apart from the quote I made above, the rest of post #17 is non-technical social waffle.
It serves no part in a technical discussion simple because both parties can not share the same provable evidence.

Hence the necessity to reduce the problem… not to expand it. Reduce the problem to its essence. Test that reduction on both sides. Add small steps one at a time. Test it again on both sides.

And, at this point, you still have not said if you have even tested the code in post #14.



Chris.
 

mdlueck

Sr. Application Developer
Local time
Today, 07:46
Joined
Jun 23, 2011
Messages
2,631
And, at this point, you still have not said if you have even tested the code in post #14.

I tested Err.Raise on the Clear method (and about 10 different scenarios clawing at the keyboard searching for a way to not end up with "Automation Error") prior to you suggesting that I test it and replied to your suggestion:

Production use of my classes uses the Clear() method to reset the class to all defaults. So code itself calling Clear() would have its own error handling present, creating an instance of the object / Clear() getting in trouble, THAT I need to bomb out of the application for.

Thus, I let Clear() take care of its own errors and only Class_Initialize() raises errors.

For now the standard I am proposing is that all methods take care of their own errors. Only in two cases do I Err.Raise

1) In code that I do not intend to have to check the return code of each call to a Class... such as my TxtFile class... I want to be able to put a lot of calls to that class in succession without having to code error checking on a per-call basis, just Err.Raise and let the Err handler of that code escalate the Err. No matter was it the first or N'th call to the TxtFile class, the general error handler is good enough protection... I do not need customized error handling beyond what is already provided. (Which that was the thread I referenced in my OP)

2) Now in Class_Initialize() it appears to be helpful to capture failed initialization of class instances.
 

ChrisO

Registered User.
Local time
Today, 21:46
Joined
Apr 30, 2003
Messages
3,202
Why can’t you answer this question directly; did you test the code I posted in post #14?

Since the code was posted in post #14 the answer to that question should be in a post after post #14.

Without all the waffle; did you test that code, yes or no?

Chris.
 

Users who are viewing this thread

Top Bottom