Класс CFileMap
Опубликовано: 12.11.2001
Исправлено: 13.03.2005
Версия текста: 1.1
Класс CFileMap был создан для удобства работы с проецируемыми в память файлами
(memory-mapped files). Отправной точкой послужила задача: в маленьком
(по размеру) WTL-приложении обеспечить произвольный (random) доступ к файлам данных.
При этом библиотеки ввода/вывода C++ (и, вообще, рантайм) использовать было нельзя.
Необходимо было открывать файлы, читать их определенные участки и на основе
анализа этой информации создавать другие файлы, заполняя их данными. Кроме того,
эти операции требовалось кешировать (по соображениям производительности). Когда
голова начала болеть достаточно сильно, я вспомнил про memory-mapped files.
Результат - это практически файл-обертка для основных операций с проецируемыми
файлами: открытие файла, создание объекта File Mapping, создание представления
в памяти (file view). В деструкторе объекта класса происходит автоматическое
освобождение выделенных ресурсов. На мой взгляд, получилось достаточно удобно.
Если же Вам понадобится более тонкое управление процессом, можете написать свой
класс, отталкиваясь от этого. Для демонстрации этого класса я написал очень простое консольное приложение
Xtract, позволяющее "выдернуть" звуки формата WAV из произвольных файлов,
т.е., простейший "граббер".
Необходимо заметить, что данная реализация класса имеет существенное ограничение.
Дело в том, что функции File Mapping поддерживают работу с файлами, превышающими
по размеру 2 гигабайта. Однако детали этой поддержки несколько отличаются в реализации
для 9x и NT/2000/XP систем. Кроме того, при работе в 32-битной архитектуре невозможно
напрямую отобразить на адресное пространство файл размером более 4 Гб. Поэтому
для работы с такими файлами их необходимо отображать в память "по кускам", используя
последовательные вызовы MapViewOfFile/UnmapViewOfFile.
Такая работа мне здорово напомнила "старые добрые" времена MS-DOS и сегментированых
16-битных моделей памяти. В данном случае, такой необходимости не было, и принята
следующая схема:
sizeof(file)==sizeof(file map)==sizeof(map view)<=2 Gb |
Иначе говоря, класс не поддерживает работу с файлами больше 2 гигабайт.
Исходный текст класса (1k)
Демонстрационный проект (20k)
Описание класса
В несколько упрощенном виде, описание класса выглядит так:
class CFileMap
{
public:
CFileMap();
CFileMap(LPCTSTR path, bool write=false);
CFileMap(LPCTSTR path, DWORD size);
~CFileMap();
operator bool();
bool Open(LPCTSTR path, bool write);
bool Create(LPCTSTR path, DWORD size);
void Close();
BYTE* Base();
DWORD Size();
bool OpenInternal(LPCTSTR path, DWORD dwAccess, DWORD dwCreation, DWORD flProtect,
DWORD dwPageAccess, DWORD size=0);
}; |
Детали реализации можно увидеть в заголовочном файле filemap.h
Использование методов
Конструкторы
Конструктор по умолчанию не создает никаких объектов File Mapping. Это можно
сделать позднее, вызвав Open/Create.
CFileMap(LPCTSTR path, bool write=false);
|
Этот конструктор открывает существующий файл, путь к которому указан в
параметре path. Вид доступа определяется параметром write.
По умолчанию файл открывается для чтения.
CFileMap(LPCTSTR path, DWORD size);
|
Этот конструктор создает для записи файл, путь к которому указан в path.
При создании файла Вы должны указать его размер, передав его в параметре size.
operator bool()
Предназначен для удобной индикации состояния объекта. Возвращает false, если
произошла какая-нибудь ошибка, и true в успешных обстоятельствах.
ПРИМЕЧАНИЕ
Данная функция проверяет дескриптор (описатель) открытого файла на недопустимое
значение INVALID_HANDLE_VALUE.
При компиляции класса и тестового примера под UNICODE выявилась одна неприятность:
вопреки документации, будучи вызванной в системе Windows 98, функция
CreateFileW возвращает не INVALID_HANDLE_VALUE, а нулевой дескриптор файла.
Это досадное обстоятельство выявляется в коде функции OpenInternal (о ней
речь пойдет чуть дальше), и в таком случае, дескриптору "насильственно"
присваивается INVALID_HANDLE_VALUE.
|
Open
bool Open(LPCTSTR path, bool write);
|
Открывает файл с именем path для чтения или записи (вид доступа
определяется значением параметра write). Файл должен существовать, иначе
функция завершится возвратом ошибки.
Возвращаемое значение сигнализирует об успехе или неудаче операции.
Сreate
bool Create(LPCTSTR path, DWORD size);
|
Создает на диске файл с именем path для записи. Параметр size
определяет его размер. Если файл с таким именем уже существует, он будет перезаписан.
Возвращаемое значение сигнализирует об успехе или неудаче операции.
Close
Закрывает файл и уничтожает связанные с ним ресурсы. Этот метод автоматически
будет вызван в деструкторе, но он может потребоваться, если Вам необходимо
явно закрыть файл. Метод также вызывается при открытии, если объект уже был
связан с каким-либо файлом.
Base
Возвращает указатель на первый байт области памяти, в которую был спроецирован
файл. Для файла, открытого на чтение, запись по этому адресу смысла не имеет.
Size
Возвращает размер файла (и соответственно, области File Mapping View).
OpenInternal
bool OpenInternal(LPCTSTR path, DWORD dwAccess, DWORD dwCreation, DWORD flProtect,
DWORD dwPageAccess, DWORD size=0);
|
Этот метод вызывается из более высокоуровневых методов Open и Create и
выполняет основную "грязную работу". Но в некоторых случаях, если Вам
понадобится контролировать детали процесса, Вы можете вызвать его самостоятельно.
При этом параметры dwAccess и dwCreation имеют смысл
(и будут переданы) для функции CreateFile, параметр flProtect - для
функции CreateFileMapping, а параметры dwPageAccess и size,
соответственно - для функции MapViewOfFile. За деталями обращайтесь к
исходному коду метода.
Демонстрационное приложение
Тестовое приложение получилось настолько небольшим, что рискну привести его код
здесь целиком:
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include "filemap.h"
int msg(LPCTSTR msg)
{
MessageBox(0, msg, _T("Xtract"), MB_OK);
return 1;
}
void save(BYTE* start, DWORD size)
{
static int num=0;
TCHAR name[20];
_stprintf(name, _T("%05d.wav"), num++);
CFileMap f(name, size);
if(f) memcpy(f.Base(), start, size);
}
void xtract(BYTE* input, DWORD size)
{
BYTE* ptr=input;
while(ptr && (ptr<(input+size)))
{
if(!memcmp(ptr, "RIFF", 4))
if (!memcmp(ptr+8, "WAVEfmt ", 8))
{
DWORD sz=reinterpret_cast<DWORD*>(ptr+4)[0]+8;
save(ptr, sz);
ptr+=sz;
continue;
}
ptr++;
}
}
int _tmain(int argc, TCHAR* argv[])
{
if(argc!=2) return msg(_T("Required parameter: file name"));
CFileMap source(argv[1]);
if(!source) return msg(_T("Could not open source file"));
xtract(source.Base(), source.Size());
return 0;
} |
При запуске этой программы с параметром - именем файла для извлечения звуков она
будет сохранять найденные WAV'ы в текущем каталоге под именами 00000.WAV,
00001.WAV и т.д. Единственное ограничение, как уже упоминалось - это то, что
размер как исходного, так и сохраняемых файлов не должен превышать 2 гигабайта.
Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы
то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских
прав.