Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove DeviceContext HWND handling #47081

Merged
merged 2 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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