С++ Чтение файла с диска и запись его в общую память

Моя цель - добиться следующего:

Я хочу прочитать файл с диска (допустим, это файл изображения) и записать его в общую память, чтобы я мог прочитать его из общей памяти из другого процесса. Сначала я следовал этому учебнику msdn для создания простой реализации разделяемой памяти, содержащей строку. Это работает нормально.

Затем я нашел способ прочитать образ с диска. Реализация выглядит следующим образом:

std::ifstream fin("path/to/img.png", std::ios::in | std::ios::binary);
std::ostringstream oss;
oss << fin.rdbuf();
std::string data(oss.str());

Итак, теперь у меня есть std::string, содержащий мои данные. data.length() указывает, что файл, который я прочитал, успешно сохранен там. В примере msdn тип результата MapViewOfFile равен LPTSTR, поэтому я искал способ преобразовать std::string, который у меня есть, в LPTSTR, что, насколько я понимаю, является const wchar_t*. Я делаю это следующим образом:

std::wstring widestr = std::wstring(data.begin(), data.end());
const wchar_t* widecstr = widestr.c_str();

Но если я сейчас проверю _tcslen(widecstr), результат будет 4. Так что я предполагаю, что то, что я пытался сделать, не работает. Я также нашел эту цитату по другому вопросу SO:

Примечание: std::string подходит для хранения «бинарного» буфера, а std::wstring — нет!

(Источник) Это звучит так, как будто я не могу хранить данные файла так, как я пытался.

Итак, мой вопрос: я только что где-то ошибся или мой подход ошибочен? Может быть, мне нужно использовать другой тип файла для результата MapViewOfFile? Может быть, мне нужно изначально загрузить файл в другой тип?


person Jdv    schedule 06.05.2018    source источник
comment
Когда вы загрузили файл изображения в std::string, вы злоупотребили char представлением байтов. Это нормально, и я бы поступил так же. Пример с общей памятью MSDN имеет дело с текстовыми строками, которые могут использовать расширенные символы в зависимости от того, как определено TCHAR. Забудьте об этом - в вашем случае это не имеет значения. CopyMemory((PVOID)pBuf, data.data(), data.size()); должно быть хорошо.   -  person Scheff's Cat    schedule 06.05.2018
comment
Не по теме, но чтобы сделать ваш код переносимым, лучше используйте boost.interprocess, а не Win32 API.   -  person Asesh    schedule 06.05.2018
comment
@Scheff спасибо, похоже, это сработало. Если вы сделаете это ответом, я могу принять его. Кроме того, не могли бы вы сказать мне, как я могу прочитать данные в массив строк/байтов на другой стороне, где я читал из общей памяти?   -  person Jdv    schedule 08.05.2018


Ответы (1)


Я бы не стал давать полноценный ответ, поскольку у меня нет под рукой MCVE. Однако ОП запросил дополнительные разъяснения и относительно CopyMemory() Я нашел некоторые вещи, которые стоит отметить (и это было слишком долго, чтобы писать комментарий только об этом).

CopyMemory() ничто особенно посвящен файлам с отображением памяти. Это просто функция для копирования данных в место назначения из источника с размером в байтах.

При поиске в Google CopyMemory() я наткнулся на "CopyMemory() против memcpy()" и нашел краткий ответ на Разработчик игр:

Прямо из WINBASE.H

#define CopyMemory RtlCopyMemory

Затем прямо из WINNT.H

#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))

Итак, мы здесь:

std::memcpy()

Определено в заголовке <cstring>

void* memcpy( void* dest, const void* src, std::size_t count );

Копирует count байт из объекта, на который указывает src, в объект, на который указывает dest. Оба объекта интерпретируются как массивы unsigned char.

Если объекты перекрываются, поведение не определено.

Для особого случая (потенциально) перекрывающихся диапазонов источника/назначения, memcpy() имеет «родственного брата» memmove(). В этом случае файлов с отображением памяти я не верю, что источник и место назначения могут когда-либо перекрываться. Таким образом, memcpy() может подойти (и потенциально даже быстрее, чем memmove()).

Таким образом, это не CopyMemory(), который обеспечивает «магию доступа к файлам с отображением памяти». Это уже произошло в другом вызове функции, который наверняка есть в исходном коде OP, но не упоминается в вопросе:

MapViewOfFile()

Сопоставляет представление сопоставления файла с адресным пространством вызывающего процесса.

Возвращаемое значение

Если функция завершается успешно, возвращаемое значение является начальным адресом отображаемого представления.

Следовательно, в случае успеха MapViewOfFile() возвращает указатель на память, в которую был отображен файл. Доступ для чтения/записи может быть выполнен впоследствии, как и любой другой доступ к памяти процесса через оператор присваивания, через memcpy() (или CopyMemory()) или что-либо еще, что только можно себе представить.

Наконец, ответ на дополнительный вопрос ОП:

как я мог прочитать данные в массив строк/байтов на «другой» стороне, где я читал из общей памяти?

Чтение может быть выполнено точно таким же образом, за исключением того, что указатель на представление карты становится источником, а локальный буфер становится местом назначения. Но как определить размер? Этот вопрос на самом деле более общий: сколько байтов занимают данные переменной длины? В C/C++ есть два типичных ответа:

  • либо сохранить размер данных (например, std::string, std::vector и т. д.)
  • или как-то отметить конец данных (например, нулевой терминатор в строках C).

В конкретном случае ОП первый вариант, вероятно, более разумен. Таким образом, размер данных полезной нагрузки (изображение) также может храниться в файле отображения памяти. На стороне считывателя сначала оценивается размер (который должен иметь определенный тип int и, следовательно, известное количество байтов), и этот размер используется для копирования данных полезной нагрузки.

Следовательно, на стороне писателя это может выглядеть так:

/* prior something like
 * unsigned char *pBuf = MapViewOfFile(...);
 * has been done.
 */
// write size:
size_t size = data.size();
CopyMemory(pBuf, (const void*)&size, sizeof size);
// write pay-load from std::string data:
CopyMemory(pBuf + sizeof size, data.data(), size);

Со стороны читателя это может выглядеть так:

/* prior something like
 * const unsigned char *pBuf = MapViewOfFile(...);
 * has been done.
 */
// read size:
size_t size = 0;
CopyMemory((void*)&size, pBuf, sizeof size);
// In C, I had probably done: size_t size = *(size_t*)pBuf; instead...
// allocate local buffer for pay-load
std::string data(size, '\0');
// read pay-load
CopyMemory(&data[0], pBuf + sizeof size, size);

Обратите внимание, что &data[0] предоставляет тот же адрес, что и data.data(). До C++ 17 не существовало неконстантной версии std::string::data(), поэтому хак с std::string::operator[]() имел неконстантную версию.

person Scheff's Cat    schedule 09.05.2018