Если работа с файлом, отображенным на память, завершена, необходимо обратиться к вызову UnmapViewOfFile. После этого следует дважды обратиться к функции СloseHandle для того, чтобы уничтожить объект отображения файла, а затем закрыть дескриптор файла.
Пример
Теперь посмотрим, как все это происходит на практике. В качестве примера создадим консольную программу, которая принимает в качестве аргумента командной строки список имен файлов, и копирует содержимое этих файлов один за другим в стандартный поток вывода. Таким образом, при помощи этой утилиты можно объединить несколько файлов в один или отобразить содержимое файлов на экране консоли.
Программа в нашем примере — это классический вариант программы, которую можно написать с использованием стандартных функций STDIO или потоков ввода/вывода C++. В листинге 1 приводится исходный код, использующий для этой цели системные вызовы ввода/вывода Windows. Теперь попробуем реализовать эту программу с использованием механизма отображения файлов на оперативную память.
Полный текст программы содержится в листинге 4.
Листинг 4. Версия NTCAT, использующая отображение файла в память
// обработка ошибок отсутствует
#include <windows.h>
#include <iostream.h>
// для удобства использования MessageBox:
void MB(char *s)
{
MessageBox(NULL, s, NULL, MB_OK|MB_ICONSTOP);
}
// эта программа выполняет основную работу:
void docat(char *fname)
{
HANDLE file; // файл
HANDLE map; // объект отображения файла
char *base; // указатель на начальную позицию
int n; // размер файла
// Открыть файл с атрибутами: exclusive access/read only/no create
file=CreateFile(fname, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (file==INVALID_HANDLE_VALUE)
{
MB("Can't open file");
exit(1);
}
// Размер файла не может быть больше <4GB
n=GetFileSize(file,NULL); // определение размера файла
// создать отображение файла (без имени, поскольку только для одного потока)
map=CreateFileMapping(file, NULL, PAGE_READONLY, 0, 0, NULL);
if (!map)
{
MB("Не могу открыть файл");
exit(2);
}
// преобразовать в указатель
base=(char *)MapViewOfFile(map, FILE_MAP_READ, 0, 0, 0);
if (!base)
{
MB("Не могу получить указатель на отображение");
exit(3);
}
// Вывод символа в поток:
// можно использовать cout<<base[i] в цикле либо putchar
// но вызов cout.write более эффективен
cout.write(base,n);
// завершение работы с отображением
UnmapViewOfFile(base);
CloseHandle(map);
CloseHandle(file);
}
void main(int argc,char *argv[])
{
if (argc==1)
{
MB("Надо запускать строкой: ntcat FILENAME [FILENAME ....]");
exit(9);
}
// обработать все файлы
while (--argc) docat(*++argv);
exit(0);
}
Не правда ли, ничего сложного? Получив указатель на место в памяти, программа использует единственный вызов cout.write для того, чтобы передать все содержимое файла в поток вывода. Обратите внимание, что программа приказывает отразить в оперативную память количество байт, равное точному размеру файла (соответствующий аргумент вызова CreateFileMapping равен 0). Вместе с тем программа использует вызов GetFileSize для того, чтобы определить точный размер файла и в дальнейшем передать этот размер вызову write. Также обратите внимание на то, что программа не рассчитана на работу с файлами, размер которых превышает 4 Гбайт.
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.