Статьи Королевства Дельфи

       

Класс TSharedSream.


unit SharedStream; interface uses SysUtils, Windows, Classes, Consts; type { TSharedStream } TSharedStream = class(TStream) { Для совместимости с TStream } private FMemory : Pointer; { Указатель на данные } FSize : Longint; { Реальный размер записанных данных } FPageSize : Longint; { Размер выделенной "страницы" под данные } FPosition : Longint; { Текущая позиция "курсора" на "странице" } protected public constructor Create; destructor Destroy; override; function Read(var Buffer; Count: Longint): Longint; override; function Write(const Buffer; Count: Integer): Longint; override; function Seek(Offset: Longint; Origin: Word): Longint; override; procedure SetSize(NewSize: Longint); override; procedure LoadFromStream(Stream: TStream); procedure LoadFromFile(const FileName: string); procedure SaveToStream(Stream: TStream); procedure SaveToFile(const FileName: string); public property Memory: Pointer read FMemory; end; const SwapHandle = $FFFFFFFF; { Handle файла подкачки } implementation resourcestring CouldNotMapViewOfFile = 'Could not map view of file.'; { TSharedStream } { * TSharedStream работает правильно только с файлом подкачки, с обычным файлом проще и надежнее работать TFileStream'ом. * Для тех кто знаком с File Mapping Functions'ами : Класс TSharedStream не может использоваться для синхронизации(разделения) данных среди различных процессов(программ/приложений). [пояснения в конструкторе] * Класс TSharedStream можно рассматривать как альтернативу временным файлам (т.е. как замену TFileStream). Преимущество : а. Данные никто не сможет просмотреть. б. Страница, зарезервированная под данные, автомотически освобождается после уничтожения создавшего ее TSharedStream'а. * Класс TSharedStream можно рассматривать как альтернативу TMemoryStream. Преимущество : а. Не надо опасаться нехватки памяти при большом объеме записываемых данных. [случай когда физически нехватает места на диске здесь не рассматривается]. Известные проблемы: На данный момент таких не выявлено. Но есть одно НО. Я не знаю как поведет себя TSharedStream в результате нехватки места а. на диске б. в файле подкачки (т.е. в системе с ограниченным размером файла подкачки). } constructor TSharedStream.Create; const Sz = 1024000; { Первоначальный размер страницы }{ взят с потолка } var SHandle : THandle; begin FPosition := 0; { Позиция "курсора" } FSize := 0; { Размер данных } FPageSize := Sz; { Выделенная область под данные } { Создаем дескриптор объекта отображения данных. //эта формулировка взята из книги Проще сказать - создаем страницу под данные. //разрешите, я здесь и далее //буду употреблять более протые //информационные вставки. Все подробности по CreateFileMapping в Help'e. } SHandle := CreateFileMapping( SwapHandle, nil, PAGE_READWRITE, 0, Sz, nil ); { Создаем "страницу"___| | | |} { Handle файла подкачки ______| | |} { Задаем размер "страницы"[Sz]. Не может быть = нулю______________| |} { Имя "страницы" должно быть нулевым[nil]_____________________________| иначе Вам в последствии не удастся изменить размер "страницы". (Подробнее см. в TSharedStream.SetSize). * Для тех кто знаком с File Mapping Functions'ами : раз страница осталась неименованной, то Вам не удастся использовать ее для синхронизации(разделения) данных среди различных процессов(программ/приложений). [остальных недолжно волновать это отступление] } if SHandle = 0 then raise Exception.Create(CouldNotMapViewOfFile); { ошибка - неудалось создать объект отображения[т.е. "страница" не создана и указатель на нее = 0]. Это может быть: Если Вы что-либо изменяли в конструкторе - a. Из-за ошибки в параметрах, передоваемых функции CreateFileMapping б. Если Sz FMemory := MapViewOfFile(SHandle, FILE_MAP_WRITE, 0, 0, Sz); { Получаем указатель на данные } if FMemory = nil then raise Exception.Create(CouldNotMapViewOfFile); { Виндус наверно может взбрыкнуться и вернуть nil, но я таких ситуаций не встречал. естественно если на предыдущих дейсвиях не возникало ошибок и если переданы корректные параметры для функции MapViewOfFile() } CloseHandle(SHandle); end; destructor TSharedStream.Destroy; begin UnmapViewOfFile(FMemory); { закрываем страницу. если у Вас не фиксированный размер файла подкачки, то через пару минут вы должны увидеть уменьшение его размера. } inherited Destroy; end; function TSharedStream.Read(var Buffer; Count: Longint): Longint; begin { Функция аналогичная TStream.Read(). Все пояснения по работе с ней см. в help'e. } if Count > 0 then begin Result := FSize - FPosition; if Result > 0 then begin if Result > Count then Result := Count; Move((PChar(FMemory) + FPosition)^, Buffer, Result); Inc(FPosition, Result); end end else Result := 0 end; function TSharedStream.Write(const Buffer; Count: Integer): Longint; var I : Integer; begin { Функция аналогичная TStream.Write(). Все пояснения по работе с ней см. в help'e. } if Count > 0 then begin I := FPosition + Count; if FSize < I then Size := I; System.Move(Buffer, (PChar(FMemory) + FPosition)^, Count); FPosition := I; Result := Count; end else Result := 0 end; function TSharedStream.Seek(Offset: Integer; Origin: Word): Longint; begin { Функция аналогичная TStream.Seek(). Все пояснения по работе с ней см. в help'e. } case Origin of soFromBeginning : FPosition := Offset; soFromCurrent : Inc(FPosition, Offset); soFromEnd : FPosition := FSize - Offset; end; if FPosition > FSize then FPosition := FSize else if FPosition < 0 then FPosition := 0; Result := FPosition; end; procedure TSharedStream.SetSize(NewSize: Integer); const Sz = 1024000; var NewSz : Integer; SHandle : THandle; SMemory : Pointer; begin { Функция аналогичная TStream.SetSize(). Все пояснения по работе с ней см. в help'e. } inherited SetSize(NewSize); if NewSize > FPageSize then { Если размер необходимый для записи данных больше размера выделенного под "страницу", то мы должны увеличить размер "страницы", но... } begin { ...но FileMapping не поддерживает изменения размеров "страницы", что не очень удобно, поэтому приходится выкручиваться. } NewSz := NewSize + Sz; { задаем размер страницы + 1Meтр[чтобы уменьшить работу со страницами]. } { Создаем новую страницу }{ возможные ошибки создания страницы описаны в конструкторе TSharedStream. } SHandle := CreateFileMapping( SwapHandle, nil, PAGE_READWRITE, 0, NewSz, nil ); if SHandle = 0 then raise Exception.Create(CouldNotMapViewOfFile); SMemory := MapViewOfFile(SHandle, FILE_MAP_WRITE, 0, 0, NewSz); if SMemory = nil then raise Exception.Create(CouldNotMapViewOfFile); CloseHandle(SHandle); Move(FMemory^, SMemory^, FSize); { Перемещаем данные из старой "страницы" в новую } UnmapViewOfFile(FMemory); { Закрываем старую "страницу" } FMemory := SMemory; FPageSize := NewSz; { Запоминаем размер "страницы" } end; FSize := NewSize; { Запоминаем размер данных } if FPosition > FSize then FPosition := FSize; end; procedure TSharedStream.LoadFromFile(const FileName: string); var Stream: TFileStream; begin Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); try LoadFromStream(Stream) finally Stream.Free end end; procedure TSharedStream.LoadFromStream(Stream: TStream); var Count: Longint; begin Stream.Position := 0; Count := Stream.Size; SetSize(Count); if Count > 0 then Stream.Read(FMemory^, Count); end; procedure TSharedStream.SaveToFile(const FileName: string); var Stream: TFileStream; begin Stream := TFileStream.Create(FileName, fmCreate); try SaveToStream(Stream) finally Stream.Free end end; procedure TSharedStream.SaveToStream(Stream: TStream); begin Stream.Write(FMemory^, FSize); end; end.

Алексей Румянцев
Специально для

Прилагается демонстрационный пример использования TSharedStream : (6.2K)



Содержание раздела