Skip to content

Commit

Permalink
Remove DeviceContext HWND handling (#47081)
Browse files Browse the repository at this point in the history
* Remove DeviceContext HWND handling

* Remove IDeviceContext from DeviceContext
  • Loading branch information
hughbe authored Feb 3, 2021
1 parent 04fea3e commit 7a5e37e
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 149 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -243,38 +243,32 @@ public void CopyFromScreen(int sourceX, int sourceY, int destinationX, int desti
int destWidth = blockRegionSize.Width;
int destHeight = blockRegionSize.Height;

using (DeviceContext dc = DeviceContext.FromHwnd(IntPtr.Zero))
IntPtr screenDC = Interop.User32.GetDC(IntPtr.Zero);
try
{
// The DC of the screen.
IntPtr screenDC = dc.Hdc;

// The DC of the current graphics object.
IntPtr targetDC = GetHdc();

try
{
int result = Interop.Gdi32.BitBlt(
targetDC,
destinationX,
destinationY,
destWidth,
destHeight,
screenDC,
sourceX,
sourceY,
(Interop.Gdi32.RasterOp)copyPixelOperation);

//a zero result indicates a win32 exception has been thrown
if (result == 0)
{
throw new Win32Exception();
}
}
finally
int result = Interop.Gdi32.BitBlt(
targetDC,
destinationX,
destinationY,
destWidth,
destHeight,
screenDC,
sourceX,
sourceY,
(Interop.Gdi32.RasterOp)copyPixelOperation);

//a zero result indicates a win32 exception has been thrown
if (result == 0)
{
ReleaseHdc();
throw new Win32Exception();
}
}
finally
{
Interop.User32.ReleaseDC(IntPtr.Zero, screenDC);
ReleaseHdc();
}
}

public Color GetNearestColor(Color color)
Expand Down
121 changes: 2 additions & 119 deletions src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace System.Drawing.Internal
/// This class is divided into two files separating the code that needs to be compiled into retail builds and
/// debugging code.
/// </summary>
internal sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext, IDisposable
internal sealed partial class DeviceContext : MarshalByRefObject, IDisposable
{
/// <summary>
/// This class is a wrapper to a Win32 device context, and the Hdc property is the way to get a
Expand Down Expand Up @@ -63,10 +63,6 @@ internal sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext

private bool _disposed;

// We cache the hWnd when creating the dc from one, to provide support forIDeviceContext.GetHdc/ReleaseHdc.
// This hWnd could be null, in such case it is referring to the screen.
private readonly IntPtr _hWnd = (IntPtr)(-1); // Unlikely to be a valid hWnd.

private IntPtr _hInitialPen;
private IntPtr _hInitialBrush;
private IntPtr _hInitialBmp;
Expand All @@ -88,34 +84,7 @@ internal sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext
/// This object's hdc. If this property is called, then the object will be used as an HDC wrapper, so the hdc
/// is cached and calls to GetHdc/ReleaseHdc won't PInvoke into GDI. Call Dispose to properly release the hdc.
/// </summary>
public IntPtr Hdc
{
get
{
if (_hDC == IntPtr.Zero)
{
if (_dcType == DeviceContextType.Display)
{
Debug.Assert(!_disposed, "Accessing a disposed DC, forcing recreation of HDC - this will generate a Handle leak!");

// Note: ReleaseDC must be called from the same thread. This applies only to HDC obtained
// from calling GetDC. This means Display DeviceContext objects should never be finalized.
_hDC = ((IDeviceContext)this).GetHdc(); // _hDC will be released on call to Dispose.
CacheInitialState();
}
#if GDI_FINALIZATION_WATCH
else
{
try { Debug.WriteLine($"Allocation stack:\r\n{AllocationSite}\r\nDeallocation stack:\r\n{DeAllocationSite}"); } catch {}
}
#endif
}

Debug.Assert(_hDC != IntPtr.Zero, "Attempt to use deleted HDC - DC type: " + _dcType);

return _hDC;
}
}
public IntPtr Hdc => _hDC;

// Due to a problem with calling DeleteObject() on currently selected GDI objects, we now track the initial set
// of objects when a DeviceContext is created. Then, we also track which objects are currently selected in the
Expand All @@ -129,24 +98,6 @@ private void CacheInitialState()
_hCurrentFont = _hInitialFont = Interop.Gdi32.GetCurrentObject(new HandleRef(this, _hDC), Interop.Gdi32.ObjectType.OBJ_FONT);
}


/// <summary>
/// Constructor to construct a DeviceContext object from an window handle.
/// </summary>
private DeviceContext(IntPtr hWnd)
{
_hWnd = hWnd;
_dcType = DeviceContextType.Display;

DeviceContexts.AddDeviceContext(this);

// the hDc will be created on demand.

#if TRACK_HDC
Debug.WriteLine( DbgUtil.StackTraceToStr(string.Format( "DeviceContext( hWnd=0x{0:x8} )", unchecked((int) hWnd))));
#endif
}

/// <summary>
/// Constructor to construct a DeviceContext object from an existing Win32 device context handle.
/// </summary>
Expand All @@ -158,10 +109,6 @@ private DeviceContext(IntPtr hDC, DeviceContextType dcType)
CacheInitialState();
DeviceContexts.AddDeviceContext(this);

if (dcType == DeviceContextType.Display)
{
_hWnd = Interop.User32.WindowFromDC(new HandleRef(this, _hDC));
}
#if TRACK_HDC
Debug.WriteLine(DbgUtil.StackTraceToStr($"DeviceContext(hDC=0x{(int)hDC:X8}, Type={dcType})"));
#endif
Expand All @@ -188,19 +135,6 @@ public static DeviceContext CreateIC(string driverName, string deviceName, strin
return new DeviceContext(hdc, DeviceContextType.Information);
}

/// <summary>
/// Creates a DeviceContext object wrapping a memory DC compatible with the specified device.
/// </summary>
public static DeviceContext FromCompatibleDC(IntPtr hdc)
{
// If hdc is null, the function creates a memory DC compatible with the application's current screen.
// Win2K+: (See CreateCompatibleDC in the MSDN).
// In this case the thread that calls CreateCompatibleDC owns the HDC that is created. When this thread is destroyed,
// the HDC is no longer valid.
IntPtr compatibleDc = Interop.Gdi32.CreateCompatibleDC(hdc);
return new DeviceContext(compatibleDc, DeviceContextType.Memory);
}

/// <summary>
/// Used for wrapping an existing hdc. In this case, this object doesn't own the hdc so calls to
/// GetHdc/ReleaseHdc don't PInvoke into GDI.
Expand All @@ -211,11 +145,6 @@ public static DeviceContext FromHdc(IntPtr hdc)
return new DeviceContext(hdc, DeviceContextType.Unknown);
}

/// <summary>
/// When hwnd is null, we are getting the screen DC.
/// </summary>
public static DeviceContext FromHwnd(IntPtr hwnd) => new DeviceContext(hwnd);

~DeviceContext() => Dispose(false);

public void Dispose()
Expand All @@ -237,10 +166,6 @@ internal void Dispose(bool disposing)

switch (_dcType)
{
case DeviceContextType.Display:
Debug.Assert(disposing, "WARNING: Finalizing a Display DeviceContext.\r\nReleaseDC may fail when not called from the same thread GetDC was called from.");
((IDeviceContext)this).ReleaseHdc();
break;
case DeviceContextType.Information:
case DeviceContextType.NamedDevice:
Interop.Gdi32.DeleteDC(new HandleRef(this, _hDC));
Expand All @@ -261,48 +186,6 @@ internal void Dispose(bool disposing)
DbgUtil.AssertFinalization(this, disposing);
}

/// <summary>
/// Explicit interface method implementation to hide them a bit for usability reasons so the object is seen as
/// a wrapper around an hdc that is always available, and for performance reasons since it caches the hdc if
/// used in this way.
/// </summary>
IntPtr IDeviceContext.GetHdc()
{
if (_hDC == IntPtr.Zero)
{
Debug.Assert(_dcType == DeviceContextType.Display, "Calling GetDC from a non display/window device.");

// Note: for common DCs, GetDC assigns default attributes to the DC each time it is retrieved.
// For example, the default font is System.
_hDC = Interop.User32.GetDC(new HandleRef(this, _hWnd));
#if TRACK_HDC
Debug.WriteLine( DbgUtil.StackTraceToStr( string.Format("hdc[0x{0:x8}]=DC.GetHdc(hWnd=0x{1:x8})", unchecked((int) _hDC), unchecked((int) _hWnd))));
#endif
}

return _hDC;
}


///<summary>
/// If the object was created from a DC, this object doesn't 'own' the dc so we just ignore this call.
///</summary>
void IDeviceContext.ReleaseHdc()
{
if (_hDC != IntPtr.Zero && _dcType == DeviceContextType.Display)
{
#if TRACK_HDC
int retVal =
#endif
Interop.User32.ReleaseDC(new HandleRef(this, _hWnd), new HandleRef(this, _hDC));
// Note: retVal == 0 means it was not released but doesn't necessarily means an error; class or private DCs are never released.
#if TRACK_HDC
Debug.WriteLine( DbgUtil.StackTraceToStr( string.Format("[ret={0}]=DC.ReleaseDC(hDc=0x{1:x8}, hWnd=0x{2:x8})", retVal, unchecked((int) _hDC), unchecked((int) _hWnd))));
#endif
_hDC = IntPtr.Zero;
}
}

/// <summary>
/// Restores the device context to the specified state. The DC is restored by popping state information off a
/// stack created by earlier calls to the SaveHdc function.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ internal enum DeviceContextType
// Unknown device
Unknown = 0x00,

// Display DC - obtained from GetDC/GetDCEx/BeginPaint.
Display = 0x01,

// Window DC including non-client area - obtained from GetWindowDC
NCWindow = 0x02,

Expand Down

0 comments on commit 7a5e37e

Please sign in to comment.