Работа с файлами, созданными не текстовыми редакторами

Работа с распространенными файлами типа .doc и .xml не так удобна при использовании компоненет Borland С++ Builder и для доступа к с этими файлами приходится идти по довольно запутанным дорожкам кодов. Причина - эти файлы, привычные для Windows, (и не только эти - .dot, .xla, .wiz, .cag, .fla, .ppt и другие) устроены по правилам структурированного хранилища. Сам же файл на диске, хранящий внутри себя это хранилище, называется "файл-документ" (docfile) или "составной файл" (compound file). Первый термин применялся во времена OLE 1, второй появился вместе с OLE 2, сейчас они обычно используются как синонимы. Кроме того в OLE существует понятие "составной документ" (compound document) - термин обозначающий абстрактный подкласс хранилищ особого вида, о чем речь пойдет несколько позже.

Составной файл состоит из следующих элементов:

  • "хранилища" (storages) - внутренние каталоги, описывающие состав и расположение других объектов внутри составного файла;

  • "потоки" (streams) - массивы структурированной информации, имеющие собственное имя;

  • "заблокированные данные" (lock-bytes) - неструктурированные массивы двоичной информации, которые не могут служить OLE-объектами.

Для доступа к таким файлам используется идея экспорта объектов, заключается в том, что один модуль создает объект, а другой его использует посредством обращения к его доступным методам (сервисам) - или "Технология 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();
 }
}

Тема "Работа с файлами через компоненты вкладки Office" столь же интересна и обширна как и тема "Работа с файлами с использованием контроллеров и серверов автоматизации" и ы дальнейшем планируеися посвятить ей отдельный раздел (ближе к лету).

В начало

На главную подраздела

Домой