Класс 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 |
В несколько упрощенном виде, описание класса выглядит так:
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); }; |
Конструктор по умолчанию не создает никаких объектов File Mapping. Это можно сделать позднее, вызвав Open/Create.
CFileMap(LPCTSTR path, bool write=false); |
CFileMap(LPCTSTR path, DWORD size); |
operator bool(); |
ПРИМЕЧАНИЕ Данная функция проверяет дескриптор (описатель) открытого файла на недопустимое значение INVALID_HANDLE_VALUE. При компиляции класса и тестового примера под UNICODE выявилась одна неприятность: вопреки документации, будучи вызванной в системе Windows 98, функция CreateFileW возвращает не INVALID_HANDLE_VALUE, а нулевой дескриптор файла. Это досадное обстоятельство выявляется в коде функции OpenInternal (о ней речь пойдет чуть дальше), и в таком случае, дескриптору "насильственно" присваивается INVALID_HANDLE_VALUE. |
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); |
// xtract.cpp : a simple WAV extractor // #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)) // возможно, найден WAV if (!memcmp(ptr+8, "WAVEfmt ", 8)) // скорее всего, найден { DWORD sz=reinterpret_cast<DWORD*>(ptr+4)[0]+8; //учесть RIFF header 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; } |