53

In Windows 8; I would like to pass the contents of a MemoryStream to a class that accepts a parameter of type Windows.Storage.Streams.IRandomAccessStream. Is there any way to convert this MemoryStream to an IRandomAccessStream?

bbosak
  • 5,353
  • 7
  • 42
  • 60

7 Answers7

98

To use the extensions: you must add "using System.IO"

In Windows8, .NET and WinRT types are generally converted to/from compatible types under the hood so you don't have to care about it.

For streams, however, there are helper methods to convert between WinRT and .NET streams: For converting from WinRT streams -> .NET streams:

InMemoryRandomAccessStream win8Stream = GetData(); // Get a data stream from somewhere.
System.IO.Stream inputStream = win8Stream.AsStream()

For converting from .NET streams -> WinRT streams:

Windows.Storage.Streams.IInputStream inStream = stream.AsInputStream();
Windows.Storage.Streams.IOutputStream outStream = stream.AsOutputStream();

UPDATE: 2013-09-01

Let it not be said that Microsoft doesn't listen to it's developer community ;)

In the announcement for .NET FX 4.5.1, Microsoft states that:

Many of you have been wanting a way to convert a .NET Stream to a Windows Runtime IRandomAccessStream. Let’s just call it an AsRandomAccessStream extension method. We weren't able to get this feature into Windows 8, but it was one of our first additions to Windows 8.1 Preview.

You can now write the following code, to download an image with HttpClient, load it in a BitmapImage and then set as the source for a Xaml Image control.

    //access image via networking i/o
    var imageUrl = "http://www.microsoft.com/global/en-us/news/publishingimages/logos/MSFT_logo_Web.jpg";
    var client = new HttpClient();
    Stream stream = await client.GetStreamAsync(imageUrl);
    var memStream = new MemoryStream();
    await stream.CopyToAsync(memStream);
    memStream.Position = 0;
    var bitmap = new BitmapImage();
    bitmap.SetSource(memStream.AsRandomAccessStream());
    image.Source = bitmap;

HTH.

Community
  • 1
  • 1
Rich Turner
  • 10,800
  • 1
  • 51
  • 68
7

Found a more elegant solution:

public static class MicrosoftStreamExtensions
{
    public static IRandomAccessStream AsRandomAccessStream(this Stream stream)
    {
        return new RandomStream(stream);
    }

}

class RandomStream : IRandomAccessStream
{
    Stream internstream;

    public RandomStream(Stream underlyingstream)
    {
        internstream = underlyingstream;
    }

    public IInputStream GetInputStreamAt(ulong position)
    {
        //THANKS Microsoft! This is GREATLY appreciated!
        internstream.Position = (long)position;
        return internstream.AsInputStream();
    }

    public IOutputStream GetOutputStreamAt(ulong position)
    {
        internstream.Position = (long)position;
        return internstream.AsOutputStream();
    }

    public ulong Size
    {
        get
        {
            return (ulong)internstream.Length;
        }
        set
        {
            internstream.SetLength((long)value);
        }
    }

    public bool CanRead
    {
        get { return this.internstream.CanRead; }
    }

    public bool CanWrite
    {
        get { return this.internstream.CanWrite; }
    }

    public IRandomAccessStream CloneStream()
    {
        throw new NotSupportedException();
    }

    public ulong Position
    {
        get { return (ulong)this.internstream.Position; }
    }

    public void Seek(ulong position)
    {
        this.internstream.Seek((long)position, SeekOrigin.Begin);
    }

    public void Dispose()
    {
        this.internstream.Dispose();
    }

    public Windows.Foundation.IAsyncOperationWithProgress ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
    {
        return this.GetInputStreamAt(this.Position).ReadAsync(buffer, count, options);
    }

    public Windows.Foundation.IAsyncOperation FlushAsync()
    {
        return this.GetOutputStreamAt(this.Position).FlushAsync();
    }

    public Windows.Foundation.IAsyncOperationWithProgress WriteAsync(IBuffer buffer)
    {
        return this.GetOutputStreamAt(this.Position).WriteAsync(buffer);
    }
}
Mark Vincze
  • 7,737
  • 8
  • 42
  • 81
bbosak
  • 5,353
  • 7
  • 42
  • 60
  • Very nice. I was looking for such wrapper from Microsoft and didn't succeed. – Roman Boiko Oct 06 '11 at 16:32
  • 2
    This solution has two problems, though. 1) If somebody calls to `GetInputStreamAt(0)`, then `GetOutputStreamAt(1)`, position of input stream will be also changed to `1`. 2) It accepts any stream, so `CanSeek` property may be equal to `false`. The `NotSupportedException` will be thrown in this case. Copying stream is less efficient for big steams but should not have these problems. – Roman Boiko Oct 06 '11 at 17:24
  • @RomanBoiko Granted, it's not a general solution for any type of stream, but it serves its purpose for many types of applications which would still use the new type of stream as though it were the old method (for example, just reading data from the stream and passing it into a WinRT component, then getting rid of the IRandomAccessStream. I should also point out that IRandomAccessStream was not intended to be used as a System.IO.Stream, and vice-versa, but this serves the purpose for when you want to use an IRandomAccessStream as though it were a Stream. – bbosak Oct 06 '11 at 22:14
  • It's a nice and simple solution for many problems when you know exactly that nobody is going to call both `GetInputStreamAt(someNumber)` and `GetOutputStreamAt(otherNumber)` and use them both in the same time period. It can be extended to prevent such situations by storing a flag that `Position` has been changed and throwing if it is changed later. Some bugs may still appear, though... The problems which I highlighted deal with breaking contract of the `IRandomAccessStream`, although it has not been documented yet. – Roman Boiko Oct 07 '11 at 07:06
  • @RomanBoiko Microsoft seems to have very poor quality documentation for Windows 8, very few code samples, and lots of TBDs (to-be-determined's) – bbosak Oct 07 '11 at 20:23
  • This class does not seem to work any more, because the IRandomAccessStream interface has been extended with methods such as CanRead, CanWrite, ReadAsync, etc. Might be a good idea to update the answer, because it is on top of Google results for the topic. – Mark Vincze Jun 03 '13 at 12:27
  • I expanded the sample code with the additional elements of the interface. – Mark Vincze Jun 03 '13 at 13:02
5

After some experimenting I found the following code to be working.

using System;
using System.IO;
using System.Threading.Tasks;
using Windows.Storage.Streams;

partial class MainPage
{
    public MainPage()
    {
        var memoryStream = new MemoryStream(new byte[] { 65, 66, 67 });
        ConvertToRandomAccessStream(memoryStream, UseRandomAccessStream);
        InitializeComponent();
    }

    void UseRandomAccessStream(IRandomAccessStream stream)
    {
        var size = stream.Size;
    } // put breakpoint here to check size

    private static async void ConvertToRandomAccessStream(MemoryStream memoryStream,
         Action<IRandomAccessStream> callback)
    {
        var randomAccessStream = new InMemoryRandomAccessStream();
        var outputStream = randomAccessStream.GetOutputStreamAt(0);
        var dw = new DataWriter(outputStream);
        var task = new Task(() => dw.WriteBytes(memoryStream.ToArray()));
        task.Start();
        await task;
        await dw.StoreAsync();
        var success = await outputStream.FlushAsync();
        callback(randomAccessStream);
    }
}

UPDATE: I also tried more elegant method implementation:

    private static void ConvertToRandomAccessStream(MemoryStream memoryStream,
        Action<IRandomAccessStream> callback)
    {
        var randomAccessStream = new InMemoryRandomAccessStream();
        var outputStream = randomAccessStream.GetOutputStreamAt(0);
        RandomAccessStream.Copy(memoryStream.AsInputStream(), outputStream);
        callback(randomAccessStream);
    }

Strangely, it doesn't work. When I call stream.Size later, I get zero.

UPDATE I changed the function to return the IRandomAccessStream rather than using the callback function

public static async Task<IRandomAccessStream> ConvertToRandomAccessStream(MemoryStream memoryStream)
{
    var randomAccessStream = new InMemoryRandomAccessStream();

    var outputStream = randomAccessStream.GetOutputStreamAt(0);
    var dw = new DataWriter(outputStream);
    var task = new Task(() => dw.WriteBytes(memoryStream.ToArray()));
    task.Start();

    await task;
    await dw.StoreAsync();

    await outputStream.FlushAsync();

    return randomAccessStream;
}
David Basarab
  • 72,212
  • 42
  • 129
  • 156
Roman Boiko
  • 3,576
  • 1
  • 25
  • 41
4

There is no built-in way method on Windows 8. For Windows 8.1 we added a Stream.AsRandomAccessStream() extension method:

internal static IRandomAccessStream ToRandomAccessStream(byte[] array)
{
    MemoryStream stream = new MemoryStream(array);
    return stream.AsRandomAccessStream();
}
Immo Landwerth
  • 3,239
  • 21
  • 22
3

None of the above works for me today (maybe some API changes since the answers were posted). The only way that works is

IRandomAccessStream inMemoryStream = new InMemoryRandomAccessStream();
using (var inputStream = stream.AsInputStream())
{
    await RandomAccessStream.CopyAsync(inputStream, inMemoryStream);
}
inMemoryStream.Seek(0);
Igor Kulman
  • 16,211
  • 10
  • 57
  • 118
0

Take a look to this link:

How To Convert Byte Array To IRandomAccessStream

It also gives examples and an implementation of a byte array constructor (and one for .NET streams), useful if you want to use the SetSource or SetSourceAsync methods of BitmapImage class (as in my case).

Hope this helps someone...

MAXE
  • 4,978
  • 2
  • 45
  • 61
0

This code snippet turns a stream (stream) into an InMemoryRandomAccessStream (ims) which implement IRandomAccessStream. The trick is that CopyTo has to be called on a background thread.

        InMemoryRandomAccessStream ims = new InMemoryRandomAccessStream();
        var imsWriter = ims.OpenWrite();
        await Task.Factory.StartNew(() => stream.CopyTo(imsWriter));
Robert Levy
  • 28,747
  • 6
  • 62
  • 94