2

I would like to create a new System.Windows.Xps.Packaging.XpsDocument object from byte array, as I will not want to store it immediately on a local machine.

By using a temp file it works fine:

public static XpsDocument OpenXpsDocument(string url)
{
    WebClient webClient = new System.Net.WebClient();
    byte[] data = webClient.DownloadData(url);

    using (BinaryWriter writer = new System.IO.BinaryWriter(File.OpenWrite(xpsTempFilePath)))
    {
        writer.Write(data);
        writer.Flush();
    }

    XpsDocument xpsDocument = new System.Windows.Xps.Packaging.XpsDocument(xpsTempFilePath, FileAccess.Read);
    return xpsDocument;
}

However, what I want to accomplish is more like this:

public static XpsDocument OpenXpsDocument(string url)
{
    WebClient webClient = new WebClient();
    byte[] data = webClient.DownloadData(url);
    Package package;
    using (Stream stream = new MemoryStream(data))
    {
        package = System.IO.Packaging.Package.Open(stream);
    }
    XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.SuperFast, url);
    return xpsDocument;
}

Usage of the aforementioned methods goes like that:

XpsDocument xps = TaxReturnCreator.OpenXpsDocument(tempFileUrl);
documentViewer1.Document = xps.GetFixedDocumentSequence();

And, using the last-described method of trying to display the XPS content in a WPF window (without saving) crashes with a System.ObjectDisposedException ("Cannot access a closed Stream") (First method works fine).

Am I supposed to still keep the Stream open after creating the XpsDocument or am I missing something else? Maybe someone knows a completely different / better method of retrieving XPS data as bytes over network and creating an XpsDocument object from the data?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
cccec
  • 43
  • 1
  • 2
  • 9

2 Answers2

7

You cannot close a stream backing an XpsDocument. You must allow the Package to manage the backing MemoryStream, which will be collected once this Package is collected. It may seem a bit of a heresy to do the following:

public static XpsDocument OpenXpsDocument(string url)
{
    var webClient = new WebClient();
    var data = webClient.DownloadData(url);
    var package = System.IO.Packaging.Package.Open(new MemoryStream(data));
    var xpsDocument = new XpsDocument(package, 
                                      CompressionOption.SuperFast, 
                                      url);
    return xpsDocument;
}

but it is how this must be done.

  • How will the MemoryStream be disposed? – 123 456 789 0 Aug 14 '13 at 20:29
  • @LeoLorenzoLuis: It's managed by the package store, generally. The package that is opened implements IDisposable, so you can always kill it when it is time to remove it and the stream from memory. You can get any package from the store as long as you remember its URI http://msdn.microsoft.com/en-us/library/system.io.packaging.packagestore.getpackage.aspx –  Aug 14 '13 at 21:14
  • Does it mean if I close the package, the package story will dispose the memory stream that is referenced with the package that was opened? – 123 456 789 0 Aug 15 '13 at 03:56
  • 1
    @LeoLorenzoLuis: Dispose the package, dispose the stream. Don't dispose the package, it remains in memory with the stream. More than that I can't tell you. You can conduct your own tests or spelunk the code. –  Aug 15 '13 at 12:07
  • why not wrap both in an extra class that is disposable? Like: 'private class XpsDocumentAndPackage : IDisposable { public XpsDocument Document { get; set; } public Package Package { get; set; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { Document.Close(); Package.Close(); } } }' – MBoros Jan 26 '14 at 00:39
0

You should try including

XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.SuperFast, url);

into using block, i.e.

Package package;
using (Stream stream = new MemoryStream(data))
{
    package = System.IO.Packaging.Package.Open(stream);
    XpsDocument xpsDocument = new XpsDocument(package, CompressionOption.SuperFast, url);
}
return xpsDocument;
Petr Abdulin
  • 33,883
  • 9
  • 62
  • 96
  • 1
    won't work. Any action on the document will result in the closed stream being accessed. –  Jul 05 '11 at 15:33