- INDEX VB TO NET
- KNOWLEDGE BASE
- EWIS-ISSUES
- ISSUE #7003
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);
}