Внутренний формат документов MS WORD
Страница 2. Доступ к 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- функций, позволяющих создавать и открывать составные файлы в различных режимах, обращаться к их каталогам и т.п. Рассмотрим некоторые из них.

  1. Создание нового составного файла с форматом структурированного хранилища:
    HRESULT StgCreateDocfile (WCHAR *ИмяФайла,
    DWORD ФлагиДоступа,
    DWORD НеИспользуется,
    IStorage **Интерфейс);

    Обычно для параметра ФлагиДоступа используется конкатенация из битов STGM_CREATE (создать новый составной файл), STGM_READ (разрешить чтение), STGM_WRITE (разрешить запись) или STGM_READWRITE (разрешить чтение и запись). Но можно указать STGM_CONVERT, это якобы позволяет преобразовывать "сырые" данные в структурированное хранилище, помещая их в поток с предопределенныи именем CONTENTS.

  2. Открытие существующего составного файла, имеющего формат структурированного хранилища, флаги те же:
    HRESULT StgOpenStorage (WCHAR *ИмяФайла,
    IStorage *УжеОткрытыйИнтерфейс,
    DWORD ФлагиДоступа,
    SNB МаскаИсключения,
    DWORD НеИспользуется,
    IStorage **Интерфейс);
  3. Проверка файла на соответствие формату структурированного хранилища. Возвращает S_OK - это хранилище; S_FALSE - это не хранилище; STG_E_FILENOTFOUND - файла вообще нет:
    HRESULT StgIsStorageFile(WCHAR* ИмяФайла)

    Функции StgCreateDocfile и StgOpenStorage требуют имя дискового файла (например, L"C:\FILE.DOC"), а возвращают в последнем параметре интерфейс доступа - т.е. указатель на объект класса IStorage, свойства и методы которого позволяют манипулировать с элементами структуры уже открытого или созданного составного документа (физически это - просто массив адресов).

    Дальше кратко описываются некоторые, наиболее интересные методы этого интерфейса.

  4. Создание нового подкаталога в главном каталоге открытого составного файла.
    HRESULT CreateStorage(wchar_t *ИмяПодкаталога,
    DWORD ФлагиДоступа,
    DWORD НеИспользуется1,
    DWORD НеИспользуется2,
    IStorage **ОткрытыйОбъект);
  5. Открытие существующего подкаталога в главном каталоге открытого составного файла.
    HRESULT OpenStorage(wchar_t *ИмяПодкаталога,
    IStorage *УжеОткрытыйОбъект
    DWORD ФлагиДоступа,
    SNB ИменаПотоков,
    DWORD НеИспользуется,
    IStorage **ОткрытыйОбъект);
  6. Закрытие открытого каталога или подкаталога после завершения работы с ними (крайне рекомендуется использовать!).
    ULONG Release(void);
  7. Инициализация перечисления записей в каталоге или подкаталоге.
     HRESULT EnumElements(DWORD НеИспользуется1,
    void НеИспользуется2,
    DWORD НеИспользуется3,
    IEnumSTATSTG **ИнтерфейсПеречисления);

    Возвращает новый интерфейс к объекту-перечислителю, который обладает методами:

    • HRESULT Next(ULONG Сколько, void *Куда, void *РеальноПолученные) - возвращает очередные элементы;
    • HRESULT Skip(ULONG Сколько) - пропускает элементы списка;
    • HRESULT Reset(void) - возвращает позицию начала списка;
    • HRESULT Clone(Позиция) - возвращает копию перечислителя.
  8. Создание нового потока внутри открытого каталога.
    HRESULT CreateStream(wchar_t * ИмяПотока,
    DWORD ФлагиДоступа,
    DWORD НеИспользуется1,
    DWORD НеИспользуется2,
    IStream **ОткрытыйПоток);
  9. Открытие потока внутри открытого каталога.
    HRESULT OpenStream(const wchar_t *ИмяПотока,
    void *НеИспользуется1,
    DWORD ФлагиДомтупа,
    DWORD НеИспользуется2,
    IStream **ОткрытыйПоток);
  10. Запись данных в поток.
    HRESULT Write(void *Буфер,
    ULONG Размер,
    ULONG *РеальноЗаписано);
  11. Чтение данных из потока.
    HRESULT Read(void *Буфер,
    ULONG Размер,
    ULONG *РеальноПрочитано);
  12. Переименование записи в каталоге.
    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/С++.

 
« Предыдущая статья   Следующая статья »