Previous | Index | Next 

[PRB] NullReference exception in Form_Initialize

If the Form_Initialize event handler contains one or more statements that access a form’s property or one of the controls on the form, the VB.NET code throws a NullReference exception.

The reason for this exception is trivial: in VB6 the Initialize event fires when no form resource has been created yet. Migrated VB.NET forms replicate this behavior by firing the Form_Initialize_VB6 method before any control variable has been assigned a reference to a non-Nothing object, hence the exception.

This exception is the symptom of bad programming practice in VB6. In fact, the code in a Form_Initialize event should never access a control on the form, because this action triggers the load of the form itself. You can prove this detail by running the following piece of VB6 code:

        Private Sub Form_Initialize()
            Debug.Print "Enter Initialize"
            Me.Label1.Caption = "Test"
            Debug.Print "Exit Initialize"
        End Sub
		
        Private Sub Form_Load()
            Debug.Print "Enter Load"
            ' …
            Debug.Print "Exit Load"
        End Sub

This is the output in the Debug window:

        Enter Initialize
        Enter Load
        Exit Load
        Exit Initialize

In other words, the Load event fires in the middle of the Initialize event, which isn’t probably what the VB6 developer expected. In some cases, this unintended behavior is the causes of subtle bugs, for example when the code in the second half of the Form_Initialize method relies on values and properties that have been set inside the Form_Load method.

There is no easy way to migrate VB6 code that relies on this undocumented behavior. Under VB.NET a reference to a control doesn’t automatically fire the Load event. However, you can use the HasBeenLoaded variable (defined in VB6Form base class) to detect whether the form has been already loaded or not, and rearrange the code as follows:

        Protected Overrides Sub Form_Initialize_VB6()
            ' exit if this method is invoked by the VBLibrary, or proceed
            ' if the event is manually invoked from inside Form_Load.
            If Not MyBase.HasBeenLoaded Then Exit Sub
			
            '  here goes the code in the original Form_Initialize method
            ' …
        End Sub

        Private Sub Form_Load()
            ' the code in the original Form_Load method
            ' …
            ' manually invoke the code in Form_Initialize at the end of this method
            Call Form_Initialize_VB6()
       End  Sub

In some, very rare circumstances, you have to ideally split the code in Form_Initialize in two portions, the statements that don’t access any form property or control and the remaining statements. In this case you need an If…Then…Else block in Form_Initialize to exactly replicate VB6 behavior:

        Protected Overrides Sub Form_Initialize_VB6()
            If Not MyBase.HasBeenLoaded Then Exit Sub
                ' the statements that don’t access any property or control
                ' e.g. counter = 0
                ' …
            Else
                ' the first statement that accesses a property or a control
                ' and all the statements that follow
                Me.Label1.Caption  = "XXX"
                ' …
            End If
        End Sub
  
        Private Sub Form_Load()
            ' the code in the original Form_Load method
            ' …
            ' manually invoke the code in Form_Initialize at the end of this method
            Call Form_Initialize_VB6()
        End Sub

Notice that the undocumented VB6 behavior affects also the UserControl_Initialize event: if the code in this event references a constituent control on the UserControl’s surface, then the VB6 runtime anticipates the allocation of Windows resources to be allocated to the UserControl.

UserControls are dealt with slightly differently than forms in converted VB.NET programs. In a converted UserControl, the UserControl_Initialize method fires after all constituent controls have been created,therefore no runtime exception occurs even if the code in the method references a constituent control.

If it is essential that the UserControl_Initialize event fires immediately after creating the UserControl (and before any other event), you can invoke the special FireInitializeEvent method (defined in the VB6UserControl base class) from inside the constructor of your UserControl class, which you can find in the code-behind portion of that class (i.e. the *.Designer.vb file):

        <System.Diagnostics.DebuggerNonUserCode()> Public Sub New()
            MyBase.New()
            ' Create all controls and control arrays.
            InitializeComponents()
            ' fire the Initialize event now
            FireInitializeEvent()
        End Sub

 

Previous | Index | Next