Code Conversion

This section provides some transformation that we apply so that some elements will be upgraded as you need it.

1. ActiveX

1.1. .NET Assemblies with COM Visible

This option will generate special attributes for the converted classes and their members in order to keep the resulting assemblies exposed through a COM interface.

This feature enables the resulting .NET components to be called from other non-managed components or applications via COM.

General Description:

The VBUC converts Visual Basic 6 ActiveX binaries to .NET assemblies. By default, the resulting assemblies can be used only from managed code. This option should be selected to generate the .NET attributes required to expose these assemblies as COM Objects if it’s necessary to use them from non-managed code.

In addition to the “COMVisible” attribute on the class members, the resulting project is also marked as COM Visible.

Original VB6 code:

Code declared in an Activex EXE:

Module1.bas
Public myVar As String
Customer.cls
Attribute VB_Name = "Customer"

Private g_TheNumber As Integer

Public Property Let TheNumber(ByVal new_value As Integer)
   g_TheNumber = new_value
End Property

Public Property Get TheNumber() As Integer
   TheNumber = g_TheNumber
End Property

Code located in the main application that uses the Activex EXE:

Private m_Customer1 As ExeBillingObjects.Customer
Private m_Customer2 As ExeBillingObjects.Customer

Private Sub Form_Load()
   Set m_Customer1 = New ExeBillingObjects.Customer
   Set m_Customer2 = New ExeBillingObjects.Customer
End Sub

Private Sub cmdGet_Click()
   txtTheNumber.Text = m_Customer1.TheNumber
End Sub

C# code:

Code declared in an Activex EXE:

Module1.cs
internal static class Module1
{
	public static string myVar = "";
}
Customer.cs
[ComVisible(true)][ProgId("ExeBillingObjects.Customer")][ClassInterface(ClassInterfaceType.AutoDual)]
public class Customer
{
	public int TheNumber
	{
		get
		{
			return Module1.g_TheNumber;
		}
		set
		{
			Module1.g_TheNumber = value;
		}
	}	
}

Code located in the main application that uses the Activex EXE:

private ExeBillingObjects.Customer m_Customer1 = null;
private ExeBillingObjects.Customer m_Customer2 = null;
			
private void  cmdSet_Click( Object eventSender,  EventArgs eventArgs)
{
	m_Customer1.TheNumber = Convert.ToInt32(Double.Parse(txtTheNumber.Text));
	txtTheNumber.Text = "";
}

private void  Form1_Load( Object eventSender,  EventArgs eventArgs)
{
	m_Customer1 = new ExeBillingObjects.Customer();
	m_Customer2 = new ExeBillingObjects.Customer();
}

VB.NET code:

Code declared in an Activex EXE:

Module1.vb
Module Module1
	Public myVar As String = ""
End Module
Customer.vb
<ComVisible(True)> _
<ProgId("ExeBillingObjects.Customer")> _
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class Customer
	Public Property TheNumber() As Integer
		Get
			Return g_TheNumber
		End Get
		Set(ByVal Value As Integer)
			g_TheNumber = Value
		End Set
	End Property
End Class

Code located in the main application that uses the Activex EXE:

Private m_Customer1 As ExeBillingObjects.Customer
Private m_Customer2 As ExeBillingObjects.Customer

Private Sub cmdGet_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles cmdGet.Click
	txtTheNumber.Text = CStr(m_Customer1.TheNumber)
End Sub

Private Sub Form1_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	m_Customer1 = New ExeBillingObjects.Customer()
	m_Customer2 = New ExeBillingObjects.Customer()
End Sub

1.2. Out process and Application

This optional feature enables the conversion of ActiveX DLLs and EXEs (with global variables in a .bas module) via a helper class (ComponentClassHelper) that controls the instantiation of the generated classes.

  • ActiveX EXEs are converted to out-of-process .NET EXEs.

  • ActiveX DLLs are converted to .NET class libraries using application domains.

  • Component instantiation is performed using helper classes. With this approach most of the ActiveX functionality is replicated in the converted code.

General Description:

The VBUC converts Visual Basic 6 ActiveX EXEs to .NET EXE projects (out-of-process) and ActiveX DLLs to .NET assemblies (class library projects).

One of the main differences between Visual Basic 6 ActiveX binaries and .NET assemblies is how the components are instantiated and how their initialization determines its lifetime behavior.

When this feature is active, a helper class is used to replicate the following ActiveX instantiation types:

  • Private

  • PublicNotCreatable

  • SingleUse

  • GlobalSingleUse

  • MultiUse

  • GlobalMultiUse

Original VB6 code:

Code declared in an Activex EXE:

Module1.bas
Public myVar As String
Customer.cls
Attribute VB_Name = "Customer"

Private g_TheNumber As Integer

Public Property Let TheNumber(ByVal new_value As Integer)
   g_TheNumber = new_value
End Property

Public Property Get TheNumber() As Integer
   TheNumber = g_TheNumber
End Property

Code located in the main application that uses the Activex EXE:

Private m_Customer1 As ExeBillingObjects.Customer
Private m_Customer2 As ExeBillingObjects.Customer

Private Sub Form_Load()
   Set m_Customer1 = New ExeBillingObjects.Customer
   Set m_Customer2 = New ExeBillingObjects.Customer
End Sub

Private Sub cmdGet_Click()
   txtTheNumber.Text = m_Customer1.TheNumber
End Sub

C# code:

Code declared in an Activex EXE:

Module1.cs
internal static class Module1
{
	public static string myVar = "";
	internal static void InitGlobalVars()
	{
		myVar = "";
	}
}
Customer.cs
public class Customer : UpgradeHelpers.Activex.ComponentClassHelper
{
	public int TheNumber
	{
		get
		{
			return g_TheNumber;
		}
		set
		{
			g_TheNumber = value;
		}
	}	
}

Code located in the main application that uses the Activex EXE:

private ExeBillingObjects.Customer m_Customer1 = null;
private ExeBillingObjects.Customer m_Customer2 = null;
			
private void cmdGet_Click( Object eventSender,  EventArgs eventArgs)
{
	txtTheNumber.Text = Convert.ToString(m_Customer1.TheNumber);
}

private void Form1_Load( Object eventSender,  EventArgs eventArgs)
{
	m_Customer1 = ExeBillingObjects.ExeBillingObjectsFactory.Create <ExeBillingObjects.Customer>(m_Customer1);
	m_Customer2 = ExeBillingObjects.ExeBillingObjectsFactory.Create <ExeBillingObjects.Customer>(m_Customer2);

VB.NET code:

Code declared in an Activex EXE:

Module1.vb
Module Module1
	Public myVar As String = ""
	Public Sub InitGlobalVars()
		myVar = ""
	End Sub
End Module
Customer.vb
Public Class Customer Inherits UpgradeHelpers.Activex.ComponentClassHelper
	Public Property TheNumber() As Integer
		Get
			Return g_TheNumber
		End Get
		Set(ByVal Value As Integer)
			g_TheNumber = Value
		End Set
	End Property
End Class

Code located in the main application that uses the Activex EXE:

Private m_Customer1 As ExeBillingObjects.Customer
Private m_Customer2 As ExeBillingObjects.Customer
	
Private Sub cmdGet_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles cmdGet.Click
	txtTheNumber.Text = CStr(m_Customer1.TheNumber)
End Sub
	
Private Sub Form1_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	m_Customer1 = ExeBillingObjects.ExeBillingObjectsFactory.Create(Of ExeBillingObjects.Customer)(m_Customer1)
	m_Customer2 = ExeBillingObjects.ExeBillingObjectsFactory.Create(Of ExeBillingObjects.Customer)(m_Customer2)
End Sub

1.3. Standard .NET Assemblies

This optional feature converts the ActiveX DLLs and EXEs to .NET class libraries (DLLs).

  • All ActiveX binaries are converted to native .NET assemblies (class libraries).

  • With this approach, the converted code can be used only from managed code and the different ActiveX instantiation models (e.g MultiUse, GlobalMultiuse, etc) won’t be supported.

General Description:

The VBUC converts Visual Basic 6 ActiveX EXEs and ActiveX DLLs to .NET class libraries (DLLs).

One of the main differences between Visual Basic 6 ActiveX binaries and .NET assemblies is how the components are instantiated and how their initialization determines its lifetime behavior.

With this option switched on, the code is converted to pure .NET code and the ActiveX instantiation models won’t be supported.

Original VB6 code:

Code declared in an Activex EXE:

Module1.bas
Public myVar As String
Customer.cls
Attribute VB_Name = "Customer"

Private g_TheNumber As Integer

Public Property Let TheNumber(ByVal new_value As Integer)
   g_TheNumber = new_value
End Property

Public Property Get TheNumber() As Integer
   TheNumber = g_TheNumber
End Property

Code located in the main application that uses the Activex EXE:

Private m_Customer1 As ExeBillingObjects.Customer
Private m_Customer2 As ExeBillingObjects.Customer

Private Sub Form_Load()
   Set m_Customer1 = New ExeBillingObjects.Customer
   Set m_Customer2 = New ExeBillingObjects.Customer
End Sub

Private Sub cmdGet_Click()
   txtTheNumber.Text = m_Customer1.TheNumber
End Sub

C# code:

Code declared in an Activex EXE:

Module1.cs
internal static class Module1
{
	public static string myVar = "";
}
Customer.cs
public class Customer
{
	public int TheNumber
	{
		get
		{
			return g_TheNumber;
		}
		set
		{
			g_TheNumber = value;
		}
	}	
}

Code located in the main application that uses the Activex EXE:

private ExeBillingObjects.Customer m_Customer1 = null;
private ExeBillingObjects.Customer m_Customer2 = null;
			
private void  cmdGet_Click( Object eventSender,  EventArgs eventArgs)
{
	txtTheNumber.Text = m_Customer1.TheNumber.ToString();
}
			
private void  Form1_Load( Object eventSender,  EventArgs eventArgs)
{
	m_Customer1 = new ExeBillingObjects.Customer();
	m_Customer2 = new ExeBillingObjects.Customer();
}

VB.NET code:

Code declared in an Activex EXE:

Module1.vb
Module Module1
	Public myVar As String = ""
End Module
Customer.vb
Public Class Customer
	Public Property TheNumber() As Integer
		Get
			Return g_TheNumber
		End Get
		Set(ByVal Value As Integer)
			g_TheNumber = Value
		End Set
	End Property
End Class

Code located in the main application that uses the Activex EXE:

Private m_Customer1 As ExeBillingObjects.Customer
Private m_Customer2 As ExeBillingObjects.Customer

Private Sub cmdGet_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles cmdGet.Click
	txtTheNumber.Text = CStr(m_Customer1.TheNumber)
End Sub

Private Sub Form1_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	m_Customer1 = New ExeBillingObjects.Customer()
	m_Customer2 = New ExeBillingObjects.Customer()
End Sub

2. Comment Out Dead Code

2.1. On

Private methods that are not referenced will be commented out.

Original VB6 code:

Public Sub PublicMethod()
	Call CalledPrivateMethod
End Sub

Private Sub CalledPrivateMethod()
	MsgBox "This method is called."
End Sub

Private Sub UncalledPrivateMethod()
	MsgBox "This method is not called."
End Sub

C# code:

public void PublicMethod()
{
	CalledPrivateMethod();
}

private void CalledPrivateMethod()
{
	MessageBox.Show("This method is called.", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

//UPGRADE_NOTE: (7001) The following declaration (UncalledPrivateMethod) seems to be dead code
//private void UncalledPrivateMethod()
//{
	//MessageBox.Show("This method is not called.", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
//}

VB.NET code:

Public Sub PublicMethod()
	CalledPrivateMethod()
End Sub

Private Sub CalledPrivateMethod()
	MessageBox.Show("This method is called.", My.Application.Info.Title)
End Sub

'UPGRADE_NOTE: (7001) The following declaration (UncalledPrivateMethod) seems to be dead code'
'Private Sub UncalledPrivateMethod()'
	'MessageBox.Show("This method is not called.", My.Application.Info.Title)'
'End Sub'

2.1. Off

This feature will be turned off and no changes pertaining to this feature will be made.

Original VB6 code:

Public Sub PublicMethod()
	Call CalledPrivateMethod
End Sub

Private Sub CalledPrivateMethod()
	MsgBox "This method is called."
End Sub

Private Sub UncalledPrivateMethod()
	MsgBox "This method is not called."
End Sub

C# code:

public void PublicMethod()
{
	CalledPrivateMethod();
}

private void CalledPrivateMethod()
{
	MessageBox.Show("This method is called.", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

private void UncalledPrivateMethod()
{
	MessageBox.Show("This method is not called.", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

VB.NET code:

Public Sub PublicMethod()
	CalledPrivateMethod()
End Sub

Private Sub CalledPrivateMethod()
	MessageBox.Show("This method is called.", My.Application.Info.Title)
End Sub

Private Sub UncalledPrivateMethod()
	MessageBox.Show("This method is not called.", My.Application.Info.Title)
End Sub

3. Default Property Resolution

3.1. Static code analysis and helpers classes

By switching this feature on, the majority of default property cases that could not be resolved with a static code analysis are managed at runtime by the use of a helper class.

  • Manages special cases of default properties at runtime by using a helper class.

  • Increases the automation ratio and reduces the manual effort required to achieve functional equivalence.

General Description:

The Visual Basic Upgrade Companion uses a powerful static code analyzer to determine the correct type for each object. By having the correct type for each object, the VBUC is able to determine its default property and expand this property in the converted code.

However, there are some cases where it’s not possible to determine the type of an object by using this technique. For these scenarios, the VBUC provides a helper class that is able to determine the type of these objects at runtime and get or set their default property.

In the following example, the variable ctl is used to iterate over a different type of controls. Each of these controls has different default properties, thus, during runtime, the default property of ctl will change. If this example is converted using this option, the helper class will determine the type of each use of the ctl variable at runtime and will get the appropriate default property for each instance.

Original VB6 code:

Dim s As String
'Display the default property of controls of a Form'
For Each ctl In Me.Controls
	s = ctl
	MsgBox s
Next ctl
'Display the default property of TextBox'
MsgBox Me.TextBox1

C# code:

string s = String.Empty;
//Display the default property of controls of a Form
foreach (Control ctl in ContainerHelper.Controls(this))
{
	s = ReflectionHelper.GetPrimitiveValue(ctl).ToString(); 
	MessageBox.Show(s, Application.ProductName);
}
//Display the default property of TextBox
MessageBox.Show(this.TextBox1.Text, Application.ProductName);

VB.NET code:

Dim s As String = ""
'Display the default property of controls of a Form'
For Each ctl As Control In ContainerHelper.Controls(Me)
	s = ReflectionHelper.GetPrimitiveValue(ctl).ToString()
	MessageBox.Show(s, Application.ProductName)
Next ctl
'Display the default property of TextBox'
MessageBox.Show(Me.TextBox1.Text, Application.ProductName)

3.2. Static code analysis only

By switching this feature on, the VBUC will use a powerful static code analysis process to determine the correct type for each object and expands its default property.

  • Expands the default properties by using only static code analysis.

  • There are some cases where it’s not possible to determine the type of an object by using static code analysis and it may require manual intervention to achieve functional equivalence.

General Description:

The Visual Basic Upgrade Companion (VBUC) uses a static code analysis process to infer/determine the correct type for each object. By having the correct type of each object, the VBUC can determine the default property of the object and expand this property in the converted code.

However, there some cases where it is not possible to determine the type of an object by using a static code analysis. For these cases manual changes are required to achieve functional equivalence.

Original VB6 code:

Dim s As String
'Display the default property of controls of a Form'
For Each ctl In Me.Controls
	s = ctl
	MsgBox s
Next ctl
'Display the default property of TextBox'
MsgBox Me.TextBox1

C# code:

string s = String.Empty;
//Display the default property of controls of a Form
foreach (Control ctl in ContainerHelper.Controls(this))
{
	//UPGRADE_WARNING: (1068) ctl of type VB.Control is being forced to string
	s = ctl.ToString();
	MessageBox.Show(s, Application.ProductName);
}
//Display the default property of TextBox
MessageBox.Show(this.TextBox1.Text, Application.ProductName);

VB.NET code:

Dim s As String = ""
'Display the default property of controls of a Form'
For Each ctl As Control In ContainerHelper.Controls(Me)
	'UPGRADE_WARNING: (1068) ctl of type VB.Control is being forced to String.'
	s = ctl.ToString()
	MessageBox.Show(s, Application.ProductName)
Next ctl
'Display the default property of TextBox'
MessageBox.Show(Me.TextBox1.Text, Application.ProductName)

4. Error Handling

4.1. Leave on Error Statements (VB.NET Only)

By choosing this option the VBUC will generate .NET source code that employs the “On Error” statements just like the Visual Basic 6 code.

  • This option increases the automation and reduces the manual effort required to get functional equivalence.

  • This feature is optional while upgrading to Visual Basic .NET but is not available for C#.

General Description:

The “On Error Go To” and “On Error Resume Next” error handling statements present in the Visual Basic 6 code will remain the same on the Visual Basic .NET output code. This feature removes the need for manual changes to bring the error handling statements to functional equivalence since it will be used the same in the upgraded code.

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

VB.NET code:

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

4.2. Convert to Try-Catch

The most common occurrences of error handling patterns will be automatically upgraded to Try-Catch blocks using this feature. It covers a vast amount of On Error Goto patterns and very basic cases of On Error Resume Next.

  • This option generates more .NET-like code.

  • This feature is optional while upgrading to Visual Basic .NET but is compulsory for C# code generation.

General Description:

The Visual Basic Upgrade Companion analyzes the execution control of the Visual Basic 6 code detecting the “On Error GoTo” error handling patterns to generate .NET inherent Try-Catch blocks. When "On Error Resume Next" error handling patterns are found, they will be transformed to a basic Try-Catch block, which will have a different behavior. For the most common scenarios this transformation generates error-free pure .NET code out of the box. For more radical cases, the spaghetti-like error handling code found on Visual Basic 6 might require either some changes prior to the upgrade process or manual efforts once it is upgraded to .NET.

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

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

4.3. To Try-Catch with Lambdas (C# Only)

This feature covers the most common patterns of On Error Goto and, additionally, generates special patterns to support most On Error Resume Next patterns. These special patterns are based on the concept of lambda expressions.

  • This option provides a higher automation ratio, reducing the subsequent required manual work.

  • This feature is available for C# code generation only.

  • The feature will only work for the .NET framework 4.0 or higher / VS2010 or higher.

General Description:

This option applies all the transformations present in the more basic “ErrorHandlingToTryCatch” feature and additionally supports most of the On Error Resume Next patterns by introducing a special pattern based on lambda expressions.

The “On Error Resume Next” functionality can be upgraded to .NET by using a combination of lambda expressions and Try catch blocks. This allows the resulting source code to behave like the legacy source code but on pure .NET technology. This feature can be used only for target Visual Studio solutions that support lambda expressions.

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

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)
{
	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()));});
	}
}

5. Form Load

5.1. FormLoad mechanism upgraded using a helper

These optional features apply special transformation rules to the form load and unload methods in order to provide different functionality over the .NET platform.

  • This approach will use a helper class to expose the load and unload functionality.

  • This approach offers a higher automatic conversion ratio.

General Description:

Visual Basic 6 offered the “load” and “unload” methods to handle how the forms are loaded into memory and displayed. This functionality is not available by default in .NET and requires some extra considerations during the automated migration stage.

The VBUC is equipped with special transformation rules to port the Load and Unload methods and related functionality (show, hide and more) into .NET equivalent constructions by several means.

Original VB6 code:

Public RedColor As Boolean

Private Sub Form_Load()
If RedColor Then
	Text1 = "The selected color is red"
Else
	Text1 = "The selected color is blue"
End If
End Sub

C# code:

//UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load event and has a new behavior.
[FormsHelper.LoadAttribute]
private void Form2_Load(Object eventSender, EventArgs eventArgs)
{
	if (!this.IsFormLoaded())
	{
		this.SetFormLoaded(true, visualControls);
		if (RedColor)
		{
			Text1.Text = "The selected color is red";
		}
		else
		{
			Text1.Text = "The selected color is blue";
		}
	}
}

VB.NET code:

'UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load event and has a new behavior.'
<FormsHelper.LoadAttribute> _
Private Sub Form2_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	If Not Me.IsFormLoaded() Then
		Me.SetFormLoaded(True, visualControls)
		If RedColor Then
			Text1.Text = "The selected color is red"
		Else
			Text1.Text = "The selected color is blue"
		End If
	End If
End Sub

5.2. FormLoad event upgraded as a method invoked from form constructor

These optional features apply special transformation rules to the form load and unload methods in order to provide different functionality over the .NET platform.

  • Upgrade the Load event to a method invoked from the InitializeComponents method.

  • This approach might require manual adjustments to achieve functional equivalence.

General Description:

Visual Basic 6 offered the “load” and “unload” methods to handle how the forms are loaded into memory and displayed. This functionality is not available by default in .NET and requires some extra considerations during the automated migration stage.

The VBUC is equipped with special transformation rules to port the Load and Unload methods and related functionality (show, hide and more) into .NET equivalent constructions by several means.

Original VB6 code:

Public RedColor As Boolean

Private Sub Form_Load()
If RedColor Then
	Text1 = "The selected color is red"
Else
	Text1 = "The selected color is blue"
End If
End Sub

C# code:

//UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load method and has a new behavior.
private void Form_Load()
{
	if (RedColor)
	{
		Text1.Text = "The selected color is red";
	}
	else
	{
		Text1.Text = "The selected color is blue";
	}
}

VB.NET code:

'UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load method and has a new behavior.'
Private Sub Form_Load()
	If RedColor Then
		Text1.Text = "The selected color is red"
	Else
		Text1.Text = "The selected color is blue"
	End If
End Sub

5.3. FormLoad event upgraded to .NET native event

These optional features apply special transformation rules to the form load and unload methods in order to provide different functionality over the .NET platform.

Upgrade the Form load event to .NET inherent load event

General Description:

Visual Basic 6 offered the “load” and “unload” methods to handle how the forms are loaded into memory and displayed. This functionality is not available by default in .NET and requires some extra considerations during the automated migration stage.

The VBUC is equipped with special transformation rules to port the Load and Unload methods and related functionality (show, hide and more) into .NET equivalent constructions by several means.

Original VB6 code:

Public RedColor As Boolean

Private Sub Form_Load()
If RedColor Then
	Text1 = "The selected color is red"
Else
	Text1 = "The selected color is blue"
End If
End Sub

C# code:

//UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load event and has a new behavior.
private void Form2_Load(Object eventSender, EventArgs eventArgs)
{
	if (RedColor)
	{
		Text1.Text = "The selected color is red";
	}
	else
	{
		Text1.Text = "The selected color is blue";
	}
}

VB.NET code:

'UPGRADE_WARNING: (2080) Form_Load event was upgraded to Form_Load event and has a new behavior.'
Private Sub Form2_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	If RedColor Then
		Text1.Text = "The selected color is red"
	Else
		Text1.Text = "The selected color is blue"
	End If
End Sub

6. Free COM Objects

6.1. Free COM Objects memory using helper

This option will cause the resulting code to use a helper class to free the COM objects' allocated memory in the same way as Visual Basic 6.0 does.

  • Generates a call to a helper class in order to release the memory used by COM Objects.

  • This option is not required if all the COM objects are converted to .NET.

General Description:

Visual Basic 6.0 automatically releases the memory used by COM Objects. In .NET, there are some cases that require executing some functions in order to release the memory occupied by COM objects.

If this option is selected the VBUC will generate a call to a helper class in charge of releasing the memory used by COM Objects when a method is finished, a class is being destroyed or a COM reference is changing its value.

Original VB6 code:

Private Sub Command1_Click()
    Dim conConnection As New ADODB.Connection
    Dim cmdCommand As New ADODB.Command
    Dim rstRecordSet As New ADODB.Recordset
    
    conConnection.Open
    cmdCommand.CommandText = "SELECT * FROM TestTable;"
    rstRecordSet.Open
End Sub

C# code:

private void  Command1_Click( Object eventSender,  EventArgs eventArgs)
{
	ADODB.Connection conConnection = null;
	ADODB.Command cmdCommand = null;
	ADODB.Recordset rstRecordSet = null;
	try
	{
		conConnection = new ADODB.Connection();
		cmdCommand = new ADODB.Command();
		rstRecordSet = new ADODB.Recordset();
						
		conConnection.Open("", "", "", -1);
		cmdCommand.CommandText = "SELECT * FROM TestTable;";
		rstRecordSet.Open(Type.Missing, Type.Missing, 
		ADODB.CursorTypeEnum.adOpenUnspecified, ADODB.LockTypeEnum.adLockUnspecified, -1);
	}
	finally
	{
		MemoryHelper.ReleaseAndCleanObject(rstRecordSet);
		rstRecordSet = null;
		MemoryHelper.ReleaseAndCleanObject(cmdCommand);
		cmdCommand = null;
		MemoryHelper.ReleaseAndCleanObject(conConnection);
		conConnection = null;
	}								
}

VB.NET code:

Private Sub Command1_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs)
	Dim conConnection As New ADODB.Connection
	Dim cmdCommand As New ADODB.Command
	Dim rstRecordSet As New ADODB.Recordset
	Try
		conConnection.Open()
		cmdCommand.CommandText = "SELECT * FROM TestTable;"
		rstRecordSet.Open()
	Finally
		MemoryHelper.ReleaseAndCleanObject(rstRecordSet)
		rstRecordSet = Nothing
		MemoryHelper.ReleaseAndCleanObject(cmdCommand)
		cmdCommand = Nothing
		MemoryHelper.ReleaseAndCleanObject(conConnection)
		conConnection = Nothing
	End Try
End Sub

6.2. Free COM Objects memory manually

By using this option the VBUC will not generate special code to handle the COM Objects memory usage.

The VBUC will not generate any special code to release memory used by COM Objects.

General Description:

Visual Basic 6.0 automatically releases the memory used by COM Objects. In .NET, there are some cases that require executing some functions in order to release the memory occupied by COM objects

If this option is selected, the VBUC will not generate any special code to release the memory used by COM Objects and manual changes might be required to make sure that all the references to the COM Objects are properly released.

Original VB6 code:

Private Sub Command1_Click()
Dim conConnection As New ADODB.Connection
Dim cmdCommand As New ADODB.Command
Dim rstRecordSet As New ADODB.Recordset

conConnection.Open
cmdCommand.CommandText = "SELECT * FROM TestTable;"
rstRecordSet.Open
End Sub

C# code:

private void  Command1_Click( Object eventSender,  EventArgs eventArgs)
{
	ADODB.Connection conConnection = new ADODB.Connection();
	ADODB.Command cmdCommand = new ADODB.Command();
	ADODB.Recordset rstRecordSet = new ADODB.Recordset();
					
	conConnection.Open("", "", "", -1);
	cmdCommand.CommandText = "SELECT * FROM TestTable;";
	rstRecordSet.Open(Type.Missing, Type.Missing, ADODB.CursorTypeEnum.adOpenUnspecified, ADODB.LockTypeEnum.adLockUnspecified, -1);
}

VB.NET code:

Private Sub Command1_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles Command1.Click
	Dim conConnection As New ADODB.Connection
	Dim cmdCommand As New ADODB.Command
	Dim rstRecordSet As New ADODB.Recordset
		
	conConnection.Open()
	cmdCommand.CommandText = "SELECT * FROM TestTable;"
	rstRecordSet.Open()
End Sub

7. Convert HelpFile and HelpContexID Feature

7.1 Do not Convert HelpFile and HelpContextID Feature

Choosing this option, the VBUC will not apply any special pattern or behavior. Help File and HelpContextID VB6 features statements are not supported by default in .NET structured code, so VBUC solve this feature with a stub.

Original VB6 code

Private Sub Form_Load()
    App.HelpFile = "clcshelp.chm"
End Sub

C# code

private void Form_Load()
{
	//UPGRADE_ISSUE: (2064) VB method VB.Global was not upgraded. More Information: https://docs.mobilize.net/vbuc/ewis#2064
	//UPGRADE_ISSUE: (2070) Constant App was not upgraded. More Information: https://docs.mobilize.net/vbuc/ewis#2070
	//UPGRADE_ISSUE: (2064) App property App.HelpFile was not upgraded. More Information: https://docs.mobilize.net/vbuc/ewis#2064
	UpgradeStubs.VB.getGlobal().getApp().setHelpFile("clcshelp.chm");
}

VB.Net code

Private Sub Form_Load()
    'UPGRADE_ISSUE: (2064) VB method VB.Global was not upgraded. More Information: https://docs.mobilize.net/vbuc/ewis#2064
    'UPGRADE_ISSUE: (2070) Constant App was not upgraded. More Information: https://docs.mobilize.net/vbuc/ewis#2070
    'UPGRADE_ISSUE: (2064) App property App.HelpFile was not upgraded. More Information: https://docs.mobilize.net/vbuc/ewis#2064
    UpgradeSolution1Support.UpgradeStubs.VB.getGlobal().getApp().setHelpFile("clcshelp.chm")
End Sub

7.2 Convert HelpFile and HelpContextID Feature using Helper Class

Enable this feature provides a helper class to raise an information event for each control with HelpContextID property defined.

  • Generates an event for each control with HelpContextID

  • Verifies if .chm help file exists

General Description

Visual Basic 6 has offered the possibility to use HelpContextID and a .chm file to display information about the current form and controls. That feature does not exist in .Net, so Visual Basic Upgrade Companion provides the opportunity to use a Helper class to have a relative behavior.

The Helper Class transforms the number of HelpContextID, assigning an event for each control with the property defined and checking if the .chm file exists.

Original VB6 Code

Private Sub Form_Load()
    App.HelpFile = "clcshelp.chm"
End Subvb

C# code:

//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
private void Form_Load()
{
	UpgradeSupport.helpSupport.HelpFile = "clcshelp.chm";
}
//In Designer File
protected void InitializeHelp()
{
	UpgradeSupport.helpSupport.SetHelpContextId(this, 523);
	UpgradeSupport.helpSupport.SetHelpContextId(Slider1, 509);
}

VB.NET Code

'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
Private Sub Form_Load()
	UpgradeSupport.helpSupport.HelpFile = "clcshelp.chm"
End Sub
'In Designer File
Protected Sub InitializeHelp()
	UpgradeSupport.helpSupport.SetHelpContextId(Me, 523)
	UpgradeSupport.helpSupport.SetHelpContextId(Slider1, 509)
End Sub

8. Late Binding Resolution

8.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.

  • Manage late-bound reference scenarios by using a helper class.

  • This option increases the automation ratio and reduces the manual effort required to get functional equivalence.

General Description:

Visual Basic 6.0 provides the ability to use late-bound variable references. This means the variables may have not been declared with their actual type, but with a generic type. E.g. “control” instead of “label”, “form” instead of “form1” or “variant” instead of any other type. At runtime Visual Basic 6 determines the actual type of the variable and invokes the appropriate methods and/or members. On the other hand, .NET will throw a compilation error whenever a member is not in the declared type of the variable.

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 the VBUC provides a helper class that is able to determine the type of these objects at runtime and invoke or get the appropriate member.

This helper class reduces the amount of compilation errors in the resulting source code and provides an equivalent behavior in the most common scenarios. Nevertheless, not all the cases will achieve 100% functional equivalence since the upgrade process lacks information about object data type and its members during the application runtime.

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

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

8.2. Static code analysis only

By selecting this option, the VBUC will use its static code analysis process to determine the correct type for each object and resolve the late binding access.

  • Resolve Late Binding Access by using only static code analysis.

  • There are some cases where it is not possible to determine the type of an object by using a static code analysis and it may require manual intervention.

General Description:

Visual Basic 6.0 provides the ability to use late-bound variable references. This means the variables may have not been declared with their actual type, but with a generic type. E.g. “control” instead of “label”, “form” instead of “form1” or “variant” instead of any other type. At runtime Visual Basic 6 determines the actual type of the variable and invokes the appropriate methods and/or members. On the other hand, .NET will throw a compilation error whenever a member is not in the declared type of the variable.

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.

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

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

8.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.

  • Manage late-bound reference scenarios by using dynamic variables.

  • This option increases the automation ratio and reduces the manual effort required to get functional equivalence.

General Description:

Visual Basic 6.0 provides the ability to use late-bound variable references. This means the variables may have not been declared with their actual type, but with a generic type. E.g. “control” instead of “label”, “form” instead of “form1” or “variant” instead of any other type. At runtime Visual Basic 6 determines the actual type of the variable and invokes the appropriate methods and/or members. On the other hand, .NET will throw a compilation error whenever a member is not in the declared type of the variable.

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 establish a proper non-generic type. For these cases the VBUC will add castings to dynamic to every member access that needs it, therefore allowing most of the cases to be resolved at runtime, as VB6 does.

With this feature, when C# is the target language, variables with generic types such as object, control, usercontrol will have their uses resolved at runtime; thus, providing more functional equivalence in the code. The dynamic type does not exist in VB.NET; however, we provide its counterpart, which is to have Option Strict Off and apply the function CType(myVar, object) to those member accesses, that allows late binding on object types.

This option is the alternative of resolving late-binding-access with Helper Classes (Reflection Helper). The use of the Reflection Helper adds a lot of verbosity to the migrated code and therefore we provide this option as well. However, there is functionality implemented in our helper class that cannot be accomplished with the use of dynamic variables. For example, we fix at runtime a lot of members of multiple common classes in VB6 to their equivalent in .NET. The VBUC user should be aware of such limitation when turning this feature on.

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

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

9. Line and Shape

9.1. Helper class

If this option is selected, the VBUC will upgrade all the “line” control references to the inherent .NET label control.

  • The resulting code may require manual efforts to achieve functional equivalence.

  • By using this option the converted application will not have any reference to the legacy COM component.

Class

Maps to

vb.line

System.Windows.Forms.Label

vb.shape

UpgradeHelpers.Gui.ShapeHelper

Original VB6 code:

...
   Begin VB.Shape Shape2 
      Height          =   855
      Left            =   4320
      Shape           =   2  'Oval
      Top             =   1320
      Width           =   1935
   End
   ...
   Begin VB.Line Line4 
      X1              =   480
      X2              =   4080
      Y1              =   3240
      Y2              =   3240
   End
   Begin VB.Line Line3 
      X1              =   4080
      X2              =   4080
      Y1              =   240
      Y2              =   3240
   End
...

C# code:

partial class Form1
{
	...
	public UpgradeHelpers.Gui.ShapeHelper Shape2;
	public UpgradeHelpers.Gui.ShapeHelper Shape1;
	public System.Windows.Forms.Label Line4;
	public System.Windows.Forms.Label Line3;
	public System.Windows.Forms.Label Line2;
	public System.Windows.Forms.Label Line1;
	
	private void InitializeComponent()
	{
		...
		this.Shape2 = new UpgradeHelpers.Gui.ShapeHelper();
		this.Shape1 = new UpgradeHelpers.Gui.ShapeHelper();
		this.Line4 = new System.Windows.Forms.Label();
		this.Line3 = new System.Windows.Forms.Label();
		this.Line2 = new System.Windows.Forms.Label();
		this.Line1 = new System.Windows.Forms.Label();
		...
		// 
		// Shape2
		// 
		this.Shape2.AllowDrop = true;
		this.Shape2.BackColor = System.Drawing.SystemColors.Window;
		this.Shape2.BackStyle = 0;
		this.Shape2.BorderStyle = 1;
		this.Shape2.Enabled = false;
		this.Shape2.FillColor = System.Drawing.Color.Black;
		this.Shape2.FillStyle = 1;
		this.Shape2.Location = new System.Drawing.Point(288, 88);
		this.Shape2.Name = "Shape2";
		this.Shape2.Shape = 2;
		this.Shape2.Size = new System.Drawing.Size(129, 57);
		this.Shape2.Visible = true;
		...
		// 
		// Line4
		// 
		this.Line4.AllowDrop = true;
		this.Line4.BackColor = System.Drawing.SystemColors.WindowText;
		this.Line4.Enabled = false;
		this.Line4.Location = new System.Drawing.Point(32, 216);
		this.Line4.Name = "Line4";
		this.Line4.Size = new System.Drawing.Size(240, 1);
		this.Line4.Visible = true;
		// 
		// Line3
		// 
		this.Line3.AllowDrop = true;
		this.Line3.BackColor = System.Drawing.SystemColors.WindowText;
		this.Line3.Enabled = false;
		this.Line3.Location = new System.Drawing.Point(272, 16);
		this.Line3.Name = "Line3";
		this.Line3.Size = new System.Drawing.Size(1, 200);
		this.Line3.Visible = true;
		...
		}
	}
}

VB.NET code:

Partial Class Form1
	...
	Public WithEvents Shape2 As UpgradeHelpers.Gui.ShapeHelper
	Public WithEvents Shape1 As UpgradeHelpers.Gui.ShapeHelper
	Public WithEvents Line4 As System.Windows.Forms.Label
	Public WithEvents Line3 As System.Windows.Forms.Label
	Public WithEvents Line2 As System.Windows.Forms.Label
	Public WithEvents Line1 As System.Windows.Forms.Label
	...
		Me.Shape2 = New UpgradeHelpers.Gui.ShapeHelper()
		Me.Shape1 = New UpgradeHelpers.Gui.ShapeHelper()
		Me.Line4 = New System.Windows.Forms.Label()
		Me.Line3 = New System.Windows.Forms.Label()
		Me.Line2 = New System.Windows.Forms.Label()
		Me.Line1 = New System.Windows.Forms.Label()
		...
		''
		'Shape2
		' 
		Me.Shape2.AllowDrop = True
		Me.Shape2.BackColor = System.Drawing.SystemColors.Window
		Me.Shape2.BackStyle = 0
		Me.Shape2.BorderStyle = 1
		Me.Shape2.Enabled = False
		Me.Shape2.FillColor = System.Drawing.Color.Black
		Me.Shape2.FillStyle = 1
		Me.Shape2.Location = New System.Drawing.Point(288, 88)
		Me.Shape2.Name = "Shape2"
		Me.Shape2.Shape = 2
		Me.Shape2.Size = New System.Drawing.Size(129, 57)
		Me.Shape2.Visible = True
		...
		'' 
		'Line4
		' 
		Me.Line4.AllowDrop = True
		Me.Line4.BackColor = System.Drawing.SystemColors.WindowText
		Me.Line4.Enabled = False
		Me.Line4.Location = New System.Drawing.Point(32, 216)
		Me.Line4.Name = "Line4"
		Me.Line4.Size = New System.Drawing.Size(240, 1)
		Me.Line4.Visible = True
		'' 
		'Line3
		' 
		Me.Line3.AllowDrop = True
		Me.Line3.BackColor = System.Drawing.SystemColors.WindowText
		Me.Line3.Enabled = False
		Me.Line3.Location = New System.Drawing.Point(272, 16)
		Me.Line3.Name = "Line3"
		Me.Line3.Size = New System.Drawing.Size(1, 200)
		Me.Line3.Visible = True
		...
End Class

9.2. PowerPack and Helper class

By switching this optional feature on, the VBUC will convert the legacy “line” control to a .NET equivalent located under the Microsoft Visual Basic Power Packs library.

This feature requires the installation of the Visual Basic Power Packs libraries.

In order to use this functionality, the Visual Basic Power Packs must be installed on the machine. The latest Visual Basic Power Packs can be downloaded from here: http://msdn.microsoft.com/en-us/vbasic/bb735936

Class

Maps to

vb.line

Microsoft.VisualBasic.PowerPacks.LineShape

vb.shape

Microsoft.VisualBasic.PowerPacks.RectangleShape / Microsoft.VisualBasic.PowerPacks.OvalShape

Original VB6 code:

...
   Begin VB.Shape Shape2 
      Height          =   855
      Left            =   4320
      Shape           =   2  'Oval
      Top             =   1320
      Width           =   1935
   End
   ...
   Begin VB.Line Line4 
      X1              =   480
      X2              =   4080
      Y1              =   3240
      Y2              =   3240
   End
   Begin VB.Line Line3 
      X1              =   4080
      X2              =   4080
      Y1              =   240
      Y2              =   3240
   End
...

C# code:

partial class Form1
{
	...
	public Microsoft.VisualBasic.PowerPacks.OvalShape Shape2;
	public Microsoft.VisualBasic.PowerPacks.RectangleShape Shape1;
	public Microsoft.VisualBasic.PowerPacks.LineShape Line4;
	public Microsoft.VisualBasic.PowerPacks.LineShape Line3;
	public Microsoft.VisualBasic.PowerPacks.LineShape Line2;
	public Microsoft.VisualBasic.PowerPacks.LineShape Line1;
	public Microsoft.VisualBasic.PowerPacks.ShapeContainer ShapeContainer1;
	
	private void InitializeComponent()
	{
		this.Shape2 = new Microsoft.VisualBasic.PowerPacks.OvalShape();
		this.Shape1 = new Microsoft.VisualBasic.PowerPacks.RectangleShape();
		this.Line4 = new Microsoft.VisualBasic.PowerPacks.LineShape();
		this.Line3 = new Microsoft.VisualBasic.PowerPacks.LineShape();
		this.Line2 = new Microsoft.VisualBasic.PowerPacks.LineShape();
		this.Line1 = new Microsoft.VisualBasic.PowerPacks.LineShape();
		...
		// 
		// ShapeContainer1
		// 
		this.ShapeContainer1.Location = new System.Drawing.Point(0, 0);
		this.ShapeContainer1.Size = new System.Drawing.Size(434, 239);
		this.ShapeContainer1.Shapes.Add(Shape2);
		this.ShapeContainer1.Shapes.Add(Shape1);
		this.ShapeContainer1.Shapes.Add(Line4);
		this.ShapeContainer1.Shapes.Add(Line3);
		this.ShapeContainer1.Shapes.Add(Line2);
		this.ShapeContainer1.Shapes.Add(Line1);
		// 
		// Shape2
		// 
		this.Shape2.BackColor = System.Drawing.SystemColors.Window;
		this.Shape2.BackStyle = Microsoft.VisualBasic.PowerPacks.BackStyle.Transparent;
		this.Shape2.BorderColor = System.Drawing.SystemColors.WindowText;
		this.Shape2.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid;
		this.Shape2.BorderWidth = 1;
		this.Shape2.Enabled = false;
		this.Shape2.FillColor = System.Drawing.Color.Black;
		this.Shape2.FillStyle = Microsoft.VisualBasic.PowerPacks.FillStyle.Transparent;
		this.Shape2.Location = new System.Drawing.Point(288, 88);
		this.Shape2.Name = "Shape2";
		this.Shape2.Size = new System.Drawing.Size(129, 57);
		this.Shape2.Visible = true;
		...
		// 
		// Line4
		// 
		this.Line4.BorderColor = System.Drawing.SystemColors.WindowText;
		this.Line4.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid;
		this.Line4.BorderWidth = 1;
		this.Line4.Enabled = false;
		this.Line4.Name = "Line4";
		this.Line4.Visible = true;
		this.Line4.X1 = 32;
		this.Line4.X2 = 272;
		this.Line4.Y1 = 216;
		this.Line4.Y2 = 216;
		// 
		// Line3
		// 
		this.Line3.BorderColor = System.Drawing.SystemColors.WindowText;
		this.Line3.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid;
		this.Line3.BorderWidth = 1;
		this.Line3.Enabled = false;
		this.Line3.Name = "Line3";
		this.Line3.Visible = true;
		this.Line3.X1 = 272;
		this.Line3.X2 = 272;
		this.Line3.Y1 = 16;
		this.Line3.Y2 = 216;
		...		
	}
}

VB.NET code:

Partial Class Form1
	Public WithEvents Shape2 As Microsoft.VisualBasic.PowerPacks.OvalShape
	Public WithEvents Shape1 As Microsoft.VisualBasic.PowerPacks.RectangleShape
	Public WithEvents Line4 As Microsoft.VisualBasic.PowerPacks.LineShape
	Public WithEvents Line3 As Microsoft.VisualBasic.PowerPacks.LineShape
	Public WithEvents Line2 As Microsoft.VisualBasic.PowerPacks.LineShape
	Public WithEvents Line1 As Microsoft.VisualBasic.PowerPacks.LineShape
	Public WithEvents ShapeContainer1 As Microsoft.VisualBasic.PowerPacks.ShapeContainer
	...
		Me.ShapeContainer1 = New Microsoft.VisualBasic.PowerPacks.ShapeContainer()
		Me.Shape2 = New Microsoft.VisualBasic.PowerPacks.OvalShape()
		Me.Shape1 = New Microsoft.VisualBasic.PowerPacks.RectangleShape()
		Me.Line4 = New Microsoft.VisualBasic.PowerPacks.LineShape()
		Me.Line3 = New Microsoft.VisualBasic.PowerPacks.LineShape()
		Me.Line2 = New Microsoft.VisualBasic.PowerPacks.LineShape()
		Me.Line1 = New Microsoft.VisualBasic.PowerPacks.LineShape()
		...
		'' 
		'ShapeContainer1
		' 
		Me.ShapeContainer1.Location = New System.Drawing.Point(0, 0)
		Me.ShapeContainer1.Size = New System.Drawing.Size(434, 239)
		Me.ShapeContainer1.Shapes.Add(Shape2)
		Me.ShapeContainer1.Shapes.Add(Shape1)
		Me.ShapeContainer1.Shapes.Add(Line4)
		Me.ShapeContainer1.Shapes.Add(Line3)
		Me.ShapeContainer1.Shapes.Add(Line2)
		Me.ShapeContainer1.Shapes.Add(Line1)
		'' 
		'Shape2
		' 
		Me.Shape2.BackColor = System.Drawing.SystemColors.Window
		Me.Shape2.BackStyle = Microsoft.VisualBasic.PowerPacks.BackStyle.Transparent
		Me.Shape2.BorderColor = System.Drawing.SystemColors.WindowText
		Me.Shape2.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid
		Me.Shape2.BorderWidth = 1
		Me.Shape2.Enabled = False
		Me.Shape2.FillColor = System.Drawing.Color.Black
		Me.Shape2.FillStyle = Microsoft.VisualBasic.PowerPacks.FillStyle.Transparent
		Me.Shape2.Location = New System.Drawing.Point(288, 88)
		Me.Shape2.Name = "Shape2"
		Me.Shape2.Size = New System.Drawing.Size(129, 57)
		Me.Shape2.Visible = True
		...
		'' 
		'Line4
		' 
		Me.Line4.BorderColor = System.Drawing.SystemColors.WindowText
		Me.Line4.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid
		Me.Line4.BorderWidth = 1
		Me.Line4.Enabled = False
		Me.Line4.Name = "Line4"
		Me.Line4.Visible = True
		Me.Line4.X1 = 32
		Me.Line4.X2 = 272
		Me.Line4.Y1 = 216
		Me.Line4.Y2 = 216
		'' 
		'Line3
		' 
		Me.Line3.BorderColor = System.Drawing.SystemColors.WindowText
		Me.Line3.BorderStyle = System.Drawing.Drawing2D.DashStyle.Solid
		Me.Line3.BorderWidth = 1
		Me.Line3.Enabled = False
		Me.Line3.Name = "Line3"
		Me.Line3.Visible = True
		Me.Line3.X1 = 272
		Me.Line3.X2 = 272
		Me.Line3.Y1 = 16
		Me.Line3.Y2 = 216
		...
End Class

10. String Functions

Conversion options for Strings Mid, left and right functions.

10.1. Helper class functions

This option enables the VBUC to convert the String related functions Mid, Left, and Right, located in the VBA.Strings library, to functions in a helper class.

Original VB6 code:

Private Sub foo()
	Dim s As String
	Dim theLength As Integer
	
	s = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name"
	
	Dim select_index As Integer
	
	s = Trim(s)
	selectString = VBA.Strings.Left(s, 6)
	selectString = LCase(selectString)
	
	If selectString = "select" Then
	Else
		'error'
	End If
	
	Dim selectIndex As Integer
	selectIndex = VBA.Strings.InStr(1, s, "select ")
	selectIndex = selectIndex + Len("select")
	s = VBA.Strings.Mid(s, selectIndex, Len(s))
	Dim fromIndex As Integer
	fromIndex = VBA.Strings.InStr(1, s, "from ")
	Dim fromString As String
	fromString = VBA.Strings.Right(s, Len(s) - (fromIndex - 1))
	s = VBA.Strings.Replace(s, fromString, "")
	Dim theMembersArray() As String
	s = Trim(s)
	theMembersArray = VBA.Strings.Split(s, ",")
	Dim index As Integer
	Dim arrayLength As Integer
	theLBound = LBound(theMembersArray)
	theUBound = UBound(theMembersArray)
	For index = theLBound To theUBound
		theMembersArray(index) = Trim(theMembersArray(index))
	Next index
End Sub

C# code:

private void foo()
{
	object theUBound = null;
	object theLBound = null;
	object selectString = null;

	string s = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name";

	s = s.Trim();
	selectString = StringsHelper.Left(s, 6);
	selectString = ReflectionHelper.GetPrimitiveValue<string>(selectString).ToLower();

	if (ReflectionHelper.GetPrimitiveValue<string>(selectString) == "select")
	{
	}
	else
	{
		//error
	}

	int selectIndex = (s.IndexOf("select ") + 1);
	selectIndex += Strings.Len("select");
	s = StringsHelper.Mid(s, selectIndex, Strings.Len(s));
	int fromIndex = (s.IndexOf("from ") + 1);
	string fromString = StringsHelper.Right(s, Strings.Len(s) - (fromIndex - 1));
	s = Strings.Replace(s, fromString, "", 1, -1, CompareMethod.Binary);
	s = s.Trim();
	string[] theMembersArray = (string[]) s.Split(',');
	theLBound = theMembersArray.GetLowerBound(0);
	theUBound = theMembersArray.GetUpperBound(0);
	//UPGRADE_WARNING: (1068) theUBound of type Variant is being forced to Scalar.
	object tempForEndVar = ReflectionHelper.GetPrimitiveValue(theUBound);
	for (int index = ReflectionHelper.GetPrimitiveValue<int>(theLBound); index <= ReflectionHelper.GetPrimitiveValue<double>(tempForEndVar); index++)
	{
		theMembersArray[index] = theMembersArray[index].Trim();
	}

}

VB.NET code:

Private Sub foo()
	Dim theUBound, theLBound, selectString As Object

	Dim s As String = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name"

	s = s.Trim()
	selectString = StringsHelper.Left(s, 6)
	selectString = ReflectionHelper.GetPrimitiveValue(Of String)(selectString).ToLower()

	If ReflectionHelper.GetPrimitiveValue(Of String)(selectString) = "select" Then
	Else
		'error'
	End If

	Dim selectIndex As Integer = (s.IndexOf("select ") + 1)
	selectIndex += Strings.Len("select")
	s = StringsHelper.Mid(s, selectIndex, Strings.Len(s))
	Dim fromIndex As Integer = (s.IndexOf("from ") + 1)
	Dim fromString As String = StringsHelper.Right(s, Strings.Len(s) - (fromIndex - 1))
	s = s.Replace(fromString, "")
	s = s.Trim()
	Dim theMembersArray() As String = s.Split(","c)
	theLBound = theMembersArray.GetLowerBound(0)
	theUBound = theMembersArray.GetUpperBound(0)
	'UPGRADE_WARNING: (1068) theUBound of type Variant is being forced to Scalar.'
	Dim tempForEndVar As Object = ReflectionHelper.GetPrimitiveValue(theUBound)
	For index As Integer = ReflectionHelper.GetPrimitiveValue(Of Integer)(theLBound) To ReflectionHelper.GetPrimitiveValue(Of Integer)(tempForEndVar)
		theMembersArray(index) = theMembersArray(index).Trim()
	Next index
End Sub

10.2. Native .NET System.String class functions

This option enables the VBUC to convert the String related functions Mid, Left, and Right, located in the VBA.Strings library, to native .NET String class functions.

This feature generates more .NET-like code.

Original VB6 code:

Private Sub foo()
	Dim s As String
	Dim theLength As Integer
	
	s = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name"
	
	Dim select_index As Integer
	
	s = Trim(s)
	selectString = VBA.Strings.Left(s, 6)
	selectString = LCase(selectString)
	
	If selectString = "select" Then
	Else
		'error'
	End If
	
	Dim selectIndex As Integer
	selectIndex = VBA.Strings.InStr(1, s, "select ")
	selectIndex = selectIndex + Len("select")
	s = VBA.Strings.Mid(s, selectIndex, Len(s))
	Dim fromIndex As Integer
	fromIndex = VBA.Strings.InStr(1, s, "from ")
	Dim fromString As String
	fromString = VBA.Strings.Right(s, Len(s) - (fromIndex - 1))
	s = VBA.Strings.Replace(s, fromString, "")
	Dim theMembersArray() As String
	s = Trim(s)
	theMembersArray = VBA.Strings.Split(s, ",")
	Dim index As Integer
	Dim arrayLength As Integer
	theLBound = LBound(theMembersArray)
	theUBound = UBound(theMembersArray)
	For index = theLBound To theUBound
		theMembersArray(index) = Trim(theMembersArray(index))
	Next index
End Sub

C# code:

private void foo()
{
	string s = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name";

	s = s.Trim();
	string selectString = s.Substring(0, Math.Min(6, s.Length));
	selectString = selectString.ToLower();

	if (selectString == "select")
	{
	}
	else
	{
		//error
	}

	int selectIndex = (s.IndexOf("select ") + 1);
	selectIndex += Strings.Len("select");
	s = s.Substring(selectIndex - 1, Math.Min(Strings.Len(s), s.Length - (selectIndex - 1)));
	int fromIndex = (s.IndexOf("from ") + 1);
	string fromString = s.Substring(Math.Max(s.Length - (Strings.Len(s) - (fromIndex - 1)), 0));
	s = StringsHelper.Replace(s, fromString, "", 1, -1, CompareMethod.Binary);
	s = s.Trim();
	string[] theMembersArray = (string[]) s.Split(',');
	int theLBound = theMembersArray.GetLowerBound(0);
	int theUBound = theMembersArray.GetUpperBound(0);
	int tempForEndVar = theUBound;
	for (int index = theLBound; index <= tempForEndVar; index++)
	{
	theMembersArray[index] = theMembersArray[index].Trim();
	}
}

VB.NET code:

Private Sub foo()

	Dim s As String = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name"

	s = s.Trim()
	Dim selectString As String = s.Substring(0, Math.Min(6, s.Length))
	selectString = selectString.ToLower()

	If selectString = "select" Then
	Else
		'error'
	End If

	Dim selectIndex As Integer = (s.IndexOf("select ") + 1)
	selectIndex += Strings.Len("select")
	s = s.Substring(selectIndex - 1, Math.Min(Strings.Len(s), s.Length - (selectIndex - 1)))
	Dim fromIndex As Integer = (s.IndexOf("from ") + 1)
	Dim fromString As String = s.Substring(Math.Max(s.Length - (Strings.Len(s) - (fromIndex - 1)), 0))
	s = s.Replace(fromString, "")
	s = s.Trim()
	Dim theMembersArray() As String = s.Split(","c)
	Dim theLBound As Integer = theMembersArray.GetLowerBound(0)
	Dim theUBound As Integer = theMembersArray.GetUpperBound(0)
	For index As Integer = theLBound To theUBound
		theMembersArray(index) = theMembersArray(index).Trim()
	Next index
End Sub

10.3. Support function in Microsoft.VisualBasic.Compatibility.VB6.Support namespace

Converts Mid, Left, and Right vba.Strings functions to support functions available in the Microsoft.VisualBasic.Compatibility.VB6.Support namespace.

By using this option the converted application will not have any reference to the COM Component.

Original VB6 code:

Private Sub foo()
	Dim s As String
	Dim theLength As Integer
	
	s = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name"
	
	Dim select_index As Integer
	
	s = Trim(s)
	selectString = VBA.Strings.Left(s, 6)
	selectString = LCase(selectString)
	
	If selectString = "select" Then
	Else
		'error'
	End If
	
	Dim selectIndex As Integer
	selectIndex = VBA.Strings.InStr(1, s, "select ")
	selectIndex = selectIndex + Len("select")
	s = VBA.Strings.Mid(s, selectIndex, Len(s))
	Dim fromIndex As Integer
	fromIndex = VBA.Strings.InStr(1, s, "from ")
	Dim fromString As String
	fromString = VBA.Strings.Right(s, Len(s) - (fromIndex - 1))
	s = VBA.Strings.Replace(s, fromString, "")
	Dim theMembersArray() As String
	s = Trim(s)
	theMembersArray = VBA.Strings.Split(s, ",")
	Dim index As Integer
	Dim arrayLength As Integer
	theLBound = LBound(theMembersArray)
	theUBound = UBound(theMembersArray)
	For index = theLBound To theUBound
		theMembersArray(index) = Trim(theMembersArray(index))
	Next index
End Sub

C# code:

private void foo()
{
	string s = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name";

	s = s.Trim();
	string selectString = Strings.Left(s, 6);
	selectString = selectString.ToLower();

	if (selectString == "select")
	{
	}
	else
	{
		//error
	}

	int selectIndex = (s.IndexOf("select ") + 1);
	selectIndex += Strings.Len("select");
	s = Strings.Mid(s, selectIndex, Strings.Len(s));
	int fromIndex = (s.IndexOf("from ") + 1);
	string fromString = Strings.Right(s, Strings.Len(s) - (fromIndex - 1));
	s = StringsHelper.Replace(s, fromString, "", 1, -1, CompareMethod.Binary);
	s = s.Trim();
	string[] theMembersArray = (string[]) s.Split(',');
	int theLBound = theMembersArray.GetLowerBound(0);
	int theUBound = theMembersArray.GetUpperBound(0);
	int tempForEndVar = theUBound;
	for (int index = theLBound; index <= tempForEndVar; index++)
	{
		theMembersArray[index] = theMembersArray[index].Trim();
	}
}

VB.NET code:

Private Sub foo()

	Dim s As String = "select Employee_Id, Fist_Name, Middle_Name, Last_Name, Birthday, Salary from Employee order by Last_Name, First_Name, Middle_Name"

	s = s.Trim()
	Dim selectString As String = Strings.Left(s, 6)
	selectString = selectString.ToLower()

	If selectString = "select" Then
	Else
		'error'
	End If

	Dim selectIndex As Integer = (s.IndexOf("select ") + 1)
	selectIndex += Strings.Len("select")
	s = Strings.Mid(s, selectIndex, Strings.Len(s))
	Dim fromIndex As Integer = (s.IndexOf("from ") + 1)
	Dim fromString As String = Strings.Right(s, Strings.Len(s) - (fromIndex - 1))
	s = s.Replace(fromString, "")
	s = s.Trim()
	Dim theMembersArray() As String = s.Split(","c)
	Dim theLBound As Integer = theMembersArray.GetLowerBound(0)
	Dim theUBound As Integer = theMembersArray.GetUpperBound(0)
	For index As Integer = theLBound To theUBound
		theMembersArray(index) = theMembersArray(index).Trim()
	Next index
End Sub

11. Generate Skeletons

11.1. On

This feature will generate skeleton classes, which will contain empty methods. All code inside the methods will be removed.

Original VB6 code:

Private Sub Form_Load()
    Dim x As Integer
    x = 1
End Sub

Public Sub PublicMethod()
    Dim y As Integer
    y = 1
End Sub

Private Sub PrivateMethod()
    Dim z As Integer
    z = 1
End Sub

C# code:

private void Form_Load(Object eventSender, EventArgs eventArgs)
{
	//Skeletons Generation
}

public void PublicMethod()
{
	//Skeletons Generation
}

private void PrivateMethod()
{
	//Skeletons Generation
}

VB.NET code:

Private Sub Form_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	'Skeletons Generation'
End Sub

Public Sub PublicMethod()
	'Skeletons Generation'
End Sub

Private Sub PrivateMethod()
	'Skeletons Generation'
End Sub

11.2. Off

This feature will be turned off and no changes pertaining to this feature will be made.

Original VB6 code:

Private Sub Form_Load()
    Dim x As Integer
    x = 1
End Sub

Public Sub PublicMethod()
    Dim y As Integer
    y = 1
End Sub

Private Sub PrivateMethod()
    Dim z As Integer
    z = 1
End Sub

C# code:

private void Form_Load(Object eventSender, EventArgs eventArgs)
{
	int x = 1;
}

public void PublicMethod()
{
	int y = 1;
}

private void PrivateMethod()
{
	int z = 1;
}

VB.NET code:

Private Sub Form_Load(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles MyBase.Load
	Dim x As Integer = 1
End Sub

Public Sub PublicMethod()
	Dim y As Integer = 1
End Sub

Private Sub PrivateMethod()
	Dim z As Integer = 1
End Sub

12. Stubs Generation

12.1. Upgrade Stubs generation for non upgraded element

During the upgrade process, every non-updated element will include a comment in the resulting source code indicating that the expression/statement was not converted. The Upgrade Stubs Generation feature creates a dummy (stub) declaration for each kind of these elements.

  • It makes all the not supported elements compile, avoiding an important amount of compilation errors and makes easier to understand the required manual effort.

  • A solution can be implemented for each not-supported element in a unique location in the target source code, instead of changing all the not-upgraded member references.

The VBUC converts library elements to their equivalent when possible. When there is no equivalent, the default behavior was just to add a comment on the source code indicating the following expression/statement was not converted and requires manual correction. This might cause a number of compilation errors that might hide other issues that need to be resolved manually. The Stubs Generation feature creates dummy (stub) declarations for those elements that could not be mapped to .NET equivalents. It also changes all the references to the not-supported elements for references to the stub declarations. This technique does not resolve the whole issue since manual work will always be required to implement the functionality which is not present in .NET. However, it can save an important amount of time by achieving the following goals:

  • Making all the not supported elements to allow the project to compile, avoiding an important amount of compilation errors and helping to understand the required manual effort.

  • Encapsulate all the not-upgraded elements in a single location so a solution can be implemented for each not-supported element, instead of requiring changes for all its references throughout the code.

Original VB6 code:

Public Sub main()
    Dim s1 As String
    Dim s2 As String
    Dim theLen As Integer
    theLen = 8
    s1 = Chr(65)
    s2 = ChrB(65)
    MsgBox s1
    MsgBox s2
    Dim f As Form
    Dim fs As FillStyleConstants
    fs = f.FillStyle
    f.FillStyle = fs
End Sub

C# code:

internal static class Module1
{
	public static void  Main()
	{
		int theLen = 8;
		string s1 = Strings.Chr(65).ToString();
		//UPGRADE_ISSUE: (1040) ChrB function is not supported.
		string s2 = UpgradeStubs.VBA_Strings.ChrB(65);
		MessageBox.Show(s1, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
		MessageBox.Show(s2, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
		Form f = null;
		//UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.
		//UPGRADE_ISSUE: (2068) VBRUN.FillStyleConstants object was not upgraded.
		UpgradeStubs.VBRUN_FillStyleConstantsEnum fs = f.getFillStyle();
		//UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.
		f.setFillStyle(fs);
	}
}


//File UpgradeStubs.cs
namespace UpgradeStubs
{
	public static class System_Windows_Forms_Control
	{

		public static UpgradeStubs.VBRUN_FillStyleConstantsEnum getFillStyle(this Control instance)
		{
			UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VB.Control.FillStyle");
			return default(UpgradeStubs.VBRUN_FillStyleConstantsEnum);
		}
		public static void setFillStyle(this Control instance, UpgradeStubs.VBRUN_FillStyleConstantsEnum FillStyle)
		{
			UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VB.Control.FillStyle");
		}
	} 
	public class VBA_Strings
	{

		public static string ChrB(byte CharCode)
		{
			UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBA.Strings.ChrB");
			return default(string);
		}
	} 
	public enum VBRUN_FillStyleConstantsEnum
	{
		None = -1
	}
}

VB.NET code:

Module Module1
	Public Sub main()
		Dim theLen As Integer = 8
		Dim s1 As String = Strings.Chr(65).ToString()
		'UPGRADE_ISSUE: (1040) ChrB function is not supported.'
		Dim s2 As String = UpgradeSolution1Support.UpgradeStubs.VBA_Strings.ChrB(65)
		MessageBox.Show(s1, My.Application.Info.Title)
		MessageBox.Show(s2, My.Application.Info.Title)
		Dim f As Form
		'UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.'
		'UPGRADE_ISSUE: (2068) VBRUN.FillStyleConstants object was not upgraded.'
		Dim fs As UpgradeSolution1Support.UpgradeStubs.VBRUN_FillStyleConstantsEnum = f.getFillStyle()
		'UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.'
		f.setFillStyle(fs)
	End Sub


'File UpgradeStubs.cs'
Namespace UpgradeStubs
	Public Module System_Windows_Forms_Control
		<System.Runtime.CompilerServices.Extension()> _
		 Public Function getFillStyle(ByVal instance As Control) As UpgradeSolution1Support.UpgradeStubs.VBRUN_FillStyleConstantsEnum
			UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VB.Control.FillStyle")
			Return 0
		End Function
		<System.Runtime.CompilerServices.Extension()> _
		 Public Sub setFillStyle(ByVal instance As Control, ByVal FillStyle As UpgradeSolution1Support.UpgradeStubs.VBRUN_FillStyleConstantsEnum)
			UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VB.Control.FillStyle")
		End Sub
	End Module
	Public Class VBA_Strings
		Public Shared Function ChrB(ByVal CharCode As Byte) As String
			UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBA.Strings.ChrB")
			Return ""
		End Function
	End Class
	Public Enum VBRUN_FillStyleConstantsEnum
		None = -1
	End Enum
End NameSpace

12.2. Upgrade Note indicating not updated element during upgrade process

During the upgrade process, every non-updated element will include a comment on the resulting source code indicating that the expression/statement was not converted.

This option includes a warning comment on the output source code for each not supported element.

Original VB6 code:

Public Sub main()
    Dim s1 As String
    Dim s2 As String
    Dim theLen As Integer
    theLen = 8
    s1 = Chr(65)
    s2 = ChrB(65)
    MsgBox s1
    MsgBox s2
    Dim f As Form
    Dim fs As FillStyleConstants
    fs = f.FillStyle
    f.FillStyle = fs
End Sub

C# code:

internal static class Module1
{
	public static void  Main()
	{
		int theLen = 8;
		string s1 = Strings.Chr(65).ToString();
		//UPGRADE_ISSUE: (1040) ChrB function is not supported.
		string s2 = ChrB(65);
		MessageBox.Show(s1, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
		MessageBox.Show(s2, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
		Form f = null;
		//UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.
		//UPGRADE_ISSUE: (2068) VBRUN.FillStyleConstants object was not upgraded.
		VBRUN.FillStyleConstants fs = f.FillStyle;
		//UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.
		f.FillStyle = fs;
	}
}

VB.NET code:

Public Sub main()
	Dim theLen As Integer = 8
	Dim s1 As String = Strings.Chr(65).ToString()
	'UPGRADE_ISSUE: (1040) ChrB function is not supported.'
	Dim s2 As String = ChrB(65)
	MessageBox.Show(s1, My.Application.Info.Title)
	MessageBox.Show(s2, My.Application.Info.Title)
	Dim f As Form
	'UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.'
	'UPGRADE_ISSUE: (2068) VBRUN.FillStyleConstants object was not upgraded.'
	Dim fs As VBRUN.FillStyleConstants = f.FillStyle
	'UPGRADE_ISSUE: (2064) Form property f.FillStyle was not upgraded.'
	f.FillStyle = fs
End Sub

13. Remove Unused Local Variables

13.1. On

Local variables that are not used inside of the method will be automatically removed.

Original VB6 code:

Private Function foo() As String
    'var2 is an unused variable'
    Dim var2 As String
    Dim var1 As String
    var1 = "Function example"
    foo = var1
    End Function
    
    Private Sub goo()
    'var1 is an unused variable'
    Dim var1 As String
    Dim var2 As String
    var2 = "Subroutine example"
    MsgBox var2
End Sub

C# code:

private string foo()
{
	//var2 is an unused variable
	string var1 = "Function example";
	return var1;
}

private void goo()
{
	//var1 is an unused variable
	string var2 = "Subroutine example";
	MessageBox.Show(var2, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

VB.NET code:

Private Function foo() As String
	'var2 is an unused variable'
	Dim var1 As String = "Function example"
	Return var1
End Function

Private Sub goo()
	'var1 is an unused variable'
	Dim var2 As String = "Subroutine example"
	MessageBox.Show(var2, My.Application.Info.Title)
End Sub

13.2. Off

Local variables will not be removed, even if they are not used.

Original VB6 code:

Private Function foo() As String
    'var2 is an unused variable'
    Dim var2 As String
    Dim var1 As String
    var1 = "Function example"
    foo = var1
    End Function
    
    Private Sub goo()
    'var1 is an unused variable'
    Dim var1 As String
    Dim var2 As String
    var2 = "Subroutine example"
    MsgBox var2
End Sub

C# code:

private string foo()
{
	//var2 is an unused variable
	string var2 = "";
	string var1 = "Function example";
	return var1;
}

private void goo()
{
	//var1 is an unused variable
	string var1 = "";
	string var2 = "Subroutine example";
	MessageBox.Show(var2, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

VB.NET code:

Private Function foo() As String
	'var2 is an unused variable'
	Dim var2 As String = ""
	Dim var1 As String = "Function example"
	Return var1
End Function

Private Sub goo()
	'var1 is an unused variable'
	Dim var1 As String = ""
	Dim var2 As String = "Subroutine example"
	MessageBox.Show(var2, My.Application.Info.Title)
End Sub

14. VBA Collection Conversion

14.1. To System.Collections native .NET classes

Converts the Collection type to general System.Collections classes such as Hashtable, ArrayList, StringCollection, and StringDictionary. The generated type depends on the way the collection is used and what is stored in it.

By using this option the converted application will not have any reference to the COM Component.

Class

Maps to

vba.Collection

System.Collections.Hashtable | System.Collections.ArrayList | System.Collections.Specialized.StringCollection | System.Collection.Specialized.StringDictionary

Original VB6 code:

Public Sub main()
    Dim c1 As New Collection
    Dim c2 As New Collection
    Dim c3 As New Collection
    Dim i As Integer
    
    c1.Add "a"
    c1.Add "b"
    c1.Add "c"
    i = c1.Count
    
    c2.Add 0
    c2.Add 1
    c2.Add 2
    c2.Add 3
    i = c2.Count
    
    c3.Add 0
    c3.Add "a"
    c3.Add 1
    c3.Add "b"
    c3.Add 2
    c3.Add "c"
    c3.Add 3
    c3.Add "d"
    c3.Add 4
    i = c3.Count
    
    Dim c As New Collection
    c.Add "Element1", 1
    MsgBox c(1)
    c.Remove (1)
End Sub

C# code:

internal static class Module1
{
	public void main()
	{
		StringDictionary c1 = new StringDictionary();
		Hashtable c2 = new Hashtable();
		Hashtable c3 = new Hashtable();

		c1.Add("a", "a");
		c1.Add("b", "b");
		c1.Add("c", "c");
		int i = c1.Count;

		c2.Add(0, 0);
		c2.Add(1, 1);
		c2.Add(2, 2);
		c2.Add(3, 3);
		i = c2.Count;

		c3.Add(0, 0);
		c3.Add("a", "a");
		c3.Add(1, 1);
		c3.Add("b", "b");
		c3.Add(2, 2);
		c3.Add("c", "c");
		c3.Add(3, 3);
		c3.Add("d", "d");
		c3.Add(4, 4);
		i = c3.Count;

		StringDictionary c = new StringDictionary();
		c.Add("1", "Element1");
		MessageBox.Show((string) c[1], AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
		c.Remove("1");
	}
}

VB.NET code:

Module Module1
	Public Sub main()
		Dim c1 As New StringDictionary()
		Dim c2 As New Hashtable()
		Dim c3 As New Hashtable()

		c1.Add("a", "a")
		c1.Add("b", "b")
		c1.Add("c", "c")
		Dim i As Integer = c1.Count

		c2.Add(0, 0)
		c2.Add(1, 1)
		c2.Add(2, 2)
		c2.Add(3, 3)
		i = c2.Count

		c3.Add(0, 0)
		c3.Add("a", "a")
		c3.Add(1, 1)
		c3.Add("b", "b")
		c3.Add(2, 2)
		c3.Add("c", "c")
		c3.Add(3, 3)
		c3.Add("d", "d")
		c3.Add(4, 4)
		i = c3.Count

		Dim c As New StringDictionary()
		c.Add(CStr(1), "Element1")
		MessageBox.Show(CStr(c(1)), My.Application.Info.Title)
		c.Remove(CStr(1))
	End Sub

14.2. To System.Collections.Specialized.OrderedDictionary native .NET class

Converts the Collection type to System.Collections.Specialized.OrderedDictionary.

By using this option the converted application will not have any reference to the COM Component.

Class

Maps to

vba.Collection

System.Collections.Specialized.OrderedDictionary

Original VB6 code:

Public Sub main()
    Dim c1 As New Collection
    Dim c2 As New Collection
    Dim c3 As New Collection
    Dim i As Integer
    
    c1.Add "a"
    c1.Add "b"
    c1.Add "c"
    i = c1.Count
    
    c2.Add 0
    c2.Add 1
    c2.Add 2
    c2.Add 3
    i = c2.Count
    
    c3.Add 0
    c3.Add "a"
    c3.Add 1
    c3.Add "b"
    c3.Add 2
    c3.Add "c"
    c3.Add 3
    c3.Add "d"
    c3.Add 4
    i = c3.Count
    
    Dim c As New Collection
    c.Add "Element1", 1
    MsgBox c(1)
    c.Remove (1)
End Sub

C# code:

internal static class Module1
{
	public static void Main()
	{
		OrderedDictionary c1 = new OrderedDictionary();
		OrderedDictionary c2 = new OrderedDictionary();
		OrderedDictionary c3 = new OrderedDictionary();

		c1.Add("a", "a");
		c1.Add("b", "b");
		c1.Add("c", "c");
		int i = c1.Count;

		c2.Add(0, 0);
		c2.Add(1, 1);
		c2.Add(2, 2);
		c2.Add(3, 3);
		i = c2.Count;

		c3.Add(0, 0);
		c3.Add("a", "a");
		c3.Add(1, 1);
		c3.Add("b", "b");
		c3.Add(2, 2);
		c3.Add("c", "c");
		c3.Add(3, 3);
		c3.Add("d", "d");
		c3.Add(4, 4);
		i = c3.Count;

		OrderedDictionary c = new OrderedDictionary();
		c.Add(1, "Element1");
		//UPGRADE_WARNING: (1068) c(1) of type Variant is being forced to string.
		MessageBox.Show(Convert.ToString(c[0]), Application.ProductName);
		c.RemoveAt(0);
	}
}

VB.NET code:

Module Module1
	Public Sub main()
		Dim c1 As New OrderedDictionary
		Dim c2 As New OrderedDictionary
		Dim c3 As New OrderedDictionary

		c1.Add("a", "a")
		c1.Add("b", "b")
		c1.Add("c", "c")
		Dim i As Integer = c1.Count

		c2.Add(0, 0)
		c2.Add(1, 1)
		c2.Add(2, 2)
		c2.Add(3, 3)
		i = c2.Count

		c3.Add(0, 0)
		c3.Add("a", "a")
		c3.Add(1, 1)
		c3.Add("b", "b")
		c3.Add(2, 2)
		c3.Add("c", "c")
		c3.Add(3, 3)
		c3.Add("d", "d")
		c3.Add(4, 4)
		i = c3.Count

		Dim c As New OrderedDictionary
		c.Add(1, "Element1")
		'UPGRADE_WARNING: (1068) c(1) of type Variant is being forced to String.'
		MessageBox.Show(CStr(c(0)), Application.ProductName)
		c.RemoveAt(0)
	End Sub

15. Use TabControl Extension Class

Depending on other upgrade options such as MsComCtl mappings, the VBUC can generate code that uses the System.Windows.Forms.TabControl class to represent tab pages and their owner control.

The System.Windows.Forms.TabControl class provided by the .Net Framework does not provide support for HotKeys, like the MsComCtl.TabStrip or the TabDlg.SSTab. The VBUC provides a helper class called TabControlExtension that has this functionality, which inherits the TabControl class.

15.1 On

This is the default setting, and will replace uses of the TabControl class with the TabControlExtension class. The TabControlExtension works exactly like the TabControl, except that it has support for HotKeys. If the migration is set to use Helpers as code instead of Dlls, the code for this class will be available to view and modify if desired.

15.2 Off

By selecting this setting, the native TabControl class will be used. This option is best selected when the code needs to use as many native controls as possible.

16. Typer Conflict Resolution

The VBUC's Typer feature attempts to determine a primitive type for a Variant depending on how it is used in the code. For example, in the following code, the first Variant is used mostly as in integer, and in the second case, it's used as a string most of the time.

Original VB6 Code

Public Sub TyperConflictTest()
    Dim objVar1 As Variant
    Dim objVar2 As Variant
    
    objVar1 = 5
    objVar1 = 10
    objVar1 = 20
    objVar1 = "100"
    
    objVar2 = 5
    objVar2 = "Test 1"
    objVar2 = "Test 2"
End Sub

16.1 On

This is the default setting. By turning this feature on, the VBUC's Typer will determine that the type of the first variable should be an integer, and the second should be a string.

C# Code

public void TyperConflictTest()
{

	int objVar1 = 5;
	objVar1 = 10;
	objVar1 = 20;
	objVar1 = 100;

	string objVar2 = "5";
	objVar2 = "Test 1";
	objVar2 = "Test 2";
}

VB.NET Code

Public Sub TyperConflictTest()

	Dim objVar1 As Integer = 5
	objVar1 = 10
	objVar1 = 20
	objVar1 = 100

	Dim objVar2 As String = CStr(5)
	objVar2 = "Test 1"
	objVar2 = "Test 2"
End Sub

16.2 Off

By turning this feature off, these type conflicts will not be resolved by the typer, and the variables will be left as System.Object.

C# Code

public void TyperConflictTest()
{

	object objVar1 = 5;
	objVar1 = 10;
	objVar1 = 20;
	objVar1 = "100";

	object objVar2 = 5;
	objVar2 = "Test 1";
	objVar2 = "Test 2";
}

VB.NET Code

Public Sub TyperConflictTest()

	Dim objVar1 As Object = 5
	objVar1 = 10
	objVar1 = 20
	objVar1 = "100"

	Dim objVar2 As Object = 5
	objVar2 = "Test 1"
	objVar2 = "Test 2"
End Sub

Last updated