File: src\Framework\MS\Internal\PtsHost\PageBreakRecord.cs
Project: wpf\PresentationFramework.csproj (PresentationFramework)
//---------------------------------------------------------------------------
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
// 
// File: PageBreakRecord.cs
//
// Description: Break records for PTS pages.
//
//---------------------------------------------------------------------------
 
using System;                                   // IntPtr, IDisposable, ...
using System.Threading;                         // Interlocked
using System.Security;                          // SecurityCritical
using MS.Internal.PtsHost.UnsafeNativeMethods;  // PTS
 
namespace MS.Internal.PtsHost
{
    /// <summary>
    /// PageBreakRecord is used to represent a break record for top level PTS 
    /// page. Points to break record structure of PTS page.
    /// </summary>
    internal sealed class PageBreakRecord : IDisposable
    {
        //-------------------------------------------------------------------
        //
        //  Constructors
        //
        //-------------------------------------------------------------------
 
        #region Constructors
 
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="ptsContext">Current PTS Context.</param>
        /// <param name="br">PTS page break record.</param>
        /// <param name="pageNumber">Page number.</param>
        internal PageBreakRecord(PtsContext ptsContext, SecurityCriticalDataForSet<IntPtr> br, int pageNumber)
        {
            Invariant.Assert(ptsContext != null, "Invalid PtsContext object.");
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object.");
 
            _br = br;
            _pageNumber = pageNumber;
 
            // In the finalizer we may need to reference an object instance
            // to do the right cleanup. For this reason store a WeakReference
            // to such object.
            _ptsContext = new WeakReference(ptsContext);
 
            // BreakRecord contains unmanaged resources that need to be released when
            // BreakRecord is destroyed or Dispatcher is closing. For this reason keep 
            // track of this BreakRecord in PtsContext. 
            ptsContext.OnPageBreakRecordCreated(_br);
        }
 
        /// <summary>
        /// Finalizer - releases unmanaged resources.
        /// </summary>
        ~PageBreakRecord()
        {
            Dispose(false);
        }
 
        #endregion Constructors
 
        //-------------------------------------------------------------------
        //
        //  Public Methods
        //
        //-------------------------------------------------------------------
 
        #region Public Methods
 
        /// <summary>
        /// Dispose allocated resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
 
        #endregion Public Methods
 
        //-------------------------------------------------------------------
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------
 
        #region Internal Properties
 
        /// <summary>
        /// PTS page break record.
        /// </summary>
        internal IntPtr BreakRecord
        {
            get { return _br.Value; }
        }
 
        /// <summary>
        /// Page number of the page starting at the break position. 
        /// </summary>
        internal int PageNumber
        {
            get { return _pageNumber; }
        }
 
        #endregion Internal Properties
 
        //-------------------------------------------------------------------
        //
        //  Private Methods
        //
        //-------------------------------------------------------------------
 
        #region Private Methods
 
        /// <summary>
        /// Destroys all unmanaged resources.
        /// </summary>
        /// <param name="disposing">Whether dispose is caused by explicit call to Dispose.</param>
        /// <remarks>
        /// Finalizer needs to follow rules below:
        ///     a) Your Finalize method must tolerate partially constructed instances.
        ///     b) Your Finalize method must consider the consequence of failure.
        ///     c) Your object is callable after Finalization.
        ///     d) Your object is callable during Finalization.
        ///     e) Your Finalizer could be called multiple times.
        ///     f) Your Finalizer runs in a delicate security context.
        /// See: http://blogs.msdn.com/Microsoft/archive/2004/02/20/77460.aspx
        /// </remarks>
        /// <SecurityNote>
        /// Critical, because sets SecurityCriticalDataForSet.
        /// Safe, because sets SecurityCriticalDataForSet to IntPtr.Zero, which is safe.
        /// </SecurityNote>
        [SecurityCritical, SecurityTreatAsSafe]
        private void Dispose(bool disposing)
        {
            PtsContext ptsContext = null;
 
            // Do actual dispose only once.
            if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
            {
                // Dispose PTS break record.
                // According to following article the entire reachable graph from 
                // a finalizable object is promoted, and it is safe to access its 
                // members if they do not have their own finalizers.
                // Hence it is OK to access _ptsContext during finalization.
                // See: http://blogs.msdn.com/Microsoft/archive/2004/02/20/77460.aspx
                ptsContext = _ptsContext.Target as PtsContext;
                if (ptsContext != null && !ptsContext.Disposed)
                {
                    // Notify PtsContext that BreakRecord is not used anymore, so all
                    // associated resources can by destroyed.
                    ptsContext.OnPageBreakRecordDisposed(_br, disposing);
                }
 
                // Cleanup the state.
                _br.Value = IntPtr.Zero;
                _ptsContext = null;
            }
        }
 
        #endregion Private Methods
 
        //-------------------------------------------------------------------
        //
        //  Private Fields
        //
        //-------------------------------------------------------------------
 
        #region Private Fields
 
        /// <summary>
        /// PTS page break record.
        /// </summary>
        private SecurityCriticalDataForSet<IntPtr> _br;
 
        /// <summary>
        /// Page number of the page starting at the break position.
        /// </summary>
        private readonly int _pageNumber;
 
        /// <summary>
        /// In the finalizer we may need to reference an instance of PtsContext
        /// to do the right cleanup. For this reason store a WeakReference
        /// to it.
        /// </summary>
        private WeakReference _ptsContext;
 
        /// <summary>
        /// Whether object is already disposed.
        /// </summary>
        private int _disposed;
 
        #endregion Private Fields
    }
}