实现IRandomAccessStream,而不是复制缓冲区

时间:2016-06-28 12:18:54

标签: c# .net stream windows-runtime win-universal-app

我对targetBuffer实现中ReadAsync()应该做的事情感到困惑(对于win 8.1的Unversal商店应用程序)。

public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)

问题是,根据我的具体实施要求,我无法找到写入targetBuffer并更改其Length的方法。

我所拥有的是带有一些分组密码的加密流。我想用IRandomAccessStream包装它,因此它可以与xaml框架组件一起使用(,例如将加密的图像/视频传递给ImageMediaElement个对象)。在类中,我有一个字节数组,我为每个块重用它,将它传递给加密库,填充它并报告块大小。

因此,当调用IRandomAccessStream.ReadAsync()时,我需要以某种方式将我的字节输入targetBuffer并将其Length设置为正确的值...我似乎无法管理。

我试过了:

var stream = targetBuffer.AsStream();
while(count > 0) {
  /* doing something to get next chunk of data decrypted */
  // byte[] chunk is the array used to hold decrypted data
  // int chunkLength is the length of data (<= chunk.Length)

  count -= chunkLength;
  await stream.WriteAsync(chunk, 0, chunkLength);
}
return targetBuffer;

并且targetBuffer.Length仍然为零,但如果我尝试打印其内容,那么数据就在那里!

Debug.WriteLine(targetBuffer.GetByte(0..N)); 

我现在有一个使用内存流的天真实现(除了字节数组缓冲区),在那里收集数据并从中读回targetBuffer。这有效,但看起来很糟糕。托管流写入byte[]和WinRT流写入IBuffer,我只是无法找到解决方法,因此我不会浪费内存和性能。

我很感激任何想法。

这就是现在的样子。我最终使用字节数组作为解密缓冲区和可调整大小的内存流作为代理。

public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
{
  return AsyncInfo.Run<IBuffer, uint>(async (token, progress) => {
    Transport.Seek(0); // Transport is InMemoryRandomAccessStream
    var remaining = count;
    while(remaining > 0) {    
      /*
      ReadAsync() overload reads & decrypts data, 
      result length is <= remaining bytes,
      deals with block cipher alignment and the like
      */
      IBuffer chunk = await ReadAsync(remaining); 

      await Transport.WriteAsync(chunk);
      remaining -= chunk.Length;
    }
    Transport.Seek(0);
    // copy resulting bytes to target buffer
    await Transport.ReadAsync(targetBuffer, count, InputStreamOptions.None);
    return targetBuffer;
  });
}

更新:我已经使用7.9Mb的加密图像测试了上述解决方案。我把它送到Image这样的实例:

var image = new BitmapImage();
await image.SetSourceAsync(myCustomStream);
Img.Source = image; // Img is <Image> in xaml

一切都好,直到执行到达await Transport.ReadAsync(targetBuffer, count, InputStreamOptions.None);:内存消耗量猛增(从大约33mb到300 + mb),这有效地崩溃了手机模拟器(桌面版显示图像正常,尽管内存消耗相同)。到底是怎么回事?!

2017年3月解决

首先,我在某种程度上没有意识到我可以在将数据写入缓冲区后直接设置Length。其次,如果您在我的情况下做任何错误(自定义IRandomAccessStream实现是XAML Image元素的来源),应用程序崩溃不会留下任何日志并且不显示任何错误,所以它真的很难弄清楚出了什么问题。

这就是代码现在的样子:

public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
{
    return AsyncInfo.Run<IBuffer, uint>(async (token, progress) => {
        var output = targetBuffer.AsStream();
        while (count > 0) {
            //
            // do all the decryption stuff and get decrypted data
            // to a reusable buffer byte array
            //
            int bytes = Math.Min((int) count, BufferLength - BufferPosition);
            output.Write(decrypted, bufferPosition, bytes);
            targetBuffer.Length += (uint)bytes;
            BufferPosition += bytes;
            progress.Report((uint)bytes);
            count -= (uint)bytes;
        }
    }
    return targetBuffer;
});

1 个答案:

答案 0 :(得分:1)

使用System.Runtime.InteropServices.WindowsRuntime;

(your byte array).CopyTo(targetBuffer);

IBuffer中的Length属性有一个setter

以下代码完全有效

targetBuffer.Length = (your integer here)

你有更多的CopyTo变种可供选择。看看这个:

public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count);