Fog Creek Software
Discussion Board




VB6.0: How to handle an error in Form_Load

Given
====
MyForm.frm

MyForm.frm has
===========
Public Function OpenForm As VbMsgBoxResult
  On Error GoTo ErrorHandler

  Me.Show vbModal

  OpenForm = m_mbrResult

  Exit Function
ErrorHandler:
  ...Warn user of error and handle gracefully...
End Function

Private Sub Form_Load()
  On Error GoTo ErrorHandler

  code that could raise an error

  Exit Sub
ErrorHandler:
  ...DON'T WANT FORM TO DISPLAY TO USER...
Exit Sub

===

If code inside Form_Load raises an error, I don't wan't to display the form.

At first I tried "Me.Hide" inside of the ErrorHandler of Form_Load, but it doesn't "Hide" the form.

Next, I tried "Unload Me" inside of the ErrorHandler of Form_Load, and the form does not display (good).  BUT, this in turn raises its own error "Object was unloaded" (bad).

I'm not savvy enough to know anything besides Me.Hide or Unload Me.  And if Unload Me is raising an error if called inside Form_Load, I would think that I would want to find an alternative (besides putting On Error Resume Next right before the Unload Me call).

Advice appreciated (plus, this might actually be a semi-interesting question; as opposed to the "I KNOW how to make forums better than you bravado").

LearningVB
Tuesday, March 04, 2003

LearningVB,

I recommend you check out:

http://peach.ease.lsoft.com/scripts/wa.exe?A0=visbas-l

As a VB forum, its one of the best around.

For your specific question, the simplest solution is to move the code from the Load event to the Initialise event.

Unlike the Load event, errors in the Initialise event 'bubble up' as you are probably expecting. You can thus catch the error in the calling code.

Note that I also assume you have set your Startup Object to Sub Main rather than a specific form. I won't go into details of why that is a good idea here -  but you should do it...

Seeya

Matthew
Tuesday, March 04, 2003

Matthew,
I will check out the link you sent.  Thanks!

As for moving the code from the Load event to the Initialize event, I do not see what that buys me.

The code that could fail inside of Form_Load calls a utility function that opens a database and retrieves data from this database so that the form can display this info in a listView (and these utilty functions take the listView control on the form as input).

When Initialize is called, the form hasn't even been "Shown" yet (Initialize occurs as MyForm is instantiated; Me.Show happens in MyForm.OpenForm).  So I wouldn't be able to move the code to Initialize because I need the forms controls in memory.

Also, It's not that the Load event doesn't 'bubble up' errors to the calling function, it actually does do this (because Me.Show is called inside MyForm.ShowForm).  It's the fact that when I try to unload the form (by calling Unload Me) inside of Form_Load, this in turn creates another error.  If an error happens inside Form_Load, I don't want it to bubble up.  I want to close the form and not re-raise this error to the calling function (which in this case would be MyForm.OpenForm).  But when I call Unload Me, this causes a 2nd error (Object was unloaded) that would get raised to the calling function (unless I put On Error Resume Next).


I'm not entirely sure what this means:
"Note that I also assume you have set your Startup Object to Sub Main rather than a specific form. I won't go into details of why that is a good idea here -  but you should do it..."

This project is quite complex.  The form that I speak of (MyForm <- it's not actually called that) resides in a DLL.  MyForm.ShowForm is called from another DLL, and that DLL is a plug-in to a 3rd party application.  I guess I really haven't paid attention to the Sub Main at all.  That's happening inside some 3rd party app that is making calls into our DLL's.

LearningVB
Tuesday, March 04, 2003

LearningVB,

<quote>
When Initialize is called, the form hasn't even been "Shown" yet (Initialize occurs as MyForm is instantiated; Me.Show happens in MyForm.OpenForm).  So I wouldn't be able to move the code to Initialize because I need the forms controls in memory.
</quote>

You didn't mention that. ;-)

If that is the case, move the code into the OpenForm method. Instead of:

Me.Show

have:

Load Me
<code that used to be in the Load event>
Me.Show

all inside your OpenForm method.

<quote>
Also, It's not that the Load event doesn't 'bubble up' errors to the calling function, it actually does do this (because Me.Show is called inside MyForm.ShowForm).
</quote>

I won't waste everyone's time here by posting code and showing what I mean. But if you pop over to the VISBAS list (which I mentioned in the first post) we can discuss it there.

<quote>
This project is quite complex.  The form that I speak of (MyForm <- it's not actually called that) resides in a DLL. 
</quote>

You didn't mention that. ;-)

Seeya

Matthew
Tuesday, March 04, 2003

Matthew,

I just registered and I was browsing the archives (before posting).  But I'm going to post.  Hopefully we can pick up on the thoughts you ran by me here...there!

Thanks.

LearningVB
Wednesday, March 05, 2003

As an aside, Learning, don't forget that your error handler needs to only create user messages when it is appropriate to do so. All the Error_Handler: routines everywhere else should store the error somewhere for later display.

This is particularly important for things like ActiveX DLLs, controls and services which have no user interface.

Private Function blnSaveFile(ByVal vstrFileName As String) As Boolean

  On Error GoTo Error_Handler:
 
  If Not blnDoSomething() Then
      Err.Raise nnn    ' Where nnn is an application error you define
  End If
 
Error_Handler:
    ' Call library function which displays error message
    HandleError Err.Number, Err.Description, "MyForm", "blnSaveFile"

End Function

Private Function blnDoSomething() As Boolean

  On Error GoTo Error_Handler:
 

Error_Handler:
    ' Call library function which stores the error somewhere
    RaiseError Err.Number, Err.Description, "MyForm", "blnDoSomething"

End Function

Of course there are lots of ways of doing this (try, catch, finally etc).

Justin
Wednesday, March 05, 2003

Try, catch, finally in VB 6.0...?

John Topley
Wednesday, March 05, 2003

I don't know if this will be of any use to you but it is often better to generate an instance of a form rather than show the 'global' instance ie.

rather than
frmData.show

use
dim frm as frmData
set frm = new frmData
load frm 'form_load fires

frm.init 'init any data on the form
frm.show 'vbmodal ?

unload frm
set frm=nothing

this won't help with errors in form_load but gives the opportunity to keep form_load simple and moves the potential for errors into a sub which will bubble the errors up for you ...

Pete Robinson
Wednesday, March 05, 2003

*Sigh*. No John, its hand coded.

I just stuck the appropriate Try_, Catch_ Finally_ labels into the code with appropriate On Error statements [Flexes muscles - Yes! I am the FudgeMeister].

The main reason for doing this is that the site I'm currently working at uses this coding style for error handling to make porting to .Net easier. I'll take their word for it as my .Net is still immature.

Justin
Wednesday, March 05, 2003

Why don't you set the form's visible property to false and then after you have done everything you need to do in the Form_Load Subprocedure you display the form.

RRKSS
Wednesday, March 05, 2003

in fact, an error in form_load is just what you want. how else would you find out, if the form was correctly loaded or not?

alas, your form seems to be being implicitly loaded _before_ you call OpenForm. that's why the error doesn't "bubble up".

since your form is inside a dll allready, try making it private. code a proxy class to access those parts of the form you are interested in exporting. handle the form_load-error in the proxy class.

btw: read the "On Error Goto Hell" article in the msdn library. it will help you write better error-handling schemes.

Daren Thomas
Thursday, March 06, 2003

LearningVB,

Have you tried acquiring your resources before loading the form, then passing them to the form via property sets.

This way all of the contentious code is executed and tested ok before you load the form.

Public Function OpenForm As VbMsgBoxResult
  On Error GoTo ErrorHandler

  Me.Show vbModal

  rs = getRecordset()
  ... make sure rs is good before proceeding

  Dim frm as MyForm
  frm = new MyForm
  frm.setRecordset(rs)
  OpenForm = frm.getStatus()

  Exit Function
ErrorHandler:
  ...Warn user of error and handle gracefully...
End Function

Ged Byrne
Thursday, March 06, 2003

Hello all and thanks for the input.  I hate it when people start programming question threads and then don't come back with their eventual solution.  So I'm back (for those that might be interested in my solution --- *crickets chrirping* -- *can hear a pin drop*).

Just a little more background.  In an effort to make the code more robust (I didn't originally write this code -- I was working on the business logic dll before this UI dll),  I was going through all the UI code and adding error handling routines (we use HuntERR's stuff).  The project is winding down so I'm kind of performing-a-code-review/refactoring/unit-testing while the oringinal UI programmer is QA'ing the solution (we are a small company, so us engineers pretty much architect the solution, program it, then QA it).

Anyhow, I went with a variation of Matthews suggestion.

>>>>>
Load Me
[code that  used to be in Load Form]
Me.Show vbModal
>>>>>>

Pretty much all the forms in this UI dll have code inside of the Form_Load event, so I was hoping that I could figure out a way to abort the loading of the form there (no dice).  So I will have to re-factor all the forms in a way similar to this code (its probably not a good idea to put code that could fail inside Form_Load anyway!):

Public Function OpenForm() As VbMsgBoxResult
  On Error GoTo ErrorHandler

  LoadProjects

  Me.Show vbModal

  OpenForm = m_mbrResult

  Exit Function
ErrorHandler:
    ErrorIn "OpenForm"
End Function

Private Sub LoadProjects()
  On Error GoTo ErrorHandler
   
  m_mbrResult = vbCancel

  Load Me

  [boring code that could fail]
   
  m_bIsProjectsLoaded = True

  Exit Sub
ErrorHandler:
  ErrorIn "LoadProjects"
End Sub

Private Sub Form_Activate()
  On Error GoTo ErrorHandler
   
  If m_bIsProjectsLoaded = False Then
    Me.Hide
    'Assert that the OpenForm Sub is used to Show form
    Err.Raise ERR_INVALID_FORM_DISPLAY, , _
      GetErrorString(ERR_INVALID_FORM_DISPLAY,  _
      "OpenForm")
  End If
   
  Exit Sub
ErrorHandler:
  ErrorIn "Form_Activate", , EA_NORERAISE
  HandleError
End Sub

Private Sub Form_Unload(Cancel As Integer)
  m_bIsProjectsLoaded = False
End Sub

====

The solution works great.  It's simple and elegant.  Also, it didn't take too much time refactoring (just added the Form_Activate stuff and moved the code outside of Form_Load.  simple!)

LearningVB
Thursday, March 06, 2003

Darren said,

>>>
since your form is inside a dll allready, try making it private. code a proxy class to access those parts of the form you are interested in exporting. handle the form_load-error in the proxy class.
>>>

I'm sorry this caught my attention.  That seems like a level of complexity that isn't necessary.  Do you do something like this often?  Also, I'm not sure I understand the "parts of the form you are interested in exporting" comment.  Could you elaborate (cause I am curious)?  I really just want to open a form and have the user select something from a listview.

I will try and find and read the "On Error Goto Hell" article.  Thanks!  Although we use HuntERR's error libraries, so I think you need to use On Error GoTo ErrorHandler (all the HuntERR samples used this).  Of course I could be wrong (This project was the first time I've used Visual Basic -- I've used mostly C++ -- so my VB experience is now 4,5 months?)
btw: read the "On Error Goto Hell" article in the msdn library. it will help you write better error-handling schemes.

LearningVB
Thursday, March 06, 2003

If you want to stick with handling the error in the form load (not recommended) you can use the following hacky solution: 

Inside your Form_Load error handler call Unload Me.  That will raise another error (err.Number = 364 "trying to unload a loading form").  Then you can gracefully handle that error in the error handler section of the code calling form.show.

chris
Thursday, March 06, 2003

There is a natural flow to most applications.

You call up a form to load some invoice or whatever.

When the invoice form loads, perhaps the invoice number passed does not exist. Perhaps the record requested is locked, or UN available. There generally a good number test and conditions that is often need to be met when the form is loading. Any failure of any of those conditions should allow the form to bail out, and not actually load.

Ms-access for example has both a on-open event, and a on-load event. The on-open event of course has a cancel flag for just the above scenarios. If the on-open event is canceled, then the on-load event of the form thus does NOT run. The form is never loaded, and the user never sees the form. If the on-open event of the form is NOT canceled, then the on-load event of course runs. Thus, setting up field defaults and other general stuff goes in the on-load event. The on-open event is where you test for legal conditions and things to prevent the form load (such as locking etc etc etc).

A good form design obviously needs a cancel event that does cancel the form load based on many conditions. The designers of ms-access certainly thought so, and thus provided a on-open event with a cancel feature.

It makes a lot of sense of what the original poster here wants to do. It is obvious requirement, since products like ms-access have this feature built in.

A nice clean way of canceling the form load is needed. Doing so allows the code to cancel the form to stay in the form, and not the code that calls the form.

Albert D. Kallal
Edmonton, Alberta Canada
Kallal@msn.com

Albert D. Kallal
Saturday, March 08, 2003

Albert,

<quote>
A nice clean way of canceling the form load is needed. Doing so allows the code to cancel the form to stay in the form, and not the code that calls the form.
</quote>

Definitely. See my post near the top for two ways to do it. Or join the list I mention in the same post to discuss it further.

Seeya

Matthew
Tuesday, March 11, 2003

*  Recent Topics

*  Fog Creek Home