Внутренний формат документов MS WORD - Доступ к STRUCTURED STORAGE через OLE2 API
ОГЛАВЛЕНИЕ
2. Доступ к STRUCTURED STORAGE через OLE2 API
- Добро пожаловать! Это автобус для ведьм и волшебников, попавших в трудное положение! Взмахните палочкой и входите в салон: мы домчим вас куда угодно! Дж. К. Ролинг. Гарри Поттер и узник Азбакана |
Microsoft предоставляет мощные средства для работы в рамках технологии OLE, они сконцентрированы в библиотеках OLE2.DLL (для 16-разрядных приложений) и OLE32.DLL (для 32-разрядных приложений). В принципе, к ним можно обращаться из любой системы программирования (например, из MS VB и даже из языка ассемблера), использовав традиционный механизм GetModuleHandle/GetProcAddress. Но проще всего это делать из MS VC или Borland C/C++. По крайней мере, все примеры из этой статьи компилировались при помощи Borland C/C++ v5.01.
Итак, в довольно объемной и сложной библиотеке OLE32.DLL для нас с точки зрения темы статьи интересны около дюжины API- функций, позволяющих создавать и открывать составные файлы в различных режимах, обращаться к их каталогам и т.п. Рассмотрим некоторые из них.
- Создание нового составного файла с форматом структурированного хранилища:
HRESULT StgCreateDocfile (WCHAR *ИмяФайла,
DWORD ФлагиДоступа,
DWORD НеИспользуется,
IStorage **Интерфейс);Обычно для параметра ФлагиДоступа используется конкатенация из битов STGM_CREATE (создать новый составной файл), STGM_READ (разрешить чтение), STGM_WRITE (разрешить запись) или STGM_READWRITE (разрешить чтение и запись). Но можно указать STGM_CONVERT, это якобы позволяет преобразовывать "сырые" данные в структурированное хранилище, помещая их в поток с предопределенныи именем CONTENTS.
- Открытие существующего составного файла, имеющего формат структурированного хранилища, флаги те же:
HRESULT StgOpenStorage (WCHAR *ИмяФайла,
IStorage *УжеОткрытыйИнтерфейс,
DWORD ФлагиДоступа,
SNB МаскаИсключения,
DWORD НеИспользуется,
IStorage **Интерфейс); - Проверка файла на соответствие формату структурированного хранилища. Возвращает S_OK - это хранилище; S_FALSE - это не хранилище; STG_E_FILENOTFOUND - файла вообще нет:
HRESULT StgIsStorageFile(WCHAR* ИмяФайла)
Функции StgCreateDocfile и StgOpenStorage требуют имя дискового файла (например, L"C:\FILE.DOC"), а возвращают в последнем параметре интерфейс доступа - т.е. указатель на объект класса IStorage, свойства и методы которого позволяют манипулировать с элементами структуры уже открытого или созданного составного документа (физически это - просто массив адресов).
Дальше кратко описываются некоторые, наиболее интересные методы этого интерфейса.
- Создание нового подкаталога в главном каталоге открытого составного файла.
HRESULT CreateStorage(wchar_t *ИмяПодкаталога,
DWORD ФлагиДоступа,
DWORD НеИспользуется1,
DWORD НеИспользуется2,
IStorage **ОткрытыйОбъект); - Открытие существующего подкаталога в главном каталоге открытого составного файла.
HRESULT OpenStorage(wchar_t *ИмяПодкаталога,
IStorage *УжеОткрытыйОбъект
DWORD ФлагиДоступа,
SNB ИменаПотоков,
DWORD НеИспользуется,
IStorage **ОткрытыйОбъект);
- Закрытие открытого каталога или подкаталога после завершения работы с ними (крайне рекомендуется использовать!).
ULONG Release(void);
- Инициализация перечисления записей в каталоге или подкаталоге.
HRESULT EnumElements(DWORD НеИспользуется1,
void НеИспользуется2,
DWORD НеИспользуется3,
IEnumSTATSTG **ИнтерфейсПеречисления);Возвращает новый интерфейс к объекту-перечислителю, который обладает методами:
- HRESULT Next(ULONG Сколько, void *Куда, void *РеальноПолученные) - возвращает очередные элементы;
- HRESULT Skip(ULONG Сколько) - пропускает элементы списка;
- HRESULT Reset(void) - возвращает позицию начала списка;
- HRESULT Clone(Позиция) - возвращает копию перечислителя.
- Создание нового потока внутри открытого каталога.
HRESULT CreateStream(wchar_t * ИмяПотока,
DWORD ФлагиДоступа,
DWORD НеИспользуется1,
DWORD НеИспользуется2,
IStream **ОткрытыйПоток); - Открытие потока внутри открытого каталога.
HRESULT OpenStream(const wchar_t *ИмяПотока,
void *НеИспользуется1,
DWORD ФлагиДомтупа,
DWORD НеИспользуется2,
IStream **ОткрытыйПоток); - Запись данных в поток.
HRESULT Write(void *Буфер,
ULONG Размер,
ULONG *РеальноЗаписано); - Чтение данных из потока.
HRESULT Read(void *Буфер,
ULONG Размер,
ULONG *РеальноПрочитано); - Переименование записи в каталоге.
HRESULT RenameElement(wchar_t *СтароеИмя,
wchar_t *НовоеИмя);
Фактически, сервисных методов гораздо больше. Например, существует большой набор методов для работы с lock-bytes, но он нам в контексте статьи не слишком актуален.
Теперь рассмотрим примерчик работы с составным файлом через OLE API, а именно - рекурсивный просмотр дерева заключенных внутри объектов:
// Рекурсивный сканер docfile. (с) Климентьев К., Самара 2002
#include "windows.h"
#include "ole2.h"
#include "iostream.h"
#include "stdio.h"
int level=0;
walk(char *s, LPSTORAGE ls)
{
OLECHAR FileName[256]; LPENUMSTATSTG lpEnum=NULL;
LPSTORAGE pIStorage=NULL; LPSTORAGE pIStorage2=NULL;
ULONG uCount; STATSTG stat; int i;
if (!ls) {
mbstowcs(FileName, s, 256); wprintf(L"[%s]\n", FileName);
StgOpenStorage(FileName, NULL,
STGM_READ|STGM_SHARE_EXCLUSIVE,
NULL,0,&pIStorage);
walk("", pIStorage);
}
else {
ls->EnumElements(0,NULL,0,&lpEnum);
if (lpEnum)
while (lpEnum->Next(1,&stat,&uCount)==S_OK) {
for (i=0;i<level;i++) wprintf(L" ");
wprintf(L"%d: %s\n", stat.type, (LPSTR)stat.pwcsName);
if (stat.type==STGTY_STORAGE) {
ls->OpenStorage(stat.pwcsName, NULL,
STGM_READ|STGM_SHARE_EXCLUSIVE,
NULL, 0, &pIStorage2);
level++;
walk("", pIStorage2);
level--;
}
};
ls->Release();
}
}
int main(int argc, char* argv[]) {
if (argc>1) walk(argv[1],NULL);
}
Создадим в Word 97/2000/XP пустой DOC-файл без текста и напустим на него нашу программу. Мы увидим примерно следующее:
[word97.doc]
2: 1Table
2: \1CompObj
1: ObjectPool
2: WordDocument
2: \5SummaryInformation
2: \5DocumentSummaryInformation
Для DOC-файла в формате Word 6/7 (его можно создать, например, при помощи Wordpad) картинка будет попроще:
[word6.doc]
2: \1CompObj
2: WordDocument
Примерно так, как описано в этом разделе, устроены популярный FAR-плагин DocFile Browser Игоря Павлова и утилита OLE2View из MS Visual C/С++.