ISSUE #7003

    The Hdc should be released once it is used for safety

    Description

    This EWI is emitted when 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 generall 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 in 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+.

    Visual Basic 6.0 documentation

    Additional information that might be helpful while handling this EWI.

    Sample VB6

    PrivateDeclareFunction TextOut Lib"gdi32"Alias"TextOutA"

    (ByVal hdc AsLong, ByVal X AsLong, ByVal Y AsLong, ByVal lpString AsString, _

    ByVal nCount AsLong) AsLong

     

    PrivateSub DrawText(ByVal pctCanvas As PictureBox, ByVal sText AsString, ByVal lLeft AsLong, ByVal lTop AsLong)

    '== Set the picture box's properties

    pctCanvas.ForeColor = vbBlack

    pctCanvas.FontName = "MS Sans Serif"

    pctCanvas.FontBold = False

    pctCanvas.FontSize = 8.25

     

    '== draw the text

     

    Call TextOut(pctCanvas.hdc, lLeft, lTop, sText, Len(sText))

    EndSub

     

    PrivateSub Picture1_Paint()

    DrawText(Me.Picture1, "This is a test", 10, 10)

    DrawText(Me.Picture1, "This is another test", 40, 40)

    EndSub

    Target VB.NET

    PrivateDeclareFunction TextOut Lib"gdi32"Alias"TextOutA" (ByVal hdc AsInteger, ByVal X AsInteger, ByVal Y AsInteger, ByVal lpString AsString, ByVal nCount AsInteger) AsInteger

     

    PrivateSub DrawText(ByRef pctCanvas As PictureBox, ByRef sText AsString, ByRef lLeft AsInteger, ByRef lTop AsInteger)

    '== Set the picture box's properties

    'UPGRADE_ISSUE: (2069) PictureBox property pctCanvas.ForeColor was not upgraded.

    UpgradeStubs.VB_PictureBox.setForeColor(pctCanvas, Color.Black)

    'UPGRADE_WARNING: (2045) Only TrueType and OpenType fonts are supported in Windows Forms.

    pctCanvas.Font = VB6.FontChangeName(pctCanvas.Font, "MS Sans Serif")

    pctCanvas.Font = VB6.FontChangeBold(pctCanvas.Font, False)

    pctCanvas.Font = VB6.FontChangeSize(pctCanvas.Font, 8.25)

     

    '== draw the text

    'UPGRADE_WARNING: (7003) The Hdc should be released once it is used for safety

    TextOut(pctCanvas.CreateGraphics().GetHdc().ToInt32(), lLeft, lTop, sText, sText.Length)

    EndSub

     

    PrivateSub Picture1_Paint(ByVal eventSender AsObject, 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)

    EndSub

    Expected VB.NET

    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.

    PrivateDeclareFunction TextOut Lib"gdi32"Alias"TextOutA" (ByVal hdc AsInteger, ByVal X AsInteger, ByVal Y AsInteger, ByVal lpString AsString, ByVal nCount AsInteger) AsInteger

    PrivateDeclareFunction SetBkMode Lib"gdi32"Alias"SetBkMode" (ByVal hdc As IntPtr, ByVal iBkMode AsInteger) AsInteger

    PrivateDeclareFunction SetTextColor Lib"gdi32"Alias"SetTextColor" (ByVal hdc As IntPtr, ByVal crColor AsInteger) AsInteger

    PrivateConst TRANSPARENT AsInteger = 1

    PrivateConst OPAQUE AsInteger = 2

     

    PrivateSub DrawText(ByRef g As System.Drawing.Graphics, ByRef sText AsString, ByRef lLeft AsInteger, ByRef lTop AsInteger)

    Dim hdc As IntPtr

    hdc = g.GetHdc()

    SetBkMode(hdc, TRANSPARENT)

    SetTextColor(hdc, System.Drawing.ColorTranslator.ToWin32(Color.Black))

    TextOut(hdc.ToInt32(), lLeft, lTop, sText, sText.Length)

    'UPGRADE_WARNING: (7003) The Hdc should be released once it is used for safety

    g.ReleaseHdc(hdc)

    EndSub

    GDI+ solution

    In this case we use the DrawString method of the System.Drawing.Graphics object.

    PrivateSub DrawText(ByRef g As System.Drawing.Graphics, ByRef sText AsString, ByRef lLeft AsInteger, ByRef lTop AsInteger)

    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)

    EndSub

    Common code

    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.

    PrivateSub Picture1_Paint(ByVal eventSender AsObject, 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)

    EndSub

    Target C#

    [DllImport("gdi32.dll")]

    publicexternstaticint TextOut(int hdc, int X, int Y, string lpString, int nCount);

     

    privatevoid DrawText(PictureBox pctCanvas, string sText, int lLeft, int lTop)

    {

    //== Set the picture box's properties

    //UPGRADE_ISSUE: (2069) PictureBox property pctCanvas.ForeColor was not upgraded.

    UpgradeStubs.VB_PictureBox.setForeColor(pctCanvas, Color.Black);

    //UPGRADE_WARNING: (2045) Only TrueType and OpenType fonts are supported in Windows Forms.

    pctCanvas.Font = VB6.FontChangeName(pctCanvas.Font, "MS Sans Serif");

    pctCanvas.Font = VB6.FontChangeBold(pctCanvas.Font, false);

    pctCanvas.Font = VB6.FontChangeSize(pctCanvas.Font, 8.25f);

     

    //== draw the text

    //UPGRADE_WARNING: (7003) The Hdc should be released once it is used for safety

    TextOut(pctCanvas.CreateGraphics().GetHdc().ToInt32(), lLeft, lTop, sText, sText.Length);

    }

     

    privatevoid Picture1_Paint(Object eventSender, PaintEventArgs eventArgs)

    {

    DrawText(this.Picture1, "This is a test", 10, 10);

    DrawText(this.Picture1, "This is another test", 40, 40);

    }

    Expected C#

    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.

    [DllImport("gdi32.dll")]

    publicexternstaticint TextOut(int hdc, int X, int Y, string lpString, int nCount);

    [DllImport("gdi32.dll")]

    privateexternstaticint SetBkMode(IntPtr hdc, int iBkMode);

    [DllImport("gdi32.dll")]

    privateexternstaticint SetTextColor(IntPtr hdc, int crColor);

    privateconstint TRANSPARENT = 1;

    privateconstint OPAQUE = 2;

     

    privatevoid DrawText(Graphics g, string sText, int lLeft, int lTop)

    {

    //UPGRADE_WARNING: (7003) The Hdc should be released once it is used for safety

    IntPtr hdc = g.GetHdc();

    SetBkMode(hdc, TRANSPARENT);

    SetTextColor(hdc, System.Drawing.ColorTranslator.ToWin32(Color.Black));

    TextOut(hdc.ToInt32(), lLeft, lTop, sText, sText.Length);

    g.ReleaseHdc(hdc);

    }

    GDI+ solution

    In this case we use the DrawString method of the System.Drawing.Graphics object.

    privatevoid DrawText(Graphics g, string sText, int lLeft, int lTop)

    {

    //UPGRADE_WARNING: (7003) The Hdc should be released

    Font font = newFont("Microsoft Sans Serif", 8.25f, FontStyle.Regular, GraphicsUnit.Point, 0);

    SolidBrush brush = newSolidBrush(Color.Black);

    g.DrawString(sText, font, brush, lLeft, lTop);

    }

    Common code

    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.

    privatevoid Picture1_Paint(Object eventSender, PaintEventArgs eventArgs)

    {

    DrawText(eventArgs.Graphics, "This is a test", 10, 10);

    DrawText(eventArgs.Graphics, "This is another test", 40, 40);

    }


    Download VBUC Free Trial
    Download VBUC Now

    It's time to eradicate VB6
    ROI of eradicating VB6

    8 Proven Tips for
    Planning a Successful Migration

    8 Tips for migration