VB to .NET

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)

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);

}

 

Talk To An Engineer