Search code examples
c#.netwindows-runtimeiasyncoperation

How do wrap an Windows.Storage.Streams.IInputStream in an IInputStream interface?


I want to implement an IInputStream which delegates to another IInputStream and processes the read data before returning it to the user, like this:

using System;

using Windows.Storage.Streams;

using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;

namespace Core.Crypto {
    public class RC4InputStream : IInputStream {

        public RC4InputStream(IInputStream stream, byte[] readKey) {
            _stream = stream;

            _cipher = new RC4Engine();
            _cipher.Init(false, new KeyParameter(readKey));
        }

        public Windows.Foundation.IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
        {
            var op = _stream.ReadAsync(buffer, count, options);
            // Somehow magically hook up something so that I can call _cipher.ProcessBytes(...)
            return op;
        }

        private readonly IInputStream _stream;
        private readonly IStreamCipher _cipher;
    }
}

I have two different problems which I have not been able to answer by searching the vastness of the internets:

  • What is the best way to chain another operation to run after delegated ReadAsync() (I could use 'await' and maybe create a new IAsyncOperation using AsyncInfo but I do not know how to connect the progress reporter et al)
  • How do I access the data behind the 'IBuffer'?

Solution

  • You'll need to return your own IAsyncOperationWithProgress. You can use AsyncInfo.Run to do that:

    public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer buffer, uint count, InputStreamOptions options)
    {
        return AsyncInfo.Run<IBuffer, uint>(async (token, progress) =>
            {
                progress.Report(0);
                await _stream.ReadAsync(buffer, count, options);
                progress.Report(50);
                // call _cipher.ProcessBytes(...)
                progress.Report(100);
                return buffer;
            });
    }
    

    Of course you could make your own progress reporting more granular depending on what you are doing.

    To access data in IBuffer you can use either ToArray or AsStream extension method.