Внутренний формат документов 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/С++.
