Глава 8. Объекты приложения


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

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

Минимальные требования


    Чтобы ваше программа ObjectWindows стала рабочим приложением Windows, она должна в своем основном блоке begin..end делать следующее:

    Используемый по умолчанию объект выполняет эти задачи путем вызова трех его методов: Init, Run и Done. Основной блок любой программы ObjectWindows содержит обычно эти три метода. Чтобы изменить поведение по умолчанию, методы нужно переопределить.

Поиск объекта приложения


    Когда программа выполняется, ObjectWindows поддерживает глобальную переменную Application - указатель на объект приложения. Этот указатель позволяет подпрограммам вне объекта приложения обращаться к его полям и методам. По умолчанию Application устанавливается в @Self конструктором объекта приложения и в nil деструктором объекта.

Минимальное приложение


    Приведем пример минимального приложения ObjectWindows:

     program MinApp;
     uses OWindows;
     var
       MyApp: TApplication;
     begin
       MyApp.Init('TtstApp');
       MyApp.Run;
       MyApp.Done;
     end;

    MinApp - это абсолютный минимум приложения ObjectWindows. Эта программа не требует определения других объектов. Обычно вы определяете новый объектные типы как минимум для приложения и основного окна.

Методы Init, Run и Done


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

Конструктор Init


    Первый оператор - это вызов конструктора Init приложения. Этот вызов делает следующее:

    Когда Init завершает работу, основное окно вашего приложения находится на экране. В большинстве случаев вам нужно только переопределить InitMainWindow.

Метод Run


    Второй оператор вызывает метод Run приложения, который реализует работу приложения, вызывая MessageLoop. MessageLoop обрабатывает сообщения от Windows и выполняет инструкции, которые управляют работой приложения. MessageLoop - это цикл, который продолжает работу до закрытия приложения.

    MessageLoop вызывает несколько методов, обрабатывающих конкретные поступающие сообщения (см. далее).

Деструктор Done


    Done - это деструктор объекта приложения. Перед завершением работы приложения он освобождает память объекта приложения.


Рис. 8.1 Вызовы методов, управляющие работой приложения.

Инициализация приложения


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

Инициализация основного окна


    Вы должны определить метод InitMainWindow, который строит и инициализирует объект основного окна и сохраняет его в поле MainWindow объекта приложения. Ниже показан пример описания объекта приложения и метода InitMainWindow.

    Данный метод создает новый экземпляр типа TWindow ObjectWindows (PWindow - это указатель на тип TWindow). Обычно ваша программа будет определять для своего основного окна новый оконный тип, а InuitMainWindow будет использовать этот тип вместо TWindow.

    Примечание: Объекты окон подробно описываются в Главе 10.

    Следующее простое приложение ObjectWindows объединяет в себе новый тип TMyApplication и старое приложение MinApp. Оно отличается от MinApp только тем, что основное окно имеет заголовок:

     program TestApp;
     uses OWindows;
     type
       TMyApplication = object(TApplication)
          procedure InitMainWindow; virtual;
       end;
     procedure TMyApplication.InitMainWindow;
     begin
       MainWindow := New(PWindow, Init(nil, 'Основное окно'));
     end;
     var
       MyApp: TApplication;
     begin
       MyApp.Init('TestApp');
       MyApp.Run;
       MyApp.Done;
     end;

    Программа TestApp выводит окно с заголовком 'Основное окно'. Вы можете легко перемещать это окно и изменять его размер, минимизировать его, восстанавливать или максимизировать. Закрытие окна завершает приложение. Короче, TestApp - это полнофункциональный "скелет" приложения, оснащенный только простейшим основным окном.

Специальный вывод основного окна


    Начальный вывод основного окна управляется переменной CmdShow модуля System. CmdShow содержит одну из констант sw_ и передается в качестве параметра функции API Windows ShowWindow, когда приложение создает свое основное окно.

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

Инициализация первого экземпляра


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

    Если текущим экземпляром является первый экземпляр, то конструктор Init вызывает InitApplication. TApplication определяет заместитель метода InitApplication, который вы можете для выполнения специальной инициализации первого экземпляра переопределить.

    Например, вы можете модифицировать TestApp таким образом, чтобы о первом экземпляре сообщалось в заголовке основного окна. Для этого добавьте в тип приложения TMyApplication булевское поле с именем FirstApp, затем переопределите метод InitApplication, чтобы он устанавливал FirstApp в True. Наконец, модифицируйте InitMainWindow для проверки FirstApp и вывода в основном окне приложения соответствующего заголовка (см. Рис. 8.2).


Рис. 8.2 Новая инициализация приложения.

     program TestApp;
     uses OWindows;
     type
       TMyApplication = object(TApplication)
           FirstApp: Boolean;
           procedure InitMainWindow; virtual;
           procedure InitApplication; virtual;
       end;
     procedure TMyApplication.InitMainWindow;
     begin
       if FirstApp then
           MainWindow := New(PWindow, Init(nil,
          'Первый экземпляр'))
       else MainWindow := New(PWindow, Init(nil,
          'Дополнительный экземпляр'));
     end;
     procedure TMyApplication.InitApplication;
     begin
       FirstApp := True;
     end;
     var MyApp; TMyApplication;
     begin
       MyApp.Init('TestApp');
       MyApp.Run;
       MyApp.Done;
     end.

Инициализация каждого экземпляра


    Пользователь может одновременно выполнять несколько экземпляров ObjectWindows. Метод InitInstance инициализирует каждый экземпляр приложения. Он должен инициализировать только само приложение, а не его основное окно. Основное окно инициализируйте в InitMaionWindow.

    InitInstance вызывает InitMainWindow, а затем создает и выводит основное окно. Для модификации стандартной инициализации приложения (например, для загрузки таблицы оперативных клавиш) вам нужно только переопределить InitInstance. Если вы переопределяете для своего приложения InitInstance, убедитесь сначала, что он вызывает метод InitInstance, наследуемый из TApplication.

    Приведем метод InitInstance, который перед выполнением приложения загружает метод InitInstance. 'MeHotKeys' - это идентификатор ресурса таблицы оперативных клавиш, определенный в файле ресурса:

     procedure TEditApplication.InitInstance;
     begin
       inherited InitInstance;
       HAccTable := LoadAccelerators(HInstance, 'MyHotKeys');
     end;

    Вы можете также использовать InitInstance для регистрации экземпляра приложения с внешней DLL (типа Paradox Engine).

Выполнение приложений


    Метод Run вашего приложения вызывает метод MessageLoop, который вызывает цикл обработки сообщений вашей программы. Во время выполнения вашей программы в цикле обработки сообщений обрабатываются поступающие от Windows сообщения. Программы ObjectWindows наследуют цикл MessageLoop, который работает автоматически. Дополняйте цикл обработки сообщений только специальными диалогами, оперативными клавишами или обработкой MDI.

    Метод MessageLoop для обработки сообщений Windows вызывает три метода. ProcessDlgMsg работает с безрежимными диалоговыми окнами, ProcessAccels - обрабатывает оперативные клавиши, а ProcessMDIAccels - оперативные клавиши для приложений MDI. Для приложений, не использующих командные клавиши или безрежимные диалоговые окна или не являющихся приложениями MDI MessageLoop можно несколько упростить. См. методы TApplication в Главе 21.

Закрытие приложений


    Вызов деструктора Done в основной программе вашего приложения уничтожает объект приложения. Однако, перед тем как это происходит, программа должна прервать цикл обработки сообщения. Это может произойти, когда пользователь пытается закрыть основное окно приложения. Мы говорим, что это может произойти, так как ObjectWindows предусматривает механизм изменения поведения приложения при закрытии: вы можете задавать условия закрытия.

Модификация поведения при закрытии


    Все оконные объекты, наследуют булевский метод CanClose, возвращающий по умолчанию True (что указывает на подтверждение закрытия, то есть TestApp закрывается немедленно). Для изменения поведения при закрытии вы можете переопределить методы CanClose приложения или основного типа окна. Если какие-либо объекты возвращают из CanClose значение False, то приложение завершиться не может. Обычно вы можете изменить поведение при закрытии объектного типа основного окна. Например, перед завершением можно убедиться, что приложение сохранило файлы.

Механизм CanClose

    Механизм CanClose дает основному окну, объекту приложения и другим окнам возможность подготовиться с закрытию или предотвратить его. В итоге объект приложения должен разрешить закрытие приложения. По умолчанию он проверяет основное окно. Обычная последовательность закрытия выглядит следующим образом:

    1. Windows посылает основному окну приложения сообщение wm_Close.

    2. Объект основного окна вызывает метод CanClose объекта приложения.

    3. Объект приложения вызывает метод CanClose.

    4. Объект основного окна вызывает метод CanClose для каждого из дочерних окон и возвращает True только в том случае, если методы CanClose дочерних окон возвращают True.

Модификация CanClose

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

    Например, метод CanClose окна редактора может проверять изменение редактируемого текста, а затем выводить диалоговое окно с запросом, нужно ли сохранить текст перед закрытием, и воспринимать ответ Yes (Да), No (Нет) или Cancel (Отмена). Cancel будет указывать, что пользователь пока не хочет закрывать приложение, так что CanClose должен возвращать False. CanClose следует также возвращать False при обнаружении ошибки в сохранении текста, предоставляя пользователю другую возможность сохранения данных перед закрытием.

    Если метод CanClose не переопределяется, тип основного окна наследует его от TWindowsObject, где CanClose возвращает True после вызовов методов CanClose дочерних окон. Чтобы модифицировать поведение при закрытии основного окна, переопределите метод CanClose. Например, для проверки того, что пользователь собирается закрыть окно, он может проверять открытые файлы.