user3743210
user3743210

Reputation: 211

How to read byte data from a StorageFile in cppwinrt?

A 2012 answer at StackOverflow (“How do I read a binary file in a Windows Store app”) suggests this method of reading byte data from a StorageFile in a Windows Store app:

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

That looks simple enough. As I am working in cppwinrt I have translated that to the following, within the same IAsyncAction that produced a vector of StorageFiles. First I obtain a StorageFile from the VectorView using theFilesVector.GetAt(index);

//Then this line compiles without error:

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

//But I can’t find a way to make the buffer call work.

byte[] bytes = buffer.ToArray();

“byte[]” can’t work, to begin with, so I change that to byte*, but then the error is “class ‘winrt::Windows::Storage::Streams::IBuffer’ has no member ‘ToArray’”

And indeed Intellisense lists no such member for IBuffer. Yet IBuffer was specified as the return type for ReadBufferAsync. It appears the above sample code cannot function as it stands.

In the documentation for FileIO I find it recommended to use DataReader to read from the buffer, which in cppwinrt should look like

DataReader dataReader = DataReader::FromBuffer(buffer);

That compiles. It should then be possible to read bytes with the following DataReader method, which is fortunately supplied in the UWP docs in cppwinrt form:

void ReadBytes(Byte[] value) const;

However, that does not compile because the type Byte is not recognized in cppwinrt. If I create a byte array instead:

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

that is not accepted. The error is

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

uint8_t is of course a byte, so let’s try

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

That is wrong - clearly we really need to create a winrt::array_view. Yet a 2015 Reddit post says that array_view “died” and I’m not sure how to declare one, or if it will help. That original one-line method for reading bytes from a buffer is looking so beautiful in retrospect. This is a long post, but can anyone suggest the best current method for simply reading raw bytes from a StorageFile reference in cppwinrt? It would be so fine if there were simply GetFileBytes() and GetFileBytesAsync() methods on StorageFile.

---Update: here's a step forward. I found a comment from Kenny Kerr last year explaining that array_view should not be declared directly, but that std::vector or std::array can be used instead. And that is accepted as an argument for the ReadBytes method of DataReader:

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

Only trouble now is that the std::vector is receiving no bytes, even though the size of the referenced file is correctly returned in buffer.Length() as 167,513 bytes. That seems to suggest the buffer is good, so I'm not sure why the ReadBytes method applied to that buffer would produce no data.

Update #2: Kenny suggests reserving space in the vector, which is something I had tried, this way:

m_file_bytes.reserve(buffer.Length());

But it didn't make a difference. Here is a sample of the code as it now stands, using 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);

Is it confirmed, then, that the original 2012 solution quoted above cannot work in today's world? But of course there must be some way to read bytes from a file, so I'm just missing something that may be obvious to another.

Final (I think) update: Kenny's suggestion that the vector needs a size has hit the mark. If the vector is first prepared with m_file_bytes.assign(buffer.Length(),0) then it does get filled with file data. Now my only worry is that I don't really understand the way IAsyncAction is working and maybe could have trouble looping this asynchronously, but we'll see.

Upvotes: 1

Views: 2377

Answers (2)

Wim Vanhenden
Wim Vanhenden

Reputation: 861

When you know how many bytes to expect (in this case 2 bytes), this worked for me:

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

Upvotes: 0

Kenny Kerr
Kenny Kerr

Reputation: 3115

The array_view bridges the gap between Windows APIs and C++ array types. In this example, the ReadBytes method expects the caller to provide some array that it can copy bytes into. The array_view forwards a pointer to the caller's array as well as its size. In this case, you're passing an empty vector. Try resizing the vector before calling ReadBytes.

Upvotes: 4

Related Questions