Fixing RunTime Errors

After an application is upgraded, the most difficult errors to detect are those that can only be identified at run time. A high percentage of potential run-time errors are detected by the upgrade wizard. However, there may be problems that will be detected only after the upgraded application runs and is thoroughly tested. The original application test cases and design documentation is particularly important to identify such errors.

The remainder of this section presents some common upgrade issues that are detected by the upgrade wizard and that produce run-time errors. These issues will need to be manually corrected.

Changes to User-Defined Types

User-defined types can be defined in Visual Basic 6.0 using the Type keyword. All the members included in a Type block are automatically initialized by the Visual Basic 6.0 runtime. The following code example illustrates creating a user-defined type.

Const LF_FACESIZE = 32 Private Type MyLogFont fHeight As Integer fWidth As Integer FaceName(LF_FACESIZE) As Byte End Type

Sub TestFont() Dim a As Byte Dim f As MyLogFont a = f.FaceName(1) End Sub

Notice how the FaceName field is declared and initialized with a fixed size.

The equivalent construct in Visual Basic .NET is the Structure. Unlike the Type construct, the members of a Structure need to be explicitly initialized using the Initialize method. When the upgrade wizard is applied to the preceding code, it translates the type to a structure and includes the Initialize method for you. The following code shows the result produced by the upgrade wizard.

Const LF_FACESIZE As Short = 32 Private Structure MyLogFont Dim fHeight As Short Dim fWidth As Short

<VBFixedArray(LF_FACESIZE)> Dim FaceName() As Byte

' UPGRADE_TODO: "Initialize" must be called to initialize instances of this ' structure. Public Sub Initialize()

ReDim FaceName(LF_FACESIZE) End Sub End Structure

Sub TestFont()

Dim a As Byte

' UPGRADE_WARNING: Arrays in structure f may need to be ' initialized before they can be used. Dim f As MyLogFont a = f.FaceName(1) End Sub

This upgraded code will compile without problems. However, when the application is executed and TestFont is invoked, a run-time error will occur because the Initialize method has not been invoked and the array members of the structure have not been initialized. To correct this problem, you can apply the following modification to TestFont to invoke the Initialize method.

Sub TestFont()

Dim a As Byte Dim f As MyLogFont f.Initialize() a = f.FaceName(1) End Sub

After the Initialize method is invoked, the array members of the type are initialized and the run-time error no longer occurs.

Changes to Null and IsNull

Null and IsNull have different behaviors in Visual Basic .NET than in Visual Basic 6.0. The upgrade wizard detects the usage of Null and IsNull. Null is upgraded to the closest value available in Visual Basic .NET, which is System.DBNull.Value. However, Visual Basic .NET does not support propagating the Null value. This will result in behavior differences that produce run-time errors. This is illustrated in the following code example.

Sub TestNull() dbVal = Null res = 5 * dbVal If IsNull(res) Then

' Perform some action End If End Sub

Note that dbVal is usually obtained by accessing a database through a query. To simplify this example, the Null value is directly assigned to this variable.

When this code is upgraded using the upgrade wizard, the following result is obtained.

' UPGRADE_ISSUE: IsNull function is not supported. Sub TestNull()

Dim res As Object Dim dbVal As Object

' UPGRADE_WARNING: Use of Null/IsNull() detected. ' UPGRADE_WARNING: Could not resolve default property of ' object dbVal. dbVal = System.DBNull.Value

' UPGRADE_WARNING: Could not resolve default property of ' object dbVal.

' UPGRADE_WARNING: Could not resolve default property of ' object res. res = 5 * dbVal

' UPGRADE_WARNING: Use of Null/IsNull() detected. If IsDbNull(res) Then

' Perform some action End If End Sub

Note the number of UPGRADE_WARNING comments inserted into the code. This code will compile without problems, but it will result in run-time errors when executed.

To correct this problem, it is necessary to manually adjust the Null value propagation and adjust the conditions that rely on the null checking functions. The necessary corrections are shown in bold the following code example.

Sub TestNull()

Dim res As Object Dim dbVal As Object dbVal = System.DBNull.Value If Not IsDBNull(dbVal) Then res = 5 * dbVal End If

If IsDBNull(dbVal) Or IsDBNull(res) Then

' Perform some action End If End Sub

Changes to Array Indexing

In Visual Basic 6.0, it is possible to create arrays whose lower index bound is a value other than 0. In Visual Basic .NET, all arrays must have a lower index bound of 0.

The upgrade wizard ensures all arrays have a lower bound of 0. However, if the source code relies on the calculated size of arrays, the behavior of the array code in the upgraded version will likely have different behavior than the original code. The following code example illustrates this situation.

Function GetArraySize(a As Variant) As Long Size = UBound(a) - LBound(a) + 1 GetArraySize = Size End Function Sub testArraySize() Dim a(5 To 10) If GetArraySize(a) > 7 Then

' Perform some action End If End Sub

After the code is upgraded with the upgrade wizard, the following result is obtained.

Function GetArraySize(ByRef a As Object) As Integer Dim aSize As Object

' UPGRADE_WARNING: Could not resolve default property of ' object size.

' UPGRADE_WARNING: Could not resolve default property of ' object size. GetArraySize = aSize End Function Sub testArraySize()

' UPGRADE_WARNING: Lower bound of array was changed from 5 to 0. Dim a(10) As Object Dim res As Integer If GetArraySize(a) > 7 Then

' Perform some action End If End Sub

In the original version of the code, the statements in the If block will not be executed because the size of the array is only 6. However, after the automated upgrade, the resulting array in the upgraded code has a lager size of 11, which will trigger the execution of the If block. This will most likely be an undesired behavior.

Fixing this specific problem requires a detailed review of the original and upgraded code to conserve the original array size without affecting the application behavior. In this example, the upgraded array should have 6 elements, indexed from 0 to 5. All the code that depends on the array size will be corrected with that change. Code that depends on the specific position of elements will require review because the index range has changed. The following code example demonstrates this adjustment to array bounds.

Function GetArraySize(ByRef a As Object) As Integer Dim aSize As Object aSize = UBound(a) - LBound(a) + 1 GetArraySize = aSize End Function Sub testArraySize()

Dim a(5) As Object Dim res As Integer If GetArraySize(a) > 7 Then

'Perform some action End If End Sub

For more strategies that you can apply to deal with changes in array bounds, see Chapter 8, "Upgrading Commonly-Used Visual Basic 6.0 Language Features."

Changes to the Activate Event

It is not uncommon to include code to recalculate or refresh the state of a Form in the Visual Basic 6.0 Activate event. In Visual Basic 6.0, this event is raised only when switching between forms in the application. In Visual Basic .NET, the equivalent Activated event is raised in these situations, but it is also raised when switching from other applications. This will produce run-time differences between the two versions. The following Visual Basic 6.0 source code illustrates the situation.

Private Sub Form_Activate()

' Refreshing code End Sub

After upgrading the code with the upgrade wizard, the following code is produced.

' UPGRADE_WARNING: Form event Form1.Activate has a new behavior. Private Sub Form1_Activated(ByVal eventSender As System.Object, _

ByVal eventArgs As System.EventArgs) Handles MyBase.Activated ' Refreshing code End Sub

The problem is that the Activated event is raised more frequently than the Visual Basic Activate event. Activated is raised whenever the focus is switched from other applications. This results in different behavior than the original code.

To correct this difference, it will be necessary to differentiate activations coming from other applications and activations coming from other forms in the same application. This can be done by using a global variable that stores the last application form that was activated. This variable can be implemented as a Public Shared member in a new application class. When the focus is changed to another application and then returns to the same form, the global variable will still have the same value. The Activated event code can be skipped based on that value. The following code example demonstrates this solution.

Friend Class MyAppGlobals

Public Shared AppActiveForm = "" End Class

Private Sub Form1_Activated(ByVal eventSender As System.Object, _

ByVal eventArgs As System.EventArgs) Handles MyBase.Activated If Not MyAppGlobals.AppActiveForm = MyBase.Name Then ' Refreshing code

MyAppGlobals.AppActiveForm = MyBase.Name End If End Sub

Changes to ComboBox Control Events

This example shows another situation in which behavior differences exist between the original and the upgraded version of an application. These differences may produce run-time errors.

In Visual Basic 6.0, the ComboBox control has a Change event that is raised when the user writes text into the control. In Visual Basic .NET, the corresponding event, TextChanged, is raised in the same situation. However, this event is also raised when the corresponding form is initialized, before it is even displayed. This may cause errors at run time because the TextChanged event will be raised form loads and the ComboBox has not yet been initialized.

The following Visual Basic 6.0 code example demonstrates this problem.

Dim myClass1 As Class1

Private Sub Combo1_Change()

myClass1.hello End Sub

Private Sub Form_Load()

Set myClass1 = New Class1 End Sub

The auto-upgraded code is shown here. Dim myClass1 As Class1

' UPGRADE_WARNING: Event Combo1.TextChanged may ' initialized.

' UPGRADE_WARNING: ComboBox event Combo1.Change ' Combo1.TextChanged which has a new behavior. Private Sub Combo1_TextChanged(ByVal eventSender As System.Object, _

ByVal eventArgs As System.EventArgs) Handles Combo1.TextChanged myClass1.hello() End Sub

Private Sub Form1_Load(ByVal eventSender As System.Object, _

ByVal eventArgs As System.EventArgs) Handles MyBase.Load myClass1 = New Class1 End Sub

Now the Combo1_TextChanged event handler method will be executed whenever the corresponding form is initialized. This will produce a run-time error because myClass1 is not yet initialized; this happens in the Form1_Load event handler. The solution to this problem requires the creation of a new attribute in the corresponding Form class. This attribute will indicate whether the form is being initialized. This attribute must be set at the beginning and at the end of the InitializeComponent raise when form is was upgraded to method. The following code extract, which should be contained in the Forml class, demonstrates the correction.

Friend Class Form1

Inherits System.Windows.Forms.Form

Private initializing As Boolean Dim myClass1 As Class1

<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent() initializing = True

' The original code included in InitializeComponent must be copied ' here initializing = False End Sub

Private Sub Combo1_TextChanged(ByVal eventSender As System.Object, _

ByVal eventArgs As System.EventArgs) Handles Combo1.TextChanged If Not initializing Then myClass1.hello() End If End Sub

Private Sub Form1_Load(ByVal eventSender As System.Object, _

ByVal eventArgs As System.EventArgs) Handles MyBase.Load myClass1 = New Class1 End Sub End Class

0 0

Post a comment

  • Receive news updates via email from this site