Есть колеса,

нету гривы,

нет

на хвост волосиков.

В.Маяковский. Конь-огонь.

В этой статье мы, как и в предыдущих* разовьем с помощью техники конечных автоматов типовой пример программы из комплекта Visual C++. На сей раз речь пойдет об управляющих элементах ActiveX.

Но если создать такой модуль совсем легко, то "оживить" намно о сложнее. А иногда необходимо (или просто очень хочется), чтобы объект не только красиво выглядел, но и "жил собственной жизнью". Тот, кто знаком с ассистентами Microsoft Office 97 (Word, Excel и т. д.) или переводчиком Magic Gooddy фирмы ПРОМТ, без труда поймет, о чем идет речь.

Чтобы наделить объект "жизнью", необходима модель, определяющая его поведение во времени. В этой роли выступит конечный автомат, а создать объекты ActiveX с автоматными свойствами нам, как и в предыдущих статьях, поможет библиотека FSA. Ее можно получить по адресу http://www.osp.ru/pcworld/2000/02/spfire.zip.

Создание просто о элемента ActiveX

Проектирование

Первым делом создадим элемент ActiveX, стараясь как можно меньше вмешиваться в работу Visual C++.

  1. В меню File выберем пункт New, а в открывшемся окне - закладку Project.
  2. В поле Project Name введем имя проекта (например, Ellipse), а в поле Location - путь к папке, де он будет располагаться, после че о запустим мастера проектов, дважды щелкнув на значке MFC ActiveX Control Wizard.
  3. Нажмем кнопку Finish.
  4. Прочтем информацию о создаваемом проекте и нажмем кнопку OK. Мастер завершит работу.
  5. Скомпилируем и скомпонуем элемент, нажав клавишу или выбрав команду Build Ellipse.ocx в меню Build.

Так, "одной левой", мы создали "фирменный" элемент ActiveX. Но мы еще не знаем, как он выглядит и как работает. Чтобы удовлетворить наше любопытство, элемент необходимо протестировать. Это чуть сложнее, чем создать его, но ненамного.

Тестирование

Посмотреть на элемент ActiveX можно либо прямо из среды Visual C++, либо встроив его в какой-нибудь документ.

В первом случае следует выбрать в меню Tools команду ActiveX Control Test Container, которая запускает специальную про рамму тестирования элементов на базе модели COM из Microsoft SDK. В меню Edit этой про раммы выберем пункт Insert OLE Control, после чего (возможно, с небольшой задержкой) на экране появится диалоговое окно с перечнем зарегистрированных на текущий момент в операционной среде элементов. Найдем в нем только что созданный элемент (его имя - EllipseCtl) и нажмем или кнопку OK.

После всех этих манипуляций мы наконец увидим наш элемент ActiveX, представляющий собой прямоу ольное окно с вписанным в не о эллипсом. Элемент можно будет немножко "помучить" - подвигать, изменить е о размеры и т. д., - а также размножить, для че о достаточно выбрать в меню появившийся там пункт OCX. На экране возникнет очередной эллипс в прямоу ольнике; только не забудьте предварительно сдвинуть ранее вставленный элемент, иначе новый его закроет.

Во втором случае нам понадобится документ, например, такой, как показан в Листинге 1. За основу в нем был взят текст из проекта с именем Poly, созданно о мастером ATL COM AppWizard (почему-то этот мастер создает файл с расширением htm, а MFC ActiveX Control Wizard - нет), в котором потребовалось только изменить значение параметра CLASSID на идентификатор нужно о нам класса. Первоначально в документе стояло:

  CLASSID="CLSID: B1BFF0B2-5C58-11D3-9BDF
-00E04CDD233A">

Идентификатор содержится в файле Ellipse.odl, который откроется при щелчке на пункте _DEllipseEvents в списке классов проекта, и находится в строках:

// Class information for CЕllipseCtrl
  [ uuid(B1BFF0B2-5C58-11D3-9BDF-00E04CDD233A),
  helpstring("Ellipse Control"), control]

Если дважды щелкнуть на созданном HTML-файле в Проводнике Windows, откроется окно Internet Explorer с тем же самым эллипсом.

И в первом, и во втором случае это скучный, "мертвый" эллипс. Есть ли способ как-нибудь "оживить" его, не очень при этом напрягаясь? Есть. Но прежде чем рассказать о необходимых для это о изменениях в проекте, обсудим новую "жизнь" эллипса, т. е. алгоритм, или, иначе, модель е о поведения.

Дышите... Не дышите...

Предположим, мы хотим, чтобы эллипс "дышал", изменяя свои размеры по оси X от некоторого заданно о значения до нуля и обратно. Пусть его максимальная ширина определяется размером по оси X окна, отображающе о элемент ActiveX (про рамма ActiveX Control Test Container очерчивает это окно прямоугольником).

Можно создать "дышащий" эллипс, внеся изменения в метод OnDraw, - например, так, как показано в Листинге 2 (строки, порожденные мастером, закомментированы). Однако тогда будет сложно организовать взаимодействие элемента ActiveX с приложением (в приведенном варианте проблемы будут связаны прежде всего с "вешающим" приложение циклом While).

Поэтому применим для решения задачи принципиально иной метод, взяв за основу не последовательную алгоритмическую модель, которая заложена в подавляющее большинство языков программирования (влючая и Си++), а автоматную, изначально рассчитанную на параллельную работу. Это снимет многие вопросы, в том числе и упомянутую опасность зависания.

Автоматная модель эллипса

Автоматная модель "дышащего" эллипса

Граф конечного автомата, реализующего"дышащий" эллипс

Построим автоматную модель требуемого поведения эллипса. Граф соответствующего конечного автомата показан на рисунке, а его таблица переходов - в таблице. Все о у автомата три внутренних состояния: E0, E+ и E-.

В нашей модели два предиката (x1 и x2) и четыре действия (y1, y2, y3 и y4), которые можно условно описать, например, так:

// Предикаты
  x1 истина, если X <= nX;
  x2 истина, если X <= 0;
  // Действия
  y1 определить начальные размеры эллипса;
  y2 увеличить размер по оси X;
  y3 уменьшить размер по оси X;
  y4 отобразить эллипс;

(Здесь: X -- текущее значение радиуса эллипса по оси X, nX -- начальный размер эллипса по оси X.)

Функционирование модели

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

Из состояния Е0 автомат все да переходит в состояние Е+ и при этом выполняет начальное действие y1 - определяет ширину окна отображения и разрешает рисование объекта. В состоянии Е+ он наращивает длину оризонтальной полуоси эллипса до максимума (половина ширины окна), в состоянии E- уменьшает до нуля. При максимальном значении длины автомат переходит из состояния Е+ в состояние Е-, при нулевом - из E- в E+.

Запро раммировать такой автомат с помощью библиотеки FSA достаточно просто. В следующих разделах мы покажем, как это сделать.

Настройка проекта на FSA-среду

Прежде, чем приступить к изменению класса CElipseCtrl, отвечающе о за "жизнь" элемента ActiveX, необходимо произвести стандартную настройку проекта и внести изменения в некоторые файлы, общие для всех подобных проектов.

"Прошивка" путей к файлам за оловкам библиотеки FSA

Предположим, что файлы библиотеки FSA находятся в каталоге с именем LW_MC50NN. Чтобы установить с ними связь, выполним следующие шаги:

1. В поле настроек проекта Additional include directories (Project*Settings...+*закладка С/С++: Preprocessor) внесем строку:

  lw_mc50nninclude

2. В окне списка файлов проекта выберем закладку Fil..., затем папку Header files, откроем в ней файл StdAfx.h и вставим в него строку:

  #include 
 // FSA support for Finit 
State Automation Processes

3. Добавим к файлам наше о проекта FSA-библиотеку, чтобы разрешить внешние ссылки на нее. Для это о перейдем на закладку Fil..., щелкнем на лавной папке правой кнопкой мыши, выберем в контекстном меню пункт Add Files to Project, установим тип файлов .lib и укажем нужный путь. Файл библиотеки называется fsam532.lib и находится в катало е lw_mc50nn/source/fsa/release.

Создание автоматной среды

Для создания среды функционирования про раммных автоматов необходимо выполнить два условия:

  • определить место для класса CArrayNetFsa;
  • создать механизм периодическо о запуска метода OnIdleFsa, принадлежаще о классу CArrayNetFsa.

Класс CArrayNetFsa содержит все необходимое для реализации параллельной работы множества про раммных автоматов (даже более того - множества автоматных сред). Он же обеспечит нам дискретное автоматное время, которое моделируется с помощью периодическо о вызова метода OnIdleFsa.

Удобнее все о сделать CArrayNetFsa одним из родительских классов CEllipseApp. При этом файл заголовка примет следующий вид:

class CEllipseApp : public COleControlModule,
 public CArrayNetFsa
  {
  ...
  };

Метод OnIdleFsa хорошо было бы запускать, используя метод OnIdle, принадлежащий классу COleControlModule, а точнее, его предку CWinApp, но в случае ActiveX-объектов вызов это о метода перехватывается "на дальних подступах" средой (окном, документом), в которой находится объект. Поэтому реализуем дискретное время другим способом - подключим к приложению таймер, а в метод OnTimer вставим обращение к OnIdleFsa.

Таймер ле ко подключить к любому окну, т. е. классу, порожденному от CWin. В нашем проекте окно представляет собой класс с именем CEllipseCtrl, порожденный от COleControl, который, в свою очередь, порожден от нужного нам CWin.

Программируем ActiveX с автоматом

Прикладной класс элемента ActiveX представлен в проекте классом CEllipseCtrl. Чтобы придать ему автоматные свойства, нужно:

  • включить в перечень е о классов-предков автоматный класс LFsaAppl из состава FSA-библиотеки;
  • запро раммировать необходимые для функционирования автомата предикаты и действия;
  • построить таблицу переходов автомата;
  • подключить к приложению таймер.

В результате за оловок класса CEllipseCtrl примет вид, показанный в Листинге 3.

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

. bIfDraw -- признак разрешения рисования фи уры (для метода OnDraw);

. nX; -- максимальный размер эллипса по оси X;

. rcEllipse -- текущие размеры эллипса;

. x1, x2 -- предикаты автомата;

. y1, y2, y3, y4 -- действия автомата.

Для работы с таймером нам потребуются, кроме того, методы OnCreate и OnTimer (Листин 4), которые можно добавить с помощью AppWizard. Первый произведет при создании объекта инициализацию таймера, а второй будет обрабатывать поступающие от таймера сообщения. В конструкторе класса (Листин 5) нужно запретить рисование фигуры и подключить автомат к автоматной среде.

Необходимы также небольшие изменения в методе OnDraw класса CEllipseCtrl (Листинг 6). Во-первых, эллипс должен отображаться только при условии, что его рисование разрешено (т. е. когда bIfDraw=TRUE). Во-вторых, за размеры эллипса теперь отвечает не параметр rcBounds, а атрибут самого класса переменная rcEllipse.

Рассмотрим теперь компоненты, составляющие собственно автомат: таблицу переходов, предикаты и действия. Таблица переходов, задающая поведение автомата, показана в Листинге 7. Как видим, эта запись очень похожа на приведенную выше табличную форму. Сходным образом, реализация автоматных методов (Листинг 8) определяет на языке про раммирования то, что было изложено в свободной форме в перечне предикатов и действий модели эллипса.

Теперь все готово. Эллипс из простой статической картинки превратился в объект, живущий "полнокровной жизнью", которая определяется его автоматным поведением.

"Жить - хорошо, а хорошо жить..."

Итак, конечный автомат - простое средство придания динамики любому элементу ActiveX. Необходимые для этого условия понятны всякому, кто знаком с принципами построения и функционирования автоматной модели, а требуемые усилия невелики.

К минусам проекта следует отнести зависимость от таймера. Использование метода OnIdle сделало бы процедуру формирования дискретно о автоматно о времени более независимой и ускорило бы работу автоматной среды. Однако и с таймером можно добиться большей скорости работы, если, не выходя из обработчика, нужное число раз в цикле вызывать функцию OnIdleFsa.

Автоматная модель дополняет понятие объекта в ООП и COM, позволяя определить его поведение во времени, причем подходит как для единичных объектов, так и для множеств, и для сложных объектов, состоящих из нескольких параллельно работающих компонентов.

Задачу "оживления" типового элемента ActiveX мы решили. Теперь можно поработать над е о внешним видом, раскрасить, создать для не о новый алгоритм поведения - конечно же, автоматный.

Об авторе

Любченко Вячеслав Селиверстович. E-mail: slava@ivvson.kc.ru

Литература

1. Любченко В.С. О бильярде с Microsoft Visual C++ 5.0. "Мир ПК", 1998, N 1, с.202.

2. Любченко В.С. Батарея, огонь! или Задача Майхилла для Microsoft Visual C++. "Мир ПК", 2000, N 2, с. 148


* См. О бильярде с Microsoft Visual C++ 5.0. "Мир ПК", N 1/98, с.202; Батарея, огонь! или Задача Майхилла для Microsoft Visual C++. "Мир ПК", N 2/2000, с. 148.


Таблица переходов автомата
Текущее состояниеСледующее состояниеУсловие переходаДействия
E0E+-y1
E+E+x1y2y4
E+E-^x1y3y4
E+E+^x2y3y4
E+E-x2y2y4