Categories: C/C++

winapi-file-mapping/

Отображением файла в память мы с вами пользовались в заметках об OpenGL, посвященных работе с моделями и текстурами , но не рассматривали подробно, как именно это работает. И хотя я почти уверен, что многие читатели этого блога уже знакомы с отображением файлов в память, я все же посчитал необходимом написать соответствующие заметки для тех читателей, кому данный механизм не знаком. Сегодня мы рассмотрим, как все это работает под Windows.

Заведем такую структуру, хранящую хэндл файла, хэндл отображения, размер файла, а также указатель на участок памяти с отображением:

#include <windows.h>

struct FileMapping {
HANDLE hFile ;
HANDLE hMapping ;
size_t fsize ;
unsigned char * dataPtr ;
} ;

Рассмотрим чтение из файла с использованием отображения.

Открываем файл:

HANDLE hFile = CreateFile ( fname, GENERIC_READ, 0 , nullptr,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, nullptr ) ;
if ( hFile == INVALID_HANDLE_VALUE ) {
std :: cerr << «fileMappingCreate — CreateFile failed, fname = »
<< fname << std :: endl ;
return nullptr ;
}

С процедурой CreateFile мы уже знакомы по заметке Учимся работать с файлами через Windows API , поэтому двигаемся дальше.

Получаем размер файла:

DWORD dwFileSize = GetFileSize ( hFile, nullptr ) ;
if ( dwFileSize == INVALID_FILE_SIZE ) {
std :: cerr << «fileMappingCreate — GetFileSize failed, fname = »
<< fname << std :: endl ;
CloseHandle ( hFile ) ;
return nullptr ;
}

Вторым аргументом процедура GetFileSize получает указатель на DWORD для записи старшей части размера файла. Передав в качестве этого аргумента nullptr, мы ограничили размер файла, который может вернутся. Если размер файла будет 4 Гб или больше, процедура вернет ошибку.

Создаем отображение:

HANDLE hMapping = CreateFileMapping ( hFile, nullptr, PAGE_READONLY,
0 , 0 , nullptr ) ;
if ( hMapping == nullptr ) {
std :: cerr << «fileMappingCreate — CreateFileMapping failed, fname = »
<< fname << std :: endl ;
CloseHandle ( hFile ) ;
return nullptr ;
}

Заметьте, что hMapping проверяется на равенство nullptr. Это не баг — согласно MSDN, в случае ошибки CreateFileMapping действительно возвращает пустой указатель, а не INVALID_HANDLE_VALUE, как можно было бы ожидать. Такая неконсистентность, к сожалению, встречается время от времени в WinAPI. Мы уже встречались с этой проблемой, изучая работу с реестром .

Наконец, получаем указатель на участок памяти с отображением, используя процедуру MapViewOfFile :

unsigned char * dataPtr = ( unsigned char * ) MapViewOfFile ( hMapping,
FILE_MAP_READ,
0 ,
0 ,
dwFileSize ) ;
if ( dataPtr == nullptr ) {
std :: cerr << «fileMappingCreate — MapViewOfFile failed, fname = »
<< fname << std :: endl ;
CloseHandle ( hMapping ) ;
CloseHandle ( hFile ) ;
return nullptr ;
}

Затем заполняем структуру FileMapping и возвращаем указатель на нее в качестве результата:

FileMapping * mapping = ( FileMapping * ) malloc ( sizeof ( FileMapping ) ) ;
if ( mapping == nullptr ) {
std :: cerr << «fileMappingCreate — malloc failed, fname = »
<< fname << std :: endl ;
UnmapViewOfFile ( dataPtr ) ;
CloseHandle ( hMapping ) ;
CloseHandle ( hFile ) ;
return nullptr ;
}

mapping > hFile = hFile ;
mapping > hMapping = hMapping ;
mapping > dataPtr = dataPtr ;
mapping > fsize = ( size_t ) dwFileSize ;

return mapping ;

Теперь читая mapping->fsize байт памяти по адресу mapping->dataPtr можно получить содержимое файла.

Когда отображение становится ненужным, не забываем его закрыть:

UnmapViewOfFile ( mapping > dataPtr ) ;
CloseHandle ( mapping > hMapping ) ;
CloseHandle ( mapping > hFile ) ;
free ( mapping ) ;

Как видите, все предельно просто. Исходники к этой заметке вы найдете здесь .

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

Задание со звездочкой для желающих — ответьте на следующий вопрос. Можно ли, работая с отображением файла в память, менять его размер, например, делать append или обрезать файл посередине, и если можно, то как? Если честно, я сам так с лету не готов ответить на этот вопрос, потому что такой задачи передо мной не вставало. Так что, с нетерпением жду ваших вариантов ответа в комментариях!

Дополнение: Пример отображения файла в память под Linux и MacOS

admin

Share
Published by
admin
Tags: C/C++

Recent Posts

После включения диагностического запуска не удается войти в Windows — как исправить?

Некоторые пользователи, экспериментируя с конфигурацией системы в окне msconfig могут столкнуться с ситуацией, когда после…

2 недели ago

WinScript — очистка и настройка Windows 11 и 10

На сайте не раз публиковались обзоры программ, предназначенных для очистки или настройки последних версий Windows.…

2 недели ago

Предварительный просмотр накопительного обновления не устанавливается — решение

При установке некоторых обновлений Windows 11, имеющих в названии «Предварительный просмотр накопительного обновления», многие пользователи…

2 недели ago

FixExec — восстановление ассоциаций .exe, .bat и .com файлов

Некоторые пользователи Windows 11, 10 и предыдущих версий системы могут столкнуться с ситуацией, когда исполняемые…

2 недели ago

Ошибка 0x800705b4 при обновлении Windows 11 и 10 — как исправить?

При установке обновлений Windows 11/10 некоторые пользователи могут столкнуться с ошибкой с кодом 0x800705b4 и…

2 недели ago

Обслуживание вашей версии Windows 11 окончено — что делать?

Пользователи Windows 11 могут столкнуться с сообщением «Обслуживание вашей версии Windows окончено» (Your version of…

2 недели ago