Страница 2 из 8 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/С++. |