PtsContext.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / PtsHost / PtsContext.cs / 1 / PtsContext.cs

                            //---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: PtsContext.cs 
//
// Description: Context used to communicate with PTS component. 
// 
//---------------------------------------------------------------------------
 
using System;                                   // IntPtr, IDisposable, ...
using System.Collections;                       // ArrayList
using System.Collections.Generic;               // List
using System.Security;                          // SecurityCritical, SecurityTreatAsSafe 
using System.Threading;                         // Interlocked
using System.Windows.Media.TextFormatting;      // TextFormatter 
using System.Windows.Threading;                 // DispatcherObject 
using MS.Internal.PtsHost.UnsafeNativeMethods;  // PTS
 
namespace MS.Internal.PtsHost
{
    /// 
    /// Context used to communicate with PTS component. 
    /// Context keeps track of all unmanaged resources created by PTS.
    /// It also maps an instance of Object into an identity that can be used 
    /// in unmanaged world. This identity can by easily mapped back into 
    /// original instance of Object.
    ///  
    internal sealed class PtsContext : DispatcherObject, IDisposable
    {
        //-------------------------------------------------------------------
        // 
        //  Constructors
        // 
        //------------------------------------------------------------------- 

        #region Constructors 

        /// 
        /// Constructor.
        ///  
        /// 
        /// The array of entries initially can store up to 16 entries. Upon 
        /// adding elements the capacity increased in multiples of two as 
        /// required. The first element always contains index to the next
        /// free entry. All free entries are forming a linked list. 
        /// 
        /// 
        /// Critical, because allocates Critical collections: _pages, and _pageBreakRecords.
        /// Safe, because creating empty collections is safe. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal PtsContext(bool isOptimalParagraphEnabled) 
        {
            _pages = new ArrayList(1); 
            _pageBreakRecords = new ArrayList(1);
            _unmanagedHandles = new HandleIndex[_defaultHandlesCapacity]; // Limit initial size
            _isOptimalParagraphEnabled = isOptimalParagraphEnabled;
 
            BuildFreeList(1); // 1 is the first free index in UnmanagedHandles array
 
            // Acquire PTS Context 
            _ptsHost = PtsCache.AcquireContext(this);
        } 

        #endregion Constructors

        //-------------------------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Internal Methods

        /// 
        /// Destroy all unmanaged resources associated with the PtsContext. 
        /// 
        ///  
        /// Critical, because calls Critical functions PTS.FsDestroyPageBreakRecord 
        ///     and PTS.FsDestroyPage.
        /// Safe, because parameters passed to PTS.FsDestroyPageBreakRecord 
        ///     and PTS.FsDestroyPage are Critical for set, so cannot have
        ///     randomly assigned value.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public void Dispose()
        { 
            int index; 

            // Do actual dispose only once. 
            if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
            {
                // Destroy all page break records. The collection is allocated during creation
                // of the context, and can be only destroyed during dispose process. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try 
                { 
                    Enter();
                    for (index = 0; index < _pageBreakRecords.Count; index++) 
                    {
                        Invariant.Assert(((IntPtr)_pageBreakRecords[index]) != IntPtr.Zero, "Invalid break record object");
                        PTS.Validate(PTS.FsDestroyPageBreakRecord(_ptsHost.Context, (IntPtr)_pageBreakRecords[index]));
                    } 
                }
                finally 
                { 
                    Leave();
                    _pageBreakRecords = null; 
                }

                // Destroy all pages. The collection is allocated during creation
                // of the context, and can be only destroyed during dispose process. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try 
                { 
                    Enter();
                    for (index = 0; index < _pages.Count; index++) 
                    {
                        Invariant.Assert(((IntPtr)_pages[index]) != IntPtr.Zero, "Invalid break record object");
                        PTS.Validate(PTS.FsDestroyPage(_ptsHost.Context, (IntPtr)_pages[index]));
                    } 
                }
                finally 
                { 
                    Leave();
                    _pages = null; 
                }

                if (Invariant.Strict && _unmanagedHandles != null)
                { 
                    // Verify that PtsContext does not contain any reference to objects.
                    // Because order of finalizers is not deterministic, only objects 
                    // that can be part of the NameTable are allowed here. 
                    for (index = 0; index < _unmanagedHandles.Length; ++index)
                    { 
                        Object obj = _unmanagedHandles[index].Obj;
                        if (obj != null)
                        {
                            Invariant.Assert( 
                                obj is BaseParagraph ||
                                obj is Section || 
                                obj is MS.Internal.PtsHost.LineBreakRecord,  // Suppress line break record leak, looks like a PTS issue but we cannot 
                                                                             // get a firm repro for now. Workaround for bug #1294210.
                                "One of PTS Client objects is not properly disposed."); 

#if DEBUG
                            // Make sure that FigureParagraphs are only used by TextParagraph
                            if (obj is FigureParagraph || obj is FloaterParagraph) 
                            {
                                bool found = false; 
                                for (int i = 0; i < _unmanagedHandles.Length; ++i) 
                                {
                                    Object objDbg = _unmanagedHandles[i].Obj; 
                                    if (objDbg is TextParagraph)
                                    {
                                        List attachedObjects = ((TextParagraph)objDbg).AttachedObjectDbg;
                                        if (attachedObjects != null) 
                                        {
                                            foreach (AttachedObject attachedObject in attachedObjects) 
                                            { 
                                                if (attachedObject.Para == obj)
                                                { 
                                                    found = true;
                                                    break;
                                                }
                                            } 
                                        }
                                        if (found) { break; } 
                                    } 
                                }
                                Invariant.Assert(found, "FigureParagraph is not properly disposed."); 
                            }
#endif
                        }
                    } 
                }
                _ptsHost = null; 
                _unmanagedHandles = null; 
                _callbackException = null;
                _disposeCompleted = true; 
            }
        }

        ///  
        /// Inserts the object into reference array and returns its handle.
        ///  
        /// Object to be mapped to an unmanaged handle. 
        /// Unmanaged handle associated with the object.
        ///  
        /// Thread safe, see facts for the class description.
        /// 
        internal IntPtr CreateHandle(object obj)
        { 
            Invariant.Assert(obj != null, "Cannot create handle for non-existing object.");
            Invariant.Assert(!this.Disposed, "PtsContext is already disposed."); 
 
            // Ensure the size of handle array. The first item of the
            // array contains index of the next free position. If this 
            // index is 0, it means that the array is full and needs to
            // be resized.
            if (_unmanagedHandles[0].Index == 0)
            { 
                Resize();
            } 
 
            // Assign a handle to the Object and adjust free index.
            int handle = _unmanagedHandles[0].Index; 
            _unmanagedHandles[0].Index = _unmanagedHandles[handle].Index;
            _unmanagedHandles[handle].Obj = obj;
            _unmanagedHandles[handle].Index = 0;
 
            return (IntPtr)handle;
        } 
 
        /// 
        /// Removes reference to the object pointed by handle and release 
        /// the entry associated with it.
        /// 
        /// Handle of an Object being removed.
        ///  
        /// Thread safe, see facts for the class description.
        ///  
        internal void ReleaseHandle(IntPtr handle) 
        {
            int handleInt = (int)handle; 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            Invariant.Assert(handleInt > 0 && handleInt < _unmanagedHandles.Length, "Invalid object handle.");
            Invariant.Assert(_unmanagedHandles[handleInt].IsHandle(), "Handle has been already released.");
            _unmanagedHandles[handleInt].Obj = null; 
            _unmanagedHandles[handleInt].Index = _unmanagedHandles[0].Index;
            _unmanagedHandles[0].Index = handleInt; 
        } 

        ///  
        /// Returns true if IntPtr is a handle
        /// 
        /// Handle of an Object.
        ///  
        /// Thread safe, see facts for the class description.
        ///  
        internal bool IsValidHandle(IntPtr handle) 
        {
            int handleInt = (int)handle; 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            if (handleInt < 0 || handleInt >= _unmanagedHandles.Length)
            {
                return false; 
            }
            return _unmanagedHandles[handleInt].IsHandle(); 
        } 

        ///  
        /// Maps handle to an Object.
        /// 
        /// Handle of an Object.
        /// Reference to an Object. 
        /// 
        /// Thread safe, see facts for the class description. 
        ///  
        internal object HandleToObject(IntPtr handle)
        { 
            int handleInt = (int)handle;
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            Invariant.Assert(handleInt > 0 && handleInt < _unmanagedHandles.Length, "Invalid object handle.");
            Invariant.Assert(_unmanagedHandles[handleInt].IsHandle(), "Handle has been already released."); 
            return _unmanagedHandles[handleInt].Obj;
        } 
 
        /// 
        /// Enters the PTS context. Called before executing PTS methods. 
        /// 
        /// 
        /// Thread safe, see facts for the class description.
        ///  
        internal void Enter()
        { 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose. 
            _ptsHost.EnterContext(this);
        } 

        /// 
        /// Leaves the PTS context. Called after executing PTS methods.
        ///  
        /// 
        /// Thread safe, see facts for the class description. 
        ///  
        internal void Leave()
        { 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            _ptsHost.LeaveContext(this);
        }
 
        /// 
        /// Keeps track of created pages (unmanaged resource). 
        /// When page is created, add it to the list. 
        /// 
        /// PTS Page object that was just created. 
        /// 
        /// Critical, because adds new entry to Critical collection _pages.
        /// Safe, because ptsPage parameter is Critical for set, so cannot have
        ///     randomly assigned value. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void OnPageCreated(SecurityCriticalDataForSet ptsPage) 
        {
            Invariant.Assert(ptsPage.Value != IntPtr.Zero, "Invalid page object."); 
            Invariant.Assert(!this.Disposed, "PtsContext is already disposed.");
            Invariant.Assert(!_pages.Contains(ptsPage.Value), "Page already exists.");

            _pages.Add(ptsPage.Value); 
        }
 
        ///  
        /// Destroys PTS page.
        ///  
        /// Pointer to PTS Page object that should be destroyed.
        /// Whether dispose is caused by explicit call to Dispose.
        /// Whether needs to enter PtsContext or not (during layout it is not needed).
        internal void OnPageDisposed(SecurityCriticalDataForSet ptsPage, bool disposing, bool enterContext) 
        {
            Invariant.Assert(ptsPage.Value != IntPtr.Zero, "Invalid page object."); 
 
            // If explicitly disposing (not called during finalization), synchronously
            // destroy the page. 
            if (disposing)
            {
                OnDestroyPage(ptsPage, enterContext);
            } 
            else
            { 
                // If PtsContext has been already disposed, ignore this call. 
                if (!this.Disposed && !this.Dispatcher.HasShutdownStarted)
                { 
                    // Schedule background operation to destroy the page.
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnDestroyPage), ptsPage);
                }
            } 
        }
 
        ///  
        /// Keeps track of created page BreakRecords (unmanaged resource).
        /// When PageBreakRecord is created, add it to the list. 
        /// 
        /// PTS Page BR object that was just created.
        /// 
        /// Critical, because adds new entry to Critical collection _pageBreakRecords. 
        /// Safe, because br parameter is Critical for set, so cannot have randomly assigned value.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void OnPageBreakRecordCreated(SecurityCriticalDataForSet br)
        { 
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object.");
            Invariant.Assert(!this.Disposed, "PtsContext is already disposed.");
            Invariant.Assert(!_pageBreakRecords.Contains(br.Value), "Break record already exists.");
 
            _pageBreakRecords.Add(br.Value);
        } 
 
        /// 
        /// Destroys PTS break record. 
        /// 
        /// Pointer to PTS Page BR object that should be destroyed.
        /// Whether dispose is caused by explicit call to Dispose.
        internal void OnPageBreakRecordDisposed(SecurityCriticalDataForSet br, bool disposing) 
        {
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object."); 
 
            // If explicitly disposing (not called during finalization), synchronously
            // destroy the page break record. 
            if (disposing)
            {
                OnDestroyBreakRecord((object)br);
            } 
            else
            { 
                // If PtsContext has been already disposed, ignore this call. 
                if (!this.Disposed && !this.Dispatcher.HasShutdownStarted)
                { 
                    // Schedule background operation to destroy the page.
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnDestroyBreakRecord), br);
                }
            } 
        }
 
        #endregion Internal Methods 

        //-------------------------------------------------------------------- 
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------- 

        #region Internal Properties 
 
        /// 
        /// Whether object is already disposed. 
        /// 
        internal bool Disposed
        {
            get { return (_disposed != 0); } 
        }
 
        ///  
        /// Context Id used to communicate with PTS.
        ///  
        internal IntPtr Context
        {
            get { return _ptsHost.Context; }
        } 

        ///  
        /// Whether optimal paragraph is enabled 
        /// 
        internal bool IsOptimalParagraphEnabled 
        {
            get { return _isOptimalParagraphEnabled; }
        }
 
        /// 
        /// Text formatter context for this pts context 
        ///  
        internal TextFormatter TextFormatter
        { 
            get { return _textFormatter; }
            set { _textFormatter = value; }
        }
 
        /// 
        /// Exception caught during callback execution. Those exceptions are 
        /// converted into error codes and passed to PTS to provide appropriate 
        /// clenaup. Later this exception is re-thrown.
        ///  
        internal Exception CallbackException
        {
            get { return _callbackException; }
            set { _callbackException = value; } 
        }
 
        #endregion Internal Properties 

        //------------------------------------------------------------------- 
        //
        //  Private Methods
        //
        //-------------------------------------------------------------------- 

        #region Private Methods 
 
        /// 
        /// Rebuilds list of free entries starting from specified index. 
        /// 
        /// Index to start from.
        private void BuildFreeList(int freeIndex)
        { 
            // Point to the first empty slot
            _unmanagedHandles[0].Index = freeIndex; 
            // Link all entries starting from freeIndex 
            while (freeIndex < _unmanagedHandles.Length)
            { 
                _unmanagedHandles[freeIndex].Index = ++freeIndex;
            }
            // End of free entries list
            _unmanagedHandles[freeIndex - 1].Index = 0; 
        }
 
        ///  
        /// Increases the capacity of the handle array.
        ///      new size = current size * 2 
        /// 
        private void Resize()
        {
            int freeIndex = _unmanagedHandles.Length; 

            // Allocate new array and copy all existing entries into it 
            HandleIndex[] newItems = new HandleIndex[_unmanagedHandles.Length * 2]; 
            Array.Copy(_unmanagedHandles, newItems, _unmanagedHandles.Length);
            _unmanagedHandles = newItems; 

            // Build list of free entries
            BuildFreeList(freeIndex);
        } 

        ///  
        /// Destroys PTS page. 
        /// 
        /// Pointer to PTS Page object that should be destroyed. 
        private object OnDestroyPage(object args)
        {
            SecurityCriticalDataForSet ptsPage = (SecurityCriticalDataForSet)args;
            OnDestroyPage(ptsPage, true); 
            return null;
        } 
 
        /// 
        /// Destroys PTS page. 
        /// 
        /// Pointer to PTS Page object that should be destroyed.
        /// Whether needs to enter PTS Context.
        ///  
        /// Critical, because:
        ///     a) calls Critical functions PTS.FsDestroyPage, 
        ///     b) modifies Critical collection _pages 
        /// Safe, because:
        ///     a) parameter passed to PTS.FsDestroyPage is Critical 
        ///        for set, so cannot have randomly assigned value.
        ///     b) removing from Critical collection is safe operation.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void OnDestroyPage(SecurityCriticalDataForSet ptsPage, bool enterContext)
        { 
            Invariant.Assert(ptsPage.Value != IntPtr.Zero, "Invalid page object."); 

            // Dispatcher may invoke this operation when PtsContext is already explicitly 
            // disposed.
            if (!this.Disposed)
            {
                Invariant.Assert(_pages != null, "Collection of pages does not exist."); 
                Invariant.Assert(_pages.Contains(ptsPage.Value), "Page does not exist.");
 
                // Destroy given page. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try 
                {
                    if (enterContext)
                    {
                        Enter(); 
                    }
                    PTS.Validate(PTS.FsDestroyPage(_ptsHost.Context, ptsPage.Value)); 
                } 
                finally
                { 
                    if (enterContext)
                    {
                        Leave();
                    } 
                    _pages.Remove(ptsPage.Value);
                } 
            } 
        }
 
        /// 
        /// Destroys PTS page break record.
        /// 
        /// Pointer to PTS Page BreakRecord object that should be destroyed. 
        /// 
        /// Critical, because: 
        ///     a) calls Critical functions PTS.FsDestroyPageBreakRecord, 
        ///     b) modifies Critical collection _pageBreakRecords
        /// Safe, because: 
        ///     a) parameter passed to PTS.FsDestroyPageBreakRecord is Critical
        ///        for set, so cannot have randomly assigned value.
        ///     b) removing from Critical collection is safe operation.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private object OnDestroyBreakRecord(object args) 
        { 
            SecurityCriticalDataForSet br = (SecurityCriticalDataForSet)args;
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object."); 

            // Dispatcher may invoke this operation when PtsContext is already explicitly
            // disposed.
            if (!this.Disposed) 
            {
                Invariant.Assert(_pageBreakRecords != null, "Collection of break records does not exist."); 
                Invariant.Assert(_pageBreakRecords.Contains(br.Value), "Break record does not exist."); 

                // Destroy given page break record. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try
                {
                    Enter(); 
                    PTS.Validate(PTS.FsDestroyPageBreakRecord(_ptsHost.Context, br.Value));
                } 
                finally 
                {
                    Leave(); 
                    _pageBreakRecords.Remove(br.Value);
                }
            }
            return null; 
        }
 
        #endregion Private Methods 

        //------------------------------------------------------------------- 
        //
        //  Private Fields
        //
        //------------------------------------------------------------------- 

        #region Private Fields 
 
        /// 
        /// Array of HandleIndex. This array stores 2 kind of information: 
        /// {1) reference to Object; array index is a handle of the Object,
        /// (2) linked list of free entries; the first element (0) is always used
        ///     to point to the next free entry.
        ///  
        /// 
        /// See: http://blogs.msdn.com/[....]/archive/2004/02/20/77460.aspx 
        /// According to this 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 this array during finalization.
        /// 
        private HandleIndex[] _unmanagedHandles;
 
        /// 
        /// List of created PTS pages. Those are unmanaged resources and 
        /// have to be disposed. 
        /// 
        ///  
        /// See: http://blogs.msdn.com/[....]/archive/2004/02/20/77460.aspx
        /// According to this 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 this array during finalization.
        ///  
        ///  
        /// Members of this list are pointers that are passed to Critical functions
        /// that'll write to the memory directly in unmanaged code. 
        /// 
        [SecurityCritical]
        private ArrayList _pages;
 
        /// 
        /// List of created PTS BreakRecords. Those are unmanaged resources and 
        /// have to be disposed. 
        /// 
        ///  
        /// See: http://blogs.msdn.com/[....]/archive/2004/02/20/77460.aspx
        /// According to this 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 this array during finalization.
        ///  
        ///  
        /// Members of this list are pointers that are passed to Critical functions
        /// that'll write to the memory directly in unmanaged code. 
        /// 
        [SecurityCritical]
        private ArrayList _pageBreakRecords;
 
        /// 
        /// Exception caught during callback execution. Those exceptions are 
        /// converted into error codes and passed to PTS to provide appropriate 
        /// clenaup. Later this exception is re-thrown.
        ///  
        private Exception _callbackException;

        /// 
        /// PTS Host: all PTS callbacks are defined here. 
        /// 
        private PtsHost _ptsHost; 
 
        /// 
        /// Whether optimal paragraph is enabled for this ptscontext 
        /// 
        private bool _isOptimalParagraphEnabled;

        ///  
        /// TextFormatter - Used only in optimal mode
        ///  
        private TextFormatter _textFormatter; 

        ///  
        /// Whether object is already disposed.
        /// 
        private int _disposed;
 
        /// 
        /// Whether Dispose has been completed. It may be set to 'false' even when 
        /// _disposed is set to 'true'. It may happen during Dispose execution. 
        /// This flag is used for verification only.
        ///  
        private bool _disposeCompleted;

        /// 
        /// Default capacity of the UnmanagedHandles array. The array capacity 
        /// is always increased in multiples of two as required: 16*(2^N).
        ///  
        private const int _defaultHandlesCapacity = 16; 

        #endregion Private Fields 

        //-------------------------------------------------------------------
        //
        //  Private Types 
        //
        //-------------------------------------------------------------------- 
 
        #region Private Types
 
        /// 
        /// HandleIndex can store one of following information:
        /// {1) reference to Object
        /// (2) index of the next free entry 
        /// 
        private struct HandleIndex 
        { 
            internal int Index;
            internal object Obj; 
            internal bool IsHandle()
            {
                return (Obj != null && Index == 0);
            } 
        }
 
        #endregion Private Types 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//---------------------------------------------------------------------------- 
//
// Copyright (C) Microsoft Corporation.  All rights reserved.
//
// File: PtsContext.cs 
//
// Description: Context used to communicate with PTS component. 
// 
//---------------------------------------------------------------------------
 
using System;                                   // IntPtr, IDisposable, ...
using System.Collections;                       // ArrayList
using System.Collections.Generic;               // List
using System.Security;                          // SecurityCritical, SecurityTreatAsSafe 
using System.Threading;                         // Interlocked
using System.Windows.Media.TextFormatting;      // TextFormatter 
using System.Windows.Threading;                 // DispatcherObject 
using MS.Internal.PtsHost.UnsafeNativeMethods;  // PTS
 
namespace MS.Internal.PtsHost
{
    /// 
    /// Context used to communicate with PTS component. 
    /// Context keeps track of all unmanaged resources created by PTS.
    /// It also maps an instance of Object into an identity that can be used 
    /// in unmanaged world. This identity can by easily mapped back into 
    /// original instance of Object.
    ///  
    internal sealed class PtsContext : DispatcherObject, IDisposable
    {
        //-------------------------------------------------------------------
        // 
        //  Constructors
        // 
        //------------------------------------------------------------------- 

        #region Constructors 

        /// 
        /// Constructor.
        ///  
        /// 
        /// The array of entries initially can store up to 16 entries. Upon 
        /// adding elements the capacity increased in multiples of two as 
        /// required. The first element always contains index to the next
        /// free entry. All free entries are forming a linked list. 
        /// 
        /// 
        /// Critical, because allocates Critical collections: _pages, and _pageBreakRecords.
        /// Safe, because creating empty collections is safe. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal PtsContext(bool isOptimalParagraphEnabled) 
        {
            _pages = new ArrayList(1); 
            _pageBreakRecords = new ArrayList(1);
            _unmanagedHandles = new HandleIndex[_defaultHandlesCapacity]; // Limit initial size
            _isOptimalParagraphEnabled = isOptimalParagraphEnabled;
 
            BuildFreeList(1); // 1 is the first free index in UnmanagedHandles array
 
            // Acquire PTS Context 
            _ptsHost = PtsCache.AcquireContext(this);
        } 

        #endregion Constructors

        //-------------------------------------------------------------------- 
        //
        //  Internal Methods 
        // 
        //-------------------------------------------------------------------
 
        #region Internal Methods

        /// 
        /// Destroy all unmanaged resources associated with the PtsContext. 
        /// 
        ///  
        /// Critical, because calls Critical functions PTS.FsDestroyPageBreakRecord 
        ///     and PTS.FsDestroyPage.
        /// Safe, because parameters passed to PTS.FsDestroyPageBreakRecord 
        ///     and PTS.FsDestroyPage are Critical for set, so cannot have
        ///     randomly assigned value.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        public void Dispose()
        { 
            int index; 

            // Do actual dispose only once. 
            if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 0)
            {
                // Destroy all page break records. The collection is allocated during creation
                // of the context, and can be only destroyed during dispose process. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try 
                { 
                    Enter();
                    for (index = 0; index < _pageBreakRecords.Count; index++) 
                    {
                        Invariant.Assert(((IntPtr)_pageBreakRecords[index]) != IntPtr.Zero, "Invalid break record object");
                        PTS.Validate(PTS.FsDestroyPageBreakRecord(_ptsHost.Context, (IntPtr)_pageBreakRecords[index]));
                    } 
                }
                finally 
                { 
                    Leave();
                    _pageBreakRecords = null; 
                }

                // Destroy all pages. The collection is allocated during creation
                // of the context, and can be only destroyed during dispose process. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try 
                { 
                    Enter();
                    for (index = 0; index < _pages.Count; index++) 
                    {
                        Invariant.Assert(((IntPtr)_pages[index]) != IntPtr.Zero, "Invalid break record object");
                        PTS.Validate(PTS.FsDestroyPage(_ptsHost.Context, (IntPtr)_pages[index]));
                    } 
                }
                finally 
                { 
                    Leave();
                    _pages = null; 
                }

                if (Invariant.Strict && _unmanagedHandles != null)
                { 
                    // Verify that PtsContext does not contain any reference to objects.
                    // Because order of finalizers is not deterministic, only objects 
                    // that can be part of the NameTable are allowed here. 
                    for (index = 0; index < _unmanagedHandles.Length; ++index)
                    { 
                        Object obj = _unmanagedHandles[index].Obj;
                        if (obj != null)
                        {
                            Invariant.Assert( 
                                obj is BaseParagraph ||
                                obj is Section || 
                                obj is MS.Internal.PtsHost.LineBreakRecord,  // Suppress line break record leak, looks like a PTS issue but we cannot 
                                                                             // get a firm repro for now. Workaround for bug #1294210.
                                "One of PTS Client objects is not properly disposed."); 

#if DEBUG
                            // Make sure that FigureParagraphs are only used by TextParagraph
                            if (obj is FigureParagraph || obj is FloaterParagraph) 
                            {
                                bool found = false; 
                                for (int i = 0; i < _unmanagedHandles.Length; ++i) 
                                {
                                    Object objDbg = _unmanagedHandles[i].Obj; 
                                    if (objDbg is TextParagraph)
                                    {
                                        List attachedObjects = ((TextParagraph)objDbg).AttachedObjectDbg;
                                        if (attachedObjects != null) 
                                        {
                                            foreach (AttachedObject attachedObject in attachedObjects) 
                                            { 
                                                if (attachedObject.Para == obj)
                                                { 
                                                    found = true;
                                                    break;
                                                }
                                            } 
                                        }
                                        if (found) { break; } 
                                    } 
                                }
                                Invariant.Assert(found, "FigureParagraph is not properly disposed."); 
                            }
#endif
                        }
                    } 
                }
                _ptsHost = null; 
                _unmanagedHandles = null; 
                _callbackException = null;
                _disposeCompleted = true; 
            }
        }

        ///  
        /// Inserts the object into reference array and returns its handle.
        ///  
        /// Object to be mapped to an unmanaged handle. 
        /// Unmanaged handle associated with the object.
        ///  
        /// Thread safe, see facts for the class description.
        /// 
        internal IntPtr CreateHandle(object obj)
        { 
            Invariant.Assert(obj != null, "Cannot create handle for non-existing object.");
            Invariant.Assert(!this.Disposed, "PtsContext is already disposed."); 
 
            // Ensure the size of handle array. The first item of the
            // array contains index of the next free position. If this 
            // index is 0, it means that the array is full and needs to
            // be resized.
            if (_unmanagedHandles[0].Index == 0)
            { 
                Resize();
            } 
 
            // Assign a handle to the Object and adjust free index.
            int handle = _unmanagedHandles[0].Index; 
            _unmanagedHandles[0].Index = _unmanagedHandles[handle].Index;
            _unmanagedHandles[handle].Obj = obj;
            _unmanagedHandles[handle].Index = 0;
 
            return (IntPtr)handle;
        } 
 
        /// 
        /// Removes reference to the object pointed by handle and release 
        /// the entry associated with it.
        /// 
        /// Handle of an Object being removed.
        ///  
        /// Thread safe, see facts for the class description.
        ///  
        internal void ReleaseHandle(IntPtr handle) 
        {
            int handleInt = (int)handle; 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            Invariant.Assert(handleInt > 0 && handleInt < _unmanagedHandles.Length, "Invalid object handle.");
            Invariant.Assert(_unmanagedHandles[handleInt].IsHandle(), "Handle has been already released.");
            _unmanagedHandles[handleInt].Obj = null; 
            _unmanagedHandles[handleInt].Index = _unmanagedHandles[0].Index;
            _unmanagedHandles[0].Index = handleInt; 
        } 

        ///  
        /// Returns true if IntPtr is a handle
        /// 
        /// Handle of an Object.
        ///  
        /// Thread safe, see facts for the class description.
        ///  
        internal bool IsValidHandle(IntPtr handle) 
        {
            int handleInt = (int)handle; 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            if (handleInt < 0 || handleInt >= _unmanagedHandles.Length)
            {
                return false; 
            }
            return _unmanagedHandles[handleInt].IsHandle(); 
        } 

        ///  
        /// Maps handle to an Object.
        /// 
        /// Handle of an Object.
        /// Reference to an Object. 
        /// 
        /// Thread safe, see facts for the class description. 
        ///  
        internal object HandleToObject(IntPtr handle)
        { 
            int handleInt = (int)handle;
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            Invariant.Assert(handleInt > 0 && handleInt < _unmanagedHandles.Length, "Invalid object handle.");
            Invariant.Assert(_unmanagedHandles[handleInt].IsHandle(), "Handle has been already released."); 
            return _unmanagedHandles[handleInt].Obj;
        } 
 
        /// 
        /// Enters the PTS context. Called before executing PTS methods. 
        /// 
        /// 
        /// Thread safe, see facts for the class description.
        ///  
        internal void Enter()
        { 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose. 
            _ptsHost.EnterContext(this);
        } 

        /// 
        /// Leaves the PTS context. Called after executing PTS methods.
        ///  
        /// 
        /// Thread safe, see facts for the class description. 
        ///  
        internal void Leave()
        { 
            Invariant.Assert(!_disposeCompleted, "PtsContext is already disposed."); // May be called from Dispose.
            _ptsHost.LeaveContext(this);
        }
 
        /// 
        /// Keeps track of created pages (unmanaged resource). 
        /// When page is created, add it to the list. 
        /// 
        /// PTS Page object that was just created. 
        /// 
        /// Critical, because adds new entry to Critical collection _pages.
        /// Safe, because ptsPage parameter is Critical for set, so cannot have
        ///     randomly assigned value. 
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void OnPageCreated(SecurityCriticalDataForSet ptsPage) 
        {
            Invariant.Assert(ptsPage.Value != IntPtr.Zero, "Invalid page object."); 
            Invariant.Assert(!this.Disposed, "PtsContext is already disposed.");
            Invariant.Assert(!_pages.Contains(ptsPage.Value), "Page already exists.");

            _pages.Add(ptsPage.Value); 
        }
 
        ///  
        /// Destroys PTS page.
        ///  
        /// Pointer to PTS Page object that should be destroyed.
        /// Whether dispose is caused by explicit call to Dispose.
        /// Whether needs to enter PtsContext or not (during layout it is not needed).
        internal void OnPageDisposed(SecurityCriticalDataForSet ptsPage, bool disposing, bool enterContext) 
        {
            Invariant.Assert(ptsPage.Value != IntPtr.Zero, "Invalid page object."); 
 
            // If explicitly disposing (not called during finalization), synchronously
            // destroy the page. 
            if (disposing)
            {
                OnDestroyPage(ptsPage, enterContext);
            } 
            else
            { 
                // If PtsContext has been already disposed, ignore this call. 
                if (!this.Disposed && !this.Dispatcher.HasShutdownStarted)
                { 
                    // Schedule background operation to destroy the page.
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnDestroyPage), ptsPage);
                }
            } 
        }
 
        ///  
        /// Keeps track of created page BreakRecords (unmanaged resource).
        /// When PageBreakRecord is created, add it to the list. 
        /// 
        /// PTS Page BR object that was just created.
        /// 
        /// Critical, because adds new entry to Critical collection _pageBreakRecords. 
        /// Safe, because br parameter is Critical for set, so cannot have randomly assigned value.
        ///  
        [SecurityCritical, SecurityTreatAsSafe] 
        internal void OnPageBreakRecordCreated(SecurityCriticalDataForSet br)
        { 
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object.");
            Invariant.Assert(!this.Disposed, "PtsContext is already disposed.");
            Invariant.Assert(!_pageBreakRecords.Contains(br.Value), "Break record already exists.");
 
            _pageBreakRecords.Add(br.Value);
        } 
 
        /// 
        /// Destroys PTS break record. 
        /// 
        /// Pointer to PTS Page BR object that should be destroyed.
        /// Whether dispose is caused by explicit call to Dispose.
        internal void OnPageBreakRecordDisposed(SecurityCriticalDataForSet br, bool disposing) 
        {
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object."); 
 
            // If explicitly disposing (not called during finalization), synchronously
            // destroy the page break record. 
            if (disposing)
            {
                OnDestroyBreakRecord((object)br);
            } 
            else
            { 
                // If PtsContext has been already disposed, ignore this call. 
                if (!this.Disposed && !this.Dispatcher.HasShutdownStarted)
                { 
                    // Schedule background operation to destroy the page.
                    this.Dispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(OnDestroyBreakRecord), br);
                }
            } 
        }
 
        #endregion Internal Methods 

        //-------------------------------------------------------------------- 
        //
        //  Internal Properties
        //
        //-------------------------------------------------------------------- 

        #region Internal Properties 
 
        /// 
        /// Whether object is already disposed. 
        /// 
        internal bool Disposed
        {
            get { return (_disposed != 0); } 
        }
 
        ///  
        /// Context Id used to communicate with PTS.
        ///  
        internal IntPtr Context
        {
            get { return _ptsHost.Context; }
        } 

        ///  
        /// Whether optimal paragraph is enabled 
        /// 
        internal bool IsOptimalParagraphEnabled 
        {
            get { return _isOptimalParagraphEnabled; }
        }
 
        /// 
        /// Text formatter context for this pts context 
        ///  
        internal TextFormatter TextFormatter
        { 
            get { return _textFormatter; }
            set { _textFormatter = value; }
        }
 
        /// 
        /// Exception caught during callback execution. Those exceptions are 
        /// converted into error codes and passed to PTS to provide appropriate 
        /// clenaup. Later this exception is re-thrown.
        ///  
        internal Exception CallbackException
        {
            get { return _callbackException; }
            set { _callbackException = value; } 
        }
 
        #endregion Internal Properties 

        //------------------------------------------------------------------- 
        //
        //  Private Methods
        //
        //-------------------------------------------------------------------- 

        #region Private Methods 
 
        /// 
        /// Rebuilds list of free entries starting from specified index. 
        /// 
        /// Index to start from.
        private void BuildFreeList(int freeIndex)
        { 
            // Point to the first empty slot
            _unmanagedHandles[0].Index = freeIndex; 
            // Link all entries starting from freeIndex 
            while (freeIndex < _unmanagedHandles.Length)
            { 
                _unmanagedHandles[freeIndex].Index = ++freeIndex;
            }
            // End of free entries list
            _unmanagedHandles[freeIndex - 1].Index = 0; 
        }
 
        ///  
        /// Increases the capacity of the handle array.
        ///      new size = current size * 2 
        /// 
        private void Resize()
        {
            int freeIndex = _unmanagedHandles.Length; 

            // Allocate new array and copy all existing entries into it 
            HandleIndex[] newItems = new HandleIndex[_unmanagedHandles.Length * 2]; 
            Array.Copy(_unmanagedHandles, newItems, _unmanagedHandles.Length);
            _unmanagedHandles = newItems; 

            // Build list of free entries
            BuildFreeList(freeIndex);
        } 

        ///  
        /// Destroys PTS page. 
        /// 
        /// Pointer to PTS Page object that should be destroyed. 
        private object OnDestroyPage(object args)
        {
            SecurityCriticalDataForSet ptsPage = (SecurityCriticalDataForSet)args;
            OnDestroyPage(ptsPage, true); 
            return null;
        } 
 
        /// 
        /// Destroys PTS page. 
        /// 
        /// Pointer to PTS Page object that should be destroyed.
        /// Whether needs to enter PTS Context.
        ///  
        /// Critical, because:
        ///     a) calls Critical functions PTS.FsDestroyPage, 
        ///     b) modifies Critical collection _pages 
        /// Safe, because:
        ///     a) parameter passed to PTS.FsDestroyPage is Critical 
        ///        for set, so cannot have randomly assigned value.
        ///     b) removing from Critical collection is safe operation.
        /// 
        [SecurityCritical, SecurityTreatAsSafe] 
        private void OnDestroyPage(SecurityCriticalDataForSet ptsPage, bool enterContext)
        { 
            Invariant.Assert(ptsPage.Value != IntPtr.Zero, "Invalid page object."); 

            // Dispatcher may invoke this operation when PtsContext is already explicitly 
            // disposed.
            if (!this.Disposed)
            {
                Invariant.Assert(_pages != null, "Collection of pages does not exist."); 
                Invariant.Assert(_pages.Contains(ptsPage.Value), "Page does not exist.");
 
                // Destroy given page. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try 
                {
                    if (enterContext)
                    {
                        Enter(); 
                    }
                    PTS.Validate(PTS.FsDestroyPage(_ptsHost.Context, ptsPage.Value)); 
                } 
                finally
                { 
                    if (enterContext)
                    {
                        Leave();
                    } 
                    _pages.Remove(ptsPage.Value);
                } 
            } 
        }
 
        /// 
        /// Destroys PTS page break record.
        /// 
        /// Pointer to PTS Page BreakRecord object that should be destroyed. 
        /// 
        /// Critical, because: 
        ///     a) calls Critical functions PTS.FsDestroyPageBreakRecord, 
        ///     b) modifies Critical collection _pageBreakRecords
        /// Safe, because: 
        ///     a) parameter passed to PTS.FsDestroyPageBreakRecord is Critical
        ///        for set, so cannot have randomly assigned value.
        ///     b) removing from Critical collection is safe operation.
        ///  
        [SecurityCritical, SecurityTreatAsSafe]
        private object OnDestroyBreakRecord(object args) 
        { 
            SecurityCriticalDataForSet br = (SecurityCriticalDataForSet)args;
            Invariant.Assert(br.Value != IntPtr.Zero, "Invalid break record object."); 

            // Dispatcher may invoke this operation when PtsContext is already explicitly
            // disposed.
            if (!this.Disposed) 
            {
                Invariant.Assert(_pageBreakRecords != null, "Collection of break records does not exist."); 
                Invariant.Assert(_pageBreakRecords.Contains(br.Value), "Break record does not exist."); 

                // Destroy given page break record. 
                // It is necessary to enter PTS Context when executing any PTS methods.
                try
                {
                    Enter(); 
                    PTS.Validate(PTS.FsDestroyPageBreakRecord(_ptsHost.Context, br.Value));
                } 
                finally 
                {
                    Leave(); 
                    _pageBreakRecords.Remove(br.Value);
                }
            }
            return null; 
        }
 
        #endregion Private Methods 

        //------------------------------------------------------------------- 
        //
        //  Private Fields
        //
        //------------------------------------------------------------------- 

        #region Private Fields 
 
        /// 
        /// Array of HandleIndex. This array stores 2 kind of information: 
        /// {1) reference to Object; array index is a handle of the Object,
        /// (2) linked list of free entries; the first element (0) is always used
        ///     to point to the next free entry.
        ///  
        /// 
        /// See: http://blogs.msdn.com/[....]/archive/2004/02/20/77460.aspx 
        /// According to this 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 this array during finalization.
        /// 
        private HandleIndex[] _unmanagedHandles;
 
        /// 
        /// List of created PTS pages. Those are unmanaged resources and 
        /// have to be disposed. 
        /// 
        ///  
        /// See: http://blogs.msdn.com/[....]/archive/2004/02/20/77460.aspx
        /// According to this 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 this array during finalization.
        ///  
        ///  
        /// Members of this list are pointers that are passed to Critical functions
        /// that'll write to the memory directly in unmanaged code. 
        /// 
        [SecurityCritical]
        private ArrayList _pages;
 
        /// 
        /// List of created PTS BreakRecords. Those are unmanaged resources and 
        /// have to be disposed. 
        /// 
        ///  
        /// See: http://blogs.msdn.com/[....]/archive/2004/02/20/77460.aspx
        /// According to this 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 this array during finalization.
        ///  
        ///  
        /// Members of this list are pointers that are passed to Critical functions
        /// that'll write to the memory directly in unmanaged code. 
        /// 
        [SecurityCritical]
        private ArrayList _pageBreakRecords;
 
        /// 
        /// Exception caught during callback execution. Those exceptions are 
        /// converted into error codes and passed to PTS to provide appropriate 
        /// clenaup. Later this exception is re-thrown.
        ///  
        private Exception _callbackException;

        /// 
        /// PTS Host: all PTS callbacks are defined here. 
        /// 
        private PtsHost _ptsHost; 
 
        /// 
        /// Whether optimal paragraph is enabled for this ptscontext 
        /// 
        private bool _isOptimalParagraphEnabled;

        ///  
        /// TextFormatter - Used only in optimal mode
        ///  
        private TextFormatter _textFormatter; 

        ///  
        /// Whether object is already disposed.
        /// 
        private int _disposed;
 
        /// 
        /// Whether Dispose has been completed. It may be set to 'false' even when 
        /// _disposed is set to 'true'. It may happen during Dispose execution. 
        /// This flag is used for verification only.
        ///  
        private bool _disposeCompleted;

        /// 
        /// Default capacity of the UnmanagedHandles array. The array capacity 
        /// is always increased in multiples of two as required: 16*(2^N).
        ///  
        private const int _defaultHandlesCapacity = 16; 

        #endregion Private Fields 

        //-------------------------------------------------------------------
        //
        //  Private Types 
        //
        //-------------------------------------------------------------------- 
 
        #region Private Types
 
        /// 
        /// HandleIndex can store one of following information:
        /// {1) reference to Object
        /// (2) index of the next free entry 
        /// 
        private struct HandleIndex 
        { 
            internal int Index;
            internal object Obj; 
            internal bool IsHandle()
            {
                return (Obj != null && Index == 0);
            } 
        }
 
        #endregion Private Types 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK