Skip to content

System.IO.Packaging.Package should have a way to handle common errors with malformed packages #26084

@twsouthwick

Description

@twsouthwick

System.IO.Packaging.Package provides a simple abstraction over a container to hold multiple objects in a single entity. This is commonly used in nupkg packages, vsix extensions, appx, and also Office documents. The package format makes heavy use of URIs to maintain relationships between various components. However, Office will allow a user to generate an invalid URI for one of these relationships, and then opening via System.IO.Packaging fails with no easy way of recovery besides manually updating the file, which necessitates a deeper understanding of the file format that should be abstracted by the library.

This is to work around a breaking change that occurred between 4.0->4.5. This proposal is to provide a way to work around that change.

Example

There are multiple issues filed on the OpenXml SDK project (which abstracts the Office format to strongly typed classes) and related projects where users have been hit by this. For example:

A simple Office document that will fail can be created by the following:

  1. Create a new Word document
  2. Add a hyperlink to an invalid URI (ie mailto:one@)
  3. Try to load with Package:
using (var package = Package.Open(pathToDoc))
{
    foreach (var part in package.GetParts())
    {
        part.GetRelationships();
    }
}

This will fail with the following exception:

System.UriFormatException
  HResult=0x80131537
  Message=Invalid URI: The hostname could not be parsed.
  Source=System.Private.Uri
  StackTrace:
   at System.Uri.CreateThis(String uri, Boolean dontEscape, UriKind uriKind)
   at System.Uri..ctor(String uriString, UriKind uriKind)
   at System.IO.Packaging.InternalRelationshipCollection.ProcessRelationshipAttributes(XmlCompatibilityReader reader)
   at System.IO.Packaging.InternalRelationshipCollection.ParseRelationshipPart(PackagePart part)
   at System.IO.Packaging.InternalRelationshipCollection..ctor(Package package, PackagePart part)
   at System.IO.Packaging.PackagePart.EnsureRelationships()
   at System.IO.Packaging.PackagePart.GetRelationshipsHelper(String filterString)
   at PackagingExample.Program.Main(String[] args) in c:\users\tasou\source\repos\PackagingExample\PackagingExample\Program.cs:line 15

Proposed API

namespace System.IO.Packaging
{
    public abstract class Package // Existing class
    {
        public static Package Open(string path, FileShare packageShare, PackageSettings settings);
        
        public static Package Open(string path, PackageSettings settings);

        public static Package Open(Stream stream, PackageSettings settings);
    }

    public class PackageSettings
    {
        public FileMode PackageMode { get; set; }

        public FileAccess PackageAccess { get; set; }

        public IUriProvider UriProvider { get; set; }
    }

    public interface IUriProvider
    {
        Uri CreateUri(string uriString, UriKind uriKind);
    }
}

Details

  • The PackageSettings class would allow for easily adding additional settings at a later point without adding more factory methods (i.e. Package.Open(...)). Since the FileMode and FileAccess properties are used on all factory methods, these are added to the settings object.
  • The Package.Open(...) methods listed extend the current factory methods that take a Stream or a path with an optional FileShare.
  • The default implementations of IUriProvider would essentially be this line: https://source.dot.net/#System.IO.Packaging/System/IO/Packaging/InternalRelationshipCollection.cs,363

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions