Моя цель — добиться следующего:
Я хочу прочитать файл с диска (скажем, это файл изображения) и записать его в общую память, чтобы я мог прочитать его из общей памяти из другого процесса.
Сначала я последовал этот 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
, Так что я думаю, что я пытался сделать, не работает. Я также нашел эту цитату на другом вопросе:
Примечание: std :: string подходит для хранения двоичного буфера, а std :: wstring — нет!
(ИсточникЭто звучит так, будто я не могу сохранить данные файла так, как я пытался.
Итак, мой вопрос: я только что где-то допустил ошибку или мой подход некорректен? Может быть, мне нужно использовать другой тип файла для результата MapViewOfFile
? Может быть, мне нужно изначально загрузить файл в другой тип?
Я бы не стал давать полноценный ответ, так как у меня нет MCVE под рукой. Однако ФП попросил дать дополнительные разъяснения и, в отношении CopyMemory()
Я нашел некоторые вещи, которые стоит отметить (и было слишком долго писать комментарии только по этому поводу).
CopyMemory()
ничего особенно не посвящено файлам, отображенным в память. Это просто функция для копирования данных в пункт назначения из источника с размером в байтах.
Пока гуглил CopyMemory()
Я споткнулсяCopyMemory()
против memcpy()
«и нашел короткий ответ на Разработка игр:
Прямо из WINBASE.H
#define CopyMemory RtlCopyMemory
Тогда прямо из WINNT.H
#define RtlCopyMemory(Destination,Source,Length) memcpy((Destination),(Source),(Length))
Итак, мы здесь:
Определено в заголовке
<cstring>
void* memcpy( void* dest, const void* src, std::size_t count );
Копии
count
байты от объекта, на который указываетsrc
к объекту, указанномуdest
, Оба объекта интерпретируются как массивыunsigned char
,Если объекты перекрываются, поведение не определено.
Для особого случая (потенциально) перекрывающихся диапазонов источника / назначения, memcpy()
имеет «родного брата» memmove()
. В этом случае файлы с отображением в памяти, я не верю, что источник и назначение могут когда-либо пересекаться. Итак memcpy()
может быть хорошо (и, возможно, даже быстрее, чем memmove()
.)
Так что это не CopyMemory()
который обеспечивает «Магию доступа к файлу с отображением в памяти». Это уже произошло в другом вызове функции, который обязательно находится в исходном коде OP, но не упоминается в вопросе:
Сопоставляет представление файла с адресным пространством вызывающего процесса.
Возвращаемое значение
Если функция завершается успешно, возвращаемое значение является начальным адресом отображенного представления.
Следовательно, в случае успеха MapViewOfFile()
возвращает указатель на память, в которую был отображен файл. Доступ на чтение / запись может быть сделан впоследствии, как и любой другой доступ к памяти процесса — через оператор присваивания, через memcpy()
(или же CopyMemory()
) или что еще можно вообразить.
Наконец, ответ на дополнительный вопрос OP:
Как я мог читать данные в строку / байтовый массив на «другой» стороне, где я читал из общей памяти?
Чтение может быть выполнено точно таким же образом, за исключением того, что указатель на представление карты становится источником, а локальный буфер становится местом назначения. Но как определить размер? Эта проблема на самом деле более общая: сколько байтов занимают данные с переменной длиной? В C / C ++ есть два типичных ответа:
std::string
, std::vector
, так далее.)В конкретном случае 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[]()
которая имеет неконстантную версию.
Других решений пока нет …