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
- SpAudioStreamWrapper.cs
- VarInfo.cs
- XmlTextAttribute.cs
- PointAnimationUsingKeyFrames.cs
- BufferedStream.cs
- SchemaImporterExtensionsSection.cs
- XmlDataDocument.cs
- DocumentReferenceCollection.cs
- PreviewPageInfo.cs
- MsmqActivation.cs
- Util.cs
- AspNetSynchronizationContext.cs
- CrossAppDomainChannel.cs
- Types.cs
- HostProtectionException.cs
- httpapplicationstate.cs
- ModelPropertyCollectionImpl.cs
- CompoundFileDeflateTransform.cs
- XmlNamedNodeMap.cs
- WorkflowServiceNamespace.cs
- EmbossBitmapEffect.cs
- XsdDuration.cs
- BamlCollectionHolder.cs
- SurrogateSelector.cs
- X509CertificateChain.cs
- DropDownList.cs
- ClassHandlersStore.cs
- ViewEvent.cs
- WebPartMinimizeVerb.cs
- GreenMethods.cs
- SizeKeyFrameCollection.cs
- _NetRes.cs
- FlowLayoutPanelDesigner.cs
- FixedDocument.cs
- ListViewInsertedEventArgs.cs
- NavigateUrlConverter.cs
- figurelength.cs
- TransformedBitmap.cs
- Rect3D.cs
- Parser.cs
- DataColumnMappingCollection.cs
- SegmentInfo.cs
- ConversionValidationRule.cs
- ResourceReferenceExpressionConverter.cs
- __Error.cs
- ScrollableControlDesigner.cs
- EntitySetBase.cs
- BindingsCollection.cs
- TextClipboardData.cs
- ServiceContractGenerationContext.cs
- CodeIdentifier.cs
- DataSourceXmlSerializer.cs
- ConfigXmlDocument.cs
- WebPartConnectionsCancelEventArgs.cs
- ListViewUpdatedEventArgs.cs
- DateTimeConverter2.cs
- WebResourceUtil.cs
- webbrowsersite.cs
- DesignTimeTemplateParser.cs
- ToolStripSeparatorRenderEventArgs.cs
- ValuePatternIdentifiers.cs
- AudioStateChangedEventArgs.cs
- List.cs
- MissingMemberException.cs
- TextTreeTextNode.cs
- DeclarationUpdate.cs
- ImageListStreamer.cs
- DataRecord.cs
- OdbcUtils.cs
- SqlTypesSchemaImporter.cs
- _SslSessionsCache.cs
- UnsafeNativeMethods.cs
- EntityDataSourceDataSelectionPanel.cs
- PickBranch.cs
- DataKey.cs
- Separator.cs
- Vector3dCollection.cs
- TerminatorSinks.cs
- LockCookie.cs
- Column.cs
- PointValueSerializer.cs
- DragStartedEventArgs.cs
- Marshal.cs
- TokenBasedSet.cs
- SingleResultAttribute.cs
- ReflectionPermission.cs
- RuleSettingsCollection.cs
- FactoryRecord.cs
- ListBox.cs
- WebPart.cs
- _HTTPDateParse.cs
- IisTraceWebEventProvider.cs
- PageThemeCodeDomTreeGenerator.cs
- BamlMapTable.cs
- TempFiles.cs
- UserPersonalizationStateInfo.cs
- QilXmlWriter.cs
- XmlTextEncoder.cs
- Parameter.cs
- DataControlExtensions.cs