Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Framework / MS / Internal / IO / Packaging / XpsFilter.cs / 1 / 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; using MS.Internal.PresentationFramework; // for SecurityHelper 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. ////// 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 ///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. ////// 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 static readonly Int32 _int16Size = SecurityHelper.SizeOf(typeof(Int16)); #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; using MS.Internal.PresentationFramework; // for SecurityHelper 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. ////// 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 ///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. ////// 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 static readonly Int32 _int16Size = SecurityHelper.SizeOf(typeof(Int16)); #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
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataGridViewButtonColumn.cs
- Brush.cs
- PreProcessInputEventArgs.cs
- OverrideMode.cs
- recordstate.cs
- DynamicUpdateCommand.cs
- DesignerToolStripControlHost.cs
- ListControlDesigner.cs
- OptionalMessageQuery.cs
- NamespaceInfo.cs
- Action.cs
- AspNetSynchronizationContext.cs
- FunctionParameter.cs
- EnumerableCollectionView.cs
- XamlPointCollectionSerializer.cs
- DialogResultConverter.cs
- ReliableChannelFactory.cs
- SpellerInterop.cs
- Delegate.cs
- TailPinnedEventArgs.cs
- ClientTargetSection.cs
- WindowsSysHeader.cs
- XmlSchemaCompilationSettings.cs
- MimeObjectFactory.cs
- SynchronizationLockException.cs
- MetadataCollection.cs
- FloatAverageAggregationOperator.cs
- UITypeEditor.cs
- TreeNodeBindingCollection.cs
- WebPartExportVerb.cs
- MappingItemCollection.cs
- Column.cs
- WhiteSpaceTrimStringConverter.cs
- SoapInteropTypes.cs
- SoapReflector.cs
- ProgressBarHighlightConverter.cs
- ScriptComponentDescriptor.cs
- OdbcReferenceCollection.cs
- XsltInput.cs
- QueryCursorEventArgs.cs
- SimpleHandlerFactory.cs
- Source.cs
- SpecialFolderEnumConverter.cs
- ClientProtocol.cs
- MatrixAnimationUsingPath.cs
- OutputCacheProfile.cs
- SQLResource.cs
- CodeObjectCreateExpression.cs
- DbProviderServices.cs
- ProxyDataContractResolver.cs
- DataRelationCollection.cs
- BaseParaClient.cs
- SystemColorTracker.cs
- RecordConverter.cs
- SafePEFileHandle.cs
- AnnouncementEndpoint.cs
- Volatile.cs
- TimeIntervalCollection.cs
- DataContractSet.cs
- BitmapPalettes.cs
- OutputCacheModule.cs
- TextFormatterImp.cs
- DeleteMemberBinder.cs
- Lock.cs
- TimelineGroup.cs
- KeyTime.cs
- TitleStyle.cs
- TokenBasedSet.cs
- StringComparer.cs
- OdbcInfoMessageEvent.cs
- NavigateEvent.cs
- DataGridViewComboBoxCell.cs
- MD5.cs
- EntityTypeEmitter.cs
- CodeNamespaceImport.cs
- XmlBuffer.cs
- SqlClientWrapperSmiStreamChars.cs
- AttributeExtensions.cs
- LicenseContext.cs
- ProtectedConfiguration.cs
- WindowsAltTab.cs
- HebrewCalendar.cs
- DependencyPropertyAttribute.cs
- IdentityHolder.cs
- TimeSpanConverter.cs
- PageWrapper.cs
- WindowsListViewGroup.cs
- ResourcesChangeInfo.cs
- util.cs
- SqlClientWrapperSmiStream.cs
- KeyConverter.cs
- _TLSstream.cs
- ButtonBaseAutomationPeer.cs
- basevalidator.cs
- SetterBase.cs
- Rule.cs
- RectConverter.cs
- AnchoredBlock.cs
- StrongNameMembershipCondition.cs
- XmlDataProvider.cs