Issues

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

1002 - LSet cannot assign one type to another

Description

This occurs when a type is assigned to another, using the LSet Function, LSet Function copies all the source bytes from one type and then assigns then makes that the other type points to the copy. Currently, there is no way to determine the expected behavior for these cases.

Recommendations

  • You can manually assign all the values and pass them from a class to another. You could require byte functions, so please be sure of how the function passes the data from one type to another. For example, if you have a type that contains a string and the other has an array of strings of length 2, then you must make a function that splits and assigns the first array into two characters pieces, and then assign them to the second type array.

VB6 Original Code

Public Type TypeA
    x As Integer
End Type

Public Type TypeB
    x As Integer
End Type

Public Sub LSetTest()
    Dim aType As TypeA
    Dim bType As TypeB
    aType.x = 3
    LSet bType = aType
End Sub

C# Upgraded Code

public struct TypeA
{
    public short x;
}

public struct TypeB
{
    public short x;
}

public static void LSetTest()
{
    TypeA aType = new TypeA();
    TypeB bType = new TypeB();
    aType.x = 3;
    //UPGRADE_ISSUE: (1002) LSet cannot assign one type to another.
    //LSet(bType, aType);
    UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("LSet");
}

VB.NET Upgraded Code

Public Structure TypeA
    Dim x As Short
End Structure

Public Structure TypeB
    Dim x As Short
End Structure

Public Sub LSetTest()
    Dim aType As New TypeA()
    Dim bType As New TypeB()
    aType.x = 3
    'UPGRADE_ISSUE: (1002) LSet cannot assign one type to another.'
    'LSet(bType, aType)'
    UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("LSet")
End Sub

1010 - The preceding line couldn't be parsed

Description

Because of an unexpected error, VBUC could not parse a specific line, method, or code block.

Recommendations

  • This issue should be addressed manually by comparing the upgraded file with the original one and rewriting the missing code in the upgraded file.

1014 - 1% statement is not supported

Recommendations

  • We recommend using the Upgrade Option GoSub Conversion, this feature enables the support of GoSub statements by creating Local Functions (Only in C#). For more information check the latest MSDN documentation on Local functions.

  • Another way could be adding a method with the body of the label called by the GoSub manually. This will require passing all the necessary variables as byref parameters in the respective method.

VB6 Original Code

Private Sub Form_Load()
    Dim total As Integer
    GoSub InsideSub
    MsgBox total
    Exit Sub
InsideSub:
    For i = 0 To 10
        total = total + i
    Next i
    Return
End Sub

C# Upgraded Code

private void Form_Load()
{
    int total = 0;
    //UPGRADE_ISSUE: (1014) GoSub statement is not supported.
    //UPGRADE_TODO: (1065) Error handling statement (Go Sub) could not be converted.
    UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("GoSub Label (InsideSub)");
    MessageBox.Show(total.ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    return;
    InsideSub:
    for (int i = 0; i <= 10; i++)
    {
        total += i;
    }
    //UPGRADE_WARNING: (2081) Return has a new behavior.
    return ;
}

C# Expected Code

In this case, GoSub Conversion was selected, creating a local function.

private void Form_Load()
{
    int i = 0;
    int total = 0;
    InsideSub();
    MessageBox.Show(total.ToString(), AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
    return;
    void InsideSub()
    {
        for (i = 0; i <= 10; i++)
        {
            total += i;
        }
    }
}

VB.NET Upgraded Code

Private Sub Form_Load()
    Dim total As Integer
    'UPGRADE_ISSUE: (1014) GoSub statement is not supported.'
    GoSub InsideSub
    MessageBox.Show(CStr(total), My.Application.Info.Title)
    Exit Sub
InsideSub:
    For i As Integer = 0 To 10
        total += i
    Next i
    'UPGRADE_WARNING: (2081) Return has a new behavior.'
    Return 
End Sub

VB.NET Expected Code

As we mentioned before, the GoSub conversion is a feature available only in C#, which means we are not using this feature in this case.

Private Sub Form_Load()
    Dim total As Integer
    InsideSub(total)
    MessageBox.Show(CStr(total), My.Application.Info.Title)
End Sub

Private Sub InsideSub(ByRef total As Integer)
    For i As Integer = 0 To 10
        total += i
    Next i
End Sub

1043 - Class instancing was changed to public

Description

In Visual Basic 6.0, instance creation was controlled by the instancing property of a class, which set both the access level (Public, Private, Friend) of a class and the way an object could be created. In Visual Basic .NET, the instancing property is no longer supported; you control how an object can be created by setting the access level of the constructor (Sub New):

Visual Basic 6.0 Instancing

Visual Basic .NET

Private

Class Access attribute: Private

PublicNotCreatable

Class Access attribute: Public. Declare the constructor Friend (Friend Sub New)

MultiUse

Class Access attribute: Public. Declare the constructor Public (Public Sub New)

There is no direct equivalent for SingleUse, GlobalSingleUse, or GlobalMultiUse instancing in Visual Basic .NET; when the instancing property is set to one of these values, the access level of both the class and the constructor is set to Public during the upgrade.

Recommendations

  • Check the code. The behavior may be slightly different than it was in Visual Basic 6.0, and you may need to change the access level of the class and/or the constructor.

1046 - %1 Parameter '%2' is not supported, and was removed

Description

The VBUC contains a set of methods and parameter mappings for VB6 and .NET. Included in this information are markers that indicate when parameters have been removed from their .NET counterparts. Whenever a parameter that has been removed is found in the original source code, this EWI is emitted by the VBUC. Currently, there is no way to determine expected behavior for these cases, the final solution will depend on the resolution approach.

Recommendations

  • There is no straightforward solution for this issue because some of the removed parameters may not have an equivalent in .NET at all. In some cases, the parameters change the behavior of the invoked method. The same effect might be possible by setting property values in the new .NET object. Likewise, an overload of the method might be available that provides this functionality. In other cases, there is no equivalent parameter to substitute, and it will be necessary to include code to replicate the desired functionality in .NET. Finally, in some cases, the omitted parameters cause no discernable difference. They can be safely omitted in most cases without affecting the functional equivalence. Common causes of this EWI involve the use of the Size, ColorDepth, X, and Y parameters of the LoadPicture function. It is also emitted in some uses of Interaction.AppActivate.

VB6 Original Code

Public Sub LoadFormPicture(ByVal picBox As PictureBox)
    picBox.Picture = VB.LoadPicture("calc3.gif", 4, vbLPColor, 320, 86)
End Sub

C# Upgraded Code

public void LoadFormPicture(PictureBox picBox)
{
    //UPGRADE_ISSUE: (1046) LoadPicture Parameter 'Y' is not supported, and was removed.
    //UPGRADE_ISSUE: (1046) LoadPicture Parameter 'X' is not supported, and was removed.
    //UPGRADE_ISSUE: (1046) LoadPicture Parameter 'ColorDepth' is not supported, and was removed.
    //UPGRADE_ISSUE: (1046) LoadPicture Parameter 'Size' is not supported, and was removed.
    picBox.Image = Image.FromFile("calc3.gif");
}

VB.NET Upgraded Code

Public Sub LoadFormPicture(ByVal picBox As PictureBox)
    'UPGRADE_ISSUE: (1046) LoadPicture Parameter 'Y' is not supported, and was removed.'
    'UPGRADE_ISSUE: (1046) LoadPicture Parameter 'X' is not supported, and was removed.'
    'UPGRADE_ISSUE: (1046) LoadPicture Parameter 'ColorDepth' is not supported, and was removed.'
    'UPGRADE_ISSUE: (1046) LoadPicture Parameter 'Size' is not supported, and was removed.'
    picBox.Image = Image.FromFile("calc3.gif")
End Sub

2038 - Form property 1%.ScaleMode is not supported

Description

In VB6, you could use a scale mode different from pixels to use as the unit for measures and locations in a form. This is not longer supported by .NET.

Recommendations

  • Use these functions to convert units to pixels, depending on the scale mode. The Unit can be centimeters, millimeters, inches, etc.

    • Microsoft.VisualBasic.Compatibility.VB6.Support.ToPixelsX(value, VB6.ScaleMode.Unit)

    • Microsoft.VisualBasic.Compatibility.VB6.Support.ToPixelsY(value, VB6.ScaleMode.Unit)

    • Microsoft.VisualBasic.Compatibility.VB6.Support.TwipsToPixelsX(value)

    • Microsoft.VisualBasic.Compatibility.VB6.Support.TwipsToPixelsY(value)

VB6 Original Code

Public Sub SetScaleMode()
    Me.ScaleMode = 1 'Twips
End Sub

C# Upgraded Code

public void SetScaleMode()
{
    //UPGRADE_ISSUE: (2038) Form property ewi2038.ScaleMode is not supported.
    this.ScaleMode = PrinterHelper.ScaleModeConstants.VbTwips; //Twips
}

VB.NET Upgraded Code

Public Sub SetScaleMode()
    'UPGRADE_ISSUE: (2038) Form property ewi2038.ScaleMode is not supported.'
    Me.ScaleMode = PrinterHelper.ScaleModeConstants.VbTwips 'Twips
End Sub

2064 - %1 %2 %3.%4 was not upgraded

Description

The VBUC converts members of a VB6 control library to their .NET equivalents whenever possible. The corresponding .NET elements are chosen to provide functional equivalence on the target platform.

In particular scenarios, some class properties may not have a direct equivalent in .NET or may have not be mapped in the current release of the VBUC. The VBUC generates this EWI to inform the user about these specific cases.

Also, some constants or static classes may not have a direct equivalent in the .NET core libraries or no mapping association exists in the specific version of the VBUC in use.

Since the Stub Generation upgrade option is normally enabled, the VBUC will create dummy declarations for these properties and reference them instead of the original reference to the not-supported property. This feature reduces the amount of compilation errors and provides the user with the possibility to manually implement the property's functionality in the stub declaration.

See also: Stubs Generation

Recommendations

  • The final solution will depend on the approach taken and the particulars of the application being migrated.

  • It will be necessary to implement the original property functionality through other means. This goal can be achieved through one of the following approaches:

    • Implement the functionality from scratch.

      • If the stub generation feature is enabled, it can be implemented in one place: the generated stub declaration.

      • If the stub generation feature is not used, it is necessary to change all references to the new .NET implementation.

    • Use an already existing method/property.

      • From .NET core libraries: Depending on what functionality is expected from a property, a particular VBUC user might be satisfied with a .NET equivalent, which, while not 100% equivalent, is sufficient for the specific case.

      • From third-party libraries: Some vendors might provide functionality previously available in VB6 and not in the .NET core libraries.

    • Refactor the code to use another method/property. This strategy makes sense when the underlying .NET object models are very different to the VB6 ones. In these cases, it is likely that a new approach will be the key.

VB6 Original Code

Public Sub GetProperty(ByVal PropBag As PropertyBag)
    Dim mask As String
    mask = PropBag.ReadProperty("Mask", "none")
    MsgBox "Mask is: " & mask
End Sub

C# Upgraded Code

public void GetMaskProperty(UpgradeStubs.PropertyBag PropBag)
{
    //UPGRADE_ISSUE: (2064) PropertyBag method PropBag.ReadProperty was not upgraded.
    //UPGRADE_WARNING: (1068) PropBag.ReadProperty() of type Variant is being forced to string.
    string mask = ReflectionHelper.GetPrimitiveValue<string>(PropBag.ReadProperty("Mask", "none"));
    MessageBox.Show("Mask is: " + mask, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

public class PropertyBag
{

    public object ReadProperty(string Name_Renamed, object DefaultValue)
    {
        UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBRUN.PropertyBag.ReadProperty");
        return default(object);
    }
    public void WriteProperty(string Name_Renamed, object Value, object DefaultValue)
    {
        UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBRUN.PropertyBag.WriteProperty");
    }
}

C# Expected Code

public void GetMaskProperty(UpgradeStubs.PropertyBag PropBag)
{
    string mask = ReflectionHelper.GetPrimitiveValue<string>(PropBag.ReadProperty("Mask", "none"));
    MessageBox.Show("Mask is: " + mask, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

public class PropertyBag
{
    private Dictionary<string, object> bag = new Dictionary<string, object>();
    public object ReadProperty(string Name_Renamed, object DefaultValue)
    {
        if (bag.ContainsKey(Name_Renamed) == false)
            return DefaultValue;
        else
            return bag[Name_Renamed];
    }
    public void WriteProperty(string Name_Renamed, object Value, object DefaultValue)
    {
        if(Value == null)
        {
            bag.Add(Name_Renamed, DefaultValue);
        }
        else
        {
            bag.Add(Name_Renamed, Value);
        }
    }
}

VB.NET Upgraded Code

Public Sub GetMaskProperty(ByVal PropBag As UpgradeSolution1Support.UpgradeStubs.PropertyBag)
    'UPGRADE_ISSUE: (2064) PropertyBag method PropBag.ReadProperty was not upgraded.'
    'UPGRADE_WARNING: (1068) PropBag.ReadProperty() of type Variant is being forced to String.'
    Dim mask As String = ReflectionHelper.GetPrimitiveValue(Of String)(PropBag.ReadProperty("Mask", "none"))
    MessageBox.Show("Mask is: " & mask, My.Application.Info.Title)
End Sub

VB.NET Expected Code

Public Sub GetMaskProperty(ByVal PropBag As UpgradeSolution1Support.UpgradeStubs.PropertyBag)
    Dim mask As String = ReflectionHelper.GetPrimitiveValue(Of String)(PropBag.ReadProperty("Mask", "none"))
    MessageBox.Show("Mask is: " & mask, My.Application.Info.Title)
End Sub

Public Class PropertyBag
    Private bag As Dictionary(Of String, String) = New Dictionary(Of String, String)
    Public Function ReadProperty(ByVal Name_Renamed As String, Optional ByVal DefaultValue As Object = Nothing) As Object
        If Not bag.ContainsKey(Name_Renamed) Then
            Return DefaultValue
        Else
            Return bag(Name_Renamed)
        End If
    End Function
    Public Sub WriteProperty(ByVal Name_Renamed As String, ByVal Value As Object, Optional ByVal DefaultValue As Object = Nothing)
        If Microsoft.VisualBasic.Information.IsNothing(Value) Then
            bag.Add(Name_Renamed, DefaultValue)
        Else
            bag.Add(Name_Renamed, Value)
        End If
    End Sub
End Class

2068 - %1 %2 was not upgraded

Description

The VBUC is capable of mapping VB6 library members to .NET equivalents. These equivalents are chosen to provide maximum functional equivalence on the target platform.

Some class properties may not have a direct equivalent in specific scenarios because there is no equivalent in the .NET libraries or because no mapping association exists in VBUC.

Recommendations

  • There is no unique solution for this issue because some class properties may not have an equivalent in .NET at all.

  • The entire set of not-mapped elements requires the manual implementation of the missing member. The Stub Generation feature will assess the manual effort needed to reach functional equivalence by isolating all not-upgraded class properties in a single source file.

  • For more information please refer to Stub generation.

  • For these cases, we implement the PropertyBag and its methods in the VBRUN_PropertyBag class.

VB6 Original Code

Private Sub Command1_Click()
    Dim objBag As New PropertyBag
    ' Attempt with empty Property Bag, default value'
    GetMaskProperty objBag
    objBag.WriteProperty "Mask", "Zorro"
    'Attempt with PropertyBag with mask value'
    GetMaskProperty objBag
End Sub

Public Sub GetMaskProperty(ByVal PropBag As PropertyBag)
    Dim mask As String
    mask = PropBag.ReadProperty("Mask", "none")
    MsgBox "Mask is: " & mask
End Sub

C# Upgraded Code

private void Command1_Click(Object eventSender, EventArgs eventArgs)
{
    //UPGRADE_ISSUE: (2068) VBRUN.PropertyBag object was not upgraded.
    UpgradeStubs.PropertyBag objBag = new UpgradeStubs.PropertyBag();
    // Attempt with empty Property Bag, default value
    GetMaskProperty(objBag);
    //UPGRADE_ISSUE: (2064) PropertyBag method objBag.WriteProperty was not upgraded.
    objBag.WriteProperty("Mask", "Zorro", null);
    //Attempt with PropertyBag with mask value
    GetMaskProperty(objBag);
}

//UPGRADE_ISSUE: (2068) VBRUN.PropertyBag object was not upgraded.
public void GetMaskProperty(UpgradeStubs.PropertyBag PropBag)
{
    //UPGRADE_ISSUE: (2064) PropertyBag method PropBag.ReadProperty was not upgraded.
    //UPGRADE_WARNING: (1068) PropBag.ReadProperty() of type Variant is being forced to string.
    string mask = ReflectionHelper.GetPrimitiveValue<string>(PropBag.ReadProperty("Mask", "none"));
    MessageBox.Show("Mask is: " + mask, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

public class PropertyBag
{

    public object ReadProperty(string Name_Renamed, object DefaultValue)
    {
        UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBRUN.PropertyBag.ReadProperty");
        return default(object);
    }
    public void WriteProperty(string Name_Renamed, object Value, object DefaultValue)
    {
        UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBRUN.PropertyBag.WriteProperty");
    }
}

C# Expected Code

private void Command1_Click(Object eventSender, EventArgs eventArgs)
{
    UpgradeStubs.PropertyBag objBag = new UpgradeStubs.PropertyBag();
    // Attempt with empty Property Bag, default value
    GetMaskProperty(objBag);
    objBag.WriteProperty("Mask", "Zorro", null);
    //Attempt with PropertyBag with mask value
    GetMaskProperty(objBag);
}

public void GetMaskProperty(UpgradeStubs.PropertyBag PropBag)
{
    string mask = ReflectionHelper.GetPrimitiveValue<string>(PropBag.ReadProperty("Mask", "none"));
    MessageBox.Show("Mask is: " + mask, AssemblyHelper.GetTitle(System.Reflection.Assembly.GetExecutingAssembly()));
}

public class PropertyBag
{
    private Dictionary<string, object> bag = new Dictionary<string, object>();
    public object ReadProperty(string Name_Renamed, object DefaultValue)
    {
        if (bag.ContainsKey(Name_Renamed) == false)
            return DefaultValue;
        else
            return bag[Name_Renamed];
    }
    public void WriteProperty(string Name_Renamed, object Value, object DefaultValue)
    {
        if(Value == null)
        {
            bag.Add(Name_Renamed, DefaultValue);
        }
        else
        {
            bag.Add(Name_Renamed, Value);
        }
    }
}

VB.NET Upgraded Code

Private Sub Command1_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles Command1.Click
    'UPGRADE_ISSUE: (2068) VBRUN.PropertyBag object was not upgraded.'
    Dim objBag As New UpgradeSolution1Support.UpgradeStubs.PropertyBag()
    ' Attempt with empty Property Bag, default value'
    GetMaskProperty(objBag)
    'UPGRADE_ISSUE: (2064) PropertyBag method objBag.WriteProperty was not upgraded.'
    objBag.WriteProperty("Mask", "Zorro")
    'Attempt with PropertyBag with mask value'
    GetMaskProperty(objBag)
End Sub

'UPGRADE_ISSUE: (2068) VBRUN.PropertyBag object was not upgraded.'
Public Sub GetMaskProperty(ByVal PropBag As UpgradeSolution1Support.UpgradeStubs.PropertyBag)
    'UPGRADE_ISSUE: (2064) PropertyBag method PropBag.ReadProperty was not upgraded.'
    'UPGRADE_WARNING: (1068) PropBag.ReadProperty() of type Variant is being forced to String.'
    Dim mask As String = ReflectionHelper.GetPrimitiveValue(Of String)(PropBag.ReadProperty("Mask", "none"))
    MessageBox.Show("Mask is: " & mask, My.Application.Info.Title)
End Sub

Public Class PropertyBag
    Public Function ReadProperty(ByVal Name_Renamed As String, Optional ByVal DefaultValue As Object = Nothing) As Object
        UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBRUN.PropertyBag.ReadProperty")
        Return Nothing
    End Function
    Public Sub WriteProperty(ByVal Name_Renamed As String, ByVal Value As Object, Optional ByVal DefaultValue As Object = Nothing)
        UpgradeHelpers.Helpers.NotUpgradedHelper.NotifyNotUpgradedElement("VBRUN.PropertyBag.WriteProperty")
    End Sub
End Class

VB.NET Expected Code

Private Sub Command1_Click(ByVal eventSender As Object, ByVal eventArgs As EventArgs) Handles Command1.Click
    Dim objBag As New UpgradeSolution1Support.UpgradeStubs.PropertyBag
    ' Attempt with empty Property Bag, default value'
    GetMaskProperty(objBag)
    objBag.WriteProperty("Mask", "Zorro")
    'Attempt with PropertyBag with mask value'
    GetMaskProperty(objBag)
End Sub

Public Sub GetMaskProperty(ByVal PropBag As UpgradeSolution1Support.UpgradeStubs.PropertyBag)
    Dim mask As String = ReflectionHelper.GetPrimitiveValue(Of String)(PropBag.ReadProperty("Mask", "none"))
    MessageBox.Show("Mask is: " & mask, My.Application.Info.Title)
End Sub

Public Class PropertyBag
    Private bag As Dictionary(Of String, String) = New Dictionary(Of String, String)
    Public Function ReadProperty(ByVal Name_Renamed As String, Optional ByVal DefaultValue As Object = Nothing) As Object
        If Not bag.ContainsKey(Name_Renamed) Then
            Return DefaultValue
        Else
            Return bag(Name_Renamed)
        End If
    End Function
    Public Sub WriteProperty(ByVal Name_Renamed As String, ByVal Value As Object, Optional ByVal DefaultValue As Object = Nothing)
        If Microsoft.VisualBasic.Information.IsNothing(Value) Then
            bag.Add(Name_Renamed, DefaultValue)
        Else
            bag.Add(Name_Renamed, Value)
        End If
    End Sub
End Class

2070 - Constant %1 was not upgraded

Description

Some constants may not have a direct equivalent in .NET or may not be mapped to a given equivalent in the current release of the VBUC.

Recommendations

  • There is no straightforward solution for this issue because some class properties may not have an equivalent in .NET at all.

  • The entire not-mapped set of elements requires the manual implementation of the missing member. The stub generation feature will assess the manual effort needed to reach functional equivalence by isolating all not-upgraded class properties in a single source file.

  • For more information, please refer to Stub generation.

  • Also, be sure to check the latest MSDN documentation on Windows Forms Controls for Visual Basic 6.0 Users.

VB6 Original Code

Public Sub CustomCursor()
    Command1.MousePointer = VBRUN.MousePointerConstants.vbCustom
    Command1.MouseIcon = LoadPicture("C:\Picture.cur")
End Sub

C# Upgraded Code

public void CustomCursor()
{
    //UPGRADE_ISSUE: (2070) Constant vbCustom was not upgraded.
    Command1.Cursor = vbCustom;
    Command1.Cursor = new Cursor((new Bitmap(Image.FromFile("C:\\Picture.cur"))).GetHicon());
}

C# Expected Code

public void CustomCursor()
{
    Command1.Cursor = new Cursor((new Bitmap(Image.FromFile("C:\\Picture.cur"))).GetHicon());
}

VB.NET Upgraded Code

Public Sub CustomCursor()
    'UPGRADE_ISSUE: (2070) Constant vbCustom was not upgraded.'
    Command1.Cursor = vbCustom
    Command1.Cursor = New Cursor((New Bitmap(Image.FromFile("C:\Picture.cur"))).GetHicon())
End Sub

VB.NET Expected Code

Public Sub CustomCursor()
    Command1.Cursor = New Cursor((New Bitmap(Image.FromFile("C:\Picture.cur"))).GetHicon())
End Sub

7003 - The Hdc should be released once it is used for safety

Description

This EWI is emitted when a handle to a device context is being used in Visual Basic 6.0 code. In the .Net environment, unmanaged handles like Hdc need to be released back to the GDI system. Failure to do so can result in general errors appearing in the application.

Recommendations

The first thing to consider is that .Net uses GDI+ instead of GDI for its graphics operations. GDI+ is generally easier to work with within the .Net framework and doesn't require calls to unmanaged code. However, GDI+ is a bit slower than regular GDI. So careful consideration must be paid to the performance of the application and the benefits of GDI+ vs GDI considered. In this case, two solutions will be given, one involving GDI and an alternative version using GDI+, and in the expected code it will be shown as follows:

  • Common code - Necessary changes to apply in both cases, we changed the signature of the DrawText method, so we'll have to update the call in the Paint event since we're now sending the Graphics object from PaintEventArgs as a parameter instead of the PictureBox.

  • GDI Solution - For this solution, we had to add a few more GDI methods, namely SetBkMode and SetTextColor. Additionally, CreateFont and DeleteObject could also be added to the list if the font needs to be set. This is necessary because .NET handles drawing with GDI+, so if we want to change GDI's properties we'll have to do it through GDI calls. Also, the .Net PictureBox does not have Font properties we can set.

  • GDI+ Solution - In this case, we use the DrawString method of the System.Drawing.Graphics object.

Visual Basic 6.0 documentation

Additional information that might be helpful while handling this EWI.

VB6 Original Code

Private Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hdc As Long, ByVal X As Long, ByVal Y As Long, ByVal lpString As String, ByVal nCount As Long) As Long

Private Sub DrawText(ByVal pctCanvas As PictureBox, ByVal sText As String, ByVal lLeft As Long, ByVal lTop As Long)
    '== Set the picture box properties'
    pctCanvas.ForeColor = vbBlack
    pctCanvas.FontName = "MS Sans Serif"
    pctCanvas.FontBold = False
    pctCanvas.FontSize = 8.25
    
	'== draw the text'
	Dim hdcValue As Long
    hdcValue = pctCanvas.hdc    
    Call TextOut(hdcValue, lLeft, lTop, sText, Len(sText))
End Sub

Private Sub Picture1_Paint()
    DrawText Me.Picture1, "This is a test", 10, 10
    DrawText Me.Picture1, "This is another test", 40, 40
End Sub

C# Upgraded Code

//Form1.cs
private void DrawText(PictureBox pctCanvas, string sText, int lLeft, int lTop)
{
	//== Set the picture box's properties
	//UPGRADE_ISSUE: (2064) PictureBox property pctCanvas.ForeColor was not upgraded. More Information: https://www.mobilize.net/vbtonet/ewis/ewi2064
	pctCanvas.setForeColor(Color.Black);
	//UPGRADE_WARNING: (2045) Only TrueType and OpenType fonts are supported in Windows Forms. More Information: https://www.mobilize.net/vbtonet/ewis/ewi2045
	pctCanvas.Font = pctCanvas.Font.Change(name:"MS Sans Serif", bold:false, size:8.25f);

	//== draw the text
	//UPGRADE_WARNING: (7003) The Hdc should be released once it is used for safety More Information: https://www.mobilize.net/vbtonet/ewis/ewi7003
	int hdcValue = pctCanvas.CreateGraphics().GetHdc().ToInt32();
	UpgradeSolution1Support.PInvoke.SafeNative.gdi32.TextOut(hdcValue, lLeft, lTop, ref sText, Strings.Len(sText));
}

private void Picture1_Paint(Object eventSender, PaintEventArgs eventArgs)
{
	DrawText(this.Picture1, "This is a test", 10, 10);
	DrawText(this.Picture1, "This is another test", 40, 40);
}

//SafeMethods/gdi32.cs
public static int TextOut(int hdc, int X, int Y, ref string lpString, int nCount)
{
	return UpgradeSolution1Support.PInvoke.UnsafeNative.gdi32.TextOut(hdc, X, Y, ref lpString, nCount);
}

//UnsafeMethods/gdi32.cs
[DllImport("gdi32.dll", EntryPoint = "TextOutA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
extern public static int TextOut(int hdc, int X, int Y, [MarshalAs(UnmanagedType.VBByRefStr)] ref string lpString, int nCount);

C# Expected Code

Common Code

private void Picture1_Paint(Object eventSender, PaintEventArgs eventArgs)
{
	DrawText(eventArgs.Graphics, "This is a test", 10, 10);
	DrawText(eventArgs.Graphics, "This is another test", 40, 40);
}

GDI Solution

//Form1.cs

private const int TRANSPARENT = 1; 

private void DrawText(Graphics g, string sText, int lLeft, int lTop)
{
    IntPtr hdc = g.GetHdc();
    UpgradeSolution1Support.PInvoke.SafeNative.gdi32.SetBkMode(hdc, TRANSPARENT);
    UpgradeSolution1Support.PInvoke.SafeNative.gdi32.SetTextColor(hdc, System.Drawing.ColorTranslator.ToWin32(Color.Black));
    UpgradeSolution1Support.PInvoke.SafeNative.gdi32.TextOut(hdc.ToInt32(), lLeft, lTop, sText, Strings.Len(sText));
    g.ReleaseHdc(hdc);
}

//SafeMethods/gdi32.cs
public static int TextOut(int hdc, int X, int Y, string lpString, int nCount)
{
	return UpgradeSolution1Support.PInvoke.UnsafeNative.gdi32.TextOut(hdc, X, Y, lpString, nCount);
}
public static int SetBkMode(IntPtr hdc, int iBkMode)
{
    return UpgradeSolution1Support.PInvoke.UnsafeNative.gdi32.SetBkMode(hdc, iBkMode);
}
public static int SetTextColor(IntPtr hdc, int crColor)
{
    return UpgradeSolution1Support.PInvoke.UnsafeNative.gdi32.SetTextColor(hdc, crColor);
}

//UnsafeMethods/gdi32.cs
[DllImport("gdi32.dll", EntryPoint = "TextOutA", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
extern public static int TextOut(int hdc, int X, int Y, string lpString, int nCount);
[DllImport("gdi32.dll")]
extern public static int SetBkMode(IntPtr hdc, int iBkMode);
[DllImport("gdi32.dll")]
extern public static int SetTextColor(IntPtr hdc, int crColor);

GDI+ Solution

private void DrawText(Graphics g, string sText, int lLeft, int lTop)
{
	Font font = new Font("Microsoft Sans Serif", 8.25f, FontStyle.Regular, GraphicsUnit.Point, 0);
	SolidBrush brush = new SolidBrush(Color.Black);
	g.DrawString(sText, font, brush, lLeft, lTop);
}

VB.NET Upgraded Code

'Form1.vb'

Private Sub DrawText(ByVal pctCanvas As PictureBox, ByVal sText As String, ByVal lLeft As Integer, ByVal lTop As Integer)
	'== Set the picture boxs properties'
	'UPGRADE_ISSUE: (2064) PictureBox property pctCanvas.ForeColor was not upgraded.'
	pctCanvas.setForeColor(Color.Black)
	'UPGRADE_WARNING: (2045) Only TrueType and OpenType fonts are supported in Windows Forms.'
	pctCanvas.Font = pctCanvas.Font.Change(name:="MS Sans Serif", bold:=False, size:=8.25)

	'== draw the text'
	'UPGRADE_WARNING: (7003) The Hdc should be released once it is used for safety'
	Dim hdcValue As Integer = pctCanvas.CreateGraphics().GetHdc().ToInt32()
	UpgradeSolution1Support.SafeNative.gdi32.TextOut(hdcValue, lLeft, lTop, sText, Strings.Len(sText))
End Sub


Private Sub Picture1_Paint(ByVal eventSender As Object, ByVal eventArgs As PaintEventArgs) Handles Picture1.Paint
	DrawText(Me.Picture1, "This is a test", 10, 10)
	DrawText(Me.Picture1, "This is another test", 40, 40)
End Sub

'SafeMethods/gdi32.vb'
Public Function TextOut(ByVal hdc As Integer, ByVal X As Integer, ByVal Y As Integer, ByRef lpString As String, ByVal nCount As Integer) As Integer
	Return UpgradeSolution1Support.UnsafeNative.gdi32.TextOut(hdc, X, Y, lpString, nCount)
End Function

'UnsafeMethods/gdi32.vb'
Public Module gdi32
	Declare Function TextOut Lib "gdi32"  Alias "TextOutA"(ByVal hdc As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal lpString As String, ByVal nCount As Integer) As Integer
End Module

VB.NET Expected Code

Common Code

Private Sub Picture1_Paint(ByVal eventSender As Object, ByVal eventArgs As PaintEventArgs) Handles Picture1.Paint
    DrawText(eventArgs.Graphics, "This is a test", 10, 10)
    DrawText(eventArgs.Graphics, "This is another test", 40, 40)
End Sub

GDI Solution

'Form1.vb'

Private Const TRANSPARENT As Integer = 1
Private Sub DrawText(ByVal g As Graphics, ByVal sText As String, ByVal lLeft As Integer, ByVal lTop As Integer)
    Dim hdc As IntPtr
    hdc = g.GetHdc()
    UpgradeSolution1Support.SafeNative.gdi32.SetBkMode(hdc, TRANSPARENT)
    UpgradeSolution1Support.SafeNative.gdi32.SetTextColor(hdc, System.Drawing.ColorTranslator.ToWin32(Color.Black))
    UpgradeSolution1Support.SafeNative.gdi32.TextOut(hdc.ToInt32(), lLeft, lTop, sText, sText.Length)
    g.ReleaseHdc(hdc)
End Sub

'SafeMethods/gdi32.vb'
Public Function TextOut(ByVal hdc As Integer, ByVal X As Integer, ByVal Y As Integer, ByRef lpString As String, ByVal nCount As Integer) As Integer
    Return UpgradeSolution1Support.UnsafeNative.gdi32.TextOut(hdc, X, Y, lpString, nCount)
End Function
Public Function SetBkMode(ByVal hdc As IntPtr, ByVal iBkMode As Integer) As Integer
    Return UpgradeSolution1Support.UnsafeNative.gdi32.SetBkMode(hdc, iBkMode)
End Function
Public Function SetTextColor(ByVal hdc As IntPtr, ByVal crColor As Integer) As Integer
    Return UpgradeSolution1Support.UnsafeNative.gdi32.SetTextColor(hdc, crColor)
End Function

'UnsafeMethods/gdi32.vb'
Declare Function TextOut Lib "gdi32" Alias "TextOutA" (ByVal hdc As Integer, ByVal X As Integer, ByVal Y As Integer, ByVal lpString As String, ByVal nCount As Integer) As Integer
Declare Function SetBkMode Lib "gdi32" Alias "SetBkMode" (ByVal hdc As IntPtr, ByVal iBkMode As Integer) As Integer
Declare Function SetTextColor Lib "gdi32" Alias "SetTextColor" (ByVal hdc As IntPtr, ByVal crColor As Integer) As Integer

GDI+ Solution

Private Sub DrawText(ByVal g As Graphics, ByVal sText As String, ByVal lLeft As Integer, ByVal lTop As Integer)
    Dim font As System.Drawing.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, 0)
    Dim brush As System.Drawing.SolidBrush = New System.Drawing.SolidBrush(Color.Black)
    g.DrawString(sText, font, brush, lLeft, lTop)
End Sub

Last updated