- •Лекция 8
- •Внутренний формат документов ms word Введение
- •1. Общее описание. Терминология
- •2. Доступ к structured storage через ole2 api
- •3. Низкоуровневый доступ к structured storage
- •3.1. Fat "Больших Блоков"
- •3.2. Fat "Малых Блоков"
- •3.3. Структура каталога
- •4. Структура документа ms word
- •Заключение
- •Дополнение от зимы 2004 года
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 *„I„}„‘„U„p„z„|„p,
DWORD „U„|„p„s„y„D„Ђ„ѓ„„„…„Ѓ„p,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘,
IStorage **„I„~„„„u„‚„†„u„z„ѓ);
Обычно для параметра ФлагиДоступа используется конкатенация из битов STGM_CREATE (создать новый составной файл), STGM_READ (разрешить чтение), STGM_WRITE (разрешить запись) или STGM_READWRITE (разрешить чтение и запись). Но можно указать STGM_CONVERT, это якобы позволяет преобразовывать "сырые" данные в структурированное хранилище, помещая их в поток с предопределенныи именем CONTENTS.
Открытие существующего составного файла, имеющего формат структурированного хранилища, флаги те же:
HRESULT StgOpenStorage (WCHAR *„I„}„‘„U„p„z„|„p,
IStorage *„T„w„u„O„„„{„‚„Ќ„„„Ќ„z„I„~„„„u„‚„†„u„z„ѓ,
DWORD „U„|„p„s„y„D„Ђ„ѓ„„„…„Ѓ„p,
SNB „M„p„ѓ„{„p„I„ѓ„{„|„ђ„‰„u„~„y„‘,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘,
IStorage **„I„~„„„u„‚„†„u„z„ѓ);
Проверка файла на соответствие формату структурированного хранилища. Возвращает S_OK - это хранилище; S_FALSE - это не хранилище; STG_E_FILENOTFOUND - файла вообще нет:
HRESULT StgIsStorageFile(WCHAR* „I„}„‘„U„p„z„|„p)
Функции StgCreateDocfile и StgOpenStorage требуют имя дискового файла (например, L"C:\FILE.DOC"), а возвращают в последнем параметре интерфейс доступа - т.е. указатель на объект класса IStorage, свойства и методы которого позволяют манипулировать с элементами структуры уже открытого или созданного составного документа (физически это - просто массив адресов).
Дальше кратко описываются некоторые, наиболее интересные методы этого интерфейса.
Создание нового подкаталога в главном каталоге открытого составного файла.
HRESULT CreateStorage(wchar_t *„I„}„‘„P„Ђ„t„{„p„„„p„|„Ђ„s„p,
DWORD „U„|„p„s„y„D„Ђ„ѓ„„„…„Ѓ„p,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘1,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘2,
IStorage **„O„„„{„‚„Ќ„„„Ќ„z„O„q„Њ„u„{„„);
Открытие существующего подкаталога в главном каталоге открытого составного файла.
HRESULT OpenStorage(wchar_t *„I„}„‘„P„Ђ„t„{„p„„„p„|„Ђ„s„p,
IStorage *„T„w„u„O„„„{„‚„Ќ„„„Ќ„z„O„q„Њ„u„{„„
DWORD „U„|„p„s„y„D„Ђ„ѓ„„„…„Ѓ„p,
SNB „I„}„u„~„p„P„Ђ„„„Ђ„{„Ђ„r,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘,
IStorage **„O„„„{„‚„Ќ„„„Ќ„z„O„q„Њ„u„{„„);
Закрытие открытого каталога или подкаталога после завершения работы с ними (крайне рекомендуется использовать!).
ULONG Release(void);
Инициализация перечисления записей в каталоге или подкаталоге.
HRESULT EnumElements(DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘1,
void „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘2,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘3,
IEnumSTATSTG **„I„~„„„u„‚„†„u„z„ѓ„P„u„‚„u„‰„y„ѓ„|„u„~„y„‘);
Возвращает новый интерфейс к объекту-перечислителю, который обладает методами:
HRESULT Next(ULONG ???????, void *????, void *?????????????????)- возвращает очередные элементы;
HRESULT Skip(ULONG ???????)- пропускает элементы списка;
HRESULT Reset(void)- возвращает позицию начала списка;
HRESULT Clone(???????)- возвращает копию перечислителя.
Создание нового потока внутри открытого каталога.
HRESULT CreateStream(wchar_t * „I„}„‘„P„Ђ„„„Ђ„{„p,
DWORD „U„|„p„s„y„D„Ђ„ѓ„„„…„Ѓ„p,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘1,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘2,
IStream **„O„„„{„‚„Ќ„„„Ќ„z„P„Ђ„„„Ђ„{);
Открытие потока внутри открытого каталога.
HRESULT OpenStream(const wchar_t *„I„}„‘„P„Ђ„„„Ђ„{„p,
void *„N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘1,
DWORD „U„|„p„s„y„D„Ђ„}„„„…„Ѓ„p,
DWORD „N„u„I„ѓ„Ѓ„Ђ„|„Ћ„x„…„u„„„ѓ„‘2,
IStream **„O„„„{„‚„Ќ„„„Ќ„z„P„Ђ„„„Ђ„{);
Запись данных в поток.
HRESULT Write(void *„A„…„†„u„‚,
ULONG „Q„p„x„}„u„‚,
ULONG *„Q„u„p„|„Ћ„~„Ђ„H„p„Ѓ„y„ѓ„p„~„Ђ);
Чтение данных из потока.
HRESULT Read(void *„A„…„†„u„‚,
ULONG „Q„p„x„}„u„‚,
ULONG *„Q„u„p„|„Ћ„~„Ђ„P„‚„Ђ„‰„y„„„p„~„Ђ);
Переименование записи в каталоге.
HRESULT RenameElement(wchar_t *„R„„„p„‚„Ђ„u„I„}„‘,
wchar_t *„N„Ђ„r„Ђ„u„I„}„‘);
Фактически, сервисных методов гораздо больше. Например, существует большой набор методов для работы с 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/С++.
Если хотите подробностей, то рекомендую:
1) хорошую статью Артема Каева "ActiveX по шагам"; 2)фирменный материал от Microsoft "OLE 2 programmers reference" (его можно найти в файлах OLE.HLP или OLE2.HLP).