Managing un-managed Resources

Posted Fri, Nov 24 2006 8:19 by bill
Yesterday Chuck posted a cool example of using GDI from .NET.  One thing that was missing was the call to ReleaseDC.  You should *always* call this when finished painting with a DC obtained via GetWindowDC.
 
   DeclareFunction ReleaseDC Lib"user32" (ByVal hwnd As IntPtr, ByVal dc As IntPtr) As Int32
 
So Chuck's code on first iteration would then become:
 
Dim g As Graphics = Me.CreateGraphics()
'Get the Graphics context to blt into
Dim Desktop_Hwnd As IntPtr
Dim Desktop_HDC As IntPtr
Desktop_Hwnd = GetDesktopWindow()
Desktop_HDC = GetWindowDC(Desktop_Hwnd)
Debug.Print(StretchBlt(g.GetHdc, 0, 0, Me.Width, Me.Height, Desktop_HDC, 0, 0, Me.Height / 2, Me.Width / 2, SRCCOPY))
'Slam the super sized desktop into graphics context of your form.
 
ReleaseDC(Desktop_Hwnd, Desktop_HDC)
 
 
 
Okay so far we have released the unmanaged resource (DeviceContext).  Problem two is the graphics object itself should be disposed of by calling Dispose on it rather than waiting for a GC collection to call its finalizer .  So with the graphics object, we should probably use a Using block.
 
And with our DC, we probably want to ensure that Release gets called, so we'd need to wrap that in a Try Finalize block.  We can't use a Using block for the DC as it doesn't implement IDisposable.
 
Using g As Graphics = Me.CreateGraphics()
'Get the Graphics context to blt into
     Dim Desktop_Hwnd As IntPtr
     Dim Desktop_HDC As IntPtr
     Try
       Desktop_Hwnd = GetDesktopWindow()
       Desktop_HDC = GetWindowDC(Desktop_Hwnd)
       Debug.Print(StretchBlt(g.GetHdc, 0, 0, Me.Width, Me.Height, Desktop_HDC, 0, 0, Me.Height / 2, Me.Width / 2, SRCCOPY))
'Slam the super sized desktop into graphics context of your form.
    Finally
        ReleaseDC(Desktop_Hwnd, Desktop_HDC)
    End Try
End Using
 
 
 
Alternatively we can get all OO, and encapsulate the DC in a class that does implement IDisposable. Our code would then look like:
 
Using g As Graphics = Me.CreateGraphics(), _
        nDC As NativeDC = NativeDC.GetDeskTopDC
       Debug.Print(StretchBlt(g.GetHdc, 0, 0, Me.Width, Me.Height, nDC.DC, 0, 0, Me.Height / 2, Me.Width / 2, SRCCOPY))
End Using
 
We've now made the DC have a managed wrapper to make managing the unmanaged resource a lot easier and more in keeping with .NET memory resource management.
 
The next thing you might want to consider is moving the StretchBlt methods into the NativeDC class, allowing you to move all the win32 API calls into one class so as you can later easily look at adding declarative security assertions etc.

 

Public Class NativeDC
   Implements IDisposable
 
 
   Private m_DC As IntPtr
   Private m_Hwnd As IntPtr
   Private m_NeedsDispose As Boolean
 
   Public Sub New(ByVal dc As IntPtr)
      m_DC = dc
   End Sub
 
   Public ReadOnly Property DC() As IntPtr
      Get
         Return m_DC
      End Get
   End Property
 
 
   Public Shared Function FromWindow(ByVal hwnd As IntPtr) As NativeDC
      Dim nDC As New NativeDC(GetWindowDC(hwnd))
      nDC.m_Hwnd = hwnd
      nDC.m_NeedsDispose = True
      Return nDC
   End Function
 
   Public Shared Function GetDesktopDC() As NativeDC
      Return FromWindow(GetDesktopWindow())
   End Function
 
 
 
#Region " IDisposable Support "
 
   Public Sub Dispose() Implements IDisposable.Dispose
      Dispose(True)
      GC.SuppressFinalize(Me)
   End Sub
 
   Protected Overrides Sub Finalize()
      Dispose(True)
   End Sub
 
   Protected Overridable Sub Dispose(ByVal disposing As Boolean)
      If Me.m_NeedsDispose Then
         ReleaseDC(Me.m_Hwnd, Me.m_DC)
      End If
      Me.m_NeedsDispose = False
   End Sub
 
 
#End Region
 
 
 
#Region "win32 API"
 
   Declare Function GetDesktopWindow Lib "user32" () As IntPtr
 
   Declare Function GetWindowDC Lib "user32" (ByVal hwnd As IntPtr) As IntPtr
 
   Declare Function ReleaseDC Lib "user32" (ByVal hwnd As IntPtr, ByVal dc As IntPtr) As Int32
 
#End Region
 
 
 
End Class
 
 
Filed under: