From f919603e7c0055b1636a45eb317c6adccd58b11b Mon Sep 17 00:00:00 2001 From: Hugh Bellamy Date: Sat, 16 Jan 2021 14:20:10 +0000 Subject: [PATCH 1/2] Remove DeviceContext HWND handling --- .../src/System/Drawing/Graphics.Windows.cs | 48 ++++---- .../src/misc/GDI/DeviceContext.cs | 107 +----------------- .../src/misc/GDI/DeviceContextType.cs | 3 - 3 files changed, 23 insertions(+), 135 deletions(-) diff --git a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs index 9428febc1a763..fef8416d462be 100644 --- a/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs +++ b/src/libraries/System.Drawing.Common/src/System/Drawing/Graphics.Windows.cs @@ -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) diff --git a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs index 209445ea3f38e..752866acd3396 100644 --- a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs +++ b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs @@ -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; @@ -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. /// - 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 @@ -129,24 +98,6 @@ private void CacheInitialState() _hCurrentFont = _hInitialFont = Interop.Gdi32.GetCurrentObject(new HandleRef(this, _hDC), Interop.Gdi32.ObjectType.OBJ_FONT); } - - /// - /// Constructor to construct a DeviceContext object from an window handle. - /// - 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 - } - /// /// Constructor to construct a DeviceContext object from an existing Win32 device context handle. /// @@ -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 @@ -188,19 +135,6 @@ public static DeviceContext CreateIC(string driverName, string deviceName, strin return new DeviceContext(hdc, DeviceContextType.Information); } - /// - /// Creates a DeviceContext object wrapping a memory DC compatible with the specified device. - /// - 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); - } - /// /// 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. @@ -211,11 +145,6 @@ public static DeviceContext FromHdc(IntPtr hdc) return new DeviceContext(hdc, DeviceContextType.Unknown); } - /// - /// When hwnd is null, we are getting the screen DC. - /// - public static DeviceContext FromHwnd(IntPtr hwnd) => new DeviceContext(hwnd); - ~DeviceContext() => Dispose(false); public void Dispose() @@ -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)); @@ -266,41 +191,13 @@ internal void Dispose(bool disposing) /// a wrapper around an hdc that is always available, and for performance reasons since it caches the hdc if /// used in this way. /// - 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; - } - + IntPtr IDeviceContext.GetHdc() => _hDC; /// /// If the object was created from a DC, this object doesn't 'own' the dc so we just ignore this call. /// 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; - } } /// diff --git a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs index 9e73c71a84d38..afd80b750b67b 100644 --- a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs +++ b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContextType.cs @@ -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, From 679b64d94b3c83b917d3f8f348465ebe61b43a1d Mon Sep 17 00:00:00 2001 From: Hugh Bellamy Date: Wed, 27 Jan 2021 11:39:58 +0000 Subject: [PATCH 2/2] Remove IDeviceContext from DeviceContext --- .../src/misc/GDI/DeviceContext.cs | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs index 752866acd3396..5ca8b91b9c5c8 100644 --- a/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs +++ b/src/libraries/System.Drawing.Common/src/misc/GDI/DeviceContext.cs @@ -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. /// - internal sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext, IDisposable + internal sealed partial class DeviceContext : MarshalByRefObject, IDisposable { /// /// This class is a wrapper to a Win32 device context, and the Hdc property is the way to get a @@ -186,20 +186,6 @@ internal void Dispose(bool disposing) DbgUtil.AssertFinalization(this, disposing); } - /// - /// 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. - /// - IntPtr IDeviceContext.GetHdc() => _hDC; - - /// - /// If the object was created from a DC, this object doesn't 'own' the dc so we just ignore this call. - /// - void IDeviceContext.ReleaseHdc() - { - } - /// /// 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.