PtsContext.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / PtsHost / PtsContext.cs / 1305600 / 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
using System.Windows.Media; 

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, TextFormattingMode textFormattingMode)
        { 
            _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, textFormattingMode); 
        }

        #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
using System.Windows.Media; 

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, TextFormattingMode textFormattingMode)
        { 
            _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, textFormattingMode); 
        }

        #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