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

Implementing IRandomAccessStream, not copying buffers

我对 targetBufferReadAsync() 实现中应该做什么感到有点困惑(适用于 win 8.1 的通用商店应用程序)。

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

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

我在里面的是一个带有一些块密码的加密流。我想用 IRandomAccessStream 包装它,因此它可以与 xaml 框架组件一起使用( 例如将加密的 images/video 传递给 ImageMediaElement 对象).在 class 中,我有一个字节数组,我将其重复用于每个块,将其传递给填充它并报告块大小的加密库。

所以,当 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),这实际上导致 phone 模拟器崩溃(桌面版显示图像正常,但内存不足)消耗相同)。到底是怎么回事?!

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;
});

使用 System.Runtime.InteropServices.WindowsRuntime;

(your byte array).CopyTo(targetBuffer);

IBuffer 中的长度 属性 有一个 setter

以下代码完全有效

targetBuffer.Length = (your integer here)

您有更多 CopyTo 变体可供选择。看看这个:

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