C ++ Считать файл с диска и записать его в общую память - PullRequest
0 голосов
/ 06 мая 2018

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

Я хочу прочитать файл с диска (скажем, это файл образа) и записать его в общую память, чтобы я мог прочитать его из общей памяти из другого процесса. Сначала я следовал этому руководству 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? Может быть, мне нужно изначально загрузить файл в другой тип?

1 Ответ

0 голосов
/ 09 мая 2018

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

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

Пока гуглял для CopyMemory(), я наткнулся на "CopyMemory() против memcpy()" и нашел такой же хороший и короткий ответ на GameDev :

Прямо из 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() возвращает указатель на память, в которую был отображен файл. Доступ на чтение / запись может быть выполнен впоследствии, как и любой другой доступ к памяти процесса & ndash; через оператора присваивания, через memcpy() (или CopyMemory()) или что-либо еще мыслимое.

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

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

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

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

В конкретном случае OP первый вариант, вероятно, более целесообразен. Таким образом, размер данных полезной нагрузки (изображения) также может быть сохранен в файле отображения памяти. На стороне читателя сначала оценивается размер (который должен иметь определенный тип 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[]() с неконстантной версией.

...