Links

EWIs

The following section provides useful information about the messages (comments) that will appear on your upgraded code and how to solve them.

Working with EWIs

The number of EWIs in your code depends entirely on your programming style and the size, type and complexity of files that you want to upgrade. As a general rule, forms usually have more issues than classes and modules, because forms have a richer object model and significant transformations must be applied when upgrading to Windows Forms.

Steps to fix a migration issue

This section shows the recommended steps to fix an error related to EWIs. The steps are:

Locate the source of the problem

To find the source of the problem, you can add some breakpoints a few lines before the code where the exception or the different behavior manifests itself. You need to do this in both the original VB6 code and the upgraded code so that you can compare to find out what is causing the problem or why the problem is occurring.

Understand the code purpose in the original VB6 application

The next step is to understand, in the original VB6 application, the functionality of the section of the code causing the problem. You can perform the following activities to understand the original behavior:
  1. 1.
    Create small examples of code that use the same features.
  2. 2.
    Execute step by step (debug mode), as many times as you need, the section of code causing the problem.

Identify a solution in .NET

Once the functionality of the original VB6 application is understood, the next step is to replicate the same functionality in the upgraded .NET application. Some recommendations for this are:
  1. 1.
    Create small examples in .NET to replicate the VB6 functionality.
  2. 2.
    Review the related .NET documentation on the web to determine how to achieve the same functionality in .NET.
  3. 3.
    Ask your peers or a Mobilize.NET migration expert.

Implement the solution

If you already have a solution, you can implement it. Once you have modified the upgraded code and the found solution was implemented, you need to test your code to confirm that the behavior is the same as the original VB6 application. You can, also, review the rest of the upgraded code to determine if the same problem is present in other areas.

Mobilize.NET Customization (optional)

In some cases, you can consider Mobilize.NET Customization. Several factors must be taken into account at this point:
  1. 1.
    Timing
    • Will the current timeframe of the project allows the Mobilize Team to implement the required customization? Is the impact of the error large enough to justify it to be reported in order to be included in future releases used in future projects?
  2. 2.
    Complexity
    • Is there an opportunity to make the replacement in several places using another automated solution such as Regular Expressions?
  3. 3.
    Rework required
    • Are the manual changes performed at this point in the project so many that executing a code merge will imply too much additional work?

Document the solution

Once the solution is implemented and the problem is resolved, it is important that the solution is properly documented and stored in a shared knowledge base. This helps in cases where other developers run into the same issue and are able to access information on how to solve it.

EWIs Lists

In the following lists, you can see the most common EWIs. It is important to know that there are many other EWIs not described in those lists.

Warnings

The following are some of the most common warnings generated by the Visual Basic Upgrade Companion.
Code number
Description
206
1003
1044
1047
1049
1063
1068
2050
2065
2074
2077
2080
2081
2099
6002
6021
6022
7005
7006
7008
7009
8007

Issues

The following are some of the most common issues generated by the Visual Basic Upgrade Companion.

ToDos

The following are some of the most common ToDos generated by the Visual Basic Upgrade Companion.

Notes

The following are some of the most common Notes generated by the Visual Basic Upgrade Companion.

Warnings

206 - Untranslated statement in %1. Please check source code

Description

This warning indicates an unexpected error during the automated upgrade process. As a result, some of the VB6 source code was not upgraded.

Recommendations

  • Please report the issue to Mobilize Support.
  • If the affected code is simple enough, you can manually write the missing code.

1003 - ParamArray %1 was changed from ByRef to ByVal

Description

In VB6, the Param Array keyword could be used to specify an arbitrary number of parameters for a declaration, sub, or function. ParamArrays were always passed by reference (ByRef). In Visual Basic .NET, ParamArrays are always passed by value; the Param Array keyword must be preceded by the ByVal keyword. When a parameter is passed by value, a copy of the variable is passed; when passed by reference, a pointer to the variable is passed. When passing by value, it is possible that the value of the variable may have changed and that the copy is out of date.

Recommendations

  • Review the code to make sure that the variables being passed as parameters are not being changed between the time that they are set and the time that they are used in a procedure.
  • If necessary, modify your code so that the procedure executes before the variables are modified.

VB6 Original Code

Private Sub Form_Load()
Dim myArray(0 To 2) As String
myArray(0) = "First"
myArray(1) = "Second"
myArray(2) = "Third"
Example myArray(0), myArray(1), myArray(2)
MsgBox myArray(1)
End Sub
Public Sub Example(ParamArray params() As Variant)
MsgBox "Second Parameter: " + params(1)
params(1) = params(1) + "_modified"
End Sub

C# Upgraded Code

private void Form_Load()
{
string[] myArray = new string[]{"", "", ""};
myArray[0] = "First";
myArray[1] = "Second";
myArray[2] = "Third";
Example(myArray[0], myArray[1], myArray[2]);
MessageBox.Show(myArray[1], AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}
//UPGRADE_WARNING: (1003) ParamArray params was changed from ByRef to ByVal.
public void Example(params object[] params_Renamed)
{
MessageBox.Show("Second Parameter: " + ReflectionHelper.GetPrimitiveValue<string>(params_Renamed[1]), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
params_Renamed[1] = ReflectionHelper.GetPrimitiveValue<string>(params_Renamed[1]) + "_modified";
}

VB.NET Upgraded Code

Private Sub Form_Load()
Dim myArray(2) As String
myArray(0) = "First"
myArray(1) = "Second"
myArray(2) = "Third"
Example(myArray(0), myArray(1), myArray(2))
MessageBox.Show(myArray(1), My.Application.Info.Title)
End Sub
'UPGRADE_WARNING: (1003) ParamArray params was changed from ByRef to ByVal.'
Public Sub Example(ParamArray ByVal params() As Object)
MessageBox.Show("Second Parameter: " & ReflectionHelper.GetPrimitiveValue(Of String)(params(1)), My.Application.Info.Title)
params(1) = ReflectionHelper.GetPrimitiveValue(Of String)(params(1)) & "_modified"
End Sub

1044 - Sub Main in a DLL won't get called

Description

This EWI is generated when a non-EXE project has a Sub Main as a startup object.

Recommendations

  • Since .NET DLLs do not have any method to execute it when the respective assembly is loaded, we have to add a static constructor and call the sub-main inside.
This will not be executed at the same time as VB6 but is the most similar behavior.

VB6 Original Code

Public Sub Main()
End Sub

C# Upgraded Code

public class Class1
{
//UPGRADE_WARNING: (1044) Sub Main in a DLL won't get called.
public void Main()
{
}
}

VB.NET Upgraded Code

Public Class Class1
'UPGRADE_WARNING: (1044) Sub Main in a DLL wont get called.'
Public Sub Main()
End Sub
End Class

1047 - Application will terminate when Sub Main() finishes

Description

Standard EXE applications written in VB6 can be started either from the Main sub or a startup form. The use of the Main sub is a very common practice in large applications, because it allows the execution of initialization logic before the load of the main form of the application, and allows separation of this logic from the logic of the form.
When an application that uses the Main sub as a startup object is migrated using the VBUC, equivalent code is generated in .NET. However, due to differences in the behavior of VB6 and the .NET Framework, the applications behave differently. Some manual changes are required to achieve functional equivalence in this case.
In VB6, all the code in the Main sub is executed and the method ends, but the application continues to run while a form is displayed. In .NET, on the other hand, the application will exit as soon as the Main method exits, any open form at that point will be closed automatically.

VB6 Original Code

Public Sub Main()
Form1.Show
Dim x As Integer
x = 10
MsgBox x
End Sub

C# Upgraded Code

//UPGRADE_WARNING: (1047) Application will terminate when Sub Main() finishes.
[STAThread]
public static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(Form1.DefInstance);
int x = 10;
MessageBox.Show(x.ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

VB.NET Upgraded Code

'UPGRADE_WARNING: (1047) Application will terminate when Sub Main() finishes.'
<STAThread> _
Public Sub Main()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(False)
Application.Run(Form1.DefInstance)
Dim x As Integer = 10
MessageBox.Show(CStr(x), My.Application.Info.Title)
End Sub

1049 - Use of Null/IsNull() detected

Description

In Visual Basic 6, the Null keyword indicates that a field contained no valid data and the IsNull function is used to test for Null. In addition, Visual Basic 6 support Null propagation when Null was used in an expression; the result of the expression would also be Null. This EWI indicates the use of the Null keyword or the IsNull function.

Recommendations

  • In Visual Basic .NET, the Null keyword is still a reserved word, but it has no syntactical value, and the IsNull function is no longer supported.
  • C# also reserves the null keyword, but it is not to be confused with Null in Visual Basic 6. Visual Basic's Null keyword indicated missing or invalid data in data field, which is the purpose of DbNull in the .Net Framework. C#'s version of null is closer to Visual Basic 6's Nothing keyword.
  • Also, Null propagation is no longer supported in the .NET framework. When upgrading Visual Basic 6 applications avoid null propagation.
  • During the upgrade, Null is converted to DBNull, and IsNull is converted to IsDBNull of which there are several variants: Visual Basic's IsDBNull function, the Convert.IsDBNull method, the DataTableReader.IsDBNull method, and the IDataRecord.IsDBNull method.
  • The behavior of DBNull is slightly different than that of Null. Null could be used in functions and assignments as a Variant data type, however DBNull is a class and thus cannot be used as a direct replacement for Visual Basic's Null. DBNull.Value, however, can be passed as a value type in methods or assignments.
  • Where Null was used with a Variant data type, the Variant is converted to Object during the upgrade; in these cases, depending on the context, it may be more appropriate to use the Nothing and the IsNothing function for VB.NET. For C# use the null keyword and compare it with null with the equality operators (== and !=).

VB6 Original Code

Private Function ResultTxt(var As Variant)
If IsNull(var) Then
ResultTxt = "Null"
ElseIf TypeName(var) = "Integer" Or TypeName(var) = "Double" Or TypeName(var) = "Long" Then
ResultTxt = CStr(var)
Else
ResultTxt = """" & var & """"
End If
End Function

C# Upgraded Code

private string ResultTxt(string var)
{
//UPGRADE_WARNING: (1049) Use of Null/IsNull() detected.
if (Convert.IsDBNull(var))
{
return "Null";
}
else if (var is int || var is double || var is int)
{
return var;
}
else
{
return "\"" + var + "\"";
}
}

VB.NET Upgraded Code

Private Function ResultTxt(ByVal var As String) As String
'UPGRADE_WARNING: (2081) TypeName has a new behavior.'
'UPGRADE_WARNING: (1049) Use of Null/IsNull() detected.'
If Convert.IsDBNull(var) Then
Return "Null"
ElseIf var.GetType().Name = "Integer" Or var.GetType().Name = "Double" Or var.GetType().Name = "Long" Then
Return var
Else
Return """" & var & """"
End If
End Function

1063 - Arrays in structure %1 may need to be initialized before they can be used

Description

In C#, fixed-size arrays in structures are not supported. Fixed-size arrays that are members of structures defined in COM interfaces will need to be initialized before they can be used.

Recommendations

  • Call the Initialize function of the structure, right after the variable declaration.

VB6 Original Code

Private Type Structure2
Size As Integer
Numbers() As String
End Type
Private Type Structure1
name As String
str2 As Structure2
End Type

C# Upgraded Code

[Serializable]
private struct Structure2
{
public short Size;
public string[] Numbers;
}
[Serializable]
private struct Structure1
{
public string name;
//UPGRADE_WARNING: (1063) Arrays in structure str2 may need to be initialized before they can be used.
public Structure2 str2;
public static Structure1 CreateInstance()
{
Structure1 result = new Structure1();
result.name = String.Empty;
return result;
}
}

1068 - %1 of type %2 is being forced to %3

Description

The VBUC typing engine is able to infer the correct data type of undefined or Variant-type variables, and assign them the correct data type. In some scenarios, a variant-type variable is upgraded to a scalar value; this type of inference is done over the variable usage and context.

Recommendations

  • There are no recommendations.

VB6 Original Code

Public Function Exists(col As Collection, Index As String) As Boolean
Dim o As Variant
On Error GoTo Error
o = col(Index)
Error:
MsgBox "Fail Test"
End Function

C# Upgraded Code

internal static bool Exists(OrderedDictionary col, string Index)
{
object o = null;
try
{
//UPGRADE_WARNING: (1068) col() of type Variant is being forced to Scalar.
o = ReflectionHelper.GetPrimitiveValue(col[Index]);
}
catch
{
}
MessageBox.Show("Fail Test", AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
return false;
}

VB.NET Upgraded Code

Public Function Exists(ByVal col As OrderedDictionary, ByVal Index As String) As Boolean
Dim o As Object
Try
'UPGRADE_WARNING: (1068) col() of type Variant is being forced to Scalar.'
o = ReflectionHelper.GetPrimitiveValue(col(Index))
Catch
End Try
MessageBox.Show("Fail Test", My.Application.Info.Title)
End Function

1070 - Member access from a dynamic/late binding object will be resolved using the ReflectionHelper

Description

When the VBUC's mechanism for solving Late Binding cannot determine the type of a class, and the ReflectionHelper feature is enabled, the ReflectionHelper class will be used to attempt to correctly resolve the late binding instance at runtime.

Recommendations

  • The ReflectionHelper class should be able to determine most cases of Late Binding at runtime.
  • Manually modify the upgraded code to determine the correct base class.

VB6 Original Code

The original code uses a variant to allow any object to be passed, and have its Enabled property set to true. VB6's late binding will solve this.
Public Sub SetEnabled(ctrl As Variant)
ctrl.Enabled = True
End Sub
In a case such as this where the VBUC cannot determine a base type for all items passed to the method, it will use the ReflectionHelper to solve the late binding issue at runtime, as seen in the following example.

C# Upgraded Code

public void SetEnabled(object ctrl)
{
ReflectionHelper.LetMember(ctrl, "Enabled", true);
}

VB.NET Upgraded Code

Public Sub SetEnabled(ByVal ctrl As Object)
ReflectionHelper.LetMember(ctrl, "Enabled", True)
End Sub

2050 - %1 Event %2.%3 was not upgraded

Description

The VBUC is able to map VB6 library members to .NET equivalents. These equivalents are chosen to provide maximum functional equivalence on the target platform. In particular scenarios, some class properties and events may not have a direct equivalent in .NET or may not have been mapped in the current release of the VBUC. The event handler is declared in the target source code but there are no equivalent events to associate the handler to.

Recommendations

  • Use a more specialized/similar component to the original one, which provides the required events in the target platform.
  • Create an explicit invocation to the event handler method in the given circumstances to simulate the event triggering.
  • Associate the upgraded handler method to one or more .NET available events in case those events satisfy the code requirements and the original event behavior.
  • Refactor the code to apply a different solution. This strategy makes sense especially when the concepts in .NET are very different from the VB6 ones. In these cases it is likely that the way to implement a particular process should be done with a different approach.

VB6 Original Code

Private Sub Text1_OLEDragOver(Data As DataObject, Effect As Long, Button As Integer, Shift As Integer, X As Single, Y As Single, State As Integer)
'Make sure that the format is text.'
If Data.GetFormat(vbCFText) Then
'If it is text, enable dropping for the second Textbox.'
Text1.OLEDropMode = vbOLEDropManual
End If
End Sub

C# Upgraded Code

//UPGRADE_WARNING: (2050) TextBox Event Text1.OLEDragOver was not upgraded.
private void Text1_OLEDragOver(DataObject Data, int Effect, int Button, int Shift, float X, float Y, int State)
{
//Make sure that the format is text.
//UPGRADE_ISSUE: (2064) ClipBoardConstants property ClipBoardConstants.vbCFText was not upgraded.
if (Data.GetFormat((short) UpgradeStubs.VBRUN_ClipBoardConstants.getvbCFText()))
{
//If it is text, enable dropping for the second Textbox.
//UPGRADE_ISSUE: (2070) Constant vbOLEDropManual was not upgraded.
//UPGRADE_ISSUE: (2064) TextBox property Text1.OLEDropMode was not upgraded.
Text1.setOLEDropMode(UpgradeStubs.VBRUN_OLEDropConstants.getvbOLEDropManual());
}
}

C# Expected Code

In this case the sample code deals with manually handling Drag & Drop operations in a Visual Basic 6 application. As is clearly stated in that MSDN article we need to write custom code for the drag & drop operation. In this case, we replace the OLEDragOver event which no longer exists in .Net, and replaces it with the DragEnter event. Within this event, we add our drag & drop logic.
private void Text1_DragEnter(object sender, DragEventArgs e)
{
//Make sure that the format is text.
if (e.Data.GetDataPresent(DataFormats.Text))
{
//Allow drop
e.Effect = DragDropEffects.Copy;
}
else
{
e.Effect = DragDropEffects.None;
}
}

VB.NET Upgraded Code

'UPGRADE_WARNING: (2050) TextBox Event Text1.OLEDragOver was not upgraded.'
Private Sub Text1_OLEDragOver(ByVal Data As DataObject, ByVal Effect As Integer, ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single, ByVal State As Integer)
'Make sure that the format is text.'
'UPGRADE_ISSUE: (2064) ClipBoardConstants property ClipBoardConstants.vbCFText was not upgraded.'
If Data.GetFormat(UpgradeSolution1Support.UpgradeStubs.VBRUN_ClipBoardConstants.getvbCFText()) Then
'If it is text, enable dropping for the second Textbox.'
'UPGRADE_ISSUE: (2070) Constant vbOLEDropManual was not upgraded.'
'UPGRADE_ISSUE: (2064) TextBox property Text1.OLEDropMode was not upgraded.'
Text1.setOLEDropMode(UpgradeSolution1Support.UpgradeStubs.VBRUN_OLEDropConstants.getvbOLEDropManual())
End If
End Sub

VB.NET Expected Code

In this case, the sample code deals with manually handling Drag & Drop operations in a Visual Basic 6 application. As is clearly stated in that MSDN article we need to write custom code for the drag & drop operation. In this case, we replace the OLEDragOver event which no longer exists in .Net, and replaces it with the DragEnter event. Within this event, we add our drag & drop logic.
Private Sub Text1_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles Text1.DragEnter
'Make sure that the format is text.'
If (e.Data.GetDataPresent(DataFormats.Text)) Then
'Allow Drop'
e.Effect = DragDropEffects.Copy
Else
' Do not allow drop'
e.Effect = DragDropEffects.None
End If
End Sub

2065 - %1 %2 %3.%4 has a new behavior

Description

During the upgrade process, some events can be mapped to .NET with minor behavior differences. If this EWI is displayed in a given project, the selected target event will not threaten the functional equivalence of the resulting code but can create small differences that may require some manual adjustments.

Recommendations

Each case will have to be evaluated individually, as the behavior differences may not be significant for every project. Some common properties with new behaviors include the following:
  • System.Windows.Forms.Control.Controls collection, which is a hierarchical list of NamingContainers and is usually navigated recursively. It's important to remember that Child controls will not be in the parent's Controls collection.
  • Likewise, System.Windows.Forms.ControlCollection.Count does not represent the same number in .NET as in VB6, as only direct descendants of the current control will be counted. Any child controls will be part of their respective parent's ControlCollection.

VB6 Original Code

Public Sub ShowPicture(ByVal myForm As Form)
MsgBox (myForm.Picture.Height)
End Sub

C# Upgraded Code

internal static void ShowPicture(Form1 myForm)
{
//UPGRADE_WARNING: (2065) Form property myForm.Picture has a new behavior.
MessageBox.Show(myForm.BackgroundImage.Height.ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

VB.NET Upgraded Code

Public Sub ShowPicture(ByVal myForm As Form1)
'UPGRADE_WARNING: (2065) Form property myForm.Picture has a new behavior.'
MessageBox.Show(CStr(myForm.BackgroundImage.Height), My.Application.Info.Title)
End Sub

2074 - %1 %2 %3.%4 was upgraded to %5.%6 which has a new behavior

Description

During the upgrade process, some class members and events can be mapped to a .NET equivalent with minor behavior differences. If this EWI is displayed in a given project, the selected source structure will not threaten the functional equivalence of the resulting code but can require some manual adjustments.

Recommendations

VB6 Original Code

Public Sub NewBehaviorParent(ByVal c As Control)
Dim b As Form
Set b = c.Parent
MsgBox c.Name & "'s parent is " & b.Name
End Sub

C# Upgraded Code

public void NewBehaviorParent(Label c)
{
//UPGRADE_WARNING: (2074) Control property c.Parent was upgraded to c.FindForm which has a new behavior.
Form b = c.FindForm();
MessageBox.Show(c.Name + "'s parent is " + b.Name, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

VB.NET Upgraded Code

Public Sub NewBehaviorParent(ByVal c As Label)
'UPGRADE_WARNING: (2074) Control property c.Parent was upgraded to c.FindForm which has a new behavior.'
Dim b As Form = c.FindForm()
MessageBox.Show(c.Name & "'s parent is " & b.Name, My.Application.Info.Title)
End Sub

2077 - Change the default 0 index in the Rows property with the correct one

Description

When upgrading an ADO Recordset object to native ADO.NET, the VBUC converts all the Recordset objects to System.Data.DataSet, however, there are major differences between these two classes. Cursors in ADO control record navigation in a Recordset; this way you are always pointing to a current row in the Recordset. This concept is not available in a DataSet object, which contains a collection of tables, and each table contains a collection of Rows and Columns, among other data. Since there is no current row concept in a DataSet object, when there are uses of a Recordset's current row, the VBUC then converts them to be the first row of the first table in the DataSet, and this EWI is generated.
In ADO, most of the time Recordset objects contain a single table retrieved from the Database. Therefore, the generated DataSets will only have one table in their Tables collections.

Recommendations

  • Review case by case to see if the first record of the DataTable object is actually the one intended to be used. If not, a change of logic might be required to achieve the functional equivalence between the original application and the upgraded one.
  • Also, turn on the ADODB-RDO feature in the VBUC (when available) to generate "Foreach" structures in places that match common recordset navigation patterns.

VB6 Original Code

Dim cn As Connection
Dim rs1 As Recordset
Dim cmd As Command
Public Sub Example()
Set cmd = New Command
With cmd
.ActiveConnection = cn
.CommandText = "Select * from Customers where CustomerID = 42"
.CommandType = adCmdText
End With
Set rs1 = cmd.Execute
If rs1.EOF = False Then
If rs1!Name <> "" Then Debug.Print rs1!Name
If rs1!Email <> "" Then Debug.Print rs1!Email
End If
rs1.Close
End Sub

C# Upgraded Code

static SqlConnection cn = null;
static DataSet rs1 = null;
static SqlCommand cmd = null;
internal static void Example()
{
cmd = new SqlCommand();
cmd.Connection = cn;
cmd.CommandText = "Select * from Customers where CustomerID = 42";
cmd.CommandType = CommandType.Text;
SqlDataAdapter adap = new SqlDataAdapter(cmd.CommandText, cmd.Connection);