Рассылка закрыта
При закрытии подписчики были переданы в рассылку "О карьере и профессиональном развитии IT-специалистов" на которую и рекомендуем вам подписаться.
Вы можете найти рассылки сходной тематики в Каталоге рассылок.
← Ноябрь 2003 → | ||||||
1
|
2
|
|||||
---|---|---|---|---|---|---|
3
|
4
|
5
|
6
|
7
|
8
|
9
|
10
|
11
|
12
|
13
|
14
|
15
|
16
|
17
|
18
|
20
|
21
|
22
|
23
|
|
24
|
25
|
26
|
27
|
28
|
29
|
30
|
Статистика
-4 за неделю
Программирование для начинающих и не только
Информационный Канал Subscribe.Ru |
"СОМ хранилища: подпольная файловая система"
Перед многими программистами рано или поздно постает вопрос: "В каком формате хранить данные своей программы". Хорошо, если это тип данных с фиксированной длинной, а если надо сохранить разнородные данные, да еще чтоб в одном файле(чтоб потом не разбираться с десятком другим файлов с данными)... Тут на помощь приходить сама Windows(жуть какую сказал: "Windows... и на помощь") с технологией структурированного хранилища данных.
Определения
Структурированные хранилища данных - это файлы особой "самодокументированной" структуры, в которых могут мирно уживаться разнородные данные (от простого текста, до фильмов, архивов и... программ). Так как эта технология есть неотъемлемой частью Windows, то доступ к ней возможен из любого средства программирования, которое поддерживает технологию COM. Одним из таких приложений является и Delphi, на основе которого будет описана технология доступа к структурированным хранилищам данных.
Структура хранилищ
Как уже было сказано, COM хранилища - файлы особой структуры(рис.1) и напоминают иерархическую файловую систему. Так в них есть корневое хранилище (Root Entry) в котором могут содержаться как отдельные потоки("файлы"), так и хранилища второго уровня("каталоги"), в них в свою очередь хранилища третьего уровня и т.д. Управление каждым хранилищем и потоком осуществляется посредством отдельного экземпляра интерфейса: IStorage - для хранилищ и IStream - для потоков. А теперь рассмотрим конкретнее некоторые операции над ними.
Создание и открытие хранилищ
Создание структурированных хранилищ осуществляется с использованием функции StgCreateDocFile, из модуля ActiveX.pas. Синтаксис этой функции выгладит таким образом:
function StgCreateDocfile(pwcsName: POleStr; grfMode: Longint; reserved: Longint; out stgOpen: IStorage): HResult; stdcall;
-
где
- pwcsName
- название хранилища(т.е. название файла).
- grfMode
- флаги доступа(комбинация значений STGM_*).
- reserved
- он и в Африке RESERVED.
- StgOpen
- ссылка на интерфейс IStorage нашего главного хранилища.
Результат функции как всегда транслируем в исключения Delphi посредством OleCheck.
function StgOpenStorage(pwcsName: POleStr; stgPriority: IStorage; grfMode: Longint; snbExclude: TSNB; reserved: Longint; out stgOpen: IStorage): HResult; stdcall;
неизвестный параметр - stgPriority указывает на ранее открытый экземпляр главного хранилища (почти всегда nil).
Когда хранилище открыто (запись данных)...
Рассмотрим более подробно методы интерфейса IStorage.
Создание потока - IStorage.CreateStream.
function CreateStream(pwcsName: POleStr; grfMode: Longint; reserved1: Longint;reserved2: Longint; out stm: IStream): HResult; stdcall;
-
параметры:
- pwcsName
- название потока.
- grfMode
- Флаги доступа
- reserved1, reserved2
- соответственно.
- stm
- указатель на созданный поток.
function OpenStream(pwcsName: POleStr; reserved1: Pointer; grfMode: Longint;reserved2: Longint; out stm: IStream): HResult; stdcall;
-
параметры:
- pwcsName
- название потока.
- reserved1
- nil
- grfMode
- флаги доступа
- reserved2
- 0
- stm
- указатель на открытый поток.
function CreateStorage(pwcsName: POleStr; grfMode: Longint; dwStgFmt: Longint; reserved2: Longint; out stg: IStorage): HResult;stdcall;
Открытие подхранилища - IStorage.OpenStorage.
function OpenStorage(pwcsName: POleStr; const stgPriority: IStorage; grfMode: Longint; snbExclude: TSNB; reserved: Longint;out stg: IStorage): HResult; stdcall;
Теперь мы можем приступить к чтению(записи) данных из(в) потоков посредством интерфейсов IStream. Тут можно заметить до боли знакомые (для Дельфийцев) методы работы с потоками:Read,Write, Seek, SetSize, CopyTo... а если так, то почему бы не перевести их в более простую и понятную(по крайней мере для меня) объектную форму. Для этого воспользуемся наработками Borland собранными в модуле AxCtrls.pas, точнее классом TOleStream, который интерпретирует вызовы методов интерфейса IStream в соответствущие методы класса TStream.
А для того чтоб не быть пустословным - приведу небольшой примерчик:
Implementation Uses ActiveX,AxCtrls,ComObj; {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var Stg:IStorage; Strm:IStream; OS:TOleStream; S:String; begin OleCheck(StgCreateDocfile('Testing.stg',STGM_READWRITE or STGM_SHARE_EXCLUSIVE ,0,Stg)); OleCheck(Stg.CreateStream('Testing',STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,0,Strm)); OS:=TOleStream.Create(Strm); try S:='This is the test'; OS.WriteBuffer(Pointer(S)^,Length(S)); finally OS.free; Strm:=nil; Stg:=nil; end; end; end.
В этом фрагменте мы создаем новое хранилище с одним потоком в который записываем строку S. Естественно, ничто нам не мешает вместо:
S:='This is the test';
OS.WriteBuffer(Pointer(S)^,Length(S));
Написать например:Image1.Picture.Bitmap.SaveToStream(OS);
и тем самым записать в поток "Testing" изображение(вот она... "универсальная мусоросвалка").
Теперь ненамного отвлечемся от Delphi и посмотрим на наш файл с точки зрения, скажем, Far (или VC)... Посмотрели? А теперь откройте там же любой текстовый документ созданных в Word'е. Как видим структура та же что и в нашем файле. То же можно сказать и о Excel. Но как проверить на прибегая к помощи notepad какой файл является хранилищем, а какой нет? Для этого все в том же модуле ActiveX.pas предусмотрена функция StgIsStorageFile:
function StgIsStorageFile(pwcsName: POleStr): HResult; stdcall;
которая принимает значение S_OK( 0 ) - если файл является структурированным хранилищем данных и S_FALSE ( 1 ) - если файл не есть им, кроме того эта функция может принимать значения: STG_E_INVALIDFILENAME и STG_E_FILENOTFOUND соответственно если имя файла задано неправильно и если файла с таким именем не существует.
Чтение
Чтение данных из хранилища производится также как и чтение из стандартного потока Delphi. Все, что для этого требуется - это создать объект наследник TOleStream с использованием возвращаемого функцией IStorage.OpenStorage значения stm:
после выполнения этого кода мы в Edit1 увидим ранее записанное нами:"This is the test".procedure TForm1.Button2Click(Sender: TObject); var Stg:IStorage; Strm:IStream; OS:TOleStream; S:String; begin OleCheck(StgOpenStorage('Testing.stg',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE, nil,0,Stg)); OleCheck(Stg.OpenStream('Testing',0,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,Strm)); OS:=TOleStream.Create(Strm); try SetLength(S,OS.Size); OS.ReadBuffer(Pointer(S)^,OS.Size); Edit1.Text:=S; finally OS.free; Strm:=nil; Stg:=nil; end; end;
Последовательное перемещение по структурам хранилища
Хорошо... мы создали хранилище, записали в него данные и прочитали их. Но мы это сделали зная имя потока в котором записаны наши данные. Но как быть, если мы не знаем структуры хранилища? Для этого в Интерфейсе IStorage предусмотрен механизм перечисления его элементов, который содержится в интерфейсе IEnumStatStg, указатель на который возвращается функцией IStorage.EnumElements:
function EnumElements(reserved1: Longint; reserved2: Pointer; reserved3: Longint;out enm: IEnumStatStg): HResult; stdcall;
употребление этой функции происходит таким образом:
OleCheck(Stg.EnumElements(0,nil,0,Enum));
После этого используем только методы интерфейса IEnumStatStg:
Next(celt:Longint; out elt; pceltFetched: PLongint): HResult; stdcall;
- выборка информации следующего елемента хранилища
Skip(celt:longint):HResult;stdcall;
- пропуск количества елементов заданных в celt
Reset:HResult;stdcall;
- Reset - он и в Африке Reset.
Clone(out enm:IEnumStatStg):HResult;stdcall;
- Клонирование интерфейса
На данный момент для нас самым важным из этих методов есть метод Next:
Next(celt:Longint; out elt; pceltFetched: PLongint): HResult; stdcall;
-
Который принимает следующие параметры:
- Celt
- количество елементов структуры, которое будет извлечено при его вызове.
- Elt
- Масив приемник елементов типа TStatStg.
- PceltFetched
- указатель на переменную куда будет записано действительное количество извлеченных елементов.
кроме cbSize структура TStatStg содержит такие поля:procedure TForm1.Button2Click(Sender: TObject); var Stg:IStorage; Enum:IEnumStatStg; Data:TStatStg; begin OleCheck(StgOpenStorage('D:\1.doc',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,nil,0,Stg)); OleCheck(Stg.EnumElements(0,nil,0,Enum)); try While Enum.Next(1,Data,nil)=S_Ok do ListBox1.Items.Add(Format('%s(%d)',[Data.pwcsName,Data.cbSize])); finally Stg:=nil; Enum:=nil; end; end;
- pwcsName: POleStr;
- Название потока или хранилища
- dwType: Longint;
- Тип елемента (флаги типа STGTY_*)
- cbSize: Largeint;
- Размер конкретного елемента
- mtime: TFileTime;
- Дата последней модификации
- ctime: TFileTime;
- Дата создания
- atime: TFileTime;
- Дата последнего обращения
- grfMode: Longint;
- Флаг доступа
- grfLocksSupported: Longint;
- Не используется в хранилищах
- clsid: TCLSID;
- Идентификатор класса хранилища
- grfStateBits: Longint;
- Статусные биты
- reserved: Longint;
- Зарезервирован
Созданий дополнительных хранилищ
Для создания дополнительных хранилищ главного хранилища используется метод интерфейса IStorage под названием CreateStorage:
function CreateStorage(pwcsName: POleStr; grfMode: Longint; dwStgFmt: Longint; reserved2: Longint; out stg: IStorage): HResult;stdcall;
-
параметры:
- pwcsName - название подхранилища.
- grfMode - флаги доступа
- dwStgFmt,reserved2 - зарезервированы (принимают значение 0).
- stg - указатель на интерфейс содержащий ссылку на подхранилище.
после проделанной операции в файле 'Testing.stg' появится новое подхранилище "SubStorage" c одним потоком "SubTesting" в котором записана строка "SubTesting the stream".procedure TForm1.Button2Click(Sender: TObject); var Stg,Temp:IStorage; Str:IStream; OS:TOleStream; S:String; begin OleCheck(StgOpenStorage('Testing.stg',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,nil,0,Stg)); OleCheck(Stg.CreateStorage('SubStorage',STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,0,Temp)); OleCheck(Temp.CreateStream('SubTesting',STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,0,Str)); try OS:=TOleStream.Create(Str); S:='SubTesting the stream'; OS.WriteBuffer(Pointer(S)^,Length(S)); finally Temp:=nil; Stg:=nil; end; end;
Чтение из такого подхранилища может быть реализовано следующим образом:
procedure TForm1.Button2Click(Sender: TObject); var Stg,Temp:IStorage; Str:IStream; OS:TOleStream; S:String; begin OleCheck(StgOpenStorage('Testing.stg',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,nil,0,Stg)); OleCheck(Stg.OpenStorage('SubStorage',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,nil,0,Temp)); OleCheck(Temp.OpenStream('SubTesting',nil,STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,Str)); try OS:=TOleStream.Create(Str); SetLength(S,OS.Size); OS.ReadBuffer(Pointer(S)^,OS.Size); Edit1.Text:=S; finally Temp:=nil; Stg:=nil; end; end;
Дополнительные возможности
К дополнительным возможностям можно отнести наличие таких методов в интерфейсе IStorage:
function CopyTo(ciidExclude: Longint; rgiidExclude: PIID; snbExclude: TSNB; const stgDest: IStorage): HResult; stdcall;
Копирование содержимого хранилища в другое хранилище:
function MoveElementTo(pwcsName: POleStr; const stgDest: IStorage;pwcsNewName: POleStr; grfFlags: Longint): HResult; stdcall;
Перемещение хранилища в другое хранилище:
function Commit(grfCommitFlags: Longint): HResult; stdcall;
Подтверждение изменетий внесенных в хранилище. Используется совместно с флагом STGM_TRANSACTED при открытии или создании хранилища:
function Revert: HResult; stdcall;
Отмена изменений вносимых в хранилище. Используется совместно с флагом STGM_TRANSACTED при открытии или создании хранилища:
function DestroyElement(pwcsName: POleStr): HResult; stdcall;
Удаление элемента из хранилища:
function RenameElement(pwcsOldName: POleStr; pwcsNewName: POleStr): HResult; stdcall;
Переименование элемента хранилища:
function SetElementTimes(pwcsName: POleStr; const ctime: TFileTime; const atime: TFileTime; const mtime: TFileTime): HResult; stdcall;
Обновление информации о дате создания, модификации и последнего обращения к элементу хранилища.
Сжатие хранилищ
Как и файловая система, ее аналог - структурированные хранилища данных подвержены фрагментации. Они неспособности "экономично" заполнять освободившееся пространство от удаленных элементов. А самое главное, что в них не предусмотрен механизм автоматического сжатия данных и освобождения незанятых ресурсов диска. Но на каждый фрагмент есть свой "дефрагмент". Так сжать хранилище можно воспользовавшись методом интерфейса IStorage под названием CopyTo:
function CopyTo(ciidExclude: Longint; rgiidExclude: PIID; snbExclude: TSNB; const stgDest: IStorage): HResult; stdcall;
при этом все нужные данные переписываются в новое хранилище, а ненужные (т.е. уже удаленные) исчезают навеки. Примером для такого сжатия может служить код:
конечно такой метод эффективен если данный обрабатываются непосредственно в хранилище, а не действует принцип: "Все прочитал... удалил... изменил... написал наново".procedure TForm1.Button2Click(Sender: TObject); var Stg,Temp:IStorage; Enum:IEnumStatStg; Data:TStatStg; begin OleCheck(StgOpenStorage('D:\1.doc',nil,STGM_READ or STGM_SHARE_EXCLUSIVE,nil,0,Stg)); OleCheck(StgCreateDocFile('D:\1c.doc',STGM_READWRITE or STGM_SHARE_EXCLUSIVE,0,Temp)); try Stg.CopyTo(0,nil,nil,Temp); finally Temp:=nil; Stg:=nil; end; end;
Флаги доступа к хранилищам и потокам | ||
---|---|---|
STGM_DIRECT | Каждое изменение содержания сразу же записывается в файл | |
STGM_TRANSACTED | Изменения записываются в буфер, а потом по команде Commit в файл. Команда Revert отменяет изменения | |
STGM_SIMPLE | Упрощенный вариант хранения данных:
| |
STGM_READ | Открытие только для чтения | |
STGM_WRITE | То же для записи | |
STGM_READWRITE | Чтение и запись | |
STGM_SHARE_DENY_READ | Запрет параллельного чтения | |
STGM_SHARE_DENY_WRITE | Запрет параллельной записи | |
STGM_SHARE_EXCLUSIVE | Полный запрет на параллельное использование файла | |
STGM_PRIORITY | Блокирует возможность параллельного внесения изменений в файл по команде Commit | |
STGM_DELETEONRELEASE | Файл автоматически стирается при освобождении интерфейса. Используется для временных файлов | |
STGM_CREATE | Стирает существующий файл с тем же именем | |
STGM_CONVERT | Создает новый файл в поток CONTENTS которого заносит данные из существующего файла с тем же именем , если такой существует | |
STGM_FAILSAFE | Если существует файл с таким же именем - возвращает значение STG_E_FILEALREADYEXISTS | |
STGM_NOSCRATCH | При установленном флаге STGM_TRANSACTED вместо внешнего буфера используется незадействованное пространство в самом файле. Более эффективное использование ресурсов компьютера |
http://subscribe.ru/
E-mail: ask@subscribe.ru |
Отписаться
Убрать рекламу |
В избранное | ||