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

Консоль удаленного рабочего стола(rdp console)

Клиент удаленного рабочего стола (rdp) предоставляет нам возможность войти на сервер терминалов через консоль. Что…

1 месяц ago

Настройка сети в VMware Workstation

В VMware Workstation есть несколько способов настройки сети гостевой машины: 1) Bridged networking 2) Network…

1 месяц ago

Логи брандмауэра Windows

Встроенный брандмауэр Windows может не только остановить нежелательный трафик на вашем пороге, но и может…

1 месяц ago

Правильный способ отключения IPv6

Вопреки распространенному мнению, отключить IPv6 в Windows Vista и Server 2008 это не просто снять…

1 месяц ago

Ключи реестра Windows, отвечающие за параметры экранной заставки

Параметры экранной заставки для текущего пользователя можно править из системного реестра, для чего: Запустите редактор…

1 месяц ago

Как управлять журналами событий из командной строки

В этой статье расскажу про возможность просмотра журналов событий из командной строки. Эти возможности можно…

1 месяц ago