Generated Code

In the following section, will find the generated code (C# and VB.NET), important messages after upgrading your code (EWIs), and helpers information.

What does the VBUC generate? - C# and VB.NET Differences

There are some features that help to simulate VB6 behavior, making C# easier to use, but in some cases, VB.NET could be better. In this section, you will see a comparison of some features in C# and VB.NET.

Error Handling

It refers to the response and recovery procedures from error conditions present in a software application. The VBUC offers three options for error handling:

  1. Leave On Error Statements (VB.NET Only)

    • "On Error GoTo" and "On Error Resume Next" statements will remain the same code in the VB.NET output code.

  2. Convert To Try-Catch

    • Most patterns will be upgraded to Try-Catch blocks. This option covers a huge amount of "On Error GoTo" patterns and very basic cases of "On Error Resume Next".

  3. To Try-Catch With Lambdas (C# Only)

    • This feature covers the most common patterns of "On Error GoTo" and will generate special patterns to support most "On Error Resume Next" patterns.

Original VB6 Code:

Public Sub OnErrorGotoLabelMethod(arg As String)
    On Error GoTo errHnd
        Dim s As Integer
        s = CInt(arg)
        Foo s
        Exit Sub
errHnd:
        MsgBox "Invalid Argument"
End Sub

Public Sub OnErrorResumeNextMethod(arg As String)
On Error Resume Next
    MsgBox arg
    MsgBox CInt(arg)
    MsgBox "This line will be executed allways"
    
    If Err.Number <> 0 Then
        'This code should be executed if there were any error(s)
        MsgBox "OnErrorResumeNextMethod reached the last statement"
        MsgBox "OnErrorResumeNextMethod reached the last statement"
        MsgBox "OnErrorResumeNextMethod reached the last statement"
    End If
End Sub

Leave On Error Statements (VB.NET Only)

Public Sub OnErrorGotoLabelMethod(ByVal arg As String)
    On Error GoTo errHnd
    Dim s As Integer
    s = CInt(arg)
    Foo(s)
    Exit Sub
errHnd:
    MessageBox.Show("Invalid Argument", My.Application.Info.Title)
End Sub

Public Sub OnErrorResumeNextMethod(ByVal arg As String)
    On Error Resume Next
    MessageBox.Show(arg, My.Application.Info.Title)
    MessageBox.Show(CStr(CInt(arg)), My.Application.Info.Title)
    MessageBox.Show("This line will be executed allways", My.Application.Info.Title)

    If Information.Err().Number <> 0 Then
        'This code should be executed if there were any error(s)
        MessageBox.Show("OnErrorResumeNextMethod reached the last statement", My.Application.Info.Title)
        MessageBox.Show("OnErrorResumeNextMethod reached the last statement", My.Application.Info.Title)
        MessageBox.Show("OnErrorResumeNextMethod reached the last statement", My.Application.Info.Title)
    End If
End Sub

Convert To Try-Catch

C# Code:

public void OnErrorGotoLabelMethod(string arg)
{
    try
    {
        int s = 0;
        s = Convert.ToInt32(Double.Parse(arg));
        Foo(s);
    }
    catch
    {
        MessageBox.Show("Invalid Argument", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    }
}

public void OnErrorResumeNextMethod(string arg)
{
    //UPGRADE_TODO: (1069) Error handling statement (On Error Resume Next) was converted to a pattern that might have a different behavior.
    try
    {
        MessageBox.Show(arg, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
        MessageBox.Show(Convert.ToInt32(Double.Parse(arg)).ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
        MessageBox.Show("This line will be executed allways", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));

        //UPGRADE_WARNING: (2081) Err.Number has a new behavior.
        if (Information.Err().Number != 0)
        {
            //This code should be executed if there were any error(s)
            MessageBox.Show("OnErrorResumeNextMethod reached the last statement", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
            MessageBox.Show("OnErrorResumeNextMethod reached the last statement", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
            MessageBox.Show("OnErrorResumeNextMethod reached the last statement", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
        }
    }
    catch (Exception exc)
    {
        NotUpgradedHelper.NotifyNotUpgradedElement("Resume in On-Error-Resume-Next Block");
    }
}

VB.NET Code:

Public Sub OnErrorGotoLabelMethod(ByVal arg As String)
    Try
        Dim s As Integer
        s = CInt(arg)
        Foo(s)

    Catch
        MessageBox.Show("Invalid Argument", My.Application.Info.Title)
    End Try
End Sub

Public Sub OnErrorResumeNextMethod(ByVal arg As String)
    'UPGRADE_TODO: (1069) Error handling statement (On Error Resume Next) was converted to a pattern that might have a different behavior.'
    Try
        MessageBox.Show(arg, My.Application.Info.Title)
        MessageBox.Show(CStr(CInt(arg)), My.Application.Info.Title)
        MessageBox.Show("This line will be executed allways", My.Application.Info.Title)

        If Information.Err().Number <> 0 Then
            'This code should be executed if there were any error(s)'
            MessageBox.Show("OnErrorResumeNextMethod reached the last statement", My.Application.Info.Title)
            MessageBox.Show("OnErrorResumeNextMethod reached the last statement", My.Application.Info.Title)
            MessageBox.Show("OnErrorResumeNextMethod reached the last statement", My.Application.Info.Title)
        End If
    Catch exc As Exception
        NotUpgradedHelper.NotifyNotUpgradedElement("Resume in On-Error-Resume-Next Block")
    End Try
End Sub

To Try-Catch With Lambdas (C# Only)

public void OnErrorGotoLabelMethod(string arg)
{
    try
    {
        int s = 0;
        s = Convert.ToInt32(Double.Parse(arg));
        Foo(s);
    }
    catch
    {
        MessageBox.Show("Invalid Argument", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    }
}

public void OnErrorResumeNextMethod(string arg)
{
    Exception ex = null;
    ErrorHandlingHelper.ResumeNext(out ex, 
        () => {MessageBox.Show(arg, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));}, 
        () => {MessageBox.Show(Convert.ToInt32(Double.Parse(arg)).ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));}, 
        () => {MessageBox.Show("This line will be executed allways", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));});
    if (ex != null)
    {
        ErrorHandlingHelper.ResumeNext(
                //This code should be executed if there were any error(s)
            () => {MessageBox.Show("OnErrorResumeNextMethod reached the last statement", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));}, 
            () => {MessageBox.Show("OnErrorResumeNextMethod reached the last statement", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));}, 
            () => {MessageBox.Show("OnErrorResumeNextMethod reached the last statement", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));});
    }
}

Late Binding

This is a mechanism where the type is unknown until the variable is used during runtime; usually through the assignment. The VBUC offers three options for late binding:

  1. Static code analysis + helper classes

    • This option will cause the late binding access cases that could not be resolved with a static code analysis to be managed at runtime by means of a helper class.

  2. Static code analysis only

    • VBUC will use its static code analysis process to determine the correct type for each object and resolve the late binding access. Note: The Visual Basic Upgrade Companion solves most of these issues by using a static code analysis process to infer and determine the correct data type for each variable. However, there are some variables that take several values with inconsistent types and there is no way to infer a proper type. For these cases, manual changes are required to reach functional equivalence.

  3. Static code analysis + dynamic variables

    • This option will cause the late binding access cases that could not be resolved with a static code analysis to be managed at runtime with dynamic variables.

Original VB6 Code:

Public Sub Func(myControl As Control, myForm As Form, myVar As Variant)
    myControl.controlProp1 = myVar.varProp1
    myForm.formProp1 = myControl.controlProp1
    myVar.varProp1 = myForm.formProp1

    myControl.Func 1
    myForm.Func 1
    myVar.Func 1
End Sub

Static code analysis + helper classes

C# Code:

public void Func(Control myControl, Form myForm, object myVar)
{
ReflectionHelper.LetMember(myControl, "controlProp1", ReflectionHelper.GetMember<object>(myVar, "varProp1"));
ReflectionHelper.LetMember(myForm, "formProp1", ReflectionHelper.GetMember<object>(myControl, "controlProp1"));
ReflectionHelper.LetMember(myVar, "varProp1", ReflectionHelper.GetMember<object>(myForm, "formProp1"));

ReflectionHelper.Invoke(myControl, "Func", new object[]{1});
ReflectionHelper.Invoke(myForm, "Func", new object[]{1});
ReflectionHelper.Invoke(myVar, "Func", new object[]{1});
}

VB.NET Code:

Public Sub Func(ByVal myControl As Control, ByVal myForm As Form, ByVal myVar As Object)
    ReflectionHelper.LetMember(myControl, "controlProp1", ReflectionHelper.GetMember(myVar, "varProp1"))
    ReflectionHelper.LetMember(myForm, "formProp1", ReflectionHelper.GetMember(myControl, "controlProp1"))
    ReflectionHelper.LetMember(myVar, "varProp1", ReflectionHelper.GetMember(myForm, "formProp1"))

    ReflectionHelper.Invoke(myControl, "Func", New Object() {1})
    ReflectionHelper.Invoke(myForm, "Func", New Object() {1})
    ReflectionHelper.Invoke(myVar, "Func", New Object() {1})
End Sub

Static code analysis only

C# Code:

public void Func(Control myControl, Form myForm, object myVar)
{
    //UPGRADE_TODO: (1067) Member controlProp1 is not defined in type VB.Control.
    //UPGRADE_TODO: (1067) Member varProp1 is not defined in type Variant.
    myControl.controlProp1 = myVar.varProp1;
    //UPGRADE_TODO: (1067) Member formProp1 is not defined in type VB.Form.
    //UPGRADE_TODO: (1067) Member controlProp1 is not defined in type VB.Control.
    myForm.formProp1 = myControl.controlProp1;
    //UPGRADE_TODO: (1067) Member varProp1 is not defined in type Variant.
    //UPGRADE_TODO: (1067) Member formProp1 is not defined in type VB.Form.
    myVar.varProp1 = myForm.formProp1;

    //UPGRADE_TODO: (1067) Member Func is not defined in type VB.Control.
    myControl.Func(1);
    //UPGRADE_TODO: (1067) Member Func is not defined in type VB.Form.
    myForm.Func(1);
    //UPGRADE_TODO: (1067) Member Func is not defined in type Variant.
    myVar.Func(1);
}

VB.NET Code:

Public Sub Func(ByVal myControl As Control, ByVal myForm As Form, ByVal myVar As Object)
    'UPGRADE_TODO: (1067) Member controlProp1 is not defined in type VB.Control.'
    'UPGRADE_TODO: (1067) Member varProp1 is not defined in type Variant.'
    myControl.controlProp1 = myVar.varProp1
    'UPGRADE_TODO: (1067) Member formProp1 is not defined in type VB.Form.'
    'UPGRADE_TODO: (1067) Member controlProp1 is not defined in type VB.Control.'
    myForm.formProp1 = myControl.controlProp1
    'UPGRADE_TODO: (1067) Member varProp1 is not defined in type Variant.'
    'UPGRADE_TODO: (1067) Member formProp1 is not defined in type VB.Form.'
    myVar.varProp1 = myForm.formProp1

    'UPGRADE_TODO: (1067) Member Func is not defined in type VB.Control.'
    myControl.Func(1)
    'UPGRADE_TODO: (1067) Member Func is not defined in type VB.Form.'
    myForm.Func(1)
    'UPGRADE_TODO: (1067) Member Func is not defined in type Variant.'
    myVar.Func(1)
End Sub

Static code analysis + dynamic variables

C# Code:

public void Func(Control myControl, Form myForm, object myVar)
{
    ((dynamic) myControl).controlProp1 = ((dynamic) myVar).varProp1;
    ((dynamic) myForm).formProp1 = ((dynamic) myControl).controlProp1;
    ((dynamic) myVar).varProp1 = ((dynamic) myForm).formProp1;

    ((dynamic) myControl).Func(1);
    ((dynamic) myForm).Func(1);
    ((dynamic) myVar).Func(1);
}

VB.NET Code:

Option Strict Off

Public Sub Func(ByVal myControl As Control, ByVal myForm As Form, ByVal myVar As Object)
    CType(myControl, object).controlProp1 = myVar.varProp1
    CType(myForm, object).formProp1 = CType(myControl, object).controlProp1
    myVar.varProp1 = CType(myForm, object).formProp1

    CType(myControl, object).Func(1)
    CType(myForm, object).Func(1)
    myVar.Func(1)
End Sub

Go Sub

VB6 provides the ability to jump around in the code from one portion to another thru "labels" and create code that is not structured. By default Go Sub statements are not supported in .NET, but by turning on this option, the VBUC will apply the special pattern recognition mechanism to support the GoSub Statement.

This feature works only for C#. The user can enable or disable it.

Original VB6 Code:

Public Sub Main()

Dim i As Integer
i = 4
If i = 1 Then
    GoSub printOne
ElseIf i = 2 Then
    GoSub printTwo
ElseIf i = 3 Then
    GoSub printThree
Else
    GoSub SomethingElse
End If
GoTo exitLabel

printOne:
MsgBox "Value = 1"
Return

printTwo:
MsgBox "Value = 2"
Return

printThree:
MsgBox "Value = 3"
Return

SomethingElse:
MsgBox "Invalid Value!"
Return

exitLabel:

End Sub

C# Code:

public static void Main()
{
    int i = 4;
    if (i == 1)
    {
        printOne();
    }
    else if (i == 2)
    { 
        printTwo();
    }
    else if (i == 3)
    { 
        printThree();
    }
    else
    {
        SomethingElse();
    }
    return;

    void printOne()
    {
        MessageBox.Show("Value = 1", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    }

    void printTwo()
    {
        MessageBox.Show("Value = 2", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    }

    void printThree()
    {
        MessageBox.Show("Value = 3", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    }

    void SomethingElse()
    {
        MessageBox.Show("Invalid Value!", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    }
}

VB.NET Code:

Public Sub Main()
    Dim i As Integer = 4
    If i = 1 Then
        'UPGRADE_ISSUE: (1014) GoSub statement is not supported. '
        GoSub printOne
    ElseIf i = 2 Then 
        'UPGRADE_ISSUE: (1014) GoSub statement is not supported. '
        GoSub printTwo
    ElseIf i = 3 Then 
        'UPGRADE_ISSUE: (1014) GoSub statement is not supported. '
        GoSub printThree
    Else
        'UPGRADE_ISSUE: (1014) GoSub statement is not supported. '
        GoSub SomethingElse
    End If
    Exit Sub

printOne:
    MessageBox.Show("Value = 1", My.Application.Info.Title)
    'UPGRADE_WARNING: (2081) Return has a new behavior. '
    Return 

printTwo:
    MessageBox.Show("Value = 2", My.Application.Info.Title)
    'UPGRADE_WARNING: (2081) Return has a new behavior. '
    Return 

printThree:
    MessageBox.Show("Value = 3", My.Application.Info.Title)
    'UPGRADE_WARNING: (2081) Return has a new behavior. '
    Return 

SomethingElse:
    MessageBox.Show("Invalid Value!", My.Application.Info.Title)
    'UPGRADE_WARNING: (2081) Return has a new behavior. '
    Return 
End Sub

Getting information from the AssemblyInfo file

This feature refers to how the VBUC tool obtains the information contained in the AssemblyInfo file.

Original VB6 Code:

Private Sub Form_Load()
    MsgBox "Hello World!"
    End
End Sub

C# Code:

private void Form_Load()
{
    MessageBox.Show("Hello World!", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    Environment.Exit(0);
}

VB.NET Code:

Private Sub Form_Load()
    MessageBox.Show("Hello World!", My.Application.Info.Title)
    Environment.Exit(0)
End Sub

Auto-Implemented Properties

They are used when no additional logic is required in the property accessors, making them more concise and readable.

This feature only works for C#. The user can enable or disable it.

Original VB6 Code:

Private IntVar As Integer
Private BoolVar As Boolean
Private StrVar As String

Public Property Let IntVarProp(value As Integer)
    'some comments for IntVarProp property header'
    IntVar = value
    'some comments for IntVarProp property footer'
End Property
Public Property Get IntVarProp() As Integer
    'some comments for IntVarProp property header'
    IntVarProp = IntVar
    'some comments for IntVarProp property footer'
End Property
Public Property Let BoolVarProp(value As Boolean)
    BoolVar = value
End Property
Public Property Get BoolVarProp() As Boolean
    BoolVarProp = BoolVar
End Property
Public Property Let StrVarProp(value As String)
    StrVar = value
End Property
Public Property Get StrVarProp() As String
    StrVarProp = StrVar
End Property

Public Sub FieldsUsage()
    IntVar = 0
    BoolVar = False
    StrVar = "Hello World"
    parameterFieldsUsage IntVar, BoolVar, StrVar
End Sub

Public Sub PropertiesUsage()
    IntVarProp = 0
    BoolVarProp = False
    StrVarProp = "Hello World"
    parameterProperiesUsage IntVarProp, BoolVarProp, StrVarProp
End Sub

Public Sub parameterFieldsUsage(IntVar As Integer, BoolVar As Boolean, StrVar As String)
''
End Sub

Public Sub parameterProperiesUsage(IntVar As Integer, BoolVar As Boolean, StrVar As String)
''
End Sub

C# Code:

public int IntVarProp
{
    //some comments for IntVarProp property header
    get;
    //some comments for IntVarProp property footer
    //some comments for IntVarProp property header
    set;
    //some comments for IntVarProp property footer
}

public bool BoolVarProp
{
    get;
    set;
}

public string StrVarProp
{
    get;
    set;
}


public void FieldsUsage()
{
    this.IntVarProp = 0;
    this.BoolVarProp = false;
    this.StrVarProp = "Hello World";
    parameterFieldsUsage(this.IntVarProp, this.BoolVarProp, this.StrVarProp);
}

public void PropertiesUsage()
{
    IntVarProp = 0;
    BoolVarProp = false;
    StrVarProp = "Hello World";
    parameterProperiesUsage(IntVarProp, BoolVarProp, StrVarProp);
}

public void parameterFieldsUsage(int IntVar, bool BoolVar, string StrVar)
{
    //
}

public void parameterProperiesUsage(int IntVar, bool BoolVar, string StrVar)
{
    //
}

VB.NET Code:

Private IntVar As Integer
Private BoolVar As Boolean
Private StrVar As String = ""

Public Property IntVarProp() As Integer
    Get
        'some comments for IntVarProp property header'
        Return IntVar
        'some comments for IntVarProp property footer'
    End Get
    Set(ByVal Value As Integer)
        'some comments for IntVarProp property header'
        IntVar = Value
        'some comments for IntVarProp property footer'
    End Set
End Property
Public Property BoolVarProp() As Boolean
    Get
        Return BoolVar
    End Get
    Set(ByVal Value As Boolean)
        BoolVar = Value
    End Set
End Property
Public Property StrVarProp() As String
    Get
        Return StrVar
    End Get
    Set(ByVal Value As String)
        StrVar = Value
    End Set
End Property

Public Sub FieldsUsage()
    IntVar = 0
    BoolVar = False
    StrVar = "Hello World"
    parameterFieldsUsage(IntVar, BoolVar, StrVar)
End Sub

Public Sub PropertiesUsage()
    IntVarProp = 0
    BoolVarProp = False
    StrVarProp = "Hello World"
    parameterProperiesUsage(IntVarProp, BoolVarProp, StrVarProp)
End Sub

Public Sub parameterFieldsUsage(ByVal IntVar As Integer, ByVal BoolVar As Boolean, ByVal StrVar As String)
    ''
End Sub

Public Sub parameterProperiesUsage(ByVal IntVar As Integer, ByVal BoolVar As Boolean, ByVal StrVar As String)
    ''
End Sub

Case Sensitive

.NET Common Language Runtime is case-sensitive. VB.NET code relies on the runtime, which means it must be case-sensitive at runtime, like when it is looking up variables and methods. However, the VB.NET compiler and editor let you ignore that because they correct the case in your code, whereas C# does not, making VB.NET a case-insensitive language (there are exceptions, such as XML literals, and string comparisons).

EWIs

When upgrading a VB6 project to .NET, the Visual Basic Upgrade Companion upgrades most of the code and objects from VB6 to their equivalents in either C# or VB.NET. However, there might be some items that are not fully upgraded, requiring manual changes in order to compile, run and achieve functional equivalence. The Visual Basic Upgrade Companion helps with this process by inserting different Errors, Warnings, and Issues (EWI) information messages into the upgraded code as comments.

EWIs are a valuable part of VB6 to .NET upgrade efforts. They alert you to potential issues, and the upgraded code provides information about how to fix them. An EWI is placed on the line before the problem and it consists of three parts:

Example:

//UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load method and has a new behavior. More Information: https://docs.mobilize.net/vbuc/ewis#2080

In the example above, an EWI is made up of:

Code Number.

(2080)

A message describing the problem.

Form_Load event was upgraded to Form_Load method and has a new behavior.

A link to the help topic.

More Information: https://docs.mobilize.net/vbuc/ewis#2080

EWIs are inserted into the code as comments, they do not affect the compilation or execution of the upgraded application.

Type

Description

Comment

Code that will cause a compilation error

Marks items that you need to fix before the upgraded code can run

Code partially upgraded, it requires some additional changes

Requires some manual intervention to avoid compilation and runtime errors

Behavior difference between VB6 and .NET. The code will compile and run, but it might have a difference at runtime

Most of the time code will work fine, but it should be reviewed as a precaution

Code was changed in a way that may confuse the developer

Normally will not require user intervention

Global Warning

EWI present on the Upgrade Report only, not to the source code

Upgrade Helpers

The VBUC is able to translate a large part of the VB6 code directly to simple C# statements, but some functionality is either too specific or too complex to allow a one-to-one conversion. In these cases, the VBUC uses helper classes that encapsulate complex commands into a single function call. These functions, if desired, are included in the generated code.

Upgrade Helper classes range from simple string manipulation functions to extensions of complex controls such as grids. The use of these classes allows the VBUC-generated code to remain legible while maintaining functional equivalence with VB6.

Helpers integration

Helper Classes can be integrated into the migrated solution in three ways. This option can be selected in the Helpers Integration combo box in the Upgrade Options tab of the VBUC.

Option

Description

Source Code

The source code for the Helper Classes will be added directly to the solution in the UpgradeSupport folder.

Binary

Compiled .dll files for the Helper Classes will be referenced in the solution and will be placed in the UpgradeSupport folder.

NuGet

References to the Helper Classes will be made through NuGet. This option is available for certain .NET Platforms only:

  • .NET Framework 4.7.2

  • .NET Framework 4.8

  • .NET Core 3.1

  • .NET 5

  • .NET 6

Reflection Helper

The VB6 typing model allows late binding. This means variables may not have been declared with their actual type, but with a general type. E.g.: Control instead of Label, Form instead of Form1, or Variant instead of any other type.

Even when the variables are declared with a general type, the code can assume they contain an instance of a more specific type and access members from the latter. Neither the VB6 compiler nor the interpreter will raise an error if the actual type of the instance has the requested member.

On the other hand, .NET will give a compilation error whenever a member is not in the declared type of the variable.

VBUC solves most of these issues by using its typing mechanism. This module is able to identify and resolve most late binding cases and correct the declarations with their actual type before performing the conversion process. This mechanism allows solving the member access as well as many other typing-related upgrade issues.

However, some variables take several values with inconsistent types and there is no way to infer a proper type for them.

By selecting the Static code analysis + helper classes option in the Late Binding Resolution upgrade option, the VBUC generates ReflectionHelper calls for these last cases which were not solved by the typing mechanism.

The ReflectionHelper calls avoid .NET compilation errors and provide an equivalent behavior in many cases. However, it must be noted that not all the cases will behave in the same way since the upgrade process still lacks information about the actual type of the variable and its member during the application runtime. This is a simple example of the reflection helper usage:

Original VB6 Code:

Private Sub ShowInfo(useForm As Boolean)
   Dim x As Variant   
   If useForm Then
      Set x = Form1
   Else
      Set x = New Class1
   End If

   If useForm Then
      MsgBox x.formProp
   Else
      MsgBox x.classProp
   End If
End Sub

Resulting C#.NET Code:

private void  ShowInfo( bool useForm)
{
    object x = null;
    if (useForm)
    {
        x = Form1.DefInstance;
    }
    else
    {
        x = new Class1();
    }
    if (useForm)
    {
        //UPGRADE_TODO: (1067) Member formProp is not defined in type Variant.
        MessageBox.Show(Convert.ToString(ReflectionHelper.GetMember(x, "formProp")), Application.ProductName);
    } 
    else
    {
        //UPGRADE_TODO: (1067) Member classProp is not defined in type Variant.
        MessageBox.Show(Convert.ToString(ReflectionHelper.GetMember(x, "classProp")), Application.ProductName);
    }
}

Printer Helper

The VBUC can handle the migration of the VB.Global.Printer class in two ways. The first is using Microsoft.VisualBasic.PowerPacks.Printing.Compatibility.VB6.Printer class provided by Microsoft. This is handled inside a small helper class that manages the Printer object as a singleton and uses the methods provided within.

The second option is a helper class that handles printing using the .NET System.Drawing.Printing namespace. The code for this class is included when the Helpers Integration option is set to Source Code.

Original VB6 code:

Private Sub Command1_Click()
    Printer.FontName = "Verdana"
    Printer.FontSize = 16
    Printer.FontBold = True
    Printer.FontItalic = True
    Printer.FontUnderline = True
    Printer.Print "This is an example"
    Printer.EndDoc
End Sub

Private Sub Form_Load()
    Dim prnPrinter As Printer
    For Each prnPrinter In Printers
        If prnPrinter.DeviceName = "Device1" Then
            Set Printer = prnPrinter
            Exit For
        End If
    Next
End Sub

C# code with PowerPacks:

private void Command1_Click(Object eventSender, EventArgs eventArgs)
{
    PowerPacksPrinterHelper.Instance.FontName = "Arial";
    PowerPacksPrinterHelper.Instance.FontSize = 14;
    PowerPacksPrinterHelper.Instance.FontBold = true;
    PowerPacksPrinterHelper.Instance.FontItalic = true;
    PowerPacksPrinterHelper.Instance.FontUnderline = true;
    PowerPacksPrinterHelper.Instance.Print("This is an example");
    PowerPacksPrinterHelper.Instance.EndDoc();
}

private void Form_Load()
{
    PrinterHelper prnPrinter = null;
    foreach (PrinterHelper prnPrinterIterator in PrinterHelper.Printers)
    {
        prnPrinter = prnPrinterIterator;
        if (PowerPacksPrinterHelper.Instance.DeviceName == "Device1")
        {
            PrinterHelper.Printer = prnPrinter;
            break;
        }
    }
}

C# code with native helper class:

private void Command1_Click(Object eventSender, EventArgs eventArgs)
{
    PrinterHelper.Printer.FontName = "Arial";
    PrinterHelper.Printer.FontSize = 14;
    PrinterHelper.Printer.FontBold = true;
    PrinterHelper.Printer.FontItalic = true;
    PrinterHelper.Printer.FontUnderline = true;
    PrinterHelper.Printer.Print("This is an example");
    PrinterHelper.Printer.EndDoc();
}

private void Form_Load()
{
    PrinterHelper prnPrinter = null;
    foreach (PrinterHelper prnPrinterIterator in PrinterHelper.Printers)
    {
        prnPrinter = prnPrinterIterator;
        if (PrinterHelper.Printer.DeviceName == "Device1")
        {
            PrinterHelper.Printer = prnPrinter;
            break;
        }
    }
}

Last updated