【发布时间】:2017-03-20 21:32:27
【问题描述】:
我对在ReadAsync() 实现(win 8.1 的通用商店应用程序)中应该对targetBuffer 做什么感到有点困惑。
public IAsyncOperationWithProgress<IBuffer, uint> ReadAsync(IBuffer targetBuffer, uint count, InputStreamOptions options)
问题是,鉴于我的具体实施要求,我找不到写信给targetBuffer 并更改其Length 的方法。
我里面有一个带有一些分组密码的加密流。我想用 IRandomAccessStream 包装它,因此它可以与 xaml 框架组件一起使用(例如将加密的图像/视频传递给 Image 或 MediaElement 对象)。在类内部,我有一个字节数组,我为每个块重用它们,将它传递给加密库,加密库填充它并报告块大小。
因此,当调用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;
});
【问题讨论】:
-
你是如何初始化
targetBuffer的?targetBuffer.Length怎么可能为零?我认为在ReadAsync(...)方法中使用targetBuffer,它应该具有参数uint count的长度。 -
我根本没有创建 targetBuffer。当您执行
Image.SetSource(IRandomAccessStream)或MediaElement.SetSource(IRandomAccessStream, "video/mp4")并传递您自己的流实现时,ReadAsync()被调用,targetBuffer.Length为零。当您从 winrt 流中读入缓冲区时,长度会正确设置。 -
当你从一个winrt流读入缓冲区时,长度被正确设置,如代码所示:
Transport.ReadAsync(targetBuffer ... )。
标签: c# .net stream windows-runtime win-universal-app