StorageBasedPackageProperties( StorageRoot root ) { _pss = (IPropertySetStorage)root.GetRootIStorage(); // // Open the property sets with the same access with which the file itself // was opened. // _grfMode = SafeNativeCompoundFileConstants.STGM_DIRECT | SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE; SafeNativeCompoundFileMethods.UpdateModeFlagFromFileAccess(root.OpenAccess, ref _grfMode); OpenPropertyStorage(ref FormatId.SummaryInformation, out _psSummInfo); OpenPropertyStorage(ref FormatId.DocumentSummaryInformation, out _psDocSummInfo); }
/// <summary> /// Creates a stream with the given parameters /// </summary> /// <param name="mode">Creation mode</param> /// <param name="access">Access mode</param> /// <param name="dataSpace">Data space encoding</param> /// <returns>Stream object to manipulate data</returns> internal Stream Create(FileMode mode, FileAccess access, string dataSpace) { CheckDisposedStatus(); int grfMode = 0; IStream createdSafeIStream = null; DataSpaceManager dataSpaceManager = null; // Check to make sure root container is not read-only, and that // we're not pointlessly trying to create a read-only stream. CreateTimeReadOnlyCheck(access); // Check to see if the data space label is valid if (null != dataSpace) { if (0 == dataSpace.Length) { throw new ArgumentException( SR.Get(SRID.DataSpaceLabelInvalidEmpty)); } dataSpaceManager = parentStorage.Root.GetDataSpaceManager(); if (!dataSpaceManager.DataSpaceIsDefined(dataSpace)) { throw new ArgumentException( SR.Get(SRID.DataSpaceLabelUndefined)); } } openFileAccess = access; // becasue of the stream caching mechanism we must adjust FileAccess parameter. // We want to open stream with the widest access posible, in case Package was open in ReadWrite // we need to open stream in ReadWrite even if user explicitly asked us to do ReadOnly/WriteOnly. // There is a possibility of a next request coming in as as ReadWrite request, and we would like to // take advanatage of the cached stream by wrapping with appropriate access limitations. if (parentStorage.Root.OpenAccess == FileAccess.ReadWrite) { access = FileAccess.ReadWrite; } // Generate the access flags from the access parameter SafeNativeCompoundFileMethods.UpdateModeFlagFromFileAccess(access, ref grfMode); // Only SHARE_EXCLUSIVE for now, FileShare issue TBD grfMode |= SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE; CheckAccessMode(grfMode); // Act based on FileMode switch (mode) { case FileMode.Create: // Close down any existing streams floating out there if (null != core.exposedStream) { ((Stream)(core.exposedStream)).Close(); } core.exposedStream = null; if (null != core.safeIStream) { // Release reference ((IDisposable)core.safeIStream).Dispose(); core.safeIStream = null; } // Cleanup done, create new stream in its place. grfMode |= SafeNativeCompoundFileConstants.STGM_CREATE; createdSafeIStream = CreateStreamOnParentIStorage( core.streamName, grfMode); break; case FileMode.CreateNew: // If we've created a CFStream, this fails because stream is already there. if (null != core.safeIStream) { throw new IOException( SR.Get(SRID.StreamAlreadyExist)); } // Need to call Create API with NULL create flags createdSafeIStream = CreateStreamOnParentIStorage( core.streamName, grfMode); break; case FileMode.Append: // None of these are valid in a Create case FileMode.Open: case FileMode.OpenOrCreate: case FileMode.Truncate: default: throw new ArgumentException( SR.Get(SRID.FileModeInvalid)); } core.safeIStream = createdSafeIStream; // At this point we passed all previous checks and got the underlying IStream. // Set our data space label to the given label, and the stream to the retrieved stream. core.dataSpaceLabel = dataSpace; if (null != dataSpace) { dataSpaceManager.CreateDataSpaceMapping( new CompoundFileStreamReference(parentStorage.FullNameInternal, core.streamName), core.dataSpaceLabel); } Stream returnStream = BuildStreamOnUnderlyingIStream(core.safeIStream, openFileAccess, this); _needToGetTransformInfo = false; // We created stream with the given dataspace setting // so, there is no need to get the dataspace setting core.exposedStream = returnStream; return(returnStream); }
/// <summary> /// Opens a stream with the given open mode flags and access flags /// </summary> /// <param name="mode">Open mode flags</param> /// <param name="access">File access flags</param> /// <returns>Stream object to manipulate data</returns> public Stream GetStream(FileMode mode, FileAccess access) { CheckDisposedStatus(); int grfMode = 0; IStream openedIStream = null; openFileAccess = access; // becasue of the stream caching mechanism we must adjust FileAccess parameter. // We want to open stream with the widest access posible, in case Package was open in ReadWrite // we need to open stream in ReadWrite even if user explicitly asked us to do ReadOnly/WriteOnly. // There is a possibility of a next request coming in as as ReadWrite request, and we would like to // take advanatage of the cached stream by wrapping with appropriate access limitations. if (parentStorage.Root.OpenAccess == FileAccess.ReadWrite) { // Generate the access flags from the access parameter access = FileAccess.ReadWrite; } // Generate the access flags from the access parameter SafeNativeCompoundFileMethods.UpdateModeFlagFromFileAccess(access, ref grfMode); // Only SHARE_EXCLUSIVE for now, FileShare issue TBD grfMode |= SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE; CheckAccessMode(grfMode); // Act based on FileMode switch (mode) { case FileMode.Append: throw new ArgumentException( SR.Get(SRID.FileModeUnsupported)); case FileMode.Create: // Check to make sure root container is not read-only, and that // we're not pointlessly trying to create a read-only stream. CreateTimeReadOnlyCheck(openFileAccess); // Close down any existing streams floating out there if (null != core.exposedStream) { ((Stream)(core.exposedStream)).Close(); } core.exposedStream = null; if (null != core.safeIStream) { // Close out existing stream ((IDisposable)core.safeIStream).Dispose(); core.safeIStream = null; } // Cleanup done, create new stream in its place grfMode |= SafeNativeCompoundFileConstants.STGM_CREATE; openedIStream = CreateStreamOnParentIStorage( core.streamName, grfMode); break; case FileMode.CreateNew: throw new ArgumentException( SR.Get(SRID.FileModeUnsupported)); case FileMode.Open: // If we've got a stream, return a CFStream built from its clone if (null != core.safeIStream) { return(CFStreamOfClone(openFileAccess)); } // Need to call Open API with NULL open flags openedIStream = OpenStreamOnParentIStorage( core.streamName, grfMode); break; case FileMode.OpenOrCreate: // If we've got a stream, return a CFStream built from its clone if (null != core.safeIStream) { return(CFStreamOfClone(openFileAccess)); } // Skip creation attempt for read-only container or specifying // read-only stream if (FileAccess.Read != parentStorage.Root.OpenAccess && FileAccess.Read != openFileAccess) { // Try creating first. If it already exists then do an open. This // seems ugly but this method involves the fewest number of // managed/unmanaged transitions. if (!parentStorage.Exists) { parentStorage.Create(); } int nativeCallErrorCode = parentStorage.SafeIStorage.CreateStream( core.streamName, grfMode, 0, 0, out openedIStream); if (SafeNativeCompoundFileConstants.S_OK != nativeCallErrorCode && SafeNativeCompoundFileConstants.STG_E_FILEALREADYEXISTS != nativeCallErrorCode) { throw new IOException( SR.Get(SRID.UnableToCreateStream), new COMException( SR.Get(SRID.NamedAPIFailure, "IStorage.CreateStream"), nativeCallErrorCode)); } // Parent storage has changed - invalidate all standing enuemrators parentStorage.InvalidateEnumerators(); // else - proceed with open } if (null == openedIStream) { // If we make it here, it means the create stream call failed // because of a STG_E_FILEALREADYEXISTS // or container is read-only openedIStream = OpenStreamOnParentIStorage( core.streamName, grfMode); } break; case FileMode.Truncate: throw new ArgumentException( SR.Get(SRID.FileModeUnsupported)); default: throw new ArgumentException( SR.Get(SRID.FileModeInvalid)); } core.safeIStream = openedIStream; Stream returnStream = BuildStreamOnUnderlyingIStream(core.safeIStream, openFileAccess, this); core.exposedStream = returnStream; return(returnStream); }
GetOleProperty( Guid fmtid, uint propId ) { CheckDisposed(); // fmtid is always either DocSum or Sum. IPropertyStorage ps = fmtid == FormatId.SummaryInformation ? _psSummInfo : _psDocSummInfo; if (ps == null) { // This file doesn't even contain the property storage that this // property belongs to, so it certainly doesn't contain the property. return(null); } object obj = null; PROPSPEC[] propSpecs = new PROPSPEC[1]; PROPVARIANT[] vals = new PROPVARIANT[1]; propSpecs[0].propType = (uint)PropSpecType.Id; propSpecs[0].union.propId = propId; VARTYPE vtExpected = GetVtFromPropId(fmtid, propId); int hresult = ps.ReadMultiple(1, propSpecs, vals); if (hresult == SafeNativeCompoundFileConstants.S_OK) { try { if (vals[0].vt != vtExpected) { throw new FileFormatException( SR.Get( SRID.WrongDocumentPropertyVariantType, propId, fmtid.ToString(), vals[0].vt, vtExpected ) ); } switch (vals[0].vt) { case VARTYPE.VT_LPSTR: // // We store string properties as CP_ACP or UTF-8. // But no matter which format the string was encoded, we always use the UTF-8 // encoder/decoder to decode the byte array, because the UTF-8 code of an ASCII // string is the same as the ASCII string. // IntPtr pszVal = vals[0].union.pszVal; // // Because both the ASCII string and UTF-8 encoded string (byte array) are // stored in a memory block (pszVal) terminated by null, we can use // Marshal.PtrToStringAnsi(pszVal) to convert the memory block pointed by // pszVal to a string. Then from the string.Length, we can get the number of // bytes in the memory block. Otherwise, we cannot easily tell how many bytes // are stored in pszVal without an extra parameter. // string ansiString = Marshal.PtrToStringAnsi(pszVal); int nLen = ansiString.Length; byte[] byteArray = new byte[nLen]; Marshal.Copy(pszVal, byteArray, 0, nLen); obj = UTF8Encoding.UTF8.GetString(byteArray); break; case VARTYPE.VT_FILETIME: // // DateTime doesn't have a conversion from FILETIME. It has a // misleadingly named "FromFileTime" method that actually wants // a long. So... // obj = new Nullable <DateTime>(DateTime.FromFileTime(vals[0].union.hVal)); break; default: throw new FileFormatException( SR.Get(SRID.InvalidDocumentPropertyVariantType, vals[0].vt)); } } finally { #pragma warning suppress 6031 // suppressing a "by design" ignored return value SafeNativeCompoundFileMethods.SafePropVariantClear(ref vals[0]); } } else if (hresult == SafeNativeCompoundFileConstants.S_FALSE) { // Do nothing -- return the null object reference. } else { SecurityHelper.ThrowExceptionForHR(hresult); } return(obj); }
/// <summary>Open a container given all the settings</summary> /// <param name="path">Path to container on local file system</param> /// <param name="mode">See System.IO.FileMode in .NET SDK</param> /// <param name="access">See System.IO.FileAccess in .NET SDK</param> /// <param name="share">See System.IO.FileShare in .NET SDK</param> /// <param name="sectorSize">Compound File sector size, must be 512 or 4096</param> /// <returns>StorageRoot instance representing the file</returns> internal static StorageRoot Open( string path, FileMode mode, FileAccess access, FileShare share, int sectorSize) { int grfMode = 0; int returnValue = 0; // Simple path validation ContainerUtilities.CheckStringAgainstNullAndEmpty(path, "Path"); Guid IID_IStorage = new Guid(0x0000000B, 0x0000, 0x0000, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46); IStorage newRootStorage; //////////////////////////////////////////////////////////////////// // Generate STGM from FileMode switch (mode) { case FileMode.Append: throw new ArgumentException( SR.FileModeUnsupported); case FileMode.Create: grfMode |= SafeNativeCompoundFileConstants.STGM_CREATE; break; case FileMode.CreateNew: { FileInfo existTest = new FileInfo(path); if (existTest.Exists) { throw new IOException( SR.FileAlreadyExists); } } goto case FileMode.Create; case FileMode.Open: break; case FileMode.OpenOrCreate: { FileInfo existTest = new FileInfo(path); if (existTest.Exists) { // File exists, use open code path goto case FileMode.Open; } else { // File does not exist, use create code path goto case FileMode.Create; } } case FileMode.Truncate: throw new ArgumentException( SR.FileModeUnsupported); default: throw new ArgumentException( SR.FileModeInvalid); } // Generate the access flags from the access parameter SafeNativeCompoundFileMethods.UpdateModeFlagFromFileAccess(access, ref grfMode); // Generate STGM from FileShare // Note: the .NET SDK does not specify the proper behavior in reaction to // incompatible flags being sent in together. Should ArgumentException be // thrown? Or do some values "trump" others? if (0 != (share & FileShare.Inheritable)) { throw new ArgumentException( SR.FileShareUnsupported); } else if (share == FileShare.None) // FileShare.None is zero, using "&" to check causes unreachable code error { grfMode |= SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE; } else if (share == FileShare.Read) { grfMode |= SafeNativeCompoundFileConstants.STGM_SHARE_DENY_WRITE; } else if (share == FileShare.Write) { grfMode |= SafeNativeCompoundFileConstants.STGM_SHARE_DENY_READ; // Note that this makes little sense when we don't support combination of flags } else if (share == FileShare.ReadWrite) { grfMode |= SafeNativeCompoundFileConstants.STGM_SHARE_DENY_NONE; } else { throw new ArgumentException( SR.FileShareInvalid); } if (0 != (grfMode & SafeNativeCompoundFileConstants.STGM_CREATE)) { // STGM_CREATE set, call StgCreateStorageEx. returnValue = SafeNativeCompoundFileMethods.SafeStgCreateStorageEx( path, grfMode, stgFormatDocFile, 0, IntPtr.Zero, IntPtr.Zero, ref IID_IStorage, out newRootStorage); } else { // STGM_CREATE not set, call StgOpenStorageEx. returnValue = SafeNativeCompoundFileMethods.SafeStgOpenStorageEx( path, grfMode, stgFormatDocFile, 0, IntPtr.Zero, IntPtr.Zero, ref IID_IStorage, out newRootStorage); } switch (returnValue) { case SafeNativeCompoundFileConstants.S_OK: return(StorageRoot.CreateOnIStorage( newRootStorage)); case SafeNativeCompoundFileConstants.STG_E_FILENOTFOUND: throw new FileNotFoundException( SR.ContainerNotFound); case SafeNativeCompoundFileConstants.STG_E_INVALIDFLAG: throw new ArgumentException( SR.StorageFlagsUnsupported, new COMException( SR.CFAPIFailure, returnValue)); default: throw new IOException( SR.ContainerCanNotOpen, new COMException( SR.CFAPIFailure, returnValue)); } }
/// <summary> /// Create a container StorageRoot based on the given System.IO.Stream object /// </summary> /// <param name="baseStream">The new Stream upon which to build the new StorageRoot</param> /// <param name="mode">The mode (Open or Create) to use on the lock bytes</param> /// <returns>New StorageRoot object built on the given Stream</returns> internal static StorageRoot CreateOnStream(Stream baseStream, FileMode mode) { if (null == baseStream) { throw new ArgumentNullException("baseStream"); } IStorage storageOnStream; int returnValue; int openFlags = SafeNativeCompoundFileConstants.STGM_SHARE_EXCLUSIVE; if (baseStream.CanRead) { if (baseStream.CanWrite) { openFlags |= SafeNativeCompoundFileConstants.STGM_READWRITE; } else { openFlags |= SafeNativeCompoundFileConstants.STGM_READ; if (FileMode.Create == mode) { throw new ArgumentException( SR.CanNotCreateContainerOnReadOnlyStream); } } } else { throw new ArgumentException( SR.CanNotCreateStorageRootOnNonReadableStream); } if (FileMode.Create == mode) { returnValue = SafeNativeCompoundFileMethods.SafeStgCreateDocfileOnStream( baseStream, openFlags | SafeNativeCompoundFileConstants.STGM_CREATE, out storageOnStream); } else if (FileMode.Open == mode) { returnValue = SafeNativeCompoundFileMethods.SafeStgOpenStorageOnStream( baseStream, openFlags, out storageOnStream); } else { throw new ArgumentException( SR.CreateModeMustBeCreateOrOpen); } switch ((uint)returnValue) { case SafeNativeCompoundFileConstants.S_OK: return(StorageRoot.CreateOnIStorage( storageOnStream)); default: throw new IOException( SR.UnableToCreateOnStream, new COMException( SR.CFAPIFailure, returnValue)); } }