我有点困惑,在ReadAsync()
实现(win8.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));
我现在有一个naïve实现,它使用内存流(除了字节数组缓冲区),在那里收集数据并从它读回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;
});
}
UPDATE:我用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;
});
using 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);