XpsFilter.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 / IO / Packaging / XpsFilter.cs / 1305600 / XpsFilter.cs

                            //---------------------------------------------------------------------------- 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//              Implements indexing filter for XPS documents, 
//              which could be Package or EncryptedPackageEnvelope.
//              Uses PackageFilter or EncryptedPackageFilter accordingly. 
//
// History:
//  07/18/2005: ArindamB: Initial implementation, which also has relevant
//              code moved from previous implementation of 
//              ContainerFilterImpl and IndexingFilterMarshaler.
//--------------------------------------------------------------------------- 
 
using System;
using System.IO; 
using System.IO.Packaging;
using System.Diagnostics;                       // For Assert
using System.Runtime.InteropServices;           // For Marshal.ThrowExceptionForHR
using System.Globalization;                     // For CultureInfo 
using System.Windows;                           // for ExceptionStringTable
using System.Security;                          // For SecurityCritical 
 
using MS.Win32;
using MS.Internal.Interop;                      // For STAT_CHUNK, etc. 
using MS.Internal.IO.Packaging;                 // For ManagedIStream
using MS.Internal;

namespace MS.Internal.IO.Packaging 
{
    #region XpsFilter 
 
    /// 
    /// Implements IFilter, IPersistFile and IPersistStream methods 
    /// to support indexing on XPS files.
    /// 
    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential, Pack = 0)] 
    [Guid("0B8732A6-AF74-498c-A251-9DC86B0538B0")]
    internal sealed class XpsFilter : IFilter, IPersistFile, IPersistStream 
    { 
        #region IFilter methods
 
        /// 
        /// Initialzes the session for this filter.
        /// 
        /// usage flags 
        /// number of elements in aAttributes array
        /// array of FULLPROPSPEC structs to restrict responses 
        ///  
        /// IFILTER_FLAGS_NONE to indicate that the caller should not use the IPropertySetStorage
        /// and IPropertyStorage interfaces to locate additional properties. 
        /// 
        IFILTER_FLAGS IFilter.Init(
            [In] IFILTER_INIT grfFlags,
            [In] uint cAttributes, 
            [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] FULLPROPSPEC[] aAttributes)
        { 
            if (_filter == null) 
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded), 
                    (int)NativeMethods.E_FAIL);
            }

            if (cAttributes > 0 && aAttributes == null) 
            {
                // Attributes count and array do not match. 
                throw new COMException(SR.Get(SRID.FilterInitInvalidAttributes), 
                    (int)NativeMethods.E_INVALIDARG);
            } 

            return _filter.Init(grfFlags, cAttributes, aAttributes);
        }
 
        /// 
        /// Returns description of the next chunk. 
        ///  
        /// Chunk descriptor
        STAT_CHUNK IFilter.GetChunk() 
        {
            if (_filter == null)
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded), 
                    (int)FilterErrorCode.FILTER_E_ACCESS);
            } 
 
            try
            { 
                return _filter.GetChunk();
            }
            catch (COMException ex)
            { 
                // End-of-data?  If so, release the package.
                if (ex.ErrorCode == (int)FilterErrorCode.FILTER_E_END_OF_CHUNKS) 
                    ReleaseResources(); 

                throw ex; 
            }
        }

        ///  
        /// Gets text content corresponding to current chunk.
        ///  
        /// size of buffer in characters 
        /// buffer pointer
        /// Supported for indexing content of Package. 
        /// 
        /// Critical    - Calling Marshal.WriteInt16, which has a LinkDemand. It takes an input
        ///               pointer to write to. To be safe, the caller cannot be in Partial Trust.
        ///   This method is Internal. Not to be called from PT code. 
        ///   Not designed to be accessible from public surface at all. Invoked (indirectly) by unmanaged client code.
        ///  
        [SecurityCritical] 
        void IFilter.GetText(ref uint bufCharacterCount, IntPtr pBuffer)
        { 
            if (_filter == null)
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded),
                    (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
 
            // NULL is not an acceptable value for pBuffer 
            if (pBuffer == IntPtr.Zero)
            { 
                throw new NullReferenceException(SR.Get(SRID.FilterNullGetTextBufferPointer));
            }

            // If there is 0 byte to write, this is a no-op. 
            if (bufCharacterCount == 0)
            { 
                return; 
            }
 
            // Because we should always return the string with null terminator, a buffer size
            // of one character can hold the null terminator only, we can always write the
            // terminator to the buffer and return directly.
            if (bufCharacterCount == 1) 
            {
                Marshal.WriteInt16(pBuffer, 0); 
                return; 
            }
 
            // Record the original buffer size. bufCharacterCount may be changed later.
            // The original buffer size will be used to identify a special
            // case later.
            uint origianlBufferSize = bufCharacterCount; 

            // Normalize the buffer size, for a very large size could be due to a bug or an attempted attack. 
            if (bufCharacterCount > _maxTextBufferSizeInCharacters) 
            {
                bufCharacterCount = _maxTextBufferSizeInCharacters; 
            }

            // Memorize the buffer size.
            // We need to reserve a character for the terminator because we don't know 
            // whether the underlying layer will take care of it.
            uint maxSpaceForContent = --bufCharacterCount; 
 
            // Retrieve the result and its size.
            _filter.GetText(ref bufCharacterCount, pBuffer); 

            // An increase in the in/out size parameter would be anomalous, and could be ill-intentioned.
            if (bufCharacterCount > maxSpaceForContent)
            { 
                throw new COMException(SR.Get(SRID.AuxiliaryFilterReturnedAnomalousCountOfCharacters),
                    (int)FilterErrorCode.FILTER_E_ACCESS); 
            } 

            // We need to handle a tricky case if the input buffer size is 2 characters. 
            //
            // In this case, we actually request 1 character from the underlying layer
            // because we always reserve one character for the terminator.
            // 
            // There are two possible scenarios for the returned character in the buffer:
            // 1.   If the underlying layer will pad the returning string 
            // with the null terminator, then the returned character in the buffer is null. 
            // In this case we cannot return anything useful to the user, which is not expected.
            // What the users would expect is getting a string with one character 
            // and one null terminator when passing a buffer with size of 2 characters to us.
            // 2.   If the underlying layer will NOT pad the returning string
            // with the null terminator, then we have a useful character returned.
            // Then we pad the buffer with string terminator null, and give back to the user. 
            // This case meets the users' expectation.
            // 
            // So we need to discover the behavior of the underlying layer and act properly. 
            // Following is a solution:
            // 1.   Check the returned character in the buffer. 
            //      If it's a null, then we have scenario 1. Goto step 2.
            //      If it's not a null, then we have scenario 2. Goto step 3.
            // 2.   Call the underlying layer's GetText() again, but passing buffer size of 2.
            // 3.   Pad the buffer with null string terminator and return. 
            if (origianlBufferSize == 2)
            { 
                short shCharacter = Marshal.ReadInt16(pBuffer); 
                if (shCharacter == '\0')
                { 
                    // Scenario 1. Call underlying layer again with the actual buffer size.
                    bufCharacterCount = 2;
                    _filter.GetText(ref bufCharacterCount, pBuffer);
 
                    // An increase in the in/out size parameter would be anomalous, and could be ill-intentioned.
                    if (bufCharacterCount > 2) 
                    { 
                        throw new COMException(SR.Get(SRID.AuxiliaryFilterReturnedAnomalousCountOfCharacters),
                            (int)FilterErrorCode.FILTER_E_ACCESS); 
                    }

                    if (bufCharacterCount == 2)
                    { 
                        // If the underlying layer GetText() returns 2 characters, we need to check
                        // whether the second character is null. If it's not, then its behavior 
                        // does not match the scenario 1, we cannot handle it. 
                        shCharacter = Marshal.ReadInt16(pBuffer, _int16Size);
 
                        // We don't throw exception because such a behavior violation is not acceptable.
                        // We'd better terminate the entire process.
                        Invariant.Assert(shCharacter == '\0');
 
                        // Then we adjust the point where we should put our null terminator.
                        bufCharacterCount = 1; 
                    } 
                    // If the underlying layer GetText() returns 0 or 1 character, we
                    // don't need to do anything. 
                }
            }
            // If the buffer size is bigger than 2, then we don't care the behavior of the
            // underlying layer. The string buffer we return may contain 2 null terminators 
            // if the underlying layer also pads the terminator. But there will be at least one
            // non-null character in the buffer if there is any text to get. So the users will get 
            // something useful. 
            //
            // One possible proposal is to generalize the special case: why not make the returned 
            // string more uniform, in which there is only one terminator always? We discussed this
            // proposal. To achieve this, we must know the behavior of the underlying layer.
            // We need to call the underlying layer twice.
 
            // The first call is to request for one character to test the behavior.
            // If the returned character is null, then the underlying 
            // layer is a conforming filter, which will pad a null terminator for the string it 
            // returns. Otherwise, the underlying layer is non-conforming.
            // 
            // Suppose the input buffer size is N, then if underlying layer is conforming, we make
            // a second call to it requesting for N characters. Then we can return.
            //
            // If the underlying layer is non-conforming, things are tricky. 
            // First, the character returned
            // by the first call is useful and we cannot discard it. We should let it sit at the 
            // beginning of the input buffer. So when we make the second call requesting for (N-2) 
            // charaters, we have to use a temporary buffer. The reason is: the input buffer is
            // specified as an IntPtr. We cannot change its offset like a pointer without using 
            // unsafe context, which we want to avoid. So we need to copy the characters in the
            // temporary buffer to the input buffer when the call returns, which might be expensive.
            //
            // Second, a side effect of making 2 calls to the underlying layer 
            // is the second call may trigger a FILTER_E_NO_MORE_TEXT exception if the first call
            // exhausts all texts in the stream. We need to catch this exception, otherwise the COM 
            // will catch it and return an error HRESULT to the user, which sould not happen. So, 
            // we need to add a try-catch block for the second call to the non-conforming underlying
            // layer, which is expensive. 
            //
            // Given the overheads that can incur, we dropped this idea eventhough it provides a
            // cleaner string format returned to the user. If the filter interface requires
            // the underlying filter to provide a property field indicating its behavior, then 
            // we can implement this idea much cheaper.
 
            // Make sure the returned buffer always contains a terminating zero. 
            //    Note the conversion of uint to int involves no risk of an arithmetic overflow thanks
            // to the truncations performed above. 
            //    Provided pBuffer points to a buffer of size the minimum of _maxTextBufferSizeInCharacters
            // and the initial value of bufCharacterCount, the following write occurs within range.
            Marshal.WriteInt16(pBuffer, (int)bufCharacterCount * _int16Size, 0);
 
            // Count the terminator in the size that is returned.
            bufCharacterCount++; 
        } 

        ///  
        /// Gets the property value corresponding to current chunk.
        /// 
        /// property value
        ///  
        /// Supported for indexing core properties
        /// for Package and EncryptedPackageEnvelope. 
        ///  
        IntPtr IFilter.GetValue()
        { 
            if (_filter == null)
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded),
                    (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
 
            return _filter.GetValue(); 
        }
 
        /// 
        /// Retrieves an interface representing the specified portion of the object.
        /// 
        ///  
        /// 
        /// Not implemented. Reserved for future use. 
        IntPtr IFilter.BindRegion([In] FILTERREGION origPos, [In] ref Guid riid) 
        {
            // The following exception maps to E_NOTIMPL. 
            throw new NotImplementedException(SR.Get(SRID.FilterBindRegionNotImplemented));
        }

        #endregion IFilter methods 

        #region IPersistFile methods 
 
        /// 
        /// Return the CLSID for the XAML filtering component. 
        /// 
        /// On successful return, a reference to the CLSID.
        void IPersistFile.GetClassID(out Guid pClassID)
        { 
            pClassID = _filterClsid;
        } 
 
        /// 
        /// Return the path to the current working file or the file prompt ("*.xps"). 
        /// 
        [PreserveSig]
        int IPersistFile.GetCurFile(out string ppszFileName)
        { 
            ppszFileName = null;
 
            if (_filter == null || _xpsFileName == null) 
            {
                ppszFileName = "*." + PackagingUtilities.ContainerFileExtension; 
                return NativeMethods.S_FALSE;
            }

            ppszFileName = _xpsFileName; 
            return NativeMethods.S_OK;
        } 
 
        /// 
        /// Checks an object for changes since it was last saved to its current file. 
        /// 
        /// 
        /// S_OK if the file has changed since it was last saved;
        /// S_FALSE if the file has not changed since it was last saved. 
        /// 
        ///  
        /// Since the file is accessed only for reading, this function always returns S_FALSE. 
        /// 
        [PreserveSig] 
        int IPersistFile.IsDirty()
        {
            return NativeMethods.S_FALSE;
        } 

        ///  
        /// Opens the specified file with the specified mode.. 
        /// This can return any of the STG_E_* error codes, along
        /// with S_OK, E_OUTOFMEMORY, and E_FAIL. 
        /// 
        /// 
        /// A zero-terminated string containing the absolute path of the file to open.
        ///  
        /// The mode in which to open pszFileName. 
        void IPersistFile.Load(string pszFileName, int dwMode) 
        { 
            FileMode fileMode;
            FileAccess fileAccess; 
            FileShare fileSharing;

            // Check argument.
            if (pszFileName == null || pszFileName == String.Empty) 
            {
                throw new ArgumentException(SR.Get(SRID.FileNameNullOrEmpty), "pszFileName"); 
            } 

            // Convert mode information in flag. 
            switch ((STGM_FLAGS)(dwMode & (int)STGM_FLAGS.MODE))
            {
                case STGM_FLAGS.CREATE:
                    throw new ArgumentException(SR.Get(SRID.FilterLoadInvalidModeFlag), "dwMode"); 

                default: 
                    fileMode = FileMode.Open; 
                    break;
            } 

            // Convert access flag.
            switch ((STGM_FLAGS)(dwMode & (int)STGM_FLAGS.ACCESS))
            { 
                case STGM_FLAGS.READ:
                case STGM_FLAGS.READWRITE: 
                    fileAccess = FileAccess.Read; 
                    break;
 
                default:
                    throw new ArgumentException(SR.Get(SRID.FilterLoadInvalidModeFlag), "dwMode");
            }
 
            // Sharing flags are ignored. Since managed filters do not have the equivalent
            // of a destructor to release locks on files as soon as they get disposed of from 
            // unmanaged code, the option taken is not to lock at all while filtering. 
            // (See call to FileToStream further down.)
            fileSharing = FileShare.ReadWrite; 

            // Only one of _package and _encryptedPackage can be non-null at a time.
            Invariant.Assert(_package == null || _encryptedPackage == null);
 
            // If there has been a previous call to Load, reinitialize everything.
            // Note closing a closed stream does not cause any exception. 
            ReleaseResources(); 

            _filter = null; 
            _xpsFileName = null;

            bool encrypted = EncryptedPackageEnvelope.IsEncryptedPackageEnvelope(pszFileName);
 
            try
            { 
                // opens to MemoryStream or just returns FileStream if file exceeds _maxMemoryStreamBuffer 
                _packageStream = FileToStream(pszFileName, fileMode, fileAccess, fileSharing, _maxMemoryStreamBuffer);
 
                if (encrypted)
                {
                    // Open the encrypted package.
                    _encryptedPackage = EncryptedPackageEnvelope.Open(_packageStream); 
                    _filter = new EncryptedPackageFilter(_encryptedPackage);
                } 
                else 
                {
                    // Open the package. 
                    _package = Package.Open(_packageStream);
                    _filter = new PackageFilter(_package);
                }
            } 
            catch (IOException ex)
            { 
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
            catch (FileFormatException ex) 
            {
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_UNKNOWNFORMAT);
            }
            finally 
            {
                // failure? 
                if (_filter == null) 
                {
                    // clean up 
                    ReleaseResources();
                }

            } 

            _xpsFileName = pszFileName; 
        } 

        ///  
        /// Saves a copy of the object into the specified file.
        /// 
        /// 
        /// A zero-terminated string containing the absolute path 
        /// of the file to which the object is saved.
        ///  
        ///  
        /// Indicates whether pszFileName is to be used as the current working file.
        ///  
        /// 
        /// On the odd chance that this link is still valid when it's needed,
        /// expected error codes are described at
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/da9581e8-98c7-4592-8ee1-a1bc8232635b.asp 
        /// 
        void IPersistFile.Save(string pszFileName, bool fRemember) 
        { 
            throw new COMException(SR.Get(SRID.FilterIPersistFileIsReadOnly), NativeMethods.STG_E_CANTSAVE);
        } 

        /// 
        /// Notifies the object that it can write to its file.
        ///  
        /// 
        /// The absolute path of the file where the object was previously saved. 
        ///  
        /// 
        /// On the odd chance that this link is still valid when it's needed, 
        /// expected error codes are described at
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/da9581e8-98c7-4592-8ee1-a1bc8232635b.asp
        /// This function should always return S_OK when Save is not supported.
        ///  
        void IPersistFile.SaveCompleted(string pszFileName)
        { 
            return; // return S_OK 
        }
 
        #endregion IPersistFile methods

        #region IPersistStream methods
 
        /// 
        /// Return the CLSID for the XAML filtering component. 
        ///  
        /// On successful return, a reference to the CLSID.
        void IPersistStream.GetClassID(out Guid pClassID) 
        {
            pClassID = _filterClsid;
        }
 
        /// 
        /// Checks an object for changes since it was last saved to its current file. 
        ///  
        /// 
        /// S_OK if the file has changed since it was last saved; 
        /// S_FALSE if the file has not changed since it was last saved.
        /// 
        /// 
        /// Since the file is accessed only for reading, this function always returns S_FALSE. 
        /// 
        [PreserveSig] 
        int IPersistStream.IsDirty() 
        {
            return NativeMethods.S_FALSE; 
        }

        /// 
        /// Retrieve the container on the specified IStream. 
        /// 
        /// The OLE stream from which the container's contents are to be read. 
        ///  
        /// The interface implemented by 'stream' is defined in
        /// MS.Internal.Interop.IStream rather than the standard 
        /// managed so as to allow optimized marshaling in UnsafeIndexingFilterStream.
        /// 
        /// 
        ///     Critical: This method accesses a class - UnsafeIndexingFilterStream which calls into 
        ///               unmanaged code which provides a managed Stream like interface for an
        ///               unmanaged OLE IStream. 
        ///               This method is only called by unmanaged callers. 
        ///               There is no elevation of privilege in this method.
        ///  
        [SecurityCritical]
        void IPersistStream.Load(MS.Internal.Interop.IStream stream)
        {
            // Check argument. 
            if (stream == null)
            { 
                throw new ArgumentNullException("stream"); 
            }
 
            // Only one of _package and _encryptedPackage can be non-null at a time.
            Invariant.Assert(_package == null || _encryptedPackage == null);

            // If there has been a previous call to Load, reinitialize everything. 
            // Note closing a closed stream does not cause any exception.
            ReleaseResources(); 
 
            _filter = null;
            _xpsFileName = null; 

            try
            {
                _packageStream = new UnsafeIndexingFilterStream(stream); 

                // different filter for encrypted package 
                if (EncryptedPackageEnvelope.IsEncryptedPackageEnvelope(_packageStream)) 
                {
                    // Open the encrypted package. 
                    _encryptedPackage = EncryptedPackageEnvelope.Open(_packageStream);
                    _filter = new EncryptedPackageFilter(_encryptedPackage);
                }
                else 
                {
                    // Open the package. 
                    _package = Package.Open(_packageStream); 
                    _filter = new PackageFilter(_package);
                } 
            }
            catch (IOException ex)
            {
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
            catch (Exception ex) 
            { 
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_UNKNOWNFORMAT);
            } 
            finally
            {
                // clean-up if we failed
                if (_filter == null) 
                {
                    ReleaseResources(); 
                } 
            }
        } 

        /// 
        /// Saves a copy of the object into the specified stream.
        ///  
        /// The stream to which the object is saved. 
        /// Indicates whether the dirty state is to be cleared.  
        ///  
        /// Expected error codes are described at
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/b748b4f9-ef9c-486b-bdc4-4d23c4640ff7.asp 
        /// 
        void IPersistStream.Save(MS.Internal.Interop.IStream stream, bool fClearDirty)
        {
            throw new COMException(SR.Get(SRID.FilterIPersistStreamIsReadOnly), NativeMethods.STG_E_CANTSAVE); 
        }
 
        ///  
        /// The purpose of this function when implemented by a persistent object is to return
        /// the size in bytes of the stream needed to save the object. 
        /// Always returns COR_E_NOTSUPPORTED insofar as the filter does not use this interface for persistence.
        /// 
        void IPersistStream.GetSizeMax(out Int64 pcbSize)
        { 
            throw new NotSupportedException(SR.Get(SRID.FilterIPersistFileIsReadOnly));
        } 
 
        #endregion IPersistStream methods
 
        #region Private methods

        /// 
        /// Shared implementation for releasing package/encryptedPackage and underlying stream 
        /// 
        private void ReleaseResources() 
        { 
            if (_encryptedPackage != null)
            { 
                _encryptedPackage.Close();
                _encryptedPackage = null;
            }
            else if (_package != null) 
            {
                _package.Close(); 
                _package = null; 
            }
            if (_packageStream != null) 
            {
                _packageStream.Close();
                _packageStream = null;
            } 
        }
 
        ///  
        /// Auxiliary function of IPersistFile.Load.
        ///  
        /// 
        /// A MemoryStream of the package file or a FileStream if the file is too big.
        /// 
        ///  
        /// Use this method to load a package file completely to a memory buffer.
        /// After loading the file we can close the file, thus we can release the file 
        /// lock quickly. However, there is a size limit on the file. If the 
        /// file is too big (greater than _maxMemoryStreamBuffer), we cannot allow
        /// this method to consume too much memory. So we simply return the fileStream. 
        /// Mode, access and sharing have already been checked or adjusted and can be assumed
        /// to be compatible with the goal of reading from the file.
        /// 
        private static Stream FileToStream( 
            string filePath,
            FileMode fileMode, 
            FileAccess fileAccess, 
            FileShare fileSharing,
            long maxMemoryStream) 
        {
            FileInfo fi = new FileInfo(filePath);
            long byteCount = fi.Length;
            Stream s = new FileStream(filePath, fileMode, fileAccess, fileSharing); 

            // There is a size limit of the file that we allow to be uploaded to a 
            // memory stream. If the file size is bigger than the limit, simply return the fileStream. 
            if (byteCount < maxMemoryStream)
            { 
                // unchecked cast is safe because _maxMemoryStreamBuffer is less than Int32.Max
                MemoryStream ms = new MemoryStream(unchecked((int)byteCount));
                using (s)
                { 
                    PackagingUtilities.CopyStream(s, ms, byteCount, 0x1000);
                } 
                s = ms; 
            }
 
            return s;
        }
        #endregion Private methods
 
        #region Fields
 
        ///  
        /// CLSID for the XPS filter.
        ///  
        [ComVisible(false)]
        private static readonly Guid _filterClsid = new Guid(0x0B8732A6,
                                                    0xAF74,
                                                    0x498c, 
                                                    0xA2 , 0x51 ,
                                                    0x9D , 0xC8 , 0x6B , 0x05 , 0x38 , 0xB0); 
 
        /// 
        /// Internal IFilter implementation being used by XpsFilter. 
        /// This could be PackageFilter or EncryptedPackageFilter.
        /// 
        [ComVisible(false)]
        private IFilter _filter; 

        ///  
        /// If the XPS file/stream is a Package, reference to the Package. 
        /// 
        [ComVisible(false)] 
        private Package _package;

        /// 
        /// If the XPS file/stream is a EncryptedPackageEnvelope, reference to the EncryptedPackageEnvelope. 
        /// 
        [ComVisible(false)] 
        private EncryptedPackageEnvelope _encryptedPackage; 

        ///  
        /// If an XPS file is being filtered, refers to the file name.
        /// 
        [ComVisible(false)]
        private string _xpsFileName; 

        ///  
        /// Stream wrapper we have opened our Package or EncryptedPackage on 
        /// 
        [ComVisible(false)] 
        private Stream _packageStream;

        /// 
        /// Cache frequently used size values to incur reflection cost just once. 
        /// 
        [ComVisible(false)] 
        private const Int32 _int16Size = 2; 

        #region Constants 

        /// 
        /// The number of characters to copy in a chunk buffer is limited as a
        /// defense-in-depth device without any expected performance deterioration. 
        /// 
        [ComVisible(false)] 
        private const uint _maxTextBufferSizeInCharacters = 4096; 

 
        /// 
        /// The size of memory stream buffer used in FileToStream()
        /// should be limited. If the package file size is bigger than the limit,
        /// we cannot allow the buffer allocation, and return the fileStream itself. 
        /// 
        [ComVisible(false)] 
        private const Int32 _maxMemoryStreamBuffer = 1024 * 1024; 

        #endregion Constants 

        #endregion Fields
    }
 
    #endregion XpsFilter
} 

// 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.
//  
//
// Description: 
//              Implements indexing filter for XPS documents, 
//              which could be Package or EncryptedPackageEnvelope.
//              Uses PackageFilter or EncryptedPackageFilter accordingly. 
//
// History:
//  07/18/2005: ArindamB: Initial implementation, which also has relevant
//              code moved from previous implementation of 
//              ContainerFilterImpl and IndexingFilterMarshaler.
//--------------------------------------------------------------------------- 
 
using System;
using System.IO; 
using System.IO.Packaging;
using System.Diagnostics;                       // For Assert
using System.Runtime.InteropServices;           // For Marshal.ThrowExceptionForHR
using System.Globalization;                     // For CultureInfo 
using System.Windows;                           // for ExceptionStringTable
using System.Security;                          // For SecurityCritical 
 
using MS.Win32;
using MS.Internal.Interop;                      // For STAT_CHUNK, etc. 
using MS.Internal.IO.Packaging;                 // For ManagedIStream
using MS.Internal;

namespace MS.Internal.IO.Packaging 
{
    #region XpsFilter 
 
    /// 
    /// Implements IFilter, IPersistFile and IPersistStream methods 
    /// to support indexing on XPS files.
    /// 
    [ComVisible(true)]
    [StructLayout(LayoutKind.Sequential, Pack = 0)] 
    [Guid("0B8732A6-AF74-498c-A251-9DC86B0538B0")]
    internal sealed class XpsFilter : IFilter, IPersistFile, IPersistStream 
    { 
        #region IFilter methods
 
        /// 
        /// Initialzes the session for this filter.
        /// 
        /// usage flags 
        /// number of elements in aAttributes array
        /// array of FULLPROPSPEC structs to restrict responses 
        ///  
        /// IFILTER_FLAGS_NONE to indicate that the caller should not use the IPropertySetStorage
        /// and IPropertyStorage interfaces to locate additional properties. 
        /// 
        IFILTER_FLAGS IFilter.Init(
            [In] IFILTER_INIT grfFlags,
            [In] uint cAttributes, 
            [In, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)] FULLPROPSPEC[] aAttributes)
        { 
            if (_filter == null) 
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded), 
                    (int)NativeMethods.E_FAIL);
            }

            if (cAttributes > 0 && aAttributes == null) 
            {
                // Attributes count and array do not match. 
                throw new COMException(SR.Get(SRID.FilterInitInvalidAttributes), 
                    (int)NativeMethods.E_INVALIDARG);
            } 

            return _filter.Init(grfFlags, cAttributes, aAttributes);
        }
 
        /// 
        /// Returns description of the next chunk. 
        ///  
        /// Chunk descriptor
        STAT_CHUNK IFilter.GetChunk() 
        {
            if (_filter == null)
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded), 
                    (int)FilterErrorCode.FILTER_E_ACCESS);
            } 
 
            try
            { 
                return _filter.GetChunk();
            }
            catch (COMException ex)
            { 
                // End-of-data?  If so, release the package.
                if (ex.ErrorCode == (int)FilterErrorCode.FILTER_E_END_OF_CHUNKS) 
                    ReleaseResources(); 

                throw ex; 
            }
        }

        ///  
        /// Gets text content corresponding to current chunk.
        ///  
        /// size of buffer in characters 
        /// buffer pointer
        /// Supported for indexing content of Package. 
        /// 
        /// Critical    - Calling Marshal.WriteInt16, which has a LinkDemand. It takes an input
        ///               pointer to write to. To be safe, the caller cannot be in Partial Trust.
        ///   This method is Internal. Not to be called from PT code. 
        ///   Not designed to be accessible from public surface at all. Invoked (indirectly) by unmanaged client code.
        ///  
        [SecurityCritical] 
        void IFilter.GetText(ref uint bufCharacterCount, IntPtr pBuffer)
        { 
            if (_filter == null)
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded),
                    (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
 
            // NULL is not an acceptable value for pBuffer 
            if (pBuffer == IntPtr.Zero)
            { 
                throw new NullReferenceException(SR.Get(SRID.FilterNullGetTextBufferPointer));
            }

            // If there is 0 byte to write, this is a no-op. 
            if (bufCharacterCount == 0)
            { 
                return; 
            }
 
            // Because we should always return the string with null terminator, a buffer size
            // of one character can hold the null terminator only, we can always write the
            // terminator to the buffer and return directly.
            if (bufCharacterCount == 1) 
            {
                Marshal.WriteInt16(pBuffer, 0); 
                return; 
            }
 
            // Record the original buffer size. bufCharacterCount may be changed later.
            // The original buffer size will be used to identify a special
            // case later.
            uint origianlBufferSize = bufCharacterCount; 

            // Normalize the buffer size, for a very large size could be due to a bug or an attempted attack. 
            if (bufCharacterCount > _maxTextBufferSizeInCharacters) 
            {
                bufCharacterCount = _maxTextBufferSizeInCharacters; 
            }

            // Memorize the buffer size.
            // We need to reserve a character for the terminator because we don't know 
            // whether the underlying layer will take care of it.
            uint maxSpaceForContent = --bufCharacterCount; 
 
            // Retrieve the result and its size.
            _filter.GetText(ref bufCharacterCount, pBuffer); 

            // An increase in the in/out size parameter would be anomalous, and could be ill-intentioned.
            if (bufCharacterCount > maxSpaceForContent)
            { 
                throw new COMException(SR.Get(SRID.AuxiliaryFilterReturnedAnomalousCountOfCharacters),
                    (int)FilterErrorCode.FILTER_E_ACCESS); 
            } 

            // We need to handle a tricky case if the input buffer size is 2 characters. 
            //
            // In this case, we actually request 1 character from the underlying layer
            // because we always reserve one character for the terminator.
            // 
            // There are two possible scenarios for the returned character in the buffer:
            // 1.   If the underlying layer will pad the returning string 
            // with the null terminator, then the returned character in the buffer is null. 
            // In this case we cannot return anything useful to the user, which is not expected.
            // What the users would expect is getting a string with one character 
            // and one null terminator when passing a buffer with size of 2 characters to us.
            // 2.   If the underlying layer will NOT pad the returning string
            // with the null terminator, then we have a useful character returned.
            // Then we pad the buffer with string terminator null, and give back to the user. 
            // This case meets the users' expectation.
            // 
            // So we need to discover the behavior of the underlying layer and act properly. 
            // Following is a solution:
            // 1.   Check the returned character in the buffer. 
            //      If it's a null, then we have scenario 1. Goto step 2.
            //      If it's not a null, then we have scenario 2. Goto step 3.
            // 2.   Call the underlying layer's GetText() again, but passing buffer size of 2.
            // 3.   Pad the buffer with null string terminator and return. 
            if (origianlBufferSize == 2)
            { 
                short shCharacter = Marshal.ReadInt16(pBuffer); 
                if (shCharacter == '\0')
                { 
                    // Scenario 1. Call underlying layer again with the actual buffer size.
                    bufCharacterCount = 2;
                    _filter.GetText(ref bufCharacterCount, pBuffer);
 
                    // An increase in the in/out size parameter would be anomalous, and could be ill-intentioned.
                    if (bufCharacterCount > 2) 
                    { 
                        throw new COMException(SR.Get(SRID.AuxiliaryFilterReturnedAnomalousCountOfCharacters),
                            (int)FilterErrorCode.FILTER_E_ACCESS); 
                    }

                    if (bufCharacterCount == 2)
                    { 
                        // If the underlying layer GetText() returns 2 characters, we need to check
                        // whether the second character is null. If it's not, then its behavior 
                        // does not match the scenario 1, we cannot handle it. 
                        shCharacter = Marshal.ReadInt16(pBuffer, _int16Size);
 
                        // We don't throw exception because such a behavior violation is not acceptable.
                        // We'd better terminate the entire process.
                        Invariant.Assert(shCharacter == '\0');
 
                        // Then we adjust the point where we should put our null terminator.
                        bufCharacterCount = 1; 
                    } 
                    // If the underlying layer GetText() returns 0 or 1 character, we
                    // don't need to do anything. 
                }
            }
            // If the buffer size is bigger than 2, then we don't care the behavior of the
            // underlying layer. The string buffer we return may contain 2 null terminators 
            // if the underlying layer also pads the terminator. But there will be at least one
            // non-null character in the buffer if there is any text to get. So the users will get 
            // something useful. 
            //
            // One possible proposal is to generalize the special case: why not make the returned 
            // string more uniform, in which there is only one terminator always? We discussed this
            // proposal. To achieve this, we must know the behavior of the underlying layer.
            // We need to call the underlying layer twice.
 
            // The first call is to request for one character to test the behavior.
            // If the returned character is null, then the underlying 
            // layer is a conforming filter, which will pad a null terminator for the string it 
            // returns. Otherwise, the underlying layer is non-conforming.
            // 
            // Suppose the input buffer size is N, then if underlying layer is conforming, we make
            // a second call to it requesting for N characters. Then we can return.
            //
            // If the underlying layer is non-conforming, things are tricky. 
            // First, the character returned
            // by the first call is useful and we cannot discard it. We should let it sit at the 
            // beginning of the input buffer. So when we make the second call requesting for (N-2) 
            // charaters, we have to use a temporary buffer. The reason is: the input buffer is
            // specified as an IntPtr. We cannot change its offset like a pointer without using 
            // unsafe context, which we want to avoid. So we need to copy the characters in the
            // temporary buffer to the input buffer when the call returns, which might be expensive.
            //
            // Second, a side effect of making 2 calls to the underlying layer 
            // is the second call may trigger a FILTER_E_NO_MORE_TEXT exception if the first call
            // exhausts all texts in the stream. We need to catch this exception, otherwise the COM 
            // will catch it and return an error HRESULT to the user, which sould not happen. So, 
            // we need to add a try-catch block for the second call to the non-conforming underlying
            // layer, which is expensive. 
            //
            // Given the overheads that can incur, we dropped this idea eventhough it provides a
            // cleaner string format returned to the user. If the filter interface requires
            // the underlying filter to provide a property field indicating its behavior, then 
            // we can implement this idea much cheaper.
 
            // Make sure the returned buffer always contains a terminating zero. 
            //    Note the conversion of uint to int involves no risk of an arithmetic overflow thanks
            // to the truncations performed above. 
            //    Provided pBuffer points to a buffer of size the minimum of _maxTextBufferSizeInCharacters
            // and the initial value of bufCharacterCount, the following write occurs within range.
            Marshal.WriteInt16(pBuffer, (int)bufCharacterCount * _int16Size, 0);
 
            // Count the terminator in the size that is returned.
            bufCharacterCount++; 
        } 

        ///  
        /// Gets the property value corresponding to current chunk.
        /// 
        /// property value
        ///  
        /// Supported for indexing core properties
        /// for Package and EncryptedPackageEnvelope. 
        ///  
        IntPtr IFilter.GetValue()
        { 
            if (_filter == null)
            {
                throw new COMException(SR.Get(SRID.FileToFilterNotLoaded),
                    (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
 
            return _filter.GetValue(); 
        }
 
        /// 
        /// Retrieves an interface representing the specified portion of the object.
        /// 
        ///  
        /// 
        /// Not implemented. Reserved for future use. 
        IntPtr IFilter.BindRegion([In] FILTERREGION origPos, [In] ref Guid riid) 
        {
            // The following exception maps to E_NOTIMPL. 
            throw new NotImplementedException(SR.Get(SRID.FilterBindRegionNotImplemented));
        }

        #endregion IFilter methods 

        #region IPersistFile methods 
 
        /// 
        /// Return the CLSID for the XAML filtering component. 
        /// 
        /// On successful return, a reference to the CLSID.
        void IPersistFile.GetClassID(out Guid pClassID)
        { 
            pClassID = _filterClsid;
        } 
 
        /// 
        /// Return the path to the current working file or the file prompt ("*.xps"). 
        /// 
        [PreserveSig]
        int IPersistFile.GetCurFile(out string ppszFileName)
        { 
            ppszFileName = null;
 
            if (_filter == null || _xpsFileName == null) 
            {
                ppszFileName = "*." + PackagingUtilities.ContainerFileExtension; 
                return NativeMethods.S_FALSE;
            }

            ppszFileName = _xpsFileName; 
            return NativeMethods.S_OK;
        } 
 
        /// 
        /// Checks an object for changes since it was last saved to its current file. 
        /// 
        /// 
        /// S_OK if the file has changed since it was last saved;
        /// S_FALSE if the file has not changed since it was last saved. 
        /// 
        ///  
        /// Since the file is accessed only for reading, this function always returns S_FALSE. 
        /// 
        [PreserveSig] 
        int IPersistFile.IsDirty()
        {
            return NativeMethods.S_FALSE;
        } 

        ///  
        /// Opens the specified file with the specified mode.. 
        /// This can return any of the STG_E_* error codes, along
        /// with S_OK, E_OUTOFMEMORY, and E_FAIL. 
        /// 
        /// 
        /// A zero-terminated string containing the absolute path of the file to open.
        ///  
        /// The mode in which to open pszFileName. 
        void IPersistFile.Load(string pszFileName, int dwMode) 
        { 
            FileMode fileMode;
            FileAccess fileAccess; 
            FileShare fileSharing;

            // Check argument.
            if (pszFileName == null || pszFileName == String.Empty) 
            {
                throw new ArgumentException(SR.Get(SRID.FileNameNullOrEmpty), "pszFileName"); 
            } 

            // Convert mode information in flag. 
            switch ((STGM_FLAGS)(dwMode & (int)STGM_FLAGS.MODE))
            {
                case STGM_FLAGS.CREATE:
                    throw new ArgumentException(SR.Get(SRID.FilterLoadInvalidModeFlag), "dwMode"); 

                default: 
                    fileMode = FileMode.Open; 
                    break;
            } 

            // Convert access flag.
            switch ((STGM_FLAGS)(dwMode & (int)STGM_FLAGS.ACCESS))
            { 
                case STGM_FLAGS.READ:
                case STGM_FLAGS.READWRITE: 
                    fileAccess = FileAccess.Read; 
                    break;
 
                default:
                    throw new ArgumentException(SR.Get(SRID.FilterLoadInvalidModeFlag), "dwMode");
            }
 
            // Sharing flags are ignored. Since managed filters do not have the equivalent
            // of a destructor to release locks on files as soon as they get disposed of from 
            // unmanaged code, the option taken is not to lock at all while filtering. 
            // (See call to FileToStream further down.)
            fileSharing = FileShare.ReadWrite; 

            // Only one of _package and _encryptedPackage can be non-null at a time.
            Invariant.Assert(_package == null || _encryptedPackage == null);
 
            // If there has been a previous call to Load, reinitialize everything.
            // Note closing a closed stream does not cause any exception. 
            ReleaseResources(); 

            _filter = null; 
            _xpsFileName = null;

            bool encrypted = EncryptedPackageEnvelope.IsEncryptedPackageEnvelope(pszFileName);
 
            try
            { 
                // opens to MemoryStream or just returns FileStream if file exceeds _maxMemoryStreamBuffer 
                _packageStream = FileToStream(pszFileName, fileMode, fileAccess, fileSharing, _maxMemoryStreamBuffer);
 
                if (encrypted)
                {
                    // Open the encrypted package.
                    _encryptedPackage = EncryptedPackageEnvelope.Open(_packageStream); 
                    _filter = new EncryptedPackageFilter(_encryptedPackage);
                } 
                else 
                {
                    // Open the package. 
                    _package = Package.Open(_packageStream);
                    _filter = new PackageFilter(_package);
                }
            } 
            catch (IOException ex)
            { 
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
            catch (FileFormatException ex) 
            {
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_UNKNOWNFORMAT);
            }
            finally 
            {
                // failure? 
                if (_filter == null) 
                {
                    // clean up 
                    ReleaseResources();
                }

            } 

            _xpsFileName = pszFileName; 
        } 

        ///  
        /// Saves a copy of the object into the specified file.
        /// 
        /// 
        /// A zero-terminated string containing the absolute path 
        /// of the file to which the object is saved.
        ///  
        ///  
        /// Indicates whether pszFileName is to be used as the current working file.
        ///  
        /// 
        /// On the odd chance that this link is still valid when it's needed,
        /// expected error codes are described at
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/da9581e8-98c7-4592-8ee1-a1bc8232635b.asp 
        /// 
        void IPersistFile.Save(string pszFileName, bool fRemember) 
        { 
            throw new COMException(SR.Get(SRID.FilterIPersistFileIsReadOnly), NativeMethods.STG_E_CANTSAVE);
        } 

        /// 
        /// Notifies the object that it can write to its file.
        ///  
        /// 
        /// The absolute path of the file where the object was previously saved. 
        ///  
        /// 
        /// On the odd chance that this link is still valid when it's needed, 
        /// expected error codes are described at
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/da9581e8-98c7-4592-8ee1-a1bc8232635b.asp
        /// This function should always return S_OK when Save is not supported.
        ///  
        void IPersistFile.SaveCompleted(string pszFileName)
        { 
            return; // return S_OK 
        }
 
        #endregion IPersistFile methods

        #region IPersistStream methods
 
        /// 
        /// Return the CLSID for the XAML filtering component. 
        ///  
        /// On successful return, a reference to the CLSID.
        void IPersistStream.GetClassID(out Guid pClassID) 
        {
            pClassID = _filterClsid;
        }
 
        /// 
        /// Checks an object for changes since it was last saved to its current file. 
        ///  
        /// 
        /// S_OK if the file has changed since it was last saved; 
        /// S_FALSE if the file has not changed since it was last saved.
        /// 
        /// 
        /// Since the file is accessed only for reading, this function always returns S_FALSE. 
        /// 
        [PreserveSig] 
        int IPersistStream.IsDirty() 
        {
            return NativeMethods.S_FALSE; 
        }

        /// 
        /// Retrieve the container on the specified IStream. 
        /// 
        /// The OLE stream from which the container's contents are to be read. 
        ///  
        /// The interface implemented by 'stream' is defined in
        /// MS.Internal.Interop.IStream rather than the standard 
        /// managed so as to allow optimized marshaling in UnsafeIndexingFilterStream.
        /// 
        /// 
        ///     Critical: This method accesses a class - UnsafeIndexingFilterStream which calls into 
        ///               unmanaged code which provides a managed Stream like interface for an
        ///               unmanaged OLE IStream. 
        ///               This method is only called by unmanaged callers. 
        ///               There is no elevation of privilege in this method.
        ///  
        [SecurityCritical]
        void IPersistStream.Load(MS.Internal.Interop.IStream stream)
        {
            // Check argument. 
            if (stream == null)
            { 
                throw new ArgumentNullException("stream"); 
            }
 
            // Only one of _package and _encryptedPackage can be non-null at a time.
            Invariant.Assert(_package == null || _encryptedPackage == null);

            // If there has been a previous call to Load, reinitialize everything. 
            // Note closing a closed stream does not cause any exception.
            ReleaseResources(); 
 
            _filter = null;
            _xpsFileName = null; 

            try
            {
                _packageStream = new UnsafeIndexingFilterStream(stream); 

                // different filter for encrypted package 
                if (EncryptedPackageEnvelope.IsEncryptedPackageEnvelope(_packageStream)) 
                {
                    // Open the encrypted package. 
                    _encryptedPackage = EncryptedPackageEnvelope.Open(_packageStream);
                    _filter = new EncryptedPackageFilter(_encryptedPackage);
                }
                else 
                {
                    // Open the package. 
                    _package = Package.Open(_packageStream); 
                    _filter = new PackageFilter(_package);
                } 
            }
            catch (IOException ex)
            {
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_ACCESS); 
            }
            catch (Exception ex) 
            { 
                throw new COMException(ex.Message, (int)FilterErrorCode.FILTER_E_UNKNOWNFORMAT);
            } 
            finally
            {
                // clean-up if we failed
                if (_filter == null) 
                {
                    ReleaseResources(); 
                } 
            }
        } 

        /// 
        /// Saves a copy of the object into the specified stream.
        ///  
        /// The stream to which the object is saved. 
        /// Indicates whether the dirty state is to be cleared.  
        ///  
        /// Expected error codes are described at
        /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/com/html/b748b4f9-ef9c-486b-bdc4-4d23c4640ff7.asp 
        /// 
        void IPersistStream.Save(MS.Internal.Interop.IStream stream, bool fClearDirty)
        {
            throw new COMException(SR.Get(SRID.FilterIPersistStreamIsReadOnly), NativeMethods.STG_E_CANTSAVE); 
        }
 
        ///  
        /// The purpose of this function when implemented by a persistent object is to return
        /// the size in bytes of the stream needed to save the object. 
        /// Always returns COR_E_NOTSUPPORTED insofar as the filter does not use this interface for persistence.
        /// 
        void IPersistStream.GetSizeMax(out Int64 pcbSize)
        { 
            throw new NotSupportedException(SR.Get(SRID.FilterIPersistFileIsReadOnly));
        } 
 
        #endregion IPersistStream methods
 
        #region Private methods

        /// 
        /// Shared implementation for releasing package/encryptedPackage and underlying stream 
        /// 
        private void ReleaseResources() 
        { 
            if (_encryptedPackage != null)
            { 
                _encryptedPackage.Close();
                _encryptedPackage = null;
            }
            else if (_package != null) 
            {
                _package.Close(); 
                _package = null; 
            }
            if (_packageStream != null) 
            {
                _packageStream.Close();
                _packageStream = null;
            } 
        }
 
        ///  
        /// Auxiliary function of IPersistFile.Load.
        ///  
        /// 
        /// A MemoryStream of the package file or a FileStream if the file is too big.
        /// 
        ///  
        /// Use this method to load a package file completely to a memory buffer.
        /// After loading the file we can close the file, thus we can release the file 
        /// lock quickly. However, there is a size limit on the file. If the 
        /// file is too big (greater than _maxMemoryStreamBuffer), we cannot allow
        /// this method to consume too much memory. So we simply return the fileStream. 
        /// Mode, access and sharing have already been checked or adjusted and can be assumed
        /// to be compatible with the goal of reading from the file.
        /// 
        private static Stream FileToStream( 
            string filePath,
            FileMode fileMode, 
            FileAccess fileAccess, 
            FileShare fileSharing,
            long maxMemoryStream) 
        {
            FileInfo fi = new FileInfo(filePath);
            long byteCount = fi.Length;
            Stream s = new FileStream(filePath, fileMode, fileAccess, fileSharing); 

            // There is a size limit of the file that we allow to be uploaded to a 
            // memory stream. If the file size is bigger than the limit, simply return the fileStream. 
            if (byteCount < maxMemoryStream)
            { 
                // unchecked cast is safe because _maxMemoryStreamBuffer is less than Int32.Max
                MemoryStream ms = new MemoryStream(unchecked((int)byteCount));
                using (s)
                { 
                    PackagingUtilities.CopyStream(s, ms, byteCount, 0x1000);
                } 
                s = ms; 
            }
 
            return s;
        }
        #endregion Private methods
 
        #region Fields
 
        ///  
        /// CLSID for the XPS filter.
        ///  
        [ComVisible(false)]
        private static readonly Guid _filterClsid = new Guid(0x0B8732A6,
                                                    0xAF74,
                                                    0x498c, 
                                                    0xA2 , 0x51 ,
                                                    0x9D , 0xC8 , 0x6B , 0x05 , 0x38 , 0xB0); 
 
        /// 
        /// Internal IFilter implementation being used by XpsFilter. 
        /// This could be PackageFilter or EncryptedPackageFilter.
        /// 
        [ComVisible(false)]
        private IFilter _filter; 

        ///  
        /// If the XPS file/stream is a Package, reference to the Package. 
        /// 
        [ComVisible(false)] 
        private Package _package;

        /// 
        /// If the XPS file/stream is a EncryptedPackageEnvelope, reference to the EncryptedPackageEnvelope. 
        /// 
        [ComVisible(false)] 
        private EncryptedPackageEnvelope _encryptedPackage; 

        ///  
        /// If an XPS file is being filtered, refers to the file name.
        /// 
        [ComVisible(false)]
        private string _xpsFileName; 

        ///  
        /// Stream wrapper we have opened our Package or EncryptedPackage on 
        /// 
        [ComVisible(false)] 
        private Stream _packageStream;

        /// 
        /// Cache frequently used size values to incur reflection cost just once. 
        /// 
        [ComVisible(false)] 
        private const Int32 _int16Size = 2; 

        #region Constants 

        /// 
        /// The number of characters to copy in a chunk buffer is limited as a
        /// defense-in-depth device without any expected performance deterioration. 
        /// 
        [ComVisible(false)] 
        private const uint _maxTextBufferSizeInCharacters = 4096; 

 
        /// 
        /// The size of memory stream buffer used in FileToStream()
        /// should be limited. If the package file size is bigger than the limit,
        /// we cannot allow the buffer allocation, and return the fileStream itself. 
        /// 
        [ComVisible(false)] 
        private const Int32 _maxMemoryStreamBuffer = 1024 * 1024; 

        #endregion Constants 

        #endregion Fields
    }
 
    #endregion XpsFilter
} 

// 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