如何从 cppwinrt 中的 StorageFile 读取字节数据?

How to read byte data from a StorageFile in cppwinrt?

Whosebug 的 2012 年回答(“我如何在 Windows Store 应用程序中读取二进制文件”)建议使用这种从 Windows Store 应用程序中的 StorageFile 读取字节数据的方法:

IBuffer buffer = await FileIO.ReadBufferAsync(theStorageFile);
byte[] bytes = buffer.ToArray();

这看起来很简单。当我在 cppwinrt 中工作时,我已将其转换为以下内容,在生成 StorageFiles 向量的同一 IAsyncAction 中。首先,我使用 theFilesVector.GetAt(index);

从 VectorView 获取一个 StorageFile

//那么这一行编译没有报错:

IBuffer buffer = co_await FileIO::ReadBufferAsync(theStorageFile);

//但是我找不到使缓冲区调用起作用的方法。

byte[] bytes = buffer.ToArray();

“byte[]”一开始是行不通的,所以我把它改成 byte*,但后来 错误是“class‘winrt::Windows::Storage::Streams::IBuffer’没有成员‘ToArray’”

事实上,Intellisense 没有为 IBuffer 列出这样的成员。然而,IBuffer 被指定为 ReadBufferAsync 的 return 类型。上面的示例代码似乎无法正常运行。

在 FileIO 的文档中,我发现建议使用 DataReader 从缓冲区中读取,在 cppwinrt 中应该类似于

DataReader dataReader = DataReader::FromBuffer(buffer);

即编译。然后应该可以使用以下 DataReader 方法读取字节,幸运的是,它以 cppwinrt 形式在 UWP 文档中提供:

void ReadBytes(Byte[] value) const;

但是,由于 cppwinrt 无法识别 Byte 类型,因此无法编译。如果我改为创建字节数组:

byte* fileBytes = new byte(buffer.Length());

不接受。错误是

‘No suitable constructor exists to convert from “byte*” to “winrt::arrayView::<uint8_t>”’ 

uint8_t当然是一个字节,那我们试试

uint8_t fileBytes = new uint8_t(buffer.Length());

这是错误的 - 显然我们确实需要创建一个 winrt::array_view。然而 2015 年的 Reddit post 说 array_view “死了”,我不确定如何声明,或者它是否有帮助。回想起来,从缓冲区读取字节的原始单行方法看起来非常漂亮。这是一个很长的post,但是任何人都可以建议当前最好的方法来简单地从cppwinrt 中的StorageFile 引用读取原始字节吗?如果 StorageFile 上只有 GetFileBytes() 和 GetFileBytesAsync() 方法就好了。

---更新:这是向前迈出的一步。我发现 Kenny Kerr 去年的评论解释说 array_view 不应该直接声明,但可以使用 std::vector 或 std::array 代替。这被接受为 DataReader 的 ReadBytes 方法的参数:

std::vector<unsigned char>fileBytes;
dataReader.ReadBytes(fileBytes);

现在唯一的问题是 std::vector 没有收到任何字节,即使引用文件的大小在 buffer.Length() 中正确地 returned 为 167,513 字节。这似乎表明缓冲区是好的,所以我不确定为什么应用于该缓冲区的 ReadBytes 方法不会产生任何数据。

更新 #2:Kenny 建议在 vector 中保留 space,这是我尝试过的方法:

m_file_bytes.reserve(buffer.Length());

但这并没有什么不同。这是目前使用 DataReader 的代码示例。

buffer = co_await FileIO::ReadBufferAsync(nextFile);
dataReader = DataReader::FromBuffer(buffer);
//The following line may be needed, but crashes
//co_await dataReader.LoadAsync(buffer.Length());
if (buffer.Length())
{
m_file_bytes.reserve(buffer.Length());
dataReader.ReadBytes(m_file_bytes);
}
The crash, btw, is 

throw hresult_error(result, hresult_error::from_abi);

那么,上面引用的 2012 年原始解决方案是否在当今世界行不通?但是当然必须有某种方法可以从文件中读取字节,所以我只是遗漏了一些对其他人来说可能很明显的东西。

最终(我认为)更新:Kenny 关于向量需要大小的建议已经切中要害。如果首先使用 m_file_bytes.assign(buffer.Length(),0) 准备矢量,那么它会被文件数据填充。现在我唯一担心的是,我并不真正了解 IAsyncAction 的工作方式,可能在异步循环时遇到问题,但我们拭目以待。

array_view 弥补了 Windows API 和 C++ 数组类型之间的差距。在此示例中,ReadBytes 方法期望调用方提供一些可以将字节复制到其中的数组。 array_view 将指针转发给调用者的数组及其大小。在本例中,您传递的是一个空向量。在调用 ReadBytes 之前尝试调整向量的大小。

当您知道预期有多少字节(在本例中为 2 个字节)时,这对我有用:

    std::vector<unsigned char>fileBytes;
    fileBytes.resize(2);        
    DataReader reader = DataReader::FromBuffer(buffer);
    reader.ReadBytes(fileBytes);
    cout<< fileBytes[0] << endl;
    cout<< fileBytes[1] << endl;