Работа с файлами, созданными не текстовыми редакторамиРабота с распространенными файлами типа .doc и .xml не так удобна при использовании компоненет Borland С++ Builder и для доступа к с этими файлами приходится идти по довольно запутанным дорожкам кодов. Причина - эти файлы, привычные для Windows, (и не только эти - .dot, .xla, .wiz, .cag, .fla, .ppt и другие) устроены по правилам структурированного хранилища. Сам же файл на диске, хранящий внутри себя это хранилище, называется "файл-документ" (docfile) или "составной файл" (compound file). Первый термин применялся во времена OLE 1, второй появился вместе с OLE 2, сейчас они обычно используются как синонимы. Кроме того в OLE существует понятие "составной документ" (compound document) - термин обозначающий абстрактный подкласс хранилищ особого вида, о чем речь пойдет несколько позже. Составной файл состоит из следующих элементов:
Для доступа к таким файлам используется идея экспорта объектов, заключается в том, что один модуль создает объект, а другой его использует посредством обращения к его доступным методам (сервисам) - или "Технология COM" (Component Object Model- Модель Компонентных Объектов). Технология COM позволяет создавать и открывать составные файлы в различных режимах, обращаться к их каталогам и т.п. и явилась результатом развития принципов упрощения доступа к составным файлам Windows. COM объект можно представить классом, который имеет много своийств и методов доступ к которым возможен при помощи так называемых интерфейсов. Это стандарт, описывающий как должны работать интерфейсы класса (или объекта) - включая такие вопросы, как, например, работа с памятью или многопоточностью, и каким образом приложения могут использовать компоненты, созданные в стандарте COM. COM является стандартом, независимым от языка программирования и независимым от аппаратного окружения. Для идентификации интерфейса используется структура типа GUID (Global Universal Identifier) или CLSID - 128 - битное целое число, которое, гарантирует уникальность COM-объекта на всех компьютерахи для всех. Для того, чтобы воспользоваться COM-объектом, клиенту необходимо знать его GUID и кроме того это единственный тип данных, которые предопределены для интерфейса. Если известен GUID COM-объекта, то для создания его экземпляра можно воспользоваться стандартным WinAPI, например, вызовом функции CoCreateInstance. Для определения форматов данных интерфейс использует свои определенные типы данных - так называемые OLE Automation Datatypes. Точнее сказать, что если ссылка на данный интерфейс может быть передана в другой модуль, то список формальных параметров методов интерфейса обязан содержать только определенные типы данных. Технология COM позволила перейти от технологии OLE 1.0 (Windows 3.1 - щелчок на внедренном объекте в документе приложения, вызов приложения для работы с внедренным объектом, редактирование внедрения, и сохранение изменений путем редактирования соответствующих ссылок в исходном документе) к технологии OLE 2.0, которая позволила обеспечить взаимодействие между компонентами, написанными разными компаниями и на разных языках. СОМ модель объекта в системном обеспечении предусматривает полную совместимость за счет модульности разработки компонентов. Новая особенность, появившаяся в OLE 2.0, - это автоматизация OLE, которая обеспечивает доступ к объектам приложения и манипуляцию с ними извне. Основное отличие объектов OLE 2.0 от обычных объектов OLE состоит в том, что они доступны только программно, они создаются и используются при помощи программного кода и, следовательно, в принципе временны. Они не могут быть внедрены или связаны. Они могут существовать только в течение времени выполнения использующей их программы. Работа с файлами с использованием компонента TOleContainerПростейшим примером, позволяющим использовать OLE в C++Builder является контейнер OLE OleContainer(вкладка System) - компонент, позволяющий использовать механизмы внедренния и связывание. OLE-контейнер инкапсулирует все интерфейсы, необходимые для создания клиента OLE документов. Поместив его на форму форму и, в обработчике события нажатия кнопки, написав код:
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
if(OleContainer1->InsertObjectDialog())
{
OleContainer1->DoVerb(ovShow);
}
}
После запуска приложения и нажатия кнопки появляется диалоговое окно - "Вставка объекта". Выбрав в окошечке "Тип объекта" - "Документ Microsoft Word" (аналогично как и любого другого) и выбрав опцию "Создать из файла" после нажатия кнопочки "OK" в наше приложение внедряется Word, в котором открывается выбранный документ. Маленькое отличие внедренного приложения (не важно Word, Excel, WordPad, PaintBrush... )- в нем нет некоторых функций,и хотя документ межет редактироваться, но, например, он не может быть сохранен стандартными для приложения методами. Для сохранения необходимо написать примерно такой код:
void __fastcall
TForm1::Button2Click(TObject *Sender)
{
if(SaveDialog1->Execute ())
OleContainer1->SaveToFile(SaveDialog1->FileName));
}
Для закрытия контейнера необходимо при закрытии приложения предусмотреть код: OleContainer1->DestroyObject(); Особенность - вместо меню Файл, имеющегося в Microsoft Word, встраивается меню разрабатываемого приложения, в котором также можно предусмотреть команды открытия и закрытия файла - естественно в приложении должно быть это меню создано обычным для создания приложений образом. Диалог позволяет выбирать доступный тип документа. Однако, если тип нового объекта известен, можно внедрять его программно.
OleContainer1->CreateObject("Word.Document",false);
//или
OleContainer1->CreateObject ("Excel.Sheet",false);
...
OleContainer1->DoVerb (ovShow);
Кроме того, если выберать при создании документа опцию "Создать из файла" и в нем с помощью кнопки "Обзор" выбрать необходимый файл, а в опции "Связь" поставить галочку, то в приложении создаается внедренный и связанный объект OLE, который при его сохранении можно будет открыть из приложения, но открыть можно только как объект OLE - открытие его из родного редактора становится невозможным. Однако все изменения в исходном файле отражаются в приложении и наоборот. Кроме того, "родная" программа редактора открывается в полноценном приложении. Можно открыть документ и программно:
void __fastcall
TForm1::Button3Click(TObject *Sender)
{
if(OpenDialog1->Execute ())
{
OleContainer1->CreateObjectFromFile(OpenDialog1->FileName,false);
OleContainer1->Repaint();
}
}
Метод GreateLinkToFile позволяет создать внедренный и связанный объект: void CreateLinkToFile(OpenDialog1->FileName,false); Связанный объект можно сохранить и как обычный документ: OleContainer1->SaveAsDocument(SaveDialog1->FileName,false); Работа с любым нетекстовым файлом подобного типа полностью аналогична работе с документами формата .doc. Доступ к файлам с использованием контроллеров и серверов автоматизацииЭтот раздел оказался настолько большим, что его пришлось определить в отдельный раздел - Контроллеры и серверы автоматизации Word и Excel. Кроме того принципы работы и доступа к файлам .doc и .xls могут быть использованы и при доступе к файлам других приложений. Работа с файлами через компоненты вкладки OffiseЭтот пункт рассмотрим напримере доступа к файлам Word. Из вкладке Office паместим на форму вновь создаваемого приложения коипоненты TWordApplicaton и TWordDocument. Код работы с файлом будем писать в любом обработчике, например, события нажатия кнопки компонента TButton. Целью поставим создание (открытие) файла в приложении Word, внесение в документ некоторой информации и сохранение файла. Все пояснения к данному примеру будут описаны в качестве пояснений к коду. Доступ к файлам при применении компонент вкладки Office основан на механизмах, описанных в предыдущем параграфе и используют механизмы OLE - компоненты лишь немного упрощают работу.
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
//Переменные лучше объявить быть как TVariant
TVariant tvTemplate,tvNewTemplate,tvPath,tvParam1,
tvParam2,tvParam3,tvParam4,tvParam5;
//Место шаблона документов Word
tvTemplate=
"C:\\WINDOWS\\Application Data\\Microsoft\\Шаблоны\\Normal.dot";
tvNewTemplate=false;
tvPath=StringToOleStr("c:\\a.doc");
tvParam1 =(TVariant)True;
tvParam2 =(TVariant)False;
tvParam3 =(TVariant)1;
tvParam4 =(TVariant)0;
tvParam5 =(TVariant)"";
//Двойной перехват исключений для разделения причин их вызвавших
try
{
try
{
//Проверяем наличие Word
WordApplication1>Connect();
WordApplication1>Application>Visible=true;
}
catch(Exception &exception)
{
MessageDlg("Word скорее всего не установлен",
mtError,TMsgDlgButtons() << mbYes,0);
Abort;
}
// Создаем или чтение рабочего документа
//1. Можно указать шаблон документа по умолчанию
WordApplication1>Documents>Add(EmptyParam,tvNewTemplate);
//2. Тоже можно указать шаблон непосредственно
WordApplication1>Documents>Add(tvTemplate,tvNewTemplate);
//3. Или с использованием всех параметров - интересны параметры
// тип документы - 0 = Word и четвертый - видимомсть документа - false
// Последний параметр (Word_2k::WordDocument** prop) - идентификатор
// документа можно опустить по умолчанию
WordApplication1>Documents>Add(tvTemplate,tvNewTemplate,
tvParam3,tvParam1);
// Функция не обязательна если указан полный путь к файлу
// WordApplication1>ChangeFileOpenDirectory((wchar_t*)"C:\\");
//Сделать доступным интерфейс
WordApplication1>GetDefaultInterface()>Visible = true;
//4. Можно просто открыть документ так - все параметры по умолчанию
WordApplication1>Documents>Open(&tvPath);
//5. Можно открыть документ с указанием всех параметров -
//наиболее интересные закоментированы
WordApplication1>get_Documents()>Open(&tvPath,tvParam2,
tvParam2/*ReadOnly*/,tvParam1,tvParam5/*PasswordDocument*/,
tvParam5,tvParam2,tvParam5/*WritePasswordDocument*/,tvParam5,
(TVariant)wdOpenFormatDocument/*Format*/,tvParam1/*Visible*/);
//6. Или воспользоваться функцией OpenOld
WordApplication1>get_Documents()>OpenOld(&tvPath,tvParam2,
tvParam2/*ReadOnly*/,tvParam1,tvParam5/*PasswordDocument*/,
tvParam5,tvParam2,tvParam5/*WritePasswordDocument*/,tvParam5,
(TVariant)wdOpenFormatAuto/*Format*/);
// Открыть документ можно в следующих форматах - файл word_2k.h
/*
typedef enum WdOpenFormat
{
wdOpenFormatAuto = 0,
wdOpenFormatDocument = 1,
wdOpenFormatTemplate = 2,
wdOpenFormatRTF = 3,
wdOpenFormatText = 4,
wdOpenFormatUnicodeText = 5,
wdOpenFormatEncodedText = 5,
wdOpenFormatAllWord = 6,
wdOpenFormatWebPages = 7
} WdOpenFormat;
*/
//Имя документа
WordApplication1>set_Caption(StringToOleStr("Word документ"));
//Подключение WordDocument к WordApplication
WordDocument1>ConnectTo(WordApplication1>Documents>Item(tvParam3));
//Отключение/Включение проверки правописаня
WordApplication1>Options>CheckSpellingAsYouType=false;
WordApplication1>Options>CheckGrammarAsYouType=false;
//Выбор параграфа
SelectionPtr Sel = WordApplication1>get_Selection();
//Отступ
Sel>Paragraphs>set_LeftIndent(20);
for(int i = 0; i < 10; i++)
{
//Следующий параграф
Sel>TypeParagraph();
//Выводим текст
Sel>TypeText(StringToOleStr(IntToStr(i)+" - строка"));
}
//1. Сохраняем и закрываем.
WordDocument1->SaveAs(tvPath);
//2. Можно тспользовать и другие параметры из которых
// интересен пожалуй только формат сохранения FileFormat
WordDocument1->SaveAs(
tvPath,(TVariant)wdFormatDocument/*FileFormat*/,
tvParam2,tvParam5,tvParam1,tvParam5,tvParam2,tvParam1,tvParam1,
tvParam2,tvParam2);
WordApplication1->Disconnect();
/*
typedef enum WdSaveFormat
{
wdFormatDocument = 0,
wdFormatTemplate = 1,
wdFormatText = 2,
wdFormatTextLineBreaks = 3,
wdFormatDOSText = 4,
wdFormatDOSTextLineBreaks = 5,
wdFormatRTF = 6,
wdFormatUnicodeText = 7,
wdFormatEncodedText = 7,
wdFormatHTML = 8
} WdSaveFormat;
*/
}
catch (Exception &exception)
{
Application>ShowException(&exception);
WordApplication1>Disconnect();
}
}
Тема "Работа с файлами через компоненты вкладки Offise" столь же
интересна и обширна как и тема "Работа с файлами с использованием
контроллеров и серверов автоматизации" и ы дальнейшем планируеися
посвятить ей отдельный раздел (ближе к лету).
|