Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / Orcas / QFE / wpf / src / Base / MS / Internal / IO / Packaging / StorageBasedPackageProperties.cs / 1 / StorageBasedPackageProperties.cs
//------------------------------------------------------------------------------ // //// Copyright (C) Microsoft Corporation. All rights reserved. // // // Description: // This class provides access to the package properties of an RM-protected OPC // document. The "package properties" are a subset of the standard OLE property // sets SummaryInformation and DocumentSummaryInformation, and include such // properties as Title and Subject. // // An RM-protected OPC document is physically represented by an OLE compound // file containing a well-known stream in which an OPC Zip archive, encrypted // in its entirety, is stored. The package properties of an RM-protected OPC // document are stored in the standard OLE compound file property set streams, // \005SummaryInformation and \005DocumentSummaryInformation. The contents of // these streams is intended to mirror the corresponding metadata properties // stored in the OPC package itself. These properties are duplicated, in the // clear, outside of the encrypted OPC package stream, so that tools such as // the Shell can display the properties without having to decrypt the package. // // It is the responsibility of the application to ensure that the properties in // the OLE property set streams are synchronized with the properties in the // OPC package. // // History: // 06/16/2005: LGolding: Initial implementation. // 09/23/2005: JohnLarc: Removed from public surface. // //----------------------------------------------------------------------------- using System; using System.IO; using System.IO.Packaging; using System.Runtime.InteropServices; using System.Windows; using System.Security; // SecurityCritical using System.Text; //For UTF-8 encoding. using MS.Internal; using MS.Internal.Interop; using MS.Internal.IO.Packaging.CompoundFile; using MS.Internal.WindowsBase; //for SecurityHelper. // Enable presharp pragma warning suppress directives. #pragma warning disable 1634, 1691 namespace MS.Internal.IO.Packaging { ////// This class provides access to the package properties, such as Title and /// Subject, of an RM-protected OPC package. These properties are a subset of /// of the standard OLE property sets SummaryInformation and /// DocumentSummaryInformation. /// internal class StorageBasedPackageProperties : PackageProperties, IDisposable { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // // The constructor is internal because an application never directly // creates an object of this class. An application obtains an object // of this class through the PackageProperties CLR property of the // EncryptedPackageEnvelope class: // // EncryptedPackageEnvelope ep = EncryptedPackageEnvelope.Create("mydoc.xps", ...); // PackageProperties props = ep.PackageProperties; // internal 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); } ////// Finalizer. /// ~StorageBasedPackageProperties() { Dispose(false); } #endregion Constructors //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties #region SummaryInformation properties ////// The title. /// public override string Title { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Title) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Title, value); } } ////// The topic of the contents. /// public override string Subject { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Subject) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Subject, value); } } ////// The primary creator. The identification is environment-specific and /// can consist of a name, email address, employee ID, etc. It is /// recommended that this value be only as verbose as necessary to /// identify the individual. /// public override string Creator { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Creator) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Creator, value); } } ////// A delimited set of keywords to support searching and indexing. This /// is typically a list of terms that are not available elsewhere in the /// properties. /// public override string Keywords { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Keywords) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Keywords, value); } } ////// The description or abstract of the contents. /// public override string Description { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Description) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Description, value); } } ////// The user who performed the last modification. The identification is /// environment-specific and can consist of a name, email address, /// employee ID, etc. It is recommended that this value be only as /// verbose as necessary to identify the individual. /// public override string LastModifiedBy { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.LastModifiedBy) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.LastModifiedBy, value); } } ////// The revision number. This value indicates the number of saves or /// revisions. The application is responsible for updating this value /// after each revision. /// public override string Revision { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Revision) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Revision, value); } } ////// The date and time of the last printing. /// public override NullableLastPrinted { get { return GetDateTimeProperty(FormatId.SummaryInformation, PropertyId.LastPrinted); } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.LastPrinted, value); } } /// /// The creation date and time. /// public override NullableCreated { get { return GetDateTimeProperty(FormatId.SummaryInformation, PropertyId.DateCreated); } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.DateCreated, value); } } /// /// The date and time of the last modification. /// public override NullableModified { get { return GetDateTimeProperty(FormatId.SummaryInformation, PropertyId.DateModified); } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.DateModified, value); } } #endregion SummaryInformation properties #region DocumentSummaryInformation properties /// /// The category. This value is typically used by UI applications to create navigation /// controls. /// public override string Category { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Category) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Category, value); } } ////// A unique identifier. /// public override string Identifier { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Identifier) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Identifier, value); } } ////// The type of content represented, generally defined by a specific /// use and intended audience. Example values include "Whitepaper" /// and "Exam". (This property is distinct from /// MIME content types as defined in RFC 2045.) /// public override string ContentType { get { string contentType = GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentType) as string; if (contentType == null) { return contentType; } else { //Creating a ContentType object to validate the content type string. //Can replace this later with a static method to validate the content type. return new ContentType(contentType).ToString(); } } set { if (value == null) { //indicates that the property should be deleted. SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentType, value); } else { //Creating a ContentType object to validate the content type string. //Can replace this later with a static method to validate the content type. SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentType, new ContentType(value).ToString()); } } } ////// The primary language of the package content. The language tag is /// composed of one or more parts: A primary language subtag and a /// (possibly empty) series of subsequent subtags, for example, "EN-US". /// These values MUST follow the convention specified in RFC 3066. /// public override string Language { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Language) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Language, value); } } ////// The version number. This value is set by the user or by the application. /// public override string Version { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Version) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Version, value); } } ////// The status of the content. Example values include "Draft", /// "Reviewed", and "Final". /// public override string ContentStatus { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentStatus) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentStatus, value); } } #endregion DocumentSummaryInformation properties //------------------------------------------------------ // // IDisposable Methods // //------------------------------------------------------ #region IDisposable ////// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly /// or indirectly by a user's code. Managed and unmanaged resources /// can be disposed. /// /// If disposing equals false, the method has been called by the /// runtime from inside the finalizer and you should not reference /// other objects. Only unmanaged resources can be disposed. /// /// /// true if called from Dispose(); false if called from the finalizer. /// protected override void Dispose( bool disposing ) { try { if (!_disposed && disposing) { if (_psSummInfo != null) { try { ((IDisposable)_psSummInfo).Dispose(); } finally { _psSummInfo = null; } } if (_psDocSummInfo != null) { try { ((IDisposable)_psDocSummInfo).Dispose(); } finally { _psDocSummInfo = null; } } } } finally { // // By setting _disposed = true, we ensure that all future accesses to // this object will fail (because both GetOleProperty and SetOleProperty // call CheckDisposed). Note that we wrap the entire body of // Dispose(bool) (this method) in an "if (!_disposed)". // And we also set each reference to null immediately after attempting // to release it. So we never attempt to release any reference more // than once. // _disposed = true; base.Dispose(disposing); } } #endregion IDisposable #endregion Public Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods ////// Obtain the specified property from the specified property set. /// /// /// "Format identifier" that specifies the property set from which to /// obtain the property. /// /// /// Identifier of the property to obtain. /// ////// An object of the appropriate type for the specified property which /// contains the property value, or null if the property does not exist /// in the encrypted package. /// ////// Critical: calls Marshal.PtrToStringAnsi which LinkDemands /// TreatAsSafe: can't be used to allocate a string from an arbitrary pointer since it doesn't accept a ptr. /// [SecurityCritical, SecurityTreatAsSafe] private object 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.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; } /// /// Set or delete the specified property in the specified property set. /// /// /// "Format identifier" that specifies the property set in which to /// set the property. /// /// /// Identifier of the property to set. /// /// /// An object of the appropriate type for the specified property which /// contains the property value, or null if the property is to be deleted. /// ////// Critical: Calls Marshal.StringToCoTaskMemAnsi which LinkDemands. Also has unsafe code block. /// TreatAsSafe: the memory that's allocated is freed before returning. Unsafe code block only manipulates local memory on the stack. /// [SecurityCritical, SecurityTreatAsSafe] private void SetOleProperty( Guid fmtid, uint propId, object propVal ) { CheckDisposed(); IPropertyStorage ps = fmtid == FormatId.SummaryInformation ? _psSummInfo : _psDocSummInfo; if (ps == null) { // // The property set does not exist, so create it. // if (propVal != null) { _pss.Create( ref fmtid, ref fmtid, SafeNativeCompoundFileConstants.PROPSETFLAG_ANSI, (uint)_grfMode, out ps ); if (fmtid == FormatId.SummaryInformation) { _psSummInfo = ps; } else { _psDocSummInfo = ps; } } else { // // But if we were going to delete the property anyway, there's // nothing to do. // return; } } PROPSPEC[] propSpecs = new PROPSPEC[1]; PROPVARIANT[] vals = new PROPVARIANT[1]; propSpecs[0].propType = (uint)PropSpecType.Id; propSpecs[0].union.propId = propId; if (propVal == null) { // // New value is null => remove the property. Unlike in the case of ReadMultiple, // we can just let this one throw an exception on failure. There are no non-zero // success codes to worry about. // ps.DeleteMultiple(1, propSpecs); return; } // // New value is non-null => set a new value for the property. // IntPtr pszVal = IntPtr.Zero; try { if (propVal is string) { // // 1) We store string properties internally as UTF-16. // During save, convert the string (UTF-16) to CP_ACP and back // 2) If property value changed during that process, store it in CF OLE Storage as UTF-8 // 3) Otherwise store it as CP_ACP // string inputString = propVal as string; pszVal = Marshal.StringToCoTaskMemAnsi(inputString); string convertedString = Marshal.PtrToStringAnsi(pszVal); if (String.CompareOrdinal(inputString, convertedString) != 0) { // The string is not an ASCII string. Use UTF-8 to encode it! byte[] byteArray = UTF8Encoding.UTF8.GetBytes(inputString); int nLen = byteArray.Length; // // Before memory allocation for holding UTF-8 codes, we need to first free the memory // allocated by Marshal.StringToCoTaskMemAnsi(). // Note that if there is any exception in this try scope, the memory will still be released // by the finally of this try scope. // if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); pszVal = IntPtr.Zero; } pszVal = Marshal.AllocCoTaskMem(checked(nLen + 1)); //The extra one byte is for the string terminator null. Marshal.Copy(byteArray, 0, pszVal, nLen); Marshal.WriteByte(pszVal, nLen, 0); //Put the string terminator null at the end of the array. } vals[0].vt = VARTYPE.VT_LPSTR; vals[0].union.pszVal = pszVal; } else if (propVal is DateTime) { // set FileTime as an Int64 to avoid pointer operations vals[0].vt = VARTYPE.VT_FILETIME; vals[0].union.hVal = ((DateTime)propVal).ToFileTime(); } else { throw new ArgumentException( SR.Get(SRID.InvalidDocumentPropertyType, propVal.GetType().ToString()), "propVal"); } // // Again, we can just let it throw on failure; no non-zero success codes. It won't throw // if the property doesn't exist. // ps.WriteMultiple(1, propSpecs, vals, 0); } finally { if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); } } } ////// Obtain the specified nullable DateTime property from the specified property set. Since nullable DateTime /// is a struct, the "null" case (when the property is absent) must be treated specially. /// /// /// "Format identifier" that specifies the property set from which to obtain the property. /// /// /// Identifier of the property to obtain. /// ////// A nullable DateTime structure representing the specified property. Note that a Generic Nullable /// struct can be compared successfully to null. /// private NullableGetDateTimeProperty( Guid fmtid, uint propId ) { object obj = GetOleProperty(fmtid, propId); return obj != null ? (Nullable )obj : new Nullable (); } private void OpenPropertyStorage( ref Guid fmtid, out IPropertyStorage ips ) { int hr = _pss.Open(ref fmtid, (uint)_grfMode, out ips); // // A COM "not found" error is acceptable; it just means that the // file doesn't have the requested property set. Any other COM error code // is an error. // if (hr == SafeNativeCompoundFileConstants.STG_E_FILENOTFOUND) { ips = null; // Just for safety; the failed call to Open should have set it to null. } else { // Throw if we failed. SecurityHelper.ThrowExceptionForHR(hr); } } private VARTYPE GetVtFromPropId( Guid fmtid, uint propId ) { if (fmtid == FormatId.SummaryInformation) { switch (propId) { case PropertyId.Title: case PropertyId.Subject: case PropertyId.Creator: case PropertyId.Keywords: case PropertyId.Description: case PropertyId.LastModifiedBy: case PropertyId.Revision: return VARTYPE.VT_LPSTR; case PropertyId.LastPrinted: case PropertyId.DateCreated: case PropertyId.DateModified: return VARTYPE.VT_FILETIME; default: throw new ArgumentException( SR.Get(SRID.UnknownDocumentProperty, fmtid.ToString(), propId), "propId" ); } } else if (fmtid == FormatId.DocumentSummaryInformation) { switch (propId) { case PropertyId.Category: case PropertyId.Identifier: case PropertyId.ContentType: case PropertyId.Language: case PropertyId.Version: case PropertyId.ContentStatus: return VARTYPE.VT_LPSTR; default: throw new ArgumentException( SR.Get(SRID.UnknownDocumentProperty, fmtid.ToString(), propId), "propId" ); } } else { throw new ArgumentException( SR.Get(SRID.UnknownDocumentProperty, fmtid.ToString(), propId), "fmtid" ); } } private void CheckDisposed() { if (_disposed) throw new ObjectDisposedException(null, SR.Get(SRID.StorageBasedPackagePropertiesDiposed)); } #endregion Private Methods //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields private bool _disposed; private int _grfMode; // Mode in which the compound file was opened. // // Interface to the OLE property sets in the compound file representing // the RM-protected OPC package. // private IPropertySetStorage _pss; private IPropertyStorage _psSummInfo; private IPropertyStorage _psDocSummInfo; #endregion Private Fields } } // 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: // This class provides access to the package properties of an RM-protected OPC // document. The "package properties" are a subset of the standard OLE property // sets SummaryInformation and DocumentSummaryInformation, and include such // properties as Title and Subject. // // An RM-protected OPC document is physically represented by an OLE compound // file containing a well-known stream in which an OPC Zip archive, encrypted // in its entirety, is stored. The package properties of an RM-protected OPC // document are stored in the standard OLE compound file property set streams, // \005SummaryInformation and \005DocumentSummaryInformation. The contents of // these streams is intended to mirror the corresponding metadata properties // stored in the OPC package itself. These properties are duplicated, in the // clear, outside of the encrypted OPC package stream, so that tools such as // the Shell can display the properties without having to decrypt the package. // // It is the responsibility of the application to ensure that the properties in // the OLE property set streams are synchronized with the properties in the // OPC package. // // History: // 06/16/2005: LGolding: Initial implementation. // 09/23/2005: JohnLarc: Removed from public surface. // //----------------------------------------------------------------------------- using System; using System.IO; using System.IO.Packaging; using System.Runtime.InteropServices; using System.Windows; using System.Security; // SecurityCritical using System.Text; //For UTF-8 encoding. using MS.Internal; using MS.Internal.Interop; using MS.Internal.IO.Packaging.CompoundFile; using MS.Internal.WindowsBase; //for SecurityHelper. // Enable presharp pragma warning suppress directives. #pragma warning disable 1634, 1691 namespace MS.Internal.IO.Packaging { ////// This class provides access to the package properties, such as Title and /// Subject, of an RM-protected OPC package. These properties are a subset of /// of the standard OLE property sets SummaryInformation and /// DocumentSummaryInformation. /// internal class StorageBasedPackageProperties : PackageProperties, IDisposable { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- #region Constructors // // The constructor is internal because an application never directly // creates an object of this class. An application obtains an object // of this class through the PackageProperties CLR property of the // EncryptedPackageEnvelope class: // // EncryptedPackageEnvelope ep = EncryptedPackageEnvelope.Create("mydoc.xps", ...); // PackageProperties props = ep.PackageProperties; // internal 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); } ////// Finalizer. /// ~StorageBasedPackageProperties() { Dispose(false); } #endregion Constructors //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties #region SummaryInformation properties ////// The title. /// public override string Title { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Title) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Title, value); } } ////// The topic of the contents. /// public override string Subject { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Subject) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Subject, value); } } ////// The primary creator. The identification is environment-specific and /// can consist of a name, email address, employee ID, etc. It is /// recommended that this value be only as verbose as necessary to /// identify the individual. /// public override string Creator { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Creator) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Creator, value); } } ////// A delimited set of keywords to support searching and indexing. This /// is typically a list of terms that are not available elsewhere in the /// properties. /// public override string Keywords { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Keywords) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Keywords, value); } } ////// The description or abstract of the contents. /// public override string Description { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Description) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Description, value); } } ////// The user who performed the last modification. The identification is /// environment-specific and can consist of a name, email address, /// employee ID, etc. It is recommended that this value be only as /// verbose as necessary to identify the individual. /// public override string LastModifiedBy { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.LastModifiedBy) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.LastModifiedBy, value); } } ////// The revision number. This value indicates the number of saves or /// revisions. The application is responsible for updating this value /// after each revision. /// public override string Revision { get { return GetOleProperty(FormatId.SummaryInformation, PropertyId.Revision) as string; } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.Revision, value); } } ////// The date and time of the last printing. /// public override NullableLastPrinted { get { return GetDateTimeProperty(FormatId.SummaryInformation, PropertyId.LastPrinted); } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.LastPrinted, value); } } /// /// The creation date and time. /// public override NullableCreated { get { return GetDateTimeProperty(FormatId.SummaryInformation, PropertyId.DateCreated); } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.DateCreated, value); } } /// /// The date and time of the last modification. /// public override NullableModified { get { return GetDateTimeProperty(FormatId.SummaryInformation, PropertyId.DateModified); } set { SetOleProperty(FormatId.SummaryInformation, PropertyId.DateModified, value); } } #endregion SummaryInformation properties #region DocumentSummaryInformation properties /// /// The category. This value is typically used by UI applications to create navigation /// controls. /// public override string Category { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Category) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Category, value); } } ////// A unique identifier. /// public override string Identifier { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Identifier) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Identifier, value); } } ////// The type of content represented, generally defined by a specific /// use and intended audience. Example values include "Whitepaper" /// and "Exam". (This property is distinct from /// MIME content types as defined in RFC 2045.) /// public override string ContentType { get { string contentType = GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentType) as string; if (contentType == null) { return contentType; } else { //Creating a ContentType object to validate the content type string. //Can replace this later with a static method to validate the content type. return new ContentType(contentType).ToString(); } } set { if (value == null) { //indicates that the property should be deleted. SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentType, value); } else { //Creating a ContentType object to validate the content type string. //Can replace this later with a static method to validate the content type. SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentType, new ContentType(value).ToString()); } } } ////// The primary language of the package content. The language tag is /// composed of one or more parts: A primary language subtag and a /// (possibly empty) series of subsequent subtags, for example, "EN-US". /// These values MUST follow the convention specified in RFC 3066. /// public override string Language { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Language) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Language, value); } } ////// The version number. This value is set by the user or by the application. /// public override string Version { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Version) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.Version, value); } } ////// The status of the content. Example values include "Draft", /// "Reviewed", and "Final". /// public override string ContentStatus { get { return GetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentStatus) as string; } set { SetOleProperty(FormatId.DocumentSummaryInformation, PropertyId.ContentStatus, value); } } #endregion DocumentSummaryInformation properties //------------------------------------------------------ // // IDisposable Methods // //------------------------------------------------------ #region IDisposable ////// Dispose(bool disposing) executes in two distinct scenarios. /// If disposing equals true, the method has been called directly /// or indirectly by a user's code. Managed and unmanaged resources /// can be disposed. /// /// If disposing equals false, the method has been called by the /// runtime from inside the finalizer and you should not reference /// other objects. Only unmanaged resources can be disposed. /// /// /// true if called from Dispose(); false if called from the finalizer. /// protected override void Dispose( bool disposing ) { try { if (!_disposed && disposing) { if (_psSummInfo != null) { try { ((IDisposable)_psSummInfo).Dispose(); } finally { _psSummInfo = null; } } if (_psDocSummInfo != null) { try { ((IDisposable)_psDocSummInfo).Dispose(); } finally { _psDocSummInfo = null; } } } } finally { // // By setting _disposed = true, we ensure that all future accesses to // this object will fail (because both GetOleProperty and SetOleProperty // call CheckDisposed). Note that we wrap the entire body of // Dispose(bool) (this method) in an "if (!_disposed)". // And we also set each reference to null immediately after attempting // to release it. So we never attempt to release any reference more // than once. // _disposed = true; base.Dispose(disposing); } } #endregion IDisposable #endregion Public Properties //----------------------------------------------------- // // Private Methods // //------------------------------------------------------ #region Private Methods ////// Obtain the specified property from the specified property set. /// /// /// "Format identifier" that specifies the property set from which to /// obtain the property. /// /// /// Identifier of the property to obtain. /// ////// An object of the appropriate type for the specified property which /// contains the property value, or null if the property does not exist /// in the encrypted package. /// ////// Critical: calls Marshal.PtrToStringAnsi which LinkDemands /// TreatAsSafe: can't be used to allocate a string from an arbitrary pointer since it doesn't accept a ptr. /// [SecurityCritical, SecurityTreatAsSafe] private object 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.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; } /// /// Set or delete the specified property in the specified property set. /// /// /// "Format identifier" that specifies the property set in which to /// set the property. /// /// /// Identifier of the property to set. /// /// /// An object of the appropriate type for the specified property which /// contains the property value, or null if the property is to be deleted. /// ////// Critical: Calls Marshal.StringToCoTaskMemAnsi which LinkDemands. Also has unsafe code block. /// TreatAsSafe: the memory that's allocated is freed before returning. Unsafe code block only manipulates local memory on the stack. /// [SecurityCritical, SecurityTreatAsSafe] private void SetOleProperty( Guid fmtid, uint propId, object propVal ) { CheckDisposed(); IPropertyStorage ps = fmtid == FormatId.SummaryInformation ? _psSummInfo : _psDocSummInfo; if (ps == null) { // // The property set does not exist, so create it. // if (propVal != null) { _pss.Create( ref fmtid, ref fmtid, SafeNativeCompoundFileConstants.PROPSETFLAG_ANSI, (uint)_grfMode, out ps ); if (fmtid == FormatId.SummaryInformation) { _psSummInfo = ps; } else { _psDocSummInfo = ps; } } else { // // But if we were going to delete the property anyway, there's // nothing to do. // return; } } PROPSPEC[] propSpecs = new PROPSPEC[1]; PROPVARIANT[] vals = new PROPVARIANT[1]; propSpecs[0].propType = (uint)PropSpecType.Id; propSpecs[0].union.propId = propId; if (propVal == null) { // // New value is null => remove the property. Unlike in the case of ReadMultiple, // we can just let this one throw an exception on failure. There are no non-zero // success codes to worry about. // ps.DeleteMultiple(1, propSpecs); return; } // // New value is non-null => set a new value for the property. // IntPtr pszVal = IntPtr.Zero; try { if (propVal is string) { // // 1) We store string properties internally as UTF-16. // During save, convert the string (UTF-16) to CP_ACP and back // 2) If property value changed during that process, store it in CF OLE Storage as UTF-8 // 3) Otherwise store it as CP_ACP // string inputString = propVal as string; pszVal = Marshal.StringToCoTaskMemAnsi(inputString); string convertedString = Marshal.PtrToStringAnsi(pszVal); if (String.CompareOrdinal(inputString, convertedString) != 0) { // The string is not an ASCII string. Use UTF-8 to encode it! byte[] byteArray = UTF8Encoding.UTF8.GetBytes(inputString); int nLen = byteArray.Length; // // Before memory allocation for holding UTF-8 codes, we need to first free the memory // allocated by Marshal.StringToCoTaskMemAnsi(). // Note that if there is any exception in this try scope, the memory will still be released // by the finally of this try scope. // if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); pszVal = IntPtr.Zero; } pszVal = Marshal.AllocCoTaskMem(checked(nLen + 1)); //The extra one byte is for the string terminator null. Marshal.Copy(byteArray, 0, pszVal, nLen); Marshal.WriteByte(pszVal, nLen, 0); //Put the string terminator null at the end of the array. } vals[0].vt = VARTYPE.VT_LPSTR; vals[0].union.pszVal = pszVal; } else if (propVal is DateTime) { // set FileTime as an Int64 to avoid pointer operations vals[0].vt = VARTYPE.VT_FILETIME; vals[0].union.hVal = ((DateTime)propVal).ToFileTime(); } else { throw new ArgumentException( SR.Get(SRID.InvalidDocumentPropertyType, propVal.GetType().ToString()), "propVal"); } // // Again, we can just let it throw on failure; no non-zero success codes. It won't throw // if the property doesn't exist. // ps.WriteMultiple(1, propSpecs, vals, 0); } finally { if (pszVal != IntPtr.Zero) { Marshal.FreeCoTaskMem(pszVal); } } } ////// Obtain the specified nullable DateTime property from the specified property set. Since nullable DateTime /// is a struct, the "null" case (when the property is absent) must be treated specially. /// /// /// "Format identifier" that specifies the property set from which to obtain the property. /// /// /// Identifier of the property to obtain. /// ////// A nullable DateTime structure representing the specified property. Note that a Generic Nullable /// struct can be compared successfully to null. /// private NullableGetDateTimeProperty( Guid fmtid, uint propId ) { object obj = GetOleProperty(fmtid, propId); return obj != null ? (Nullable )obj : new Nullable (); } private void OpenPropertyStorage( ref Guid fmtid, out IPropertyStorage ips ) { int hr = _pss.Open(ref fmtid, (uint)_grfMode, out ips); // // A COM "not found" error is acceptable; it just means that the // file doesn't have the requested property set. Any other COM error code // is an error. // if (hr == SafeNativeCompoundFileConstants.STG_E_FILENOTFOUND) { ips = null; // Just for safety; the failed call to Open should have set it to null. } else { // Throw if we failed. SecurityHelper.ThrowExceptionForHR(hr); } } private VARTYPE GetVtFromPropId( Guid fmtid, uint propId ) { if (fmtid == FormatId.SummaryInformation) { switch (propId) { case PropertyId.Title: case PropertyId.Subject: case PropertyId.Creator: case PropertyId.Keywords: case PropertyId.Description: case PropertyId.LastModifiedBy: case PropertyId.Revision: return VARTYPE.VT_LPSTR; case PropertyId.LastPrinted: case PropertyId.DateCreated: case PropertyId.DateModified: return VARTYPE.VT_FILETIME; default: throw new ArgumentException( SR.Get(SRID.UnknownDocumentProperty, fmtid.ToString(), propId), "propId" ); } } else if (fmtid == FormatId.DocumentSummaryInformation) { switch (propId) { case PropertyId.Category: case PropertyId.Identifier: case PropertyId.ContentType: case PropertyId.Language: case PropertyId.Version: case PropertyId.ContentStatus: return VARTYPE.VT_LPSTR; default: throw new ArgumentException( SR.Get(SRID.UnknownDocumentProperty, fmtid.ToString(), propId), "propId" ); } } else { throw new ArgumentException( SR.Get(SRID.UnknownDocumentProperty, fmtid.ToString(), propId), "fmtid" ); } } private void CheckDisposed() { if (_disposed) throw new ObjectDisposedException(null, SR.Get(SRID.StorageBasedPackagePropertiesDiposed)); } #endregion Private Methods //----------------------------------------------------- // // Private Fields // //----------------------------------------------------- #region Private Fields private bool _disposed; private int _grfMode; // Mode in which the compound file was opened. // // Interface to the OLE property sets in the compound file representing // the RM-protected OPC package. // private IPropertySetStorage _pss; private IPropertyStorage _psSummInfo; private IPropertyStorage _psDocSummInfo; #endregion Private Fields } } // 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
- AnalyzedTree.cs
- ETagAttribute.cs
- EmptyStringExpandableObjectConverter.cs
- WebConfigurationManager.cs
- PaintEvent.cs
- BridgeDataReader.cs
- TdsParser.cs
- ProcessHostMapPath.cs
- PeerApplicationLaunchInfo.cs
- FusionWrap.cs
- ContentTypeSettingDispatchMessageFormatter.cs
- HyperLinkStyle.cs
- CodeExpressionStatement.cs
- CompilerHelpers.cs
- Nullable.cs
- DecoderFallbackWithFailureFlag.cs
- PointCollection.cs
- FormViewDeletedEventArgs.cs
- MaxMessageSizeStream.cs
- URLBuilder.cs
- CharEntityEncoderFallback.cs
- CopyNamespacesAction.cs
- DirectoryObjectSecurity.cs
- DataGridViewRowCollection.cs
- ObjectDataSourceDisposingEventArgs.cs
- FacetValues.cs
- RegexWorker.cs
- CodeIdentifier.cs
- SubpageParaClient.cs
- EntityDataSource.cs
- CodePrimitiveExpression.cs
- HttpApplication.cs
- ManipulationDevice.cs
- HtmlControl.cs
- JsonDeserializer.cs
- CFStream.cs
- JournalEntry.cs
- ScaleTransform.cs
- ProtocolsSection.cs
- EditCommandColumn.cs
- GridViewCancelEditEventArgs.cs
- ErrorHandlerFaultInfo.cs
- File.cs
- CqlWriter.cs
- ResizingMessageFilter.cs
- CategoryValueConverter.cs
- ContainerCodeDomSerializer.cs
- LoginUtil.cs
- EditableTreeList.cs
- TreeNodeSelectionProcessor.cs
- SystemNetHelpers.cs
- DictionaryBase.cs
- TextSpan.cs
- AppDomainCompilerProxy.cs
- StylusPoint.cs
- CheckableControlBaseAdapter.cs
- X509AsymmetricSecurityKey.cs
- ClientSection.cs
- NonBatchDirectoryCompiler.cs
- OnOperation.cs
- ValueSerializer.cs
- MetadataWorkspace.cs
- HttpRequest.cs
- Size3D.cs
- _RequestCacheProtocol.cs
- CopyAttributesAction.cs
- ReflectionServiceProvider.cs
- GlobalItem.cs
- SQLByteStorage.cs
- RegexReplacement.cs
- InplaceBitmapMetadataWriter.cs
- SvcMapFileSerializer.cs
- DataGridViewTopLeftHeaderCell.cs
- FormatConvertedBitmap.cs
- DocumentPageTextView.cs
- DesignerAutoFormat.cs
- SiteMapHierarchicalDataSourceView.cs
- RecordBuilder.cs
- ListSortDescription.cs
- IssuanceTokenProviderState.cs
- XmlUtil.cs
- Missing.cs
- DelegateOutArgument.cs
- PtsContext.cs
- DataGridViewCellPaintingEventArgs.cs
- ContentDisposition.cs
- wpf-etw.cs
- ManagedFilter.cs
- AnnouncementInnerClientCD1.cs
- WebServiceEnumData.cs
- QuaternionConverter.cs
- GeometryModel3D.cs
- InternalCompensate.cs
- DependencyObject.cs
- SHA1Managed.cs
- DefaultShape.cs
- ActiveDocumentEvent.cs
- Parsers.cs
- TrackingStringDictionary.cs
- Size3D.cs