Создание серверов автоматизации с помощью C++Builder

Наталия Елманова

Что такое автоматизация

Подавляющее большинство современных настольных приложений предназначено для выполнения пользователями тех или иных операций — создания документов, проведения расчетов, анализа данных и т.д. В процессе выполнения подобного рода работ применяются различные внутренние сервисы этих приложений, например, вычисление значений по формулам, автоматическая нумерация абзацев, заголовков или страниц, проверка орфографии и многое другое. Нередко подобные приложения обладают встроенными макроязыками, позволяющими создать код, использующий эти сервисы, например, в случае часто повторяющихся последовательностей операций. Иначе говоря, приложения подобного рода.обладают программируемостью.

Отметим, однако, что программирование с помощью макроязыков имеет свои недостатки, так как не существует спецификаций, которым должны подчиняться макроязыки. Соответственно в общем случае у каждого программируемого приложения имеется свой макроязык, отличный от макроязыков других программируемых приложений (Отличием здесь являются продукты фирмы Microsoft, где в качестве макроязыка выбрано подмножество Visual Basic — Visual Basic for Applications. Прим. ред).

Более удобной реализацией программируемости настольных приложений было бы наличие в них возможности предоставлять свои сервисы другим приложениям с помощью универсального механизма, не зависящего от встроенных макроязыков и позволяющего, в частности, использовать обычные языки программирования. Именно для этой цели и предназначен механизм, называемый автоматизацией (Automation) (Ранее этот механизм назывался OLE Automation. Прим. ред). В этом случае приложение, предоставляющее тот или иной сервис, использует для этой цели интерфейсы содержащихся внутри его адресного пространства COM-объектов, и называется сервером автоматизации. Приложение, использующее сервис, называется контроллером автоматизации и может быть написано с помощью подавляющего большинства современных средств разработки. Отметим, что серверами автоматизации являются, в частности, такие популярные приложения, как Microsoft Office (Word, Excel, PowerPoint), Seagate Crystal Reports, Microsoft Internet Explorer и даже сама оболочка Windows 95/98/NT.

В общем случае клиент и сервер находятся в разных адресных пространствах, и, соответственно, для управления сервером клиент должен обращаться к методам объектов, находящихся в другом адресном пространстве. Для этой цели используется технология LRPC (Local Remote Procedure Calls — локальные вызовы удаленных процедур).

Каждый COM-сервер (каковым является сервер автоматизации) и каждый класс COM-объектов обладает уникальным 128-битовым идентификатором GIUD (Global Unique Identifier). При обращении к классам COM-объектов он иногда называется CLSID (идентификатор класса). При создании COM-серверов (в том числе и серверов автоматизации) с помощью C++Builder GUID и CLSID генерируются автоматически, хотя при необходимости можно сгенерировать их с помощью вызова стандартной функции Windows API CoCreateGUID. Информация обо всех COM-серверах и классах COM-объектов хранится в системном реестре, что позволяет клиенту "не знать", в каком каталоге (или на каком компьютере локальной сети) находится COM-сервер.

В общем случае COM-сервер представляет собой приложение, которое создает COM-объект и делает его доступным для других программ. .Сервер автоматизации предоставляет для доступа объект специального типа — dispatch object. При этом в адресном пространстве приложения-контроллера, управляющего сервером, присутствует вариантная переменная, содержащая интерфейс IDispatch, открывающий предоставляющий доступ к этому объекту на COM-сервере..

Контроллер может управлять сервером, например, инициируя его выполнение, создание с его помощью документов и иных объектов, изменение размеров, положения и видимости окна сервера, копирование объектов сервера в буфер обмена, добавление данных в созданный сервером документ, и т.д.. Наличие тех или иных возможностей управления сервером зависит от того, какие объекты, свойства и методы сервера предоставлены разработчиками сервера для автоматизации с помощью внешних приложений.

Подготовительный этап: создание приложения, подлежащего автоматизации

Для создания сервера автоматизации следует создать обычное приложение и затем добавить к нему подобный объектописание класса COM-объектов, создаваемых этим приложением, для осуществления доступа к функциональности, которую нужно автоматизировать.

Разработаем простейший сервер автоматизации. С этой целью создадим обычное приложение, например, текстовый редактор, содержащий инструментальную панель с четырьмя кнопками и компонент TMemo, а также диалоговые панели открытия и сохранения файла.

Главная форма будущего сервера автоматизации

Рис. 1. Главная форма будущего сервера автоматизации

Создадим обработчики событий, связанные с нажатием на кнопки, и характерные для текстовых редакторов:

//--------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "main1.h"
//--------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//--------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------------------------
void __fastcall TForm1::SpeedButton1Click(TObject *Sender)
{
Memo1->Lines->Clear();
}
//--------------------------------------------------------------
void __fastcall TForm1::SpeedButton2Click(TObject *Sender)
{
if (OpenDialog1->Execute())
Memo1->Lines->LoadFromFile(OpenDialog1->FileName);
}
//--------------------------------------------------------------
void __fastcall TForm1::SpeedButton3Click(TObject *Sender)
{
if (SaveDialog1->Execute())
Memo1->Lines->SaveToFile(SaveDialog1->FileName);
}
//--------------------------------------------------------------
void __fastcall TForm1::SpeedButton4Click(TObject *Sender)
{
Close();
}
//--------------------------------------------------------------

Сохраним проект. Отметим, что пока созданный нами текстовый редактор представляет собой обычное Windows-приложение и не является сервером автоматизации.

Превращение приложения в сервер автоматизации

Для превращения созданного нами выше приложения в сервер автоматизации выберем элемент Automation Object со страницы ActiveX репозитария объектов:

Рис. 2. Выбор Automation Object из репозитария объектов

Введем имя класса, под которым данный класс COM-объектов будет зарегистрирован в реестре.

Диалоговая панель ввода имени класса

Рис. 3. Диалоговая панель ввода имени класса

После этого мы окажемся в редакторе библиотеки типов (Type Library Editor), в котором нам предстоит определить свойства и методы созданного класса COM-объектов.

Библиотека типов вновь созданного сервера

Рис. 4. Библиотека типов вновь созданного сервера

Библиотека типов

Что представляет собой библиотека типов и зачем она нужна? По существу, библиотека типов представляет собой двоичный файл с описанием интерфейсов COM-объекта и их методов.

Обычно такие описания создаются с помощью специального языка IDL (Interface Definition Language) и используются для того, чтобы разработчики знали, как создать код, реализующий методы COM-объекта (или вообще методы объекта, расположенного за пределами адресного пространства разрабатываемого приложения, так как IDL используется не только в COM-технологии, но и в иных технологиях, реализующих вызовы удаленных процедур или функций, например, CORBA). Помимо этого, описания методов на языке IDL могут быть использованы для автоматической генерации серверного и клиентского кода (так называемого proxy-кода и stub-кода) с помощью соответствующих утилит.

С другой стороны, proxy-код и stub-код могут быть сгенерированы динамически. В этом случае клиент должен динамически получать информацию о свойствах и методах интерфейсов COM-объекта, и в этом случае наличие библиотеки типов, содержащей такую информацию, может быть весьма удобно. Отметим, что библиотеку типов можно в принципе сгенерировать на основе описания на языке IDL с помощью специального компилятора MIDL, но в данном случае в этом нет необходимости.

Итак, приступим к редактированию библиотеки типов. Предположим, что мы хотим автоматизировать загрузку файла в окно редактора, сохранение набранного текста, очистку окна редактирования, определение и изменение ширины и видимости окна. Создадим также метод, добавляющий строку к редактируемому тексту. С этой целью реализуемjопишем для нашего сервера методы FileNew, FileOpen, FileSave, AddLine и их параметры, а также свойства Width и Visible, и опишем их параметры.

Отметим, что типы данных, используемые для описания параметров методов, не совпадают с типами данных С++, так как в этом случае используются типы данных, принятые в IDL. Наиболее часто используемые типы данных языка IDL приведены в таблице 1. Таблица 1. Наиболее часто используемые типы данных языка IDL

Тип данных Описание
short Двухбайтовое целое число со знаком
long Четырехбайтовое целое число со знаком
single Четырехбайтовое действительное число
double Восьмибайтовое действительное число
BSTR Двоичная строка
DATE Дата
VARIANT_BOOL true= -1, false = 0
VARIANT Указатель на вариантную переменную
int Целое (размер в байтах в общем случае зависит от разрядности операционной системы)

Подробный список типов данных IDL можно найти в документации С++Builder.

Отметим также, что наряду с типами данных IDL можно использовать все типы данных, определенные в самой библиотеке типов. а также в других библиотеках, на которые она ссылается.

Метод FileNew параметров не имеет. Методы FileOpen и FileSave имеют один строковый параметр типа BSTR — имя файла. Метод Addline также имеет один строковый параметр, задающий добавляемую строку. Свойство Visible имеет логический тип VARIANT_BOOL, при этом оно может быть как прочитано, так и изменено. Свойство Width имеет целый тип int (число пикселов) и также доступно как для чтения, так и для записи.

В результате библиотека типов приобретет примерно следующий вид:

Библиотека типов сервера автоматизации после описания свойств и методов объекта IDispatch

Рис. 5. Библиотека типов сервера автоматизации после описания свойств и методов объекта IDispatch

Создание кода реализации методов объекта автоматизации

Итак, мы описали свойства и методы нашего объекта, и теперь должны приступить к их реализации. Для этой цели следует нажать кнопку Refresh на инструментальной панели редактора библиотеки типов. После этого будет сгенерирован еще один модуль с заготовками процедур и функций, реализующих данные методы. В эти заготовки следует вписать соответствующий код (он показан выделенным шрифтом):

//--------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <atl\atlvcl.h>
#include "main3.h"
#include "main1.h"
//--------------------------------------------------------------
#pragma package(smart_init)
STDMETHODIMP TMyAuto3Impl::get_Width(int* Value)
{
try
{
*Value=Form1->Width;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::set_Width(int Value)
{
try
{
Form1->Width=Value;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::get_Visible(VARIANT_BOOL* Value)
{
try
{
*Value=Form1->Visible;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::set_Visible(VARIANT_BOOL Value)
{
try
{
Form1->Visible=Value;
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::NewFile()
{
try
{
Form1->Memo1->Lines->Clear();
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::SaveFile(BSTR Filename)
{
try
{
Form1->Memo1->Lines->SaveToFile(Filename);
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::AddLine(BSTR Line)
{
try
{
Form1->Memo1->Lines->Add(Line);
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------
STDMETHODIMP TMyAuto3Impl::OpenFile(BSTR Filename)
{
try
{
Form1->Memo1->Lines->LoadFromFile(Filename);
}
catch(Exception &e)
{
return Error(e.Message.c_str(), IID_IMyAuto3);
}
return S_OK;
};
//--------------------------------------------------------------

Скомпилируем и запустим сервер на выполнение. При этом он зарегистрируется в реестре.

Запись о сервере автоматизации в реестре Windows

Рис. 6. Запись о сервере автоматизации в реестре Windows

В действительности в разделах реестра HKEY_LOCAL_MASHINE\SOFTWARE и HKEY_CLASSES_ROOT содержится несколько записей, связанных с данным сервером и его интерфейсами, в том числе и информация о местоположении сервера.

Если в дальнейшем отпадет необходимость в использовании созданного сервера автоматизации, рекомендуется запустить его с ключом /unregserver. В этом случае соответствующие записи будут удалены из реестра. Если же возникнет необходимость перенести сервер автоматизации в другой каталог, можно после этого просто запустить его снова, и при этом записи в реестре обновятся.

Итак, мы создали настольное приложение, являющееся сервером автоматизации. Теперь, основываясь на информации о методах класса его объекта автоматизации, содержащейся в библиотеке типов, можно создавать приложения, управляющие этим сервером, с помощью довольно широкого спектра средств разработки (включающего Delphi, C++Builder, Visual Basic, Visual C++ и др.). Создание контроллеров автоматизации будет рассмотрено в следующей статье данного цикла.

Координаты автора:
Центр Информационных Технологий,
Тел. (095)932-92-12, 932-92-13,