Code:
/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / whidbey / NetFXspW7 / ndp / fx / src / Misc / GDI / DeviceContext.cs / 1 / DeviceContext.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- //#define TRACK_HDC //#define GDI_FINALIZATION_WATCH #if WINFORMS_NAMESPACE namespace System.Windows.Forms.Internal #elif DRAWING_NAMESPACE namespace System.Drawing.Internal #else namespace System.Experimental.Gdi #endif { using System; using System.Collections; using System.Internal; using System.Security; using System.Runtime.InteropServices; using System.Diagnostics; using System.ComponentModel; using System.Drawing; using System.Diagnostics.CodeAnalysis; using System.Globalization; ////// /// Represents a Win32 device context. Provides operations for setting some of the properties /// of a device context. It's the managed wrapper for an HDC. /// /// This class is divided into two files separating the code that needs to be compiled into /// reatail builds and debugging code. /// #if WINFORMS_PUBLIC_GRAPHICS_LIBRARY public #else internal #endif sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext, IDisposable { ////// This class is a wrapper to a Win32 device context, and the Hdc property is the way to get a /// handle to it. /// /// The hDc is released/deleted only when owned by the object, meaning it was created internally; /// in this case, the object is responsible for releasing/deleting it. /// In the case the object is created from an exisiting hdc, it is not released; this is consistent /// with the Win32 guideline that says if you call GetDC/CreateDC/CreatIC/CreateEnhMetafile, you are /// responsible for calling ReleaseDC/DeleteDC/DeleteEnhMetafile respectivelly. /// /// This class implements some of the operations commonly performed on the properties of a dc in WinForms, /// specially for interacting with GDI+, like clipping and coordinate transformation. /// Several properties are not persisted in the dc but instead they are set/reset during a more comprehensive /// operation like text rendering or painting; for instance text alignment is set and reset during DrawText (GDI), /// DrawString (GDI+). /// /// Other properties are persisted from operation to operation until they are reset, like clipping, /// one can make several calls to Graphics or WindowsGraphics obect after setting the dc clip area and /// before resetting it; these kinds of properties are the ones implemented in this class. /// This kind of properties place an extra chanllenge in the scenario where a DeviceContext is obtained /// from a Graphics object that has been used with GDI+, because GDI+ saves the hdc internally, rendering the /// DeviceContext underlying hdc out of [....]. DeviceContext needs to support these kind of properties to /// be able to keep the GDI+ and GDI HDCs in [....]. /// /// A few other persisting properties have been implemented in DeviceContext2, among them: /// 1. Window origin. /// 2. Bounding rectangle. /// 3. DC origin. /// 4. View port extent. /// 5. View port origin. /// 6. Window extent /// /// Other non-persisted properties just for information: Background/Forground color, Palette, Color adjustment, /// Color space, ICM mode and profile, Current pen position, Binary raster op (not supported by GDI+), /// Background mode, Logical Pen, DC pen color, ARc direction, Miter limit, Logical brush, DC brush color, /// Brush origin, Polygon filling mode, Bitmap stretching mode, Logical font, Intercharacter spacing, /// Font mapper flags, Text alignment, Test justification, Layout, Path, Meta region. /// See book "Windows Graphics Programming - Feng Yuang", P315 - Device Context Attributes. /// [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] IntPtr hDC; DeviceContextType dcType; public event EventHandler Disposing; 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. [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] IntPtr hWnd = (IntPtr) (-1); // Unlikely to be a valid hWnd. IntPtr hInitialPen; IntPtr hInitialBrush; IntPtr hInitialBmp; IntPtr hInitialFont; IntPtr hCurrentPen; IntPtr hCurrentBrush; IntPtr hCurrentBmp; IntPtr hCurrentFont; Stack contextStack; #if GDI_FINALIZATION_WATCH private string AllocationSite = DbgUtil.StackTrace; private string DeAllocationSite = ""; #endif /// /// Class properties... /// ////// Specifies whether a modification has been applied to the dc, like setting the clipping area or a coordinate transform. /// ////// The device type the context refers to. /// public DeviceContextType DeviceContextType { get { return this.dcType; } } ////// 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( this.hDC == IntPtr.Zero ) { if( this.dcType == DeviceContextType.Display ) { Debug.Assert(!this.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. this.hDC = ((IDeviceContext)this).GetHdc(); // this.hDC will be released on call to Dispose. CacheInitialState(); } #if GDI_FINALIZATION_WATCH else { try { Debug.WriteLine(string.Format("Allocation stack:\r\n{0}\r\nDeallocation stack:\r\n{1}", AllocationSite, DeAllocationSite)); } catch {} } #endif } Debug.Assert( this.hDC != IntPtr.Zero, "Attempt to use deleted HDC - DC type: " + this.dcType ); return this.hDC; } } // VSWhidbey 536325 // 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 DeviceContext. When // a currently selected object is disposed, it is first replaced in the DC and then // deleted. private void CacheInitialState() { Debug.Assert(this.hDC != IntPtr.Zero, "Cannot get initial state without a valid HDC"); hCurrentPen = hInitialPen = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_PEN); hCurrentBrush = hInitialBrush = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_BRUSH); hCurrentBmp = hInitialBmp = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_BITMAP); hCurrentFont = hInitialFont = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_FONT); } public void DeleteObject(IntPtr handle, GdiObjectType type) { IntPtr handleToDelete = IntPtr.Zero; switch (type) { case GdiObjectType.Pen: if (handle == hCurrentPen) { IntPtr currentPen = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialPen)); Debug.Assert(currentPen == hCurrentPen, "DeviceContext thinks a different pen is selected than the HDC"); hCurrentPen = IntPtr.Zero; } handleToDelete = handle; break; case GdiObjectType.Brush: if (handle == hCurrentBrush) { IntPtr currentBrush = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialBrush)); Debug.Assert(currentBrush == hCurrentBrush, "DeviceContext thinks a different brush is selected than the HDC"); hCurrentBrush = IntPtr.Zero; } handleToDelete = handle; break; case GdiObjectType.Bitmap: if (handle == hCurrentBmp) { IntPtr currentBmp = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialBmp)); Debug.Assert(currentBmp == hCurrentBmp, "DeviceContext thinks a different brush is selected than the HDC"); hCurrentBmp = IntPtr.Zero; } handleToDelete = handle; break; } IntUnsafeNativeMethods.DeleteObject(new HandleRef(this, handleToDelete)); } // // object construction API. Publicly constructable from static methods only. // ////// Constructor to contruct a DeviceContext object from an window handle. /// private DeviceContext(IntPtr hWnd) { this.hWnd = hWnd; this.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} )", (int) hWnd))); #endif } ////// Constructor to contruct a DeviceContext object from an existing Win32 device context handle. /// private DeviceContext(IntPtr hDC, DeviceContextType dcType) { this.hDC = hDC; this.dcType = dcType; CacheInitialState(); DeviceContexts.AddDeviceContext(this); if( dcType == DeviceContextType.Display ) { this.hWnd = IntUnsafeNativeMethods.WindowFromDC( new HandleRef( this, this.hDC) ); } #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DeviceContext( hDC=0x{0:X8}, Type={1} )", (int) hDC, dcType) )); #endif } // // ////// CreateDC creates a DeviceContext object wrapping an hdc created with the Win32 CreateDC function. /// public static DeviceContext CreateDC(string driverName, string deviceName, string fileName, HandleRef devMode) { // Note: All input params can be null but not at the same time. See MSDN for information. IntPtr hdc = IntUnsafeNativeMethods.CreateDC(driverName, deviceName, fileName, devMode); return new DeviceContext( hdc, DeviceContextType.NamedDevice ); } ////// CreateIC creates a DeviceContext object wrapping an hdc created with the Win32 CreateIC function. /// public static DeviceContext CreateIC(string driverName, string deviceName, string fileName, HandleRef devMode) { // Note: All input params can be null but not at the same time. See MSDN for information. IntPtr hdc = IntUnsafeNativeMethods.CreateIC(driverName, deviceName, fileName, devMode); 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 = IntUnsafeNativeMethods.CreateCompatibleDC( new HandleRef(null, 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. /// public static DeviceContext FromHdc(IntPtr hdc) { Debug.Assert( hdc != IntPtr.Zero, "hdc == 0" ); return new DeviceContext(hdc, DeviceContextType.Unknown); } ////// /// When hwnd is null, we are getting the screen DC. /// public static DeviceContext FromHwnd( IntPtr hwnd ) { return new DeviceContext(hwnd); } ///~DeviceContext() { Dispose(false); } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// internal void Dispose(bool disposing) { if (disposed) { return; } if (this.Disposing != null) { this.Disposing(this, EventArgs.Empty); } this.disposed = true; #if !DRAWING_NAMESPACE DisposeFont(disposing); #endif switch( this.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: // CreateDC and CreateIC add an HDC handle to the HandleCollector; to remove it properly we need // to call DeleteHDC. #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DC.DeleteHDC(hdc=0x{0:x8})", (int) this.hDC))); #endif IntUnsafeNativeMethods.DeleteHDC(new HandleRef(this, this.hDC)); this.hDC = IntPtr.Zero; break; case DeviceContextType.Memory: // CreatCompatibleDC adds a GDI handle to HandleCollector, to remove it properly we need to call // DeleteDC. #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DC.DeleteDC(hdc=0x{0:x8})", (int) this.hDC))); #endif IntUnsafeNativeMethods.DeleteDC(new HandleRef(this, this.hDC)); this.hDC = IntPtr.Zero; break; // case DeviceContextType.Metafile: - not yet supported. #if WINFORMS_PUBLIC_GRAPHICS_LIBRARY case DeviceContextType.Metafile: IntUnsafeNativeMethods.CloseEnhMetaFile(new HandleRef(this, this.Hdc)); this.hDC = IntPtr.Zero; break; #endif case DeviceContextType.Unknown: default: return; // do nothing, the hdc is not owned by this object. // in this case it is ok if disposed throught finalization. } 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() { if (this.hDC == IntPtr.Zero) { Debug.Assert( this.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. this.hDC = IntUnsafeNativeMethods.GetDC(new HandleRef(this, this.hWnd)); #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("hdc[0x{0:x8}]=DC.GetHdc(hWnd=0x{1:x8})", (int) this.hDC, (int) this.hWnd))); #endif } return this.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 (this.hDC != IntPtr.Zero && this.dcType == DeviceContextType.Display) { #if TRACK_HDC int retVal = #endif IntUnsafeNativeMethods.ReleaseDC(new HandleRef(this, this.hWnd), new HandleRef(this, 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, (int) this.hDC, (int) this.hWnd))); #endif this.hDC = IntPtr.Zero; } } ////// Specifies whether the DC is in GM_ADVANCE mode (supported only in NT platforms). /// If false, it is in GM_COMPATIBLE mode. /// public DeviceContextGraphicsMode GraphicsMode { get { return (DeviceContextGraphicsMode) IntUnsafeNativeMethods.GetGraphicsMode( new HandleRef( this, this.Hdc ) ); } #if WINFORMS_PUBLIC_GRAPHICS_LIBRARY set { SetGraphicsMode(value); } #endif } ////// Sets the dc graphics mode and returns the old value. /// public DeviceContextGraphicsMode SetGraphicsMode( DeviceContextGraphicsMode newMode ) { return (DeviceContextGraphicsMode) IntUnsafeNativeMethods.SetGraphicsMode( new HandleRef( this, this.Hdc ), (int) newMode ); } ////// 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. /// The stack can contain the state information for several instances of the DC. If the state specified by the /// specified parameter is not at the top of the stack, RestoreDC deletes all state information between the top /// of the stack and the specified instance. /// Specifies the saved state to be restored. If this parameter is positive, nSavedDC represents a specific /// instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative /// to the current state. For example, -1 restores the most recently saved state. /// See MSDN for more info. /// public void RestoreHdc() { #if TRACK_HDC bool result = #endif // Note: Don't use the Hdc property here, it would force handle creation. IntUnsafeNativeMethods.RestoreDC(new HandleRef(this, this.hDC), -1); #if TRACK_HDC // Note: Winforms may call this method during app exit at which point the DC may have been finalized already causing this assert to popup. Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("ret[0]=DC.RestoreHdc(hDc=0x{1:x8}, state={2})", result, (int) this.hDC, restoreState) )); #endif Debug.Assert(contextStack != null, "Someone is calling RestoreHdc() before SaveHdc()"); if (contextStack != null) { GraphicsState g = (GraphicsState) contextStack.Pop(); hCurrentBmp = g.hBitmap; hCurrentBrush = g.hBrush; hCurrentPen = g.hPen; hCurrentFont = g.hFont; #if !DRAWING_NAMESPACE if (g.font != null && g.font.IsAlive) { selectedFont = g.font.Target as WindowsFont; } else { WindowsFont previousFont = selectedFont; selectedFont = null; if (previousFont != null && MeasurementDCInfo.IsMeasurementDC(this)) { previousFont.Dispose(); } } #endif } #if OPTIMIZED_MEASUREMENTDC // in this case, GDI will copy back the previously saved font into the DC. // we dont actually know what the font is in our measurement DC so // we need to clear it off. MeasurementDCInfo.ResetIfIsMeasurementDC(this.hDC); #endif } ////// Saves the current state of the device context by copying data describing selected objects and graphic /// modes (such as the bitmap, brush, palette, font, pen, region, drawing mode, and mapping mode) to a /// context stack. /// The SaveDC function can be used any number of times to save any number of instances of the DC state. /// A saved state can be restored by using the RestoreHdc method. /// See MSDN for more details. /// public int SaveHdc() { HandleRef hdc = new HandleRef( this, this.Hdc); int state = IntUnsafeNativeMethods.SaveDC(hdc); if (contextStack == null) { contextStack = new Stack(); } GraphicsState g = new GraphicsState(); g.hBitmap = hCurrentBmp; g.hBrush = hCurrentBrush; g.hPen = hCurrentPen; g.hFont = hCurrentFont; #if !DRAWING_NAMESPACE g.font = new WeakReference(selectedFont); #endif contextStack.Push(g); #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("state[0]=DC.SaveHdc(hDc=0x{1:x8})", state, (int) this.hDC) )); #endif return state; } ////// Selects a region as the current clipping region for the device context. /// Remarks (From MSDN): /// - Only a copy of the selected region is used. The region itself can be selected for any number of other device contexts or it can be deleted. /// - The SelectClipRgn function assumes that the coordinates for a region are specified in device units. /// - To remove a device-context's clipping region, specify a NULL region handle. /// public void SetClip(WindowsRegion region) { HandleRef hdc = new HandleRef(this, this.Hdc); HandleRef hRegion = new HandleRef(region, region.HRegion); IntUnsafeNativeMethods.SelectClipRgn(hdc, hRegion); } ////// Creates a new clipping region from the intersection of the current clipping region and /// the specified rectangle. /// public void IntersectClip(WindowsRegion wr) { //if the incoming windowsregion is infinite, there is no need to do any intersecting. if (wr.HRegion == IntPtr.Zero) { return; } WindowsRegion clip = new WindowsRegion(0,0,0,0); try { int result = IntUnsafeNativeMethods.GetClipRgn(new HandleRef( this, this.Hdc), new HandleRef(clip, clip.HRegion)); // If the function succeeds and there is a clipping region for the given device context, the return value is 1. if (result == 1) { Debug.Assert(clip.HRegion != IntPtr.Zero); wr.CombineRegion(clip, wr, RegionCombineMode.AND); //1 = AND (or Intersect) } SetClip(wr); } finally { clip.Dispose(); } } ////// Modifies the viewport origin for a device context using the specified horizontal and vertical offsets in logical units. /// public void TranslateTransform(int dx, int dy) { IntNativeMethods.POINT orgn = new IntNativeMethods.POINT(); IntUnsafeNativeMethods.OffsetViewportOrgEx( new HandleRef( this, this.Hdc ), dx, dy, orgn ); } ////// public override bool Equals(object obj) { DeviceContext other = obj as DeviceContext; if (other == this) { return true; } if (other == null) { return false; } // Note: Use property instead of field so the HDC is initialized. Also, this avoid serialization issues (the obj could be a proxy that does not have access to private fields). return other.Hdc == this.Hdc; } ////// This allows collections to treat DeviceContext objects wrapping the same HDC as the same objects. /// public override int GetHashCode() { return this.Hdc.GetHashCode(); } internal class GraphicsState { internal IntPtr hBrush; internal IntPtr hFont; internal IntPtr hPen; internal IntPtr hBitmap; #if !DRAWING_NAMESPACE internal WeakReference font; #endif } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- //#define TRACK_HDC //#define GDI_FINALIZATION_WATCH #if WINFORMS_NAMESPACE namespace System.Windows.Forms.Internal #elif DRAWING_NAMESPACE namespace System.Drawing.Internal #else namespace System.Experimental.Gdi #endif { using System; using System.Collections; using System.Internal; using System.Security; using System.Runtime.InteropServices; using System.Diagnostics; using System.ComponentModel; using System.Drawing; using System.Diagnostics.CodeAnalysis; using System.Globalization; ////// /// Represents a Win32 device context. Provides operations for setting some of the properties /// of a device context. It's the managed wrapper for an HDC. /// /// This class is divided into two files separating the code that needs to be compiled into /// reatail builds and debugging code. /// #if WINFORMS_PUBLIC_GRAPHICS_LIBRARY public #else internal #endif sealed partial class DeviceContext : MarshalByRefObject, IDeviceContext, IDisposable { ////// This class is a wrapper to a Win32 device context, and the Hdc property is the way to get a /// handle to it. /// /// The hDc is released/deleted only when owned by the object, meaning it was created internally; /// in this case, the object is responsible for releasing/deleting it. /// In the case the object is created from an exisiting hdc, it is not released; this is consistent /// with the Win32 guideline that says if you call GetDC/CreateDC/CreatIC/CreateEnhMetafile, you are /// responsible for calling ReleaseDC/DeleteDC/DeleteEnhMetafile respectivelly. /// /// This class implements some of the operations commonly performed on the properties of a dc in WinForms, /// specially for interacting with GDI+, like clipping and coordinate transformation. /// Several properties are not persisted in the dc but instead they are set/reset during a more comprehensive /// operation like text rendering or painting; for instance text alignment is set and reset during DrawText (GDI), /// DrawString (GDI+). /// /// Other properties are persisted from operation to operation until they are reset, like clipping, /// one can make several calls to Graphics or WindowsGraphics obect after setting the dc clip area and /// before resetting it; these kinds of properties are the ones implemented in this class. /// This kind of properties place an extra chanllenge in the scenario where a DeviceContext is obtained /// from a Graphics object that has been used with GDI+, because GDI+ saves the hdc internally, rendering the /// DeviceContext underlying hdc out of [....]. DeviceContext needs to support these kind of properties to /// be able to keep the GDI+ and GDI HDCs in [....]. /// /// A few other persisting properties have been implemented in DeviceContext2, among them: /// 1. Window origin. /// 2. Bounding rectangle. /// 3. DC origin. /// 4. View port extent. /// 5. View port origin. /// 6. Window extent /// /// Other non-persisted properties just for information: Background/Forground color, Palette, Color adjustment, /// Color space, ICM mode and profile, Current pen position, Binary raster op (not supported by GDI+), /// Background mode, Logical Pen, DC pen color, ARc direction, Miter limit, Logical brush, DC brush color, /// Brush origin, Polygon filling mode, Bitmap stretching mode, Logical font, Intercharacter spacing, /// Font mapper flags, Text alignment, Test justification, Layout, Path, Meta region. /// See book "Windows Graphics Programming - Feng Yuang", P315 - Device Context Attributes. /// [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] IntPtr hDC; DeviceContextType dcType; public event EventHandler Disposing; 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. [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] IntPtr hWnd = (IntPtr) (-1); // Unlikely to be a valid hWnd. IntPtr hInitialPen; IntPtr hInitialBrush; IntPtr hInitialBmp; IntPtr hInitialFont; IntPtr hCurrentPen; IntPtr hCurrentBrush; IntPtr hCurrentBmp; IntPtr hCurrentFont; Stack contextStack; #if GDI_FINALIZATION_WATCH private string AllocationSite = DbgUtil.StackTrace; private string DeAllocationSite = ""; #endif /// /// Class properties... /// ////// Specifies whether a modification has been applied to the dc, like setting the clipping area or a coordinate transform. /// ////// The device type the context refers to. /// public DeviceContextType DeviceContextType { get { return this.dcType; } } ////// 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( this.hDC == IntPtr.Zero ) { if( this.dcType == DeviceContextType.Display ) { Debug.Assert(!this.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. this.hDC = ((IDeviceContext)this).GetHdc(); // this.hDC will be released on call to Dispose. CacheInitialState(); } #if GDI_FINALIZATION_WATCH else { try { Debug.WriteLine(string.Format("Allocation stack:\r\n{0}\r\nDeallocation stack:\r\n{1}", AllocationSite, DeAllocationSite)); } catch {} } #endif } Debug.Assert( this.hDC != IntPtr.Zero, "Attempt to use deleted HDC - DC type: " + this.dcType ); return this.hDC; } } // VSWhidbey 536325 // 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 DeviceContext. When // a currently selected object is disposed, it is first replaced in the DC and then // deleted. private void CacheInitialState() { Debug.Assert(this.hDC != IntPtr.Zero, "Cannot get initial state without a valid HDC"); hCurrentPen = hInitialPen = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_PEN); hCurrentBrush = hInitialBrush = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_BRUSH); hCurrentBmp = hInitialBmp = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_BITMAP); hCurrentFont = hInitialFont = IntUnsafeNativeMethods.GetCurrentObject(new HandleRef(this, hDC), IntNativeMethods.OBJ_FONT); } public void DeleteObject(IntPtr handle, GdiObjectType type) { IntPtr handleToDelete = IntPtr.Zero; switch (type) { case GdiObjectType.Pen: if (handle == hCurrentPen) { IntPtr currentPen = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialPen)); Debug.Assert(currentPen == hCurrentPen, "DeviceContext thinks a different pen is selected than the HDC"); hCurrentPen = IntPtr.Zero; } handleToDelete = handle; break; case GdiObjectType.Brush: if (handle == hCurrentBrush) { IntPtr currentBrush = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialBrush)); Debug.Assert(currentBrush == hCurrentBrush, "DeviceContext thinks a different brush is selected than the HDC"); hCurrentBrush = IntPtr.Zero; } handleToDelete = handle; break; case GdiObjectType.Bitmap: if (handle == hCurrentBmp) { IntPtr currentBmp = IntUnsafeNativeMethods.SelectObject(new HandleRef(this, this.Hdc), new HandleRef( this, hInitialBmp)); Debug.Assert(currentBmp == hCurrentBmp, "DeviceContext thinks a different brush is selected than the HDC"); hCurrentBmp = IntPtr.Zero; } handleToDelete = handle; break; } IntUnsafeNativeMethods.DeleteObject(new HandleRef(this, handleToDelete)); } // // object construction API. Publicly constructable from static methods only. // ////// Constructor to contruct a DeviceContext object from an window handle. /// private DeviceContext(IntPtr hWnd) { this.hWnd = hWnd; this.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} )", (int) hWnd))); #endif } ////// Constructor to contruct a DeviceContext object from an existing Win32 device context handle. /// private DeviceContext(IntPtr hDC, DeviceContextType dcType) { this.hDC = hDC; this.dcType = dcType; CacheInitialState(); DeviceContexts.AddDeviceContext(this); if( dcType == DeviceContextType.Display ) { this.hWnd = IntUnsafeNativeMethods.WindowFromDC( new HandleRef( this, this.hDC) ); } #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DeviceContext( hDC=0x{0:X8}, Type={1} )", (int) hDC, dcType) )); #endif } // // ////// CreateDC creates a DeviceContext object wrapping an hdc created with the Win32 CreateDC function. /// public static DeviceContext CreateDC(string driverName, string deviceName, string fileName, HandleRef devMode) { // Note: All input params can be null but not at the same time. See MSDN for information. IntPtr hdc = IntUnsafeNativeMethods.CreateDC(driverName, deviceName, fileName, devMode); return new DeviceContext( hdc, DeviceContextType.NamedDevice ); } ////// CreateIC creates a DeviceContext object wrapping an hdc created with the Win32 CreateIC function. /// public static DeviceContext CreateIC(string driverName, string deviceName, string fileName, HandleRef devMode) { // Note: All input params can be null but not at the same time. See MSDN for information. IntPtr hdc = IntUnsafeNativeMethods.CreateIC(driverName, deviceName, fileName, devMode); 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 = IntUnsafeNativeMethods.CreateCompatibleDC( new HandleRef(null, 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. /// public static DeviceContext FromHdc(IntPtr hdc) { Debug.Assert( hdc != IntPtr.Zero, "hdc == 0" ); return new DeviceContext(hdc, DeviceContextType.Unknown); } ////// /// When hwnd is null, we are getting the screen DC. /// public static DeviceContext FromHwnd( IntPtr hwnd ) { return new DeviceContext(hwnd); } ///~DeviceContext() { Dispose(false); } /// public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } /// internal void Dispose(bool disposing) { if (disposed) { return; } if (this.Disposing != null) { this.Disposing(this, EventArgs.Empty); } this.disposed = true; #if !DRAWING_NAMESPACE DisposeFont(disposing); #endif switch( this.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: // CreateDC and CreateIC add an HDC handle to the HandleCollector; to remove it properly we need // to call DeleteHDC. #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DC.DeleteHDC(hdc=0x{0:x8})", (int) this.hDC))); #endif IntUnsafeNativeMethods.DeleteHDC(new HandleRef(this, this.hDC)); this.hDC = IntPtr.Zero; break; case DeviceContextType.Memory: // CreatCompatibleDC adds a GDI handle to HandleCollector, to remove it properly we need to call // DeleteDC. #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("DC.DeleteDC(hdc=0x{0:x8})", (int) this.hDC))); #endif IntUnsafeNativeMethods.DeleteDC(new HandleRef(this, this.hDC)); this.hDC = IntPtr.Zero; break; // case DeviceContextType.Metafile: - not yet supported. #if WINFORMS_PUBLIC_GRAPHICS_LIBRARY case DeviceContextType.Metafile: IntUnsafeNativeMethods.CloseEnhMetaFile(new HandleRef(this, this.Hdc)); this.hDC = IntPtr.Zero; break; #endif case DeviceContextType.Unknown: default: return; // do nothing, the hdc is not owned by this object. // in this case it is ok if disposed throught finalization. } 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() { if (this.hDC == IntPtr.Zero) { Debug.Assert( this.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. this.hDC = IntUnsafeNativeMethods.GetDC(new HandleRef(this, this.hWnd)); #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("hdc[0x{0:x8}]=DC.GetHdc(hWnd=0x{1:x8})", (int) this.hDC, (int) this.hWnd))); #endif } return this.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 (this.hDC != IntPtr.Zero && this.dcType == DeviceContextType.Display) { #if TRACK_HDC int retVal = #endif IntUnsafeNativeMethods.ReleaseDC(new HandleRef(this, this.hWnd), new HandleRef(this, 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, (int) this.hDC, (int) this.hWnd))); #endif this.hDC = IntPtr.Zero; } } ////// Specifies whether the DC is in GM_ADVANCE mode (supported only in NT platforms). /// If false, it is in GM_COMPATIBLE mode. /// public DeviceContextGraphicsMode GraphicsMode { get { return (DeviceContextGraphicsMode) IntUnsafeNativeMethods.GetGraphicsMode( new HandleRef( this, this.Hdc ) ); } #if WINFORMS_PUBLIC_GRAPHICS_LIBRARY set { SetGraphicsMode(value); } #endif } ////// Sets the dc graphics mode and returns the old value. /// public DeviceContextGraphicsMode SetGraphicsMode( DeviceContextGraphicsMode newMode ) { return (DeviceContextGraphicsMode) IntUnsafeNativeMethods.SetGraphicsMode( new HandleRef( this, this.Hdc ), (int) newMode ); } ////// 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. /// The stack can contain the state information for several instances of the DC. If the state specified by the /// specified parameter is not at the top of the stack, RestoreDC deletes all state information between the top /// of the stack and the specified instance. /// Specifies the saved state to be restored. If this parameter is positive, nSavedDC represents a specific /// instance of the state to be restored. If this parameter is negative, nSavedDC represents an instance relative /// to the current state. For example, -1 restores the most recently saved state. /// See MSDN for more info. /// public void RestoreHdc() { #if TRACK_HDC bool result = #endif // Note: Don't use the Hdc property here, it would force handle creation. IntUnsafeNativeMethods.RestoreDC(new HandleRef(this, this.hDC), -1); #if TRACK_HDC // Note: Winforms may call this method during app exit at which point the DC may have been finalized already causing this assert to popup. Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("ret[0]=DC.RestoreHdc(hDc=0x{1:x8}, state={2})", result, (int) this.hDC, restoreState) )); #endif Debug.Assert(contextStack != null, "Someone is calling RestoreHdc() before SaveHdc()"); if (contextStack != null) { GraphicsState g = (GraphicsState) contextStack.Pop(); hCurrentBmp = g.hBitmap; hCurrentBrush = g.hBrush; hCurrentPen = g.hPen; hCurrentFont = g.hFont; #if !DRAWING_NAMESPACE if (g.font != null && g.font.IsAlive) { selectedFont = g.font.Target as WindowsFont; } else { WindowsFont previousFont = selectedFont; selectedFont = null; if (previousFont != null && MeasurementDCInfo.IsMeasurementDC(this)) { previousFont.Dispose(); } } #endif } #if OPTIMIZED_MEASUREMENTDC // in this case, GDI will copy back the previously saved font into the DC. // we dont actually know what the font is in our measurement DC so // we need to clear it off. MeasurementDCInfo.ResetIfIsMeasurementDC(this.hDC); #endif } ////// Saves the current state of the device context by copying data describing selected objects and graphic /// modes (such as the bitmap, brush, palette, font, pen, region, drawing mode, and mapping mode) to a /// context stack. /// The SaveDC function can be used any number of times to save any number of instances of the DC state. /// A saved state can be restored by using the RestoreHdc method. /// See MSDN for more details. /// public int SaveHdc() { HandleRef hdc = new HandleRef( this, this.Hdc); int state = IntUnsafeNativeMethods.SaveDC(hdc); if (contextStack == null) { contextStack = new Stack(); } GraphicsState g = new GraphicsState(); g.hBitmap = hCurrentBmp; g.hBrush = hCurrentBrush; g.hPen = hCurrentPen; g.hFont = hCurrentFont; #if !DRAWING_NAMESPACE g.font = new WeakReference(selectedFont); #endif contextStack.Push(g); #if TRACK_HDC Debug.WriteLine( DbgUtil.StackTraceToStr( String.Format("state[0]=DC.SaveHdc(hDc=0x{1:x8})", state, (int) this.hDC) )); #endif return state; } ////// Selects a region as the current clipping region for the device context. /// Remarks (From MSDN): /// - Only a copy of the selected region is used. The region itself can be selected for any number of other device contexts or it can be deleted. /// - The SelectClipRgn function assumes that the coordinates for a region are specified in device units. /// - To remove a device-context's clipping region, specify a NULL region handle. /// public void SetClip(WindowsRegion region) { HandleRef hdc = new HandleRef(this, this.Hdc); HandleRef hRegion = new HandleRef(region, region.HRegion); IntUnsafeNativeMethods.SelectClipRgn(hdc, hRegion); } ////// Creates a new clipping region from the intersection of the current clipping region and /// the specified rectangle. /// public void IntersectClip(WindowsRegion wr) { //if the incoming windowsregion is infinite, there is no need to do any intersecting. if (wr.HRegion == IntPtr.Zero) { return; } WindowsRegion clip = new WindowsRegion(0,0,0,0); try { int result = IntUnsafeNativeMethods.GetClipRgn(new HandleRef( this, this.Hdc), new HandleRef(clip, clip.HRegion)); // If the function succeeds and there is a clipping region for the given device context, the return value is 1. if (result == 1) { Debug.Assert(clip.HRegion != IntPtr.Zero); wr.CombineRegion(clip, wr, RegionCombineMode.AND); //1 = AND (or Intersect) } SetClip(wr); } finally { clip.Dispose(); } } ////// Modifies the viewport origin for a device context using the specified horizontal and vertical offsets in logical units. /// public void TranslateTransform(int dx, int dy) { IntNativeMethods.POINT orgn = new IntNativeMethods.POINT(); IntUnsafeNativeMethods.OffsetViewportOrgEx( new HandleRef( this, this.Hdc ), dx, dy, orgn ); } ////// public override bool Equals(object obj) { DeviceContext other = obj as DeviceContext; if (other == this) { return true; } if (other == null) { return false; } // Note: Use property instead of field so the HDC is initialized. Also, this avoid serialization issues (the obj could be a proxy that does not have access to private fields). return other.Hdc == this.Hdc; } ////// This allows collections to treat DeviceContext objects wrapping the same HDC as the same objects. /// public override int GetHashCode() { return this.Hdc.GetHashCode(); } internal class GraphicsState { internal IntPtr hBrush; internal IntPtr hFont; internal IntPtr hPen; internal IntPtr hBitmap; #if !DRAWING_NAMESPACE internal WeakReference font; #endif } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- WsdlHelpGeneratorElement.cs
- ServiceHttpHandlerFactory.cs
- TransformPatternIdentifiers.cs
- FixedSOMLineCollection.cs
- _SslStream.cs
- QuaternionRotation3D.cs
- XPathNodeInfoAtom.cs
- SafePointer.cs
- TrustSection.cs
- StylusPointDescription.cs
- TdsRecordBufferSetter.cs
- Span.cs
- AppDomainFactory.cs
- DetailsViewModeEventArgs.cs
- HostProtectionException.cs
- SHA512.cs
- SupportsEventValidationAttribute.cs
- SiteMapNode.cs
- HttpCacheParams.cs
- ResponseBodyWriter.cs
- ClientTargetCollection.cs
- GridViewSortEventArgs.cs
- HTTP_SERVICE_CONFIG_URLACL_PARAM.cs
- MaterialGroup.cs
- TimeSpanFormat.cs
- DataGridViewRowCollection.cs
- XmlTextAttribute.cs
- PrivilegedConfigurationManager.cs
- SamlAuthorizationDecisionStatement.cs
- Parser.cs
- Collection.cs
- ServiceAuthorizationElement.cs
- StringAnimationBase.cs
- IDataContractSurrogate.cs
- MLangCodePageEncoding.cs
- CharAnimationBase.cs
- CodeTypeParameterCollection.cs
- RelativeSource.cs
- DataListItem.cs
- XPathPatternBuilder.cs
- ServiceProviders.cs
- WindowsStatic.cs
- Bits.cs
- LocalizedNameDescriptionPair.cs
- FontUnit.cs
- XsltException.cs
- TextEditor.cs
- GeneralTransformGroup.cs
- SecurityPermission.cs
- XmlComplianceUtil.cs
- ControlAdapter.cs
- NameValueFileSectionHandler.cs
- SecurityTraceRecordHelper.cs
- CodeAttributeDeclaration.cs
- OutputWindow.cs
- CompiledRegexRunner.cs
- TextRangeSerialization.cs
- ScriptManagerProxy.cs
- QilPatternVisitor.cs
- DesignerActionPanel.cs
- MachineKeyConverter.cs
- Setter.cs
- SafeRightsManagementEnvironmentHandle.cs
- SecurityCriticalDataForSet.cs
- ContentOperations.cs
- SymbolType.cs
- FrameworkContentElement.cs
- XmlSerializerSection.cs
- SynchronizedDispatch.cs
- CodeValidator.cs
- RulePatternOps.cs
- ConnectionConsumerAttribute.cs
- RectangleHotSpot.cs
- wgx_render.cs
- WebException.cs
- LazyLoadBehavior.cs
- ProfileService.cs
- ScriptServiceAttribute.cs
- OptimalBreakSession.cs
- Win32Exception.cs
- AutomationInteropProvider.cs
- DataSourceXmlClassAttribute.cs
- DashStyles.cs
- SqlBulkCopyColumnMappingCollection.cs
- Int64.cs
- StatusCommandUI.cs
- ValidationSummary.cs
- DesignerToolboxInfo.cs
- ConfigurationUtility.cs
- TypeReference.cs
- BaseProcessor.cs
- ButtonChrome.cs
- SiteMapDesignerDataSourceView.cs
- ExpressionNode.cs
- MenuEventArgs.cs
- SocketElement.cs
- WsdlHelpGeneratorElement.cs
- ApplicationException.cs
- PasswordRecovery.cs
- SchemaInfo.cs