Сообщений 6    Оценка 156 [+1/-0]         Оценить  
Система Orphus

Класс CFileMap

Автор: Виталий Брусенцев
The RSDN Group
Опубликовано: 12.11.2001
Исправлено: 13.03.2005
Версия текста: 1.1

Описание класса
Использование методов
Конструкторы
operator bool()
Open
Сreate
Close
Base
Size
OpenInternal
Демонстрационное приложение

Класс 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()

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

void Close();
Закрывает файл и уничтожает связанные с ним ресурсы. Этот метод автоматически будет вызван в деструкторе, но он может потребоваться, если Вам необходимо явно закрыть файл. Метод также вызывается при открытии, если объект уже был связан с каким-либо файлом.

Base

BYTE* Base();
Возвращает указатель на первый байт области памяти, в которую был спроецирован файл. Для файла, открытого на чтение, запись по этому адресу смысла не имеет.

Size

DWORD 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. За деталями обращайтесь к исходному коду метода.

Демонстрационное приложение

Тестовое приложение получилось настолько небольшим, что рискну привести его код здесь целиком:
// 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;
}
При запуске этой программы с параметром - именем файла для извлечения звуков она будет сохранять найденные WAV'ы в текущем каталоге под именами 00000.WAV, 00001.WAV и т.д. Единственное ограничение, как уже упоминалось - это то, что размер как исходного, так и сохраняемых файлов не должен превышать 2 гигабайта.

Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 6    Оценка 156 [+1/-0]         Оценить