StorageInfo.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Base / System / IO / Packaging / CompoundFile / StorageInfo.cs / 1 / StorageInfo.cs

                            //------------------------------------------------------------------------------ 
//
// 
//    Copyright (C) Microsoft Corporation.  All rights reserved.
//  
//
// Description: 
//   Class for manipulating storages in the container file. 
//
// History: 
//  05/13/2002: [....]: Initial creation.
//  06/25/2002: [....]: Data space support.
//  07/31/2002: [....]: Make obvious that we are using security suppressed interfaces.
//  05/19/2003: [....]: Port to WCP tree. 
//  05/28/2003: [....]: Added long name support
//  06/20/2003: [....]: GetStreams() and GetSubStorages() 
//  08/11/2003: [....]: Fix Bug 864168 (some of [....]'s bug fixes were lost 
//                          in port to WCP tree).
// 
//-----------------------------------------------------------------------------

using System;
using System.Collections; 
using System.ComponentModel; // For EditorBrowsable attribute
using System.Diagnostics; // For Assert 
using System.Security; 
using System.Security.Permissions;
using System.IO; 
using System.Globalization;             //  CultureInfo.InvariantCulture


using System.Windows;                 //  SR.Get(SRID.[exception message]) 
using MS.Internal.IO.Packaging.CompoundFile;
using CU = MS.Internal.IO.Packaging.CompoundFile.ContainerUtilities; 
using MS.Internal; // for Invariant & CriticalExceptions 
using System.Runtime.InteropServices;        // COMException
 
#pragma warning disable 1634, 1691  // suppressing PreSharp warnings

namespace System.IO.Packaging
{ 
/// 
/// This class holds the core information for a StorageInfo object. 
///  
internal class StorageInfoCore
{ 
    internal StorageInfoCore(
        string nameStorage
            ) : this( nameStorage, null ) {;}
 
    internal StorageInfoCore(
        string nameStorage, 
        IStorage storage ) 
    {
        storageName = nameStorage; 
        safeIStorage = storage;
        validEnumerators = new Hashtable();
        // Storage and Stream names: we preserve casing, but do case-insensitive comparison (Native CompoundFile API behavior)
        elementInfoCores = new Hashtable(CU.StringCaseInsensitiveComparer); 
    }
 
    ///  
    /// The compound-file friendly version name.
    ///  
    internal string   storageName;

    /// 
    /// A reference to "this" storage.  This value is non-null only when 
    /// 1) The storage exists
    /// 2) We had reason to open it. 
    /// The value may be null even when the storage exists because we may not 
    /// have need to go and open it.
    ///  
    internal IStorage safeIStorage;

    /// 
    /// We keep track of the enumerator objects that we've handed out. 
    /// If anything is changed in this storage, we go and invalidate all of
    /// them and clear the list. 
    /// 
    /// In theory a simple ListDictionary class is more efficient than using
    /// a Hashtable class.  But since we're bringing in the code for the 
    /// HashTable class anyway, the savings of using ListDictionary went away.
    /// So even with a maximum of three elements, we use a Hashtable.
    /// 
    internal Hashtable validEnumerators; 

    ///  
    /// This hash table holds the standing "core" objects for its child 
    /// elements.  Each element may be a StorageInfoCore or a
    /// StreamInfoCore. 
    /// 
    internal Hashtable elementInfoCores;
}
 
/// 
/// Class for manipulating storages in the container file 
///  
public class StorageInfo
{ 
    /***********************************************************************/
    // Instance values

    ///  
    /// Each storage holds a reference to its parent, this way even if the
    /// client app releases the reference it'll be kept in the reference graph 
    /// to avoid getting prematurely garbage-collected. 
    ///
    /// The only time this is allowed to be null is when this storage is the 
    /// root storage.
    /// 
    StorageInfo parentStorage;
 
    /// 
    /// Each storage holds a reference to the container root.  This value will 
    /// be equal to null for the container root. 
    /// 
    StorageRoot rootStorage; 

    /// 
    /// There is one StorageInfoCore object per underlying IStorage. If
    /// multiple StorageInfo objects are created that point to the same 
    /// underlying storage, they share the same StorageInfoCore object.
    /// These are maintained by the parent storage for all its child 
    /// storages, with the exception of the root StorageInfo which keeps 
    /// its own instance in StorageRoot.
    ///  
    internal StorageInfoCore core;

    // Instance name for the compression transform
    private static readonly string sc_compressionTransformName = "CompressionTransform"; 

    //Dataspace label definitions for compression and encryption combinations while creating a stream 
    private static readonly string sc_dataspaceLabelNoEncryptionNormalCompression = "NoEncryptionNormalCompression"; 
    private static readonly string sc_dataspaceLabelRMEncryptionNormalCompression = "RMEncryptionNormalCompression";
 
    /// 
    /// We can have three valid enumerator types.
    /// 
    private enum EnumeratorTypes 
    {
        Everything, 
        OnlyStorages, 
        OnlyStreams
    } 

    /***********************************************************************/
    // Constructors
 
    /// 
    /// A constructor for building the root storage. 
    /// This should only happen for, well, the root storage! 
    /// 
    internal StorageInfo( IStorage safeIStorage ) 
    {
        core = new StorageInfoCore( null, safeIStorage );
    }
 
    /// 
    ///     Given a parent and a path under it, step through each of the path 
    /// elements and create an intermediate StorageInfo at each step because 
    /// a StorageInfo is only meaningful relative to its immediate parent -
    /// multi-step relations can't be represented. 
    /// 
    private void BuildStorageInfoRelativeToStorage( StorageInfo parent, string fileName )
    {
 
        parentStorage = parent;
        core = parent.CoreForChildStorage( fileName ); 
        rootStorage = parent.Root; 
    }
 
    /// 
    /// Constructor for a StorageInfo given a parent StorageInfo and a name
    /// 
    /// Reference to the parent storage 
    /// filename for the new StorageInfo
    internal StorageInfo( StorageInfo parent, string fileName ) 
    { 
        CU.CheckAgainstNull( parent, "parent" );
        CU.CheckAgainstNull( fileName, "fileName" ); 

        BuildStorageInfoRelativeToStorage( parent,  fileName );
    }
 
    /// 
    /// Respond to a request from a child StorageInfo object to give 
    /// it a StorageInfoCore object for the given name. 
    /// 
    StorageInfoCore CoreForChildStorage( string storageNname ) 
    {
        CheckDisposedStatus();

        object childElement = core.elementInfoCores[ storageNname ]; 

        if( null != childElement && 
            null == childElement as StorageInfoCore ) 
        {
            // Name is already in use, but not as a StorageInfo 
            throw new InvalidOperationException(
                SR.Get(SRID.NameAlreadyInUse, storageNname ));
        }
        else if( null == childElement ) 
        {
            // No such element with the name exist - create one. 
            // 
            childElement = new StorageInfoCore( storageNname);
            core.elementInfoCores[ storageNname ] = childElement; 
        }

        Debug.Assert( null != childElement as StorageInfoCore,
            "We should have already checked to make sure childElement is either StorageInfoCore, created as one, or thrown an exception if neither is possible"); 

        return childElement as StorageInfoCore; 
    } 

    internal StreamInfoCore CoreForChildStream( string streamName ) 
    {
        CheckDisposedStatus();

        object childElement = core.elementInfoCores[ streamName ]; 

        if( null != childElement && 
            null == childElement as StreamInfoCore ) 
        {
            // Name is already in use, but not as a StreamInfo 
            throw new InvalidOperationException(
                SR.Get(SRID.NameAlreadyInUse, streamName ));
        }
        else if( null == childElement ) 
        {
            // No such element with the name exist - create one. 
            // 

            // Check to see if there is a data space mapping on this guy 
            DataSpaceManager manager = Root.GetDataSpaceManager();
            if( null != manager )
            {
                // Have data space manager - retrieve data space label for 
                //  the child stream.  The data space manager will return
                //  null if there isn't a data space associated with this 
                //  stream. 
                childElement = new StreamInfoCore(
                    streamName, 
                    manager.DataSpaceOf(
                        new CompoundFileStreamReference( FullNameInternal, streamName ) ) );
            }
            else 
            {
                // Data space manager not yet initialized - correct behavior 
                //  is that nothing transformed should be required at this 
                //  point but in case of incorrect behavior we can not possibly
                //  recover.  User gets un-transformed data instead. 
                childElement = new StreamInfoCore( streamName, null, null );
            }
            core.elementInfoCores[ streamName ] = childElement;
        } 

        Debug.Assert( null != childElement as StreamInfoCore, 
            "We should have already checked to make sure childElement is either StreamInfoCore, created as one, or thrown an exception if neither is possible"); 

        return childElement as StreamInfoCore; 
    }

    /***********************************************************************/
    // public Properties 

    ///  
    /// The name for this storage 
    /// 
    public string Name 
    {
        get
        {
            CheckDisposedStatus(); 
            return core.storageName;
        } 
    } 

    /***********************************************************************/ 
    // public Methods

    /// 
    /// Creates "this" stream 
    /// 
    /// Name of stream 
    /// CompressionOptiont 
    /// EncryptionOption
    /// Reference to new stream 
    public StreamInfo CreateStream( string name, CompressionOption compressionOption, EncryptionOption encryptionOption )
    {
        CheckDisposedStatus();
 
        //check the arguments
        if( null == name ) 
            throw new ArgumentNullException("name"); 

        // Stream names: we preserve casing, but do case-insensitive comparison (Native CompoundFile API behavior) 
        if (((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(name,
                    EncryptedPackageEnvelope.PackageStreamName))
            throw new ArgumentException(SR.Get(SRID.StreamNameNotValid,name));
 
        //create a new streaminfo object
        StreamInfo streamInfo = new StreamInfo(this, name, compressionOption, encryptionOption); 
        if (streamInfo.InternalExists()) 
        {
            throw new IOException(SR.Get(SRID.StreamAlreadyExist)); 
        }

        //Define the compression and encryption options in the dataspacemanager
        DataSpaceManager manager = Root.GetDataSpaceManager(); 
        string dataSpaceLabel = null;
 
        if (manager != null) 
        {
            //case : Compression option is set. Stream need to be compressed. Define compression transform. 
            //At this time, we only treat CompressionOption - Normal and None. The rest are treated as Normal
            if (compressionOption != CompressionOption.NotCompressed)
            {
                //If it is not defined already, define it. 
                if (!manager.TransformLabelIsDefined(sc_compressionTransformName))
                        manager.DefineTransform(CompressionTransform.ClassTransformIdentifier, sc_compressionTransformName); 
            } 
             //case : Encryption option is set. Stream need to be encrypted. Define encryption transform.
            if (encryptionOption == EncryptionOption.RightsManagement) 
            {
                //If it not defined already, define it.
                if (!manager.TransformLabelIsDefined(EncryptedPackageEnvelope.EncryptionTransformName))
                { 
                    //We really cannot define RM transform completely here because the transform initialization cannot be done here without publishlicense and cryptoprovider.
                    //However it will always be defined because this method is accessed only through an EncryptedPackageEnvelope and RM transform is always defined in EncryptedPackageEnvelope.Create() 
                    throw new SystemException(SR.Get(SRID.RightsManagementEncryptionTransformNotFound)); 
                }
            } 

            //Now find the dataspace label that we need to define these transforms in.
            //CASE: When both CompressionOption and EncryptionOption are set
            if ( (compressionOption != CompressionOption.NotCompressed) && (encryptionOption == EncryptionOption.RightsManagement) ) 
            {
                dataSpaceLabel = sc_dataspaceLabelRMEncryptionNormalCompression; 
                if (!manager.DataSpaceIsDefined(dataSpaceLabel)) 
                {
                    string[] transformStack = new string[2]; 
                    //compress the data first. then encrypt it. This ordering will cause the content to be compressed, then encrypted, then written to the stream.
                    transformStack[0] = EncryptedPackageEnvelope.EncryptionTransformName;
                    transformStack[1] = sc_compressionTransformName;
 
                    manager.DefineDataSpace(transformStack, dataSpaceLabel);
                } 
            } 
            //CASE : when CompressionOption alone is set
            else if ( (compressionOption != CompressionOption.NotCompressed)  && (encryptionOption == EncryptionOption.None) ) 
            {
                dataSpaceLabel = sc_dataspaceLabelNoEncryptionNormalCompression;
                if (!manager.DataSpaceIsDefined(dataSpaceLabel))
                { 
                    string[] transformStack = new string[1];
                    transformStack[0] = sc_compressionTransformName; 
 
                    manager.DefineDataSpace(transformStack, dataSpaceLabel);
                } 
            }
            //CASE : when EncryptionOption alone is set
            else if (encryptionOption == EncryptionOption.RightsManagement)
            { 
                dataSpaceLabel = EncryptedPackageEnvelope.DataspaceLabelRMEncryptionNoCompression;
                if (!manager.DataSpaceIsDefined(dataSpaceLabel)) 
                { 
                    string[] transformStack = new string[1];
                    transformStack[0] = EncryptedPackageEnvelope.EncryptionTransformName; 

                    manager.DefineDataSpace(transformStack, dataSpaceLabel);
                }
            } 
            //All the other cases are not handled at this point.
        } 
 
        //create the underlying stream
        if (null == dataSpaceLabel) 
            streamInfo.Create(); //create the stream with default parameters
        else
            streamInfo.Create(dataSpaceLabel); //create the stream in the defined dataspace
 
        return streamInfo;
    } 
 
    /// 
    /// Creates "this" stream 
    /// 
    /// Name of stream
    /// Reference to new stream
    public StreamInfo CreateStream( string name ) 
    {
        //create the stream with out any compression or encryption options. 
        return CreateStream(name,CompressionOption.NotCompressed,EncryptionOption.None); 
    }
 
    /// 
    /// Returns the streaminfo by the passed name.
    /// 
    /// Name of stream 
    /// Reference to the stream
    public StreamInfo GetStreamInfo(string name) 
    { 
        CheckDisposedStatus();
 
         //check the arguments
        if( null == name )
            throw new ArgumentNullException("name");
 
        StreamInfo streamInfo = new StreamInfo(this, name);
        if (streamInfo.InternalExists()) 
        { 
            return streamInfo;
        } 
        else
        {
            throw new IOException(SR.Get(SRID.StreamNotExist));
        } 
    }
 
    ///  
    /// Check if the stream exists.
    ///  
    /// Name of stream
    /// True if exists, False if not
    public bool StreamExists(string name)
    { 
        CheckDisposedStatus();
 
        bool streamExists = false; 

        StreamInfo streamInfo = new StreamInfo(this, name); 
        streamExists = streamInfo.InternalExists();

        return streamExists;
    } 

    ///  
    /// Deleted the stream with the passed name. 
    /// 
    /// Name of stream 
    public void DeleteStream(string name)
    {
        CheckDisposedStatus();
 
         //check the arguments
        if( null == name ) 
            throw new ArgumentNullException("name"); 

        StreamInfo streamInfo = new StreamInfo(this, name); 
        if (streamInfo.InternalExists())
        {
            streamInfo.Delete();
        } 
    }
 
    ///  
    /// Creates a storage using "this" one as parent
    ///  
    /// Name of new storage
    /// Reference to new storage
    public StorageInfo CreateSubStorage( string name )
    { 
        CheckDisposedStatus();
 
         //check the arguments 
        if( null == name )
            throw new ArgumentNullException("name"); 

        return CreateStorage(name);
    }
 
    /// 
    /// Returns the storage by the passed name. 
    ///  
    /// Name of storage
    /// Reference to the storage 
    public StorageInfo GetSubStorageInfo(string name)
    {
        //Find if this storage exists
        StorageInfo storageInfo = new StorageInfo(this, name); 

        if (storageInfo.InternalExists(name)) 
        { 
            return storageInfo;
        } 
        else
        {
            throw new IOException(SR.Get(SRID.StorageNotExist));
        } 
    }
 
     ///  
    /// Checks if a storage exists by the passed name.
    ///  
    /// Name of storage
    /// Reference to new storage
    public bool SubStorageExists(string name)
    { 
        StorageInfo storageInfo = new StorageInfo(this, name);
        return storageInfo.InternalExists(name); 
    } 

    ///  
    /// Deletes a storage recursively.
    /// 
    /// Name of storage
    public void DeleteSubStorage(string name) 
    {
        CheckDisposedStatus(); 
 
         //check the arguments
        if( null == name ) 
            throw new ArgumentNullException("name");

        StorageInfo storageInfo = new StorageInfo(this, name);
        if (storageInfo.InternalExists(name)) 
        {
            InvalidateEnumerators(); 
            // Go ahead and delete "this" storage 
            DestroyElement( name );
        } 
        //We will not throw exceptions if the storage does not exist. This is to be consistent with Package.DeletePart.
    }

    ///  
    /// Provides a snapshot picture of the streams currently within this storage
    /// object.  The array that returns will not be updated with additions/ 
    /// removals of streams, but the individual StreamInfo objects within will 
    /// reflect the state of their respective streams.
    ///  
    /// 
    /// This follows the precedent of DirectoryInfo.GetFiles()
    /// 
    ///  
    /// Array of StreamInfo objects, each pointing to a stream within this
    /// storage.  Empty (zero-length) array if there are no streams. 
    ///  
    public StreamInfo[] GetStreams()
    { 
        // Make sure 'this' storage is alive and well.
        CheckDisposedStatus();
        VerifyExists();
 
        // Build an array of StreamInfo objects
        EnsureArrayForEnumeration(EnumeratorTypes.OnlyStreams); 
 
        // Because we're handing out a snapshot, we can't simply hand out a
        //  reference to the core.validEnumerators array.  We need to make a 
        //  copy that has the references of the array.  This way the array
        //  we return will remain if we invalidate the arrays.
        // Fortunately ArrayList.ToArray makes a copy, perfect for our needs.
        ArrayList streamArray = 
            (ArrayList)core.validEnumerators[EnumeratorTypes.OnlyStreams];
 
        Invariant.Assert(streamArray  != null); 

        #pragma warning suppress 6506 // Invariant.Assert(streamArray  != null) 
        return (StreamInfo[])streamArray.ToArray(typeof(StreamInfo));
    }

    ///  
    /// Provides a snapshot picture of the sub-storages currently within this storage
    /// object.  The array that returns will not be updated with additions/ 
    /// removals of storages, but the individual StorageInfo objects within will 
    /// reflect the state of their respective sub-storages.
    ///  
    /// 
    /// This follows the precedent of DirectoryInfo.GetDirectories()
    /// 
    ///  
    /// Array of StorageInfo objects, each pointing to a sub-storage within this
    /// storage.  Empty (zero-length) array if there are no sub-storages. 
    ///  
    public StorageInfo[] GetSubStorages()
    { 
        // Make sure 'this' storage is alive and well.
        CheckDisposedStatus();
        VerifyExists();
 
        // See GetStreams counterpart for details.
        EnsureArrayForEnumeration(EnumeratorTypes.OnlyStorages); 
        ArrayList storageArray = 
            (ArrayList)core.validEnumerators[EnumeratorTypes.OnlyStorages];
 
        Invariant.Assert(storageArray != null);

        #pragma warning suppress 6506 // Invariant.Assert(streamArray  != null)
        return (StorageInfo[])storageArray.ToArray(typeof(StorageInfo)); 
    }
 
    /***********************************************************************/ 
    // Internal/Private functionality
 
    internal string FullNameInternal
    {
        get
        { 
            CheckDisposedStatus();
            return CU.ConvertStringArrayPathToBackSlashPath(BuildFullNameInternalFromParentNameInternal()); 
        } 
    }
 
    /// 
    /// Get a reference to the container instance
    /// 
    internal StorageRoot Root 
    {
        get 
        { 
            CheckDisposedStatus();
            if( null == rootStorage ) 
                return (StorageRoot)this;
            else
                return rootStorage;
        } 
    }
 
    ///  
    /// Because it is valid to have a StorageInfo point to a storage that
    /// doesn't yet actually exist, use this to see if it does. 
    /// 
    internal bool Exists
    {
        get 
        {
            CheckDisposedStatus(); 
            return InternalExists(); 
        }
    } 

    /// 
    /// Creates "this" storage
    ///  
    internal void Create()
    { 
        CheckDisposedStatus(); 
        if( null != parentStorage ) // Only root storage has null parentStorage
        { 
            // Root storage always exists so we don't do any of this
            //  if we're not the root.

            if( !parentStorage.Exists ) 
            {
                // We need the parent to exist before we can exist. 
                parentStorage.Create(); 
            }
 
            if( !InternalExists() )
            {
                // If we don't exist, then ask parent to create us.
                parentStorage.CreateStorage( core.storageName ); 
            }
            //else if we already exist, we're done here. 
        } 
    }
 
    private StorageInfo CreateStorage(string name)
    {
        // Create new StorageInfo
        StorageInfo newSubStorage = new StorageInfo( this, name ); 

        // Make it real 
        if( !newSubStorage.InternalExists(name) ) 
        {
            /* TBD 
            if( !CU.IsValidCompoundFileName(name))
            {
                throw new IOException(
                        SR.Get(SRID.UnableToCreateStorage), 
                        new COMException(
                            SR.Get(SRID.NamedAPIFailure, "IStorage.CreateStorage"), 
                            nativeCallErrorCode )); 
                }
                */ 
            // It doesn't already exist, please create.
            StorageInfoCore newStorage = core.elementInfoCores[ name ] as StorageInfoCore;
            Invariant.Assert( null != newStorage);
 
            int nativeCallErrorCode = core.safeIStorage.CreateStorage(
                        name, 
                    (GetStat().grfMode & SafeNativeCompoundFileConstants.STGM_READWRITE_Bits) 
                        | SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE,
                    0, 
                    0,
                #pragma warning suppress 6506 // Invariant.Assert(null != newStorage)
                out newStorage.safeIStorage );
            if( SafeNativeCompoundFileConstants.S_OK != nativeCallErrorCode ) 
            {
                if( nativeCallErrorCode == SafeNativeCompoundFileConstants.STG_E_ACCESSDENIED ) 
                { 
                    throw new UnauthorizedAccessException(
                            SR.Get(SRID.CanNotCreateAccessDenied), 
                            new COMException(
                            SR.Get(SRID.NamedAPIFailure, "IStorage.CreateStorage"),
                            nativeCallErrorCode ));
                } 
                else
                { 
                    throw new IOException( 
                        SR.Get(SRID.UnableToCreateStorage),
                        new COMException( 
                            SR.Get(SRID.NamedAPIFailure, "IStorage.CreateStorage"),
                            nativeCallErrorCode ));
                }
            } 

            // Invalidate enumerators 
            InvalidateEnumerators(); 
        }
        else 
        {
            throw new IOException(SR.Get(SRID.StorageAlreadyExist));
        }
 
        // Return a reference
        return newSubStorage; 
    } 

    ///  
    /// Deletes a storage, recursively if specified.
    /// 
    /// Whether to recursive delete all existing content
    /// Name of storage 
    internal bool Delete( bool recursive , string name)
    { 
        bool storageDeleted = false; 
        CheckDisposedStatus();
        if( null == parentStorage ) 
        {
            // We are the root storage, you can't "delete" the root storage!
            throw new InvalidOperationException(
                SR.Get(SRID.CanNotDeleteRoot)); 
        }
 
        if( InternalExists(name) ) 
        {
            if( !recursive && !StorageIsEmpty()) 
            {
                throw new IOException(
                    SR.Get(SRID.CanNotDeleteNonEmptyStorage));
            } 

            InvalidateEnumerators(); 
            // Go ahead and delete "this" storage 
            parentStorage.DestroyElement( name );
            storageDeleted = true; 
        }
        //We will not throw exceptions if the storage does not exist. This is to be consistent with Package.DeletePart.

        return storageDeleted; 
    }
 
    ///  
    /// When a substorage is getting deleted, its references in the dataspacemanager's transform definition are removed.
    /// This is called recursively because the DeleteSubStorage deletes all its children by default. 
    /// 
    internal void RemoveSubStorageEntryFromDataSpaceMap(StorageInfo storageInfo)
    {
        StorageInfo[] subStorages = storageInfo.GetSubStorages(); 
        foreach(StorageInfo storage in subStorages)
        { 
            //If this is a storage, call recursively till we encounter a stream. Then we can use that container (storage,stream) reference to remove from the 
            // dataspace manager's data space map.
            RemoveSubStorageEntryFromDataSpaceMap(storage); //recursive call 
        }

        //Now we have StorageInfo. Find if there is a stream underneath so a container can be used as reference in data space map of data spacemanager.
        StreamInfo[] streams = storageInfo.GetStreams(); 
        DataSpaceManager manager = Root.GetDataSpaceManager();
        foreach(StreamInfo stream in streams) 
        { 
            manager.RemoveContainerFromDataSpaceMap(new CompoundFileStreamReference( storageInfo.FullNameInternal, stream.Name ));
        } 
    }

    /// 
    /// Destroys an element and removes the references ued internally. 
    /// 
    internal void DestroyElement( string elementNameInternal ) 
    { 
        object deadElementWalking = core.elementInfoCores[ elementNameInternal ];
        // It's an internal error if we try to call this without first 
        //  verifying that it is indeed there.
        Debug.Assert( null != deadElementWalking,
            "Caller should have already verified that there's something to delete.");
 
        // Can't delete if we're in read-only mode.  This catches some but not
        //  all invalid delete scenarios - anything else would come back as a 
        //  COMException of some kind that will be caught and wrapped in an 
        //  IOException in the try/catch below.
        if( FileAccess.Read == Root.OpenAccess ) 
        {
            throw new UnauthorizedAccessException(
                SR.Get(SRID.CanNotDeleteInReadOnly));
        } 

        //Clean out the entry in dataspacemanager for stream transforms 
        DataSpaceManager manager = Root.GetDataSpaceManager(); 
        if( null != manager )
        { 
             if( deadElementWalking is StorageInfoCore )
            {
                //if the element getting deleted is a storage, make sure to delete all its children's references.
                string name = ((StorageInfoCore)deadElementWalking).storageName; 
                StorageInfo stInfo = new StorageInfo(this, name);
                RemoveSubStorageEntryFromDataSpaceMap(stInfo); 
            } 
            else if( deadElementWalking is StreamInfoCore )
            { 
                //if the element getting deleted is a stream, the container reference should be removed from dataspacemap of dataspace manager.
                manager.RemoveContainerFromDataSpaceMap(new CompoundFileStreamReference( FullNameInternal, elementNameInternal ));
            }
        } 

        // Make the call to the underlying OLE mechanism to remove the element. 
        try 
        {
            core.safeIStorage.DestroyElement( elementNameInternal ); 
        }
        catch( COMException e )
        {
            if( e.ErrorCode == SafeNativeCompoundFileConstants.STG_E_ACCESSDENIED ) 
            {
                throw new UnauthorizedAccessException( 
                    SR.Get(SRID.CanNotDeleteAccessDenied), 
                    e );
            } 
            else
            {
                throw new IOException(
                    SR.Get(SRID.CanNotDelete), 
                    e );
            } 
        } 

        // Invalidate enumerators 
        InvalidateEnumerators();

        // Remove the now-meaningless name, which also signifies disposed status.
        if( deadElementWalking is StorageInfoCore ) 
        {
            StorageInfoCore deadStorageInfoCore = (StorageInfoCore)deadElementWalking; 
 
            // Erase this storage's existence
            deadStorageInfoCore.storageName = null; 
            if( null != deadStorageInfoCore.safeIStorage )
            {
                ((IDisposable) deadStorageInfoCore.safeIStorage).Dispose();
                deadStorageInfoCore.safeIStorage = null; 
            }
        } 
        else if( deadElementWalking is StreamInfoCore ) 
        {
            StreamInfoCore deadStreamInfoCore = (StreamInfoCore)deadElementWalking; 

            // Erase this stream's existence
            deadStreamInfoCore.streamName = null;
 
            try
            { 
                if (null != deadStreamInfoCore.exposedStream) 
                {
                    ((Stream)(deadStreamInfoCore.exposedStream)).Close(); 
                }
            }
            catch(Exception e)
            { 
                if(CriticalExceptions.IsCriticalException(e))
                { 
                    // PreSharp Warning 56500 
                    throw;
                } 
                else
                {
                    // We don't care if there are any issues -
                    //  the user wanted this stream gone anyway. 
                }
            } 
 
            deadStreamInfoCore.exposedStream = null;
 
            if( null != deadStreamInfoCore.safeIStream )
            {
                ((IDisposable) deadStreamInfoCore.safeIStream).Dispose();
                deadStreamInfoCore.safeIStream = null; 
            }
        } 
 
        // Remove reference for destroyed element
        core.elementInfoCores.Remove(elementNameInternal); 
    }
    /// 
    /// Looks for a storage element with the given name, retrieves its
    /// STATSTG if found. 
    /// 
    /// Name to look for in this storage 
    /// If found, a copy of STATSTG for it 
    /// true if found
    internal bool FindStatStgOfName( string streamName, out System.Runtime.InteropServices.ComTypes.STATSTG statStg ) 
    {
        bool nameFound = false;
        UInt32 actual;
        IEnumSTATSTG safeIEnumSTATSTG = null; 

        // Set up IEnumSTATSTG 
        core.safeIStorage.EnumElements( 
            0,
            IntPtr.Zero, 
            0,
            out safeIEnumSTATSTG );
        safeIEnumSTATSTG.Reset();
        safeIEnumSTATSTG.Next( 1, out statStg, out actual ); 

        // Loop and get everything 
        while( 0 < actual && !nameFound ) 
        {
            // Stream names: we preserve casing, but do case-insensitive comparison (Native CompoundFile API behavior) 
            if(((IEqualityComparer) CU.StringCaseInsensitiveComparer).Equals(streamName,
                                            statStg.pwcsName))
            {
                nameFound = true; 
            }
            else 
            { 
                // Move on to the next element
                safeIEnumSTATSTG.Next( 1, out statStg, out actual ); 
            }
        }

        // Release enumerator 
        ((IDisposable) safeIEnumSTATSTG).Dispose();
        safeIEnumSTATSTG = null; 
 
        return nameFound;
    } 

    /// 
    /// Find out if a storage is empty of elements
    ///  
    /// true if storage is empty
    internal bool StorageIsEmpty() 
    { 
        // Is there a better way than to check if enumerator has nothing?
        UInt32 actual; 
        IEnumSTATSTG safeIEnumSTATSTG = null;
        System.Runtime.InteropServices.ComTypes.STATSTG dummySTATSTG;

        // Set up IEnumSTATSTG 
        core.safeIStorage.EnumElements(
            0, 
            IntPtr.Zero, 
            0,
            out safeIEnumSTATSTG ); 
        safeIEnumSTATSTG.Reset();
        safeIEnumSTATSTG.Next( 1, out dummySTATSTG, out actual );

        // Release enumerator 
        ((IDisposable) safeIEnumSTATSTG).Dispose();
        safeIEnumSTATSTG = null; 
 
        // If the first "Next" call returns nothing, then there's nothing here.
        return ( 0 == actual ); 
    }

    /// 
    /// If anything about this storage has changed, we need to go out and 
    /// invalidate every outstanding enumerator so any attempt to use them
    /// will result in InvalidOperationException as specified for IEnumerator 
    /// interface implementers 
    /// 
    internal void InvalidateEnumerators() 
    {
        InvalidateEnumerators( core );
    }
 
    /// 
    ///   Given a StorageInfoCore, clears the enumerators associated with it. 
    ///  
    private static void InvalidateEnumerators( StorageInfoCore invalidateCore )
    { 
        // It is not enough to simply clear the validEnumerators collection,
        //  we have to clear the individual elements to let them know the
        //  outstanding enumerator is no longer valid.
        foreach( object entry in invalidateCore.validEnumerators.Values ) 
        {
            ((ArrayList)entry).Clear(); 
        } 
        invalidateCore.validEnumerators.Clear();
    } 

    /// 
    /// This will build a full path to this storage from the parent full
    /// name and our storage name.  The array is basically the same as the 
    /// parent storage's Name property plus one element - our
    /// storageName 
    ///  
    /// ArrayList designating the path of this storage
    internal ArrayList BuildFullNameFromParentName() 
    {
        if( null == parentStorage )
        {
            // special case for root storage 
            return new ArrayList();
        } 
        else 
        {
            ArrayList parentArray = parentStorage.BuildFullNameFromParentName(); 
            parentArray.Add(core.storageName);
            return parentArray;
        }
    } 

    ///  
    ///     Counterpart to BuildFullNameFromParentName that uses the internal 
    /// normalized names instead.
    ///  
    internal ArrayList BuildFullNameInternalFromParentNameInternal()
    {
        if( null == parentStorage )
        { 
            // special case for root storage
            return new ArrayList(); 
        } 
        else
        { 
            ArrayList parentArray = parentStorage.BuildFullNameInternalFromParentNameInternal();
            parentArray.Add(core.storageName);
            return parentArray;
        } 
    }
 
    ///  
    /// This needs to be available to StreamInfo so it can actually create itself
    ///  
    internal IStorage SafeIStorage
    {
        get
        { 
            VerifyExists();
            return core.safeIStorage; 
        } 
    }
 
    /// 
    /// Every method here have a need to check if the storage exists before
    /// proceeeding with the operation.  However, for reasons I don't fully
    /// understand we're discouraged from methods calling on other externally 
    /// visible methods, so they can't just call Exists().  So I just pull
    /// it out to an InternalExists method. 
    /// 
    /// At this time I believe only two methods call this - Exists() because
    /// it really wants to know, VerifyExists() because it's called by 
    /// everybody else just to see that the storage exists before proceeding.
    ///
    /// If this returns true, the storage cache pointer should be live.
    ///  
    /// Whether "this" storage exists
    bool InternalExists() 
    { 
        return InternalExists( core.storageName );
    } 

    /// 
    /// Every method here have a need to check if the storage exists before
    /// proceeeding with the operation.  However, for reasons I don't fully 
    /// understand we're discouraged from methods calling on other externally
    /// visible methods, so they can't just call Exists().  So I just pull 
    /// it out to an InternalExists method. 
    ///
    /// At this time I believe only two methods call this - Exists() because 
    /// it really wants to know, VerifyExists() because it's called by
    /// everybody else just to see that the storage exists before proceeding.
    ///
    /// If this returns true, the storage cache pointer should be live. 
    /// 
    /// Whether "this" storage exists 
    bool InternalExists(string name) 
    {
        // We can't have an IStorage unless we exist. 
        if( null != core.safeIStorage )
        {
            return true;
        } 

        // If we are the root storage, we always exist. 
        if( null == parentStorage ) 
        {
            return true; 
        }

        // If the parent storage does not exist, we can't possibly exist
        if( !parentStorage.Exists ) 
        {
            return false; 
        } 

        // Now things get more complicated... we know that: 
        //  * We are not the root
        //  * We have a valid parent
        //  * We don't have an IStorage interface
 
        // The most obvious way to check is to try opening the storage and
        //  see what happens.  It's supposed to be fairly fast and easy 
        //  because it stays within the DocFile FAT, and that'll give 
        //  us our IStorage cache pointer too.
 
        return parentStorage.CanOpenStorage( name );
    }

    bool CanOpenStorage( string nameInternal ) 
    {
        bool openSuccess = false; 
        StorageInfoCore childCore = core.elementInfoCores[ nameInternal ] as StorageInfoCore ; 

        Debug.Assert( null != childCore, "Expected a child with valid core object in cache" ); 

        int nativeCallErrorCode = 0;

        nativeCallErrorCode = core.safeIStorage.OpenStorage( 
                nameInternal,
                null, 
                (GetStat().grfMode & SafeNativeCompoundFileConstants.STGM_READWRITE_Bits) 
                    | SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE,
                IntPtr.Zero, 
                0,
                out childCore.safeIStorage );

        if( SafeNativeCompoundFileConstants.S_OK == nativeCallErrorCode ) 
        {
            openSuccess = true; 
        } 
        else if( SafeNativeCompoundFileConstants.STG_E_FILENOTFOUND != nativeCallErrorCode )
        { 
            // Error is not STG_E_FILENOTFOUND, pass it on.
            throw new IOException(
                SR.Get(SRID.CanNotOpenStorage),
                new COMException( 
                    SR.Get(SRID.NamedAPIFailure, "IStorage::OpenStorage"),
                    nativeCallErrorCode )); 
        } 
        // STG_E_NOTFOUND - return openSuccess as false
 
        return openSuccess;
    }

    ///  
    /// Most of the time internal methods that want to do an internal check
    /// to see if a storage exists is only interested in proceeding if it does. 
    /// If it doesn't, abort with an exception.  This implements the little 
    /// shortcut.
    ///  
    void VerifyExists()
    {
        if( !InternalExists() )
        { 
            throw new DirectoryNotFoundException(
                SR.Get(SRID.CanNotOnNonExistStorage)); 
        } 
        return;
    } 

    /// 
    /// Grabs the STATSTG representing us
    ///  
    System.Runtime.InteropServices.ComTypes.STATSTG GetStat()
    { 
        System.Runtime.InteropServices.ComTypes.STATSTG returnValue; 

        VerifyExists(); 

        core.safeIStorage.Stat( out returnValue, 0 );

        return returnValue; 
    }
 
    ///  
    /// Convert a System.Runtime.InteropServices.FILETIME struct to the CLR
    /// DateTime class.  Strange that the straightforward conversion doesn't 
    /// already exist.  Perhaps I'm just not finding it.  DateTime has a
    /// method to convert itself to FILETIME, but only supports creating a
    /// DateTime from a 64-bit value representing FILETIME instead of the
    /// FILETIME struct itself. 
    /// 
    DateTime ConvertFILETIMEToDateTime( System.Runtime.InteropServices.ComTypes.FILETIME time ) 
    { 

        // We should let the user know when the time is not valid, rather than 
        //  return a bogus date of Dec 31. 1600.
        if( 0 == time.dwHighDateTime &&
            0 == time.dwLowDateTime )
            throw new NotSupportedException( 
                SR.Get(SRID.TimeStampNotAvailable));
 
        // CLR 

 



 

 
 
        return DateTime.FromFileTime(
            (((long)time.dwHighDateTime) << 32) + 
              (uint)time.dwLowDateTime ); // This second uint is very important!!
    }

    ///  
    ///  Given a StorageInfoCore, recursively release objects associated with
    /// sub-storages and then releases objects associated with self. 
    ///  
    /// 
    ///  This didn't used to be a static method - but this caused some problems. 
    /// All operations need to be on the given parameter "startCore".  If this
    /// wasn't static, the code had access to the member "core" and that's not
    /// the right StorageInfoCore to use.  This caused some bugs that would have
    /// been avoided if this was static.  To prevent future bugs of similar 
    /// nature, I made this static.
    ///  
    internal static void RecursiveStorageInfoCoreRelease( StorageInfoCore startCore ) 
    {
        // If the underlying IStorage pointer is null, we never did anything 
        //  with this storage or anything under it.  We can halt our recursion
        //  here.
        if( startCore.safeIStorage == null )
            return; 

        try 
        { 
            //////////////////////////////////////////////////////////////////////
            // 
            //  Call clean-up code for things on the storage represented by startCore.

            //////////////////////////////////////////////////////////////////////
            // 
            //  Call clean-up code for things *under* startCore.
 
            // See if we have child storages and streams, and if so, close them 
            //  down.
            foreach( object o in startCore.elementInfoCores.Values ) 
            {
                if( o is StorageInfoCore )
                {
                    RecursiveStorageInfoCoreRelease( (StorageInfoCore)o ); 
                }
                else if( o is StreamInfoCore ) 
                { 
                    StreamInfoCore streamRelease = (StreamInfoCore)o;
 
                    try
                    {
                        if (null != streamRelease.exposedStream)
                        { 
                            ((Stream)(streamRelease.exposedStream)).Close();
                        } 
                        streamRelease.exposedStream = null; 
                    }
                    finally 
                    {
                        // We need this release and null-out to happen even if we
                        //  ran into problems with the clean-up code above.
                        if( null != streamRelease.safeIStream) 
                        {
                            ((IDisposable) streamRelease.safeIStream).Dispose(); 
                            streamRelease.safeIStream = null; 
                        }
 
                        // Null name in core signifies the core object is disposed
                        ((StreamInfoCore)o).streamName = null;
                    }
                } 
            }
 
            // All child objects freed, clear out the enumerators 
            InvalidateEnumerators( startCore );
        } 
        finally
        {
            //  We want to make sure this happens even if any of the cleanup
            //  above fails, so that's why it's in a "finally" block here. 

            ////////////////////////////////////////////////////////////////////// 
            // 
            //  Free unmanaged resources associated with the startCore storage
 
            if( null != startCore.safeIStorage)
            {
                ((IDisposable) startCore.safeIStorage).Dispose();
                startCore.safeIStorage = null; 
            }
 
            // Null name in core signifies the core object is disposed 
            startCore.storageName = null;
        } 
    }

    // Check whether this StorageInfo is still valid.  Throw if disposed.
    internal void CheckDisposedStatus() 
    {
        // null == parentStorage means we're root. 
        if( StorageDisposed ) 
            throw new ObjectDisposedException(null, SR.Get(SRID.StorageInfoDisposed));
    } 

    // Check whether this StorageInfo is still valid.
    internal bool StorageDisposed
    { 
        get
        { 
            // null == parentStorage means we're root. 
            if( null != parentStorage )
            { 
                // Check our core reference to see if we're valid
                if( null == core.storageName ) // Null name in core signifies the core object is disposed
                {
                    // We have been deleted 
                    return true;
                } 
 
                // We're not the root storage - check parent.
                return parentStorage.StorageDisposed; 
            }
            else if (this is StorageRoot)
            {
                return ((StorageRoot)this).RootDisposed; 
            }
            else 
            { 
                Debug.Assert(rootStorage != null, "Root storage cannot be null if StorageInfo and empty parentStorage");
                return rootStorage.RootDisposed; 
            }
        }
    }
 
    // There is a hash table in the core object core.validEnumerators that
    //  caches the arrays used to hand out the enumerations of this storage 
    //  object.  A call to this method will ensure that the array exists.  If 
    //  it already exists, this is a no-op.  If it doesn't yet exist, it is
    //  built before we return. 
    // When this function exits, core.validEnumerators(desiredArrayType) will
    //  have an array of the appropriate type.
    private void EnsureArrayForEnumeration( EnumeratorTypes desiredArrayType )
    { 
        Debug.Assert(
            desiredArrayType == EnumeratorTypes.Everything || 
            desiredArrayType == EnumeratorTypes.OnlyStorages || 
            desiredArrayType == EnumeratorTypes.OnlyStreams,
            "Invalid type enumeration value is being used to build enumerator array" ); 
        Debug.Assert( InternalExists(),
            "It is the responsibility of the caller to ensure that storage exists (and is not disposed - which is harder to check at this point so it wasn't done.)");

        if( null == core.validEnumerators[ desiredArrayType ] ) 
        {
            ArrayList storageElems = new ArrayList(); 
            string externalName = null; 

            // Set up IEnumSTATSTG 
            System.Runtime.InteropServices.ComTypes.STATSTG enumElement;
            UInt32 actual;
            IEnumSTATSTG safeIEnumSTATSTG = null;
 
            core.safeIStorage.EnumElements(
                0, 
                IntPtr.Zero, 
                0,
                out safeIEnumSTATSTG ); 
            safeIEnumSTATSTG.Reset();
            safeIEnumSTATSTG.Next( 1, out enumElement, out actual );

            // Loop and get everything 
            while( 0 < actual )
            { 
                externalName = enumElement.pwcsName; 

                // In an enumerator, we don't return anything within the reserved 
                //  name range.  (First character is \x0001 - \x001F)
                if( CU.IsReservedName(externalName ) )
                {
                    ; // Do nothing for reserved names. 
                }
                else if( SafeNativeCompoundFileConstants.STGTY_STORAGE == enumElement.type ) 
                { 
                    if( desiredArrayType == EnumeratorTypes.Everything ||
                        desiredArrayType == EnumeratorTypes.OnlyStorages ) 
                    {
                        // Add reference to a storage to enumerator array
                        storageElems.Add(new StorageInfo(this, externalName));
                    } 
                }
                else if( SafeNativeCompoundFileConstants.STGTY_STREAM == enumElement.type ) 
                { 
                    if( desiredArrayType == EnumeratorTypes.Everything ||
                        desiredArrayType == EnumeratorTypes.OnlyStreams ) 
                    {
                        // Add reference to a stream to enumerator array
                         storageElems.Add(new StreamInfo(this, externalName));
                    } 
                }
                else 
                { 
                    throw new NotSupportedException(
                        SR.Get(SRID.UnsupportedTypeEncounteredWhenBuildingStgEnum)); 
                }

                // Move on to the next element
                safeIEnumSTATSTG.Next( 1, out enumElement, out actual ); 
            }
 
            core.validEnumerators[ desiredArrayType ] = storageElems; 

            // Release IEnumSTATSTG 
            ((IDisposable) safeIEnumSTATSTG).Dispose();
            safeIEnumSTATSTG = null;
        }
 
        Debug.Assert( null != core.validEnumerators[ desiredArrayType ],
            "We failed to ensure the proper array for enumeration" ); 
    } 

} 
}



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