VB6 On Error Statements

On-Error-GoTo label

Description

Error handling statements in VB6 rely on labels that can produce complex code. Because of this, there are cases where the VBUC cannot perform an appropriate transformation, so an EWI is generated. In those cases, manual changes must be made to fix the issue.

Alternative solution

Consider the following VB6 code:

Function Foo() As Boolean

    Dim objTest As Object
    Dim flag As Boolean

    On Local Error GoTo LabelError

    Set objTest = myWorkspace.CreateObject("MyObject")
    flag = True

    GoTo LabelExit

LabelError:

    flag = False
    MsgBox "Error"

LabelExit:

    Set objTest = Nothing

    Foo = flag
End Function

For that case, the On Local Error GoTo LabelError cannot be translated. The migrated version of the method would look like this:

public bool Foo()
{
    object objTest = null;
    bool flag = false;

    //UPGRADE_TODO: (1065) Error handling statement (On Error Goto) could not be converted. More Information: https://www.mobilize.net/vbtonet/ewis/ewi1065
    UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("On Error Goto Label (LabelError)");

    objTest = new MyObject();
    flag = true;

    goto LabelExit;

LabelError:

    flag = false;
    MessageBox.Show("Error");

LabelExit:

    objTest = null;

    return flag;
}

Most cases can be translated into a try-catch-finally statement. The following code shows an equivalent way to transform the original error pattern:

public bool Foo()
{
    object objTest = null;
    bool flag = false;

    try
    {
        objTest = new MyObject();
        flag = true;
    }
    catch (Exception ex)
    {
        flag = false;
        MessageBox.Show("Error");
    }
    finally
    {
        objTest = null;
    }

    return flag;
}

Notice the migrated code shows a pattern: the LabelError contains statements to take action when an error occurs (prone to be in a catch block) and the LabelExit contains statements that execute regardless of the error occurring or not (prone to be in a finally block). In those cases, a regular expression can be helpful to make a massive transformation of the migrated code. If you are not familiar with regular expressions, please read the following information.

The following is a possible regular expression that identifies the pattern:

(?^\s)(?UpgradeHelpers."On Error Goto Label (LabelError)")(?(.|\s|\n)?)(?LabelError:)(?(.|\s|\n)?)(?LabelExit:)(?(.|\s|\n)*?)(?(return))

The following is a possible replacement pattern for the regular expression indicated above:

{p1}try\n{\n{p3}\n}\ncatch(Exception exc)\n{\n{p5}\n\n}\nfinally\n{{p7}\n}\n${p8}

Using expressions like the ones shown above can transform the code automatically for those cases where the patterns appear en masse.

On Error Resume Next

Description

In .NET, the equivalent structured model for On Error Resume Next would be a Try-Catch for every single statement in the block where the "resume next" is active. Applying that kind of conversion would result in very low-quality code. Instead, it is recommended that the error-prone statements be manually identified and handled individually with a different model.

Example 1

VB6 code:

On Error Resume Next
<CodeBlock1>
If Err.Number <> 0 Then
<CodeBlock2>
End If 
Err.Clear

C# code:

try
{
<CodeBlock1>
}
catch
{
<CodeBlock2>
}

In this example, the programmer was expecting an error in CodeBlock1. If that happens, Err.Number is going to be a number different than 0, so the code flow will be entering the following if statement. The solution is to place CodeBlock1 in a try-catch and CodeBlock2 in the catch block.

Example 2

VB6 code:

On Error Resume Next
<CodeBlock1>
If Err.Number = SomeErrorCode Then
<CodeBlock2>
End If 
Err.Clear

C# code:

try
{
<CodeBlock1>
}
catch(SomeException e)
{
<CodeBlock2>
}

This is a small variation of the first example. What changes here is the programmer wanted to handle only a specific kind of error. Since Information.Err doesn't have the same behavior in .NET as in VB6, the solution is to find the corresponding exception to match the error code and catch it in the .NET migrated code.

Example 3

VB6 code:

On Error Goto errLabel
<CodeBlock1>
On Error Resume Next
<CodeBlock2>
If Err.Number <> 0 Then
<CodeBlock3>
End If 
On Error Goto errLabel
<CodeBlock4>
errlabel:
<CodeBlock5>

C# code:

try
{
<CodeBlock1>
  try
  {
  <CodeBlock2>
  }
  catch
  {
  <CodeBlock3>
  }
<CodeBlock4>
}
catch
{
<CodeBlock5>
}

This is a combination of error handling with On Error Goto Label and On Error Resume Next. If an error happens in CodeBlock1 or CodeBlock4 it should be handled with CodeBlock5, but if something happens in CodeBlock2 the error handling is executed by CodeBlock3. The solution here is to create a combination of try-catch that allows this execution flow.

Example 4

VB6 code:

On Error Resume Next
<CodeBlock1>

C# code:

<NonErrorExpectingBlock1>
try
{
<ErrorExpectingBlock2>
}
catch{}
<NonErrorExpectingBlock3>

This is probably the worst scenario you can find in error handling. Here, the VB6 programmer wanted every statement in CodeBlock1 to be executed, without caring about what happened in previous statements.

As we explain at the beginning of this section, the equivalent in .NET of CodeBlock1 will be a try-catch for every statement, but he is undesirable. For this case, the recommendation is to find every statement that could raise an error and put it within a try with an empty catch (if possible, you can create a better code that prevents the exceptions). There are many statements where you don't expect errors, such as variable assignments (i = 0), but you should consider a try-catch for statements that use COM objects or connections to databases.

Last updated