| File: MS\Internal\Documents\Application\DocumentProperties.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationUI\PresentationUI.csproj (PresentationUI) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // Description: This is a wrapper for DocumentPropertiesDialog, which caches the values which // are displayed in the Dialog, and controls security access. using System; using System.Drawing; using System.IO; using System.IO.Packaging; // For Package namespace MS.Internal.Documents.Application { /// <summary> /// Singleton wrapper class for the DocumentPropertiesDialog. /// </summary> internal sealed class DocumentProperties { //------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors /// <summary> /// Constructs the DocumentProperties class. /// </summary> /// <param name="uri"></param> private DocumentProperties(Uri uri) { ArgumentNullException.ThrowIfNull(uri); _uri = uri; } #endregion Constructors //------------------------------------------------------ // // Internal Properties // //------------------------------------------------------ #region Internal Properties /// <summary> /// Current package CoreProperties /// </summary> internal PackageProperties CoreProperties { get { // multiple returns are bad, however allocating another local // simply to return is worse // also we can not cache this decision because the values // may change when RM is on / off if (_rmProperties != null) { return _rmProperties; } else { return _xpsProperties; } } } /// <summary> /// Gets the current singleton instance of DocumentProperties. /// </summary> internal static DocumentProperties Current { get { return _current; } } /// <summary> /// Image representing an XPS Document /// </summary> internal Image Image { get { return _image; } set { _image = value; } } /// <summary> /// Filename of the package. /// </summary> internal string Filename { get { if (_filename is null && _uri is not null) { _filename = Path.GetFileName(_uri.LocalPath); } return _filename; } } /// <summary> /// Size of the package. /// </summary> internal long FileSize { get { long fileSize = 0; if (IsFileInfoValid) { fileSize = _fileInfo.Length; } return fileSize; } } /// <summary> /// Date package was created. /// </summary> internal DateTime? FileCreated { get { DateTime? fileCreated = null; if (IsFileInfoValid) { fileCreated = _fileInfo.CreationTime; } return fileCreated; } } /// <summary> /// Date package was last modified. /// </summary> internal DateTime? FileModified { get { DateTime? fileModified = null; if (IsFileInfoValid) { fileModified = _fileInfo.LastWriteTime; } return fileModified; } } /// <summary> /// Date package was last accessed. /// </summary> internal DateTime? FileAccessed { get { DateTime? fileAccessed = null; if (IsFileInfoValid) { fileAccessed = _fileInfo.LastAccessTime; } return fileAccessed; } } #endregion Internal Properties //------------------------------------------------------ // // Internal Methods // //------------------------------------------------------ #region Internal Methods /// <summary> /// This method copies members from one PackageProperties object to another. /// </summary> /// <remarks> /// This design is very fragile as if new properties are added by PackageProperties /// we will quietly lose data. An improved implementation would be to have /// PackageProperties implement this behavior or have a KeyValue pair exposed /// that we would iterate through and copy. /// </remarks> internal static void Copy(PackageProperties source, PackageProperties target) { target.Category = source.Category; target.ContentStatus = source.ContentStatus; target.ContentType = source.ContentType; target.Created = source.Created; target.Creator = source.Creator; target.Description = source.Description; target.Identifier = source.Identifier; target.Keywords = source.Keywords; target.Language = source.Language; target.LastModifiedBy = source.LastModifiedBy; target.LastPrinted = source.LastPrinted; target.Modified = source.Modified; target.Revision = source.Revision; target.Subject = source.Subject; target.Title = source.Title; target.Version = source.Version; } /// <summary> /// Constructs and Initializes the static 'Current' DocumentProperties object which /// is the singleton instance. /// </summary> /// <param name="uri">The URI for the package.</param> internal static void InitializeCurrentDocumentProperties(Uri uri) { // Ensure that DocumentProperties has not been constructed yet, and initialize a // new instance. System.Diagnostics.Debug.Assert( Current == null, "DocumentProperties initialized twice."); if (Current == null) { _current = new DocumentProperties(uri); } } internal void SetXpsProperties(PackageProperties properties) { _xpsProperties = properties; } internal void SetRightsManagedProperties(PackageProperties properties) { _rmProperties = properties; } /// <summary> /// Confirms whether the XPS properties exactly match the RM properties /// for purposes of determining whether the OPC properties have been changed /// in violation of signing policy. /// </summary> /// <returns> /// This will return true if the properties match /// or there are no RM properties in the package, /// otherwise it will return false. /// </returns> internal bool VerifyPropertiesUnchanged() { if (_xpsProperties == null ) { return false; } if (_rmProperties == null) { return true; } return (String.Equals(_xpsProperties.Category, _rmProperties.Category, StringComparison.Ordinal) && String.Equals(_xpsProperties.ContentStatus, _rmProperties.ContentStatus, StringComparison.Ordinal) && String.Equals(_xpsProperties.ContentType, _rmProperties.ContentType, StringComparison.Ordinal) && String.Equals(_xpsProperties.Creator, _rmProperties.Creator, StringComparison.Ordinal) && String.Equals(_xpsProperties.Description, _rmProperties.Description, StringComparison.Ordinal) && String.Equals(_xpsProperties.Identifier, _rmProperties.Identifier, StringComparison.Ordinal) && String.Equals(_xpsProperties.Keywords, _rmProperties.Keywords, StringComparison.Ordinal) && String.Equals(_xpsProperties.Language, _rmProperties.Language, StringComparison.Ordinal) && String.Equals(_xpsProperties.LastModifiedBy, _rmProperties.LastModifiedBy, StringComparison.Ordinal) && String.Equals(_xpsProperties.Revision, _rmProperties.Revision, StringComparison.Ordinal) && String.Equals(_xpsProperties.Subject, _rmProperties.Subject, StringComparison.Ordinal) && String.Equals(_xpsProperties.Title, _rmProperties.Title, StringComparison.Ordinal) && String.Equals(_xpsProperties.Version, _rmProperties.Version, StringComparison.Ordinal) && _xpsProperties.Created == _rmProperties.Created && _xpsProperties.LastPrinted == _rmProperties.LastPrinted && _xpsProperties.Modified == _rmProperties.Modified); } /// <summary> /// ShowDialog: Displays the DocumentProperties dialog. /// </summary> internal void ShowDialog() { // Setup the dialog data once and cache it for future calls. if (!_isDataAcquired) { AcquireData(); _isDataAcquired = true; } // Refresh file information before showing the dialog. if (_fileInfo != null) { // We intentionally `swallow exceptions here, since any failure // will make _fileInfo invalid as determined by the property // IsFileInfoValid. We also don't set _fileInfo to null so that // we can call Refresh again. This is useful since the file may // be inaccessible now but come back later. try { _fileInfo.Refresh(); } catch (ArgumentException) { } catch (IOException) { } } DocumentPropertiesDialog dialog = null; dialog = new DocumentPropertiesDialog(); dialog.ShowDialog(); dialog?.Dispose(); } #endregion Internal Methods //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods /// <summary> /// AcquireData: Setup the URI related data for the dialog. /// </summary> private void AcquireData() { // Ensure URI exists. if (_uri is null) { return; } // Determine if the URI represents a file if (_uri.IsFile) { // Determine the full path and assert for file permission string filePath = _uri.LocalPath; // Get the FileInfo for the current file FileInfo fileInfo = new FileInfo(filePath); // Check that FileInfo is valid, and save it. if (fileInfo.Exists) { _fileInfo = fileInfo; } } } #endregion Private Methods //------------------------------------------------------ // // Private Properties // //------------------------------------------------------ #region Private Properties /// <summary> /// True if the stored FileInfo is valid and can be used to determine /// file properties. /// </summary> /// <remarks> /// If the file has become completely inaccessible (i.e. the drive has /// been disconnected, the UNC path is unreachable, etc.), according to /// MSDN the Refresh call should throw an ArgumentException or /// IOException. In reality it doesn't appear to ever throw. Instead, /// if Refresh fails, any future calls to _fileInfo.Exists return /// false. Accessing any of the file information properties at that /// point throws an exception. As a result, if _fileInfo.Refresh() /// fails, we can leave _fileInfo as is and simply check /// _fileInfo.Exists before accessing it. /// </remarks> private bool IsFileInfoValid { get { return ((_fileInfo != null) && (_fileInfo.Exists)); } } #endregion Private Properties //------------------------------------------------------ // // Private Fields // // These must be checked for null prior to use. If available, use the CLR properties // above before these. // //------------------------------------------------------ #region Private Fields private static DocumentProperties _current = null; private Uri _uri; /// <summary> /// The properties in the XpsPackage (OPC). /// </summary> private PackageProperties _xpsProperties = null; /// <summary> /// The properties for RightsManaged encrypted packages (OLE). /// </summary> private PackageProperties _rmProperties = null; private bool _isDataAcquired; private Image _image = null; private string _filename = null; private FileInfo _fileInfo; #endregion Private Fields } }