Как читать байтовые данные из StorageFile в cppwinrt?

Ответ 2012 года на StackOverflow («Как мне прочитать двоичный файл в приложении Windows Store») предлагает следующий метод чтения байтовых данных из StorageFile в приложении Windows Store:

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

Это выглядит достаточно просто. Поскольку я работаю в cppwinrt, я перевел это на следующее в том же IAsyncAction, который создал вектор StorageFiles. Сначала я получаю StorageFile из VectorView с помощью theFilesVector.GetAt (index);

// Тогда эта строка компилируется без ошибок:

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

// Но я не могу найти способ заставить работать буферный вызов.

byte[] bytes = buffer.ToArray();

«Byte []» не может работать, для начала, поэтому я изменяю его на byte *, но тогда возникает ошибка «класс 'winrt :: Windows :: Storage :: Streams :: IBuffer' не имеет члена 'ToArray' ”

И действительно, Intellisense не перечисляет таких членов для IBuffer. Тем не менее, IBuffer был указан как возвращаемый тип для ReadBufferAsync. Похоже, что приведенный выше пример кода не может работать в существующем виде.

В документации для FileIO я считаю, что рекомендуется использовать DataReader для чтения из буфера, который в cppwinrt должен выглядеть как

DataReader dataReader = DataReader::FromBuffer(buffer);

Это компилируется. После этого можно будет читать байты с помощью следующего метода DataReader, который, к счастью, предоставляется в документации UWP в форме cppwinrt:

void ReadBytes(Byte[] value) const;

Однако это не компилируется, потому что тип Byte не распознается в cppwinrt. Если вместо этого я создам массив байтов:

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. Тем не менее, в сообщении Reddit за 2015 год говорится, что array_view «умер», и я не уверен, как его объявить и поможет ли это. Этот оригинальный однострочный метод чтения байтов из буфера в ретроспективе выглядит так красиво. Это длинный пост, но может ли кто-нибудь предложить лучший текущий метод для простого чтения необработанных байтов из ссылки StorageFile в cppwinrt? Было бы хорошо, если бы в StorageFile были просто методы GetFileBytes () и GetFileBytesAsync ().

--- Обновление: вот шаг вперед. В прошлом году я нашел комментарий Кенни Керра, объясняющий, что array_view не следует объявлять напрямую, но вместо этого можно использовать std :: vector или std :: array. И это принимается в качестве аргумента для метода ReadBytes объекта DataReader:

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

Единственная проблема теперь заключается в том, что std :: vector не получает байтов, хотя размер файла, на который есть ссылка, правильно возвращается в buffer.Length () как 167 513 байт. Похоже, это говорит о том, что буфер в порядке, поэтому я не уверен, почему метод ReadBytes, примененный к этому буферу, не производит никаких данных.

Обновление №2: Кенни предлагает зарезервировать место в векторе, что я пробовал, вот так:

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 года, указанное выше, не может работать в современном мире? Но, конечно, должен быть какой-то способ читать байты из файла, поэтому мне просто не хватает чего-то, что может быть очевидно для другого.

Последнее (я думаю) обновление: предложение Кенни о том, что вектору нужен размер, попало в точку. Если вектор сначала подготовлен с помощью m_file_bytes.assign (buffer.Length (), 0), то он заполняется данными файла. Теперь меня беспокоит только то, что я действительно не понимаю, как работает IAsyncAction, и, возможно, у меня могут возникнуть проблемы с асинхронным циклом, но посмотрим.


person user3743210    schedule 14.02.2018    source источник


Ответы (2)


Array_view устраняет разрыв между Windows API и типами массивов C ++. В этом примере метод ReadBytes ожидает, что вызывающий объект предоставит некоторый массив, в который он может копировать байты. Array_view пересылает указатель на массив вызывающего абонента, а также его размер. В этом случае вы передаете пустой вектор. Попробуйте изменить размер вектора перед вызовом ReadBytes.

person Kenny Kerr    schedule 15.02.2018
comment
Спасибо, но похоже, что это не помогает. Позвольте мне обновить свой вопрос, добавив больше информации. - person user3743210; 15.02.2018
comment
Ах, вместо того, чтобы использовать резерв, я пробовал заполнить вектор нулями: - person user3743210; 15.02.2018
comment
(Что сработало!) Я все еще не уверен, что делаю это правильно, но, если возможно, добавлю исправление к приведенному выше очень длинному вопросу. Большое спасибо за вашу помощь. - person user3743210; 15.02.2018
comment
Я не сказал резерв. Я сказал изменить размер. В этом вся разница в мире. :) - person Kenny Kerr; 15.02.2018
comment
Правильно! Я не думал - запас, конечно, не влияет на размер. Я думал, что вектор будет расширяться по мере необходимости. - person user3743210; 15.02.2018
comment
Это работало в течение нескольких месяцев, как было предложено Кенни, но с последними обновлениями Win и VS у меня теперь есть ошибка после создания и определения размера массива байтов и передачи его в DataReader с помощью: data_reader.ReadBytes (file_bytes_array). Новая ошибка: «Функция, возвращающая auto, не может использоваться до того, как она определена». Но ReadBytes не возвращает никаких значений. Я попытался добавить #include winrt / Windows.Storage.Streams.h, но это привело к выходу CL.exe с кодом 2. Что-то изменилось? Я реформировал весь проект, чтобы устранить ошибку упаковки, но эти методы остались прежними. - person user3743210; 05.07.2019
comment
Отличная ссылка, спасибо. И похоже, что ошибка CL.exe, которая появлялась, когда я действительно включил необходимый заголовок, на самом деле была вызвана некоторыми проблемами пути в моем недавно реформированном решении. Очистка путей и включение заголовка снова исправили это. - person user3743210; 06.07.2019

Когда вы знаете, сколько байтов ожидать (в данном случае 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;
    
person Wim Vanhenden    schedule 15.04.2021