Главный (стартовый) файл проекта

Главный (стартовый) файл проекта
Как выделить (пометить, обозначить) главный файл
Какой тип файла сделать главным
Содержимое главного файла
Идеология построения приложения
Точка останова. Read Events
Аварийное прекращение приложения. Настройка ON ShutDown
Надо ли давать команду QUIT для закрытия приложения
Как скрыть главное окно FoxPro (SCREEN)
Как скрыть системные ToolBar
Настройка среды FoxPro
Использование глобального объекта goAPP
Передача параметров в EXE
Заключение

Главный (стартовый) файл проекта

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

Возникает закономерный вопрос, какой файл из этой кучи в готовом файле EXE должен запускаться первым? А как этот файл выделить (пометить, обозначить)?

Вот этот самый файл, который в готовом файле EXE должен запускаться первым и называется главным (стартовым) файлом. О нем и пойдет речь в данном разделе. Для простоты, далее я буду называть этот файл просто главным файлом.

Как выделить (пометить, обозначить) главный файл

На этапе создания готового EXE-файла все наши файлы включаются в общий файл проекта (файлы с расширением PJX и PJT). Файл проекта - это средство как-то упорядочить ту кучу файлов, из которой впоследствии будет собран готовый EXE-файл, а, кроме того, это инструмент собственно сборки EXE-файла.

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

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

Если Вас не устраивает такой автоматический выбор, то Вы можете в любой момент указать в качестве главного файла нужный Вам файл. Для этого щелкните внутри проекта правой клавишей мыши по нужному файлу и в появившемся меню выберите пункт "Set Main". Выбранный файл будет выделен полужирным шрифтом, а файл, ранее обозначенный как главный файл, сбросит свое выделение и станет обычным (не главным) файлом.

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

Какой тип файла сделать главным

Главным файлом проекта может быть

  1. Программный файл (PRG)
  2. Форма (SCX)
  3. Меню

Ну, по сути, файл меню - это и есть программный файл. Так исторически сложилось. Точнее, разработчики FoxPro пока "не доросли" до коренной переделки идеологии построения меню, как они это сделали с формами, а в 9 версии и с отчетами.

Дело в том, что хотя на этапе проектирования меню - это файлы с расширением MNT, MNX, но после создания макета меню необходимо запустить его генерацию. Результатом генерации меню становятся файлы MPR, MPX. Вот эти-то файлы и есть обычные файлы PRG и FXP, только с измененным расширением.

Следовательно, по сути, выбор стоит между программным файлом и файлом формы.

Так вот, всегда указывайте в качестве главного файла программный файл. Более того, это должен быть именно файл PRG и ничто другое.

Причин этому множество. По мере дальнейшего обсуждения Вы увидите, что очень многое из того, что надо сделать в главном файле очень трудно, а иногда и просто невозможно реализовать в форме.

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

Содержимое главного файла

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

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

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

  1. Сделать настройки среды FoxPro для корректной работы программы
  2. Предложить ввести пользователю пароль для входа в программу и проконтролировать корректность ввода
  3. Выполнить инициализацию служебных библиотек, пользовательских настроек
  4. Инициализировать управляющие модули приложения
  5. Организовать "точку останова" для ожидания реакции пользователя
  6. Выполнить восстановление настройки среды FoxPro

Это далеко не полный перечень всего того, что может (или должно) быть выполнено в главном файле. Более того, что-то из перечисленного может или вообще не выполняться или выполняться не в главном файле.

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

Идеология построения приложения

Как уже упоминалось ранее, содержимое главного файла проекта зависит в первую очередь от конкретной задачи. Например, если целью Вашего проекта является создание COM-сервера, то в главном файле вообще ничего не будет. Однако, в некоторой степени, содержимое главного файла зависит от принятой идеологии построения приложения. Здесь, под фразой "идеология приложения" я имею в виду одну из следующих реализаций:

  1. На базе основного окна FoxPro (SCREEN)
  2. На базе "As Top-Level" форм

Построение приложения на базе основного окна FoxPro предполагает, что в конечном приложении пользователь будет видеть это основное окно (разумеется, со своим меню и ToolBar) и все формы будут открываться внутри этого основного окна. Построение приложения на базе "As Top-Level" форм предполагает, что основное окно FoxPro вообще не будет отображаться в конечном приложении. А в качестве основного окна будет выступать созданное программистом окно со свойством ShowWindow = 2 - "As Top-Level form" Разработчики FoxPro предполагали, что основной идеологией построения приложений будет построение приложений на базе основного окна FoxPro. На это указывает хотя бы тот факт, что значение по умолчанию для свойства формы ShowWindow = 0 - In Screen (Default). Кроме того, есть еще ряд проблем, которые возникнут по мере реализации приложения на базе "As Top-Level" форм, связанные именно с отсутствием основного окна FoxPro. Как следствие, построение приложения на базе "As Top-Level" форм требует повышенной бдительности от программиста и несколько сложнее в реализации. Не то, чтобы эти сложности были непреодолимы или требовали какого-то сложного программирования, но зачем создавать себе лишние проблемы, когда без них можно обойтись? Дальнейшее описание содержимого главного файла будет строиться исходя из предположения, что создается приложение на базе основного окна FoxPro. Впрочем, даже если Вы строите приложение на базе "As Top-Level" форм содержимое главного файла останется практически таким же. Отличия будут в некоторых деталях. По мере описания я буду указывать на эти детали.

Точка останова. Read Events

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

  DO MainMenu.mpr  

Синтаксически все абсолютно правильно. Более того, когда Вы будете запускать главный файл, на этапе разработки приложения все будет работать нормально. Но вот после того, как Вы создадите готовый EXE-файл и запустите его, то вместо ожидаемого приложения Вы увидите "странный" эффект: Окно FoxPro мелькнет на экране и тут же закроется. Причиной такого "странного" поведения является то, что Вы "забыли" указать FoxPro, в каком месте ему надо остановиться и подождать реакции пользователя. На этапе отладки такой "точкой останова" является ранее открытая среда FoxPro. Но в готовом файле EXE до него никакой среды FoxPro открыто не было! Чтобы создать "точку останова" надо дать специальную команду

READ EVENTS

Т.е. содержимое главного файла будет выглядеть примерно так:

DO MainMenu.mpr
READ EVENTS

Если Вы строите приложение на базе "As Top-Level" форм, то вместо меню, основным управляющим элементом является Ваша главная форма. И содержимое главного файла будет выглядеть примерно так:

DO FORM MainForm.scx
  READ EVENTS

Вот теперь, в готовом файле EXE, когда программа дойдет до команды READ EVENTS, то произойдет остановка в ожидании реакции пользователя. Буквально, фраза "READ EVENTS" переводится как "чтение событий". Т.е. вместо "точки останова" это следовало бы назвать как-то вроде "обнаружение и выполнение событий, инициализированных пользователем". Это более точно выражает смысл и задачи данной команды. Но уж больно длинно получается. Поэтому, хотя "точка останова" - это не совсем корректный термин, но в дальнейшем я буду использовать именно его. Имейте в виду, что одновременно, во всем приложении может быть активна только одна команда READ EVENTS. Вызов другой команды READ EVENTS не приведет к ошибке, но эта команда будет просто проигнорирована. Чтобы отменить действие команды READ EVENTS надо дать специальную команду

CLEAR EVENTS

По этой команде будет отменено действие команды READ EVENTS и выполнение перейдет на команду, следующую за командой READ EVENTS. Если затем не будет подано еще одной команды READ EVENTS, то главный файл будет выполнен до конца и приложение автоматически закроется. Если команда READ EVENTS была дана в главном файле, то CLEAR EVENTS сработает как команда RETURN TO MASTER. Т.е. выполнение той процедуры или метода, где собственно и была дана команда CLEAR EVENTS, будет прервана на этой команде и все что стоит ниже этой команды просто никогда не будет выполнено. Так, где же давать команду CLEAR EVENTS? Разумеется, в специальном пункте меню "Выход". Если Вы создаете приложение на базе "As Top-Level" форм, то команду CLEAR EVENTS надо давать в событии UNLOAD Вашей главной формы. Итого, получается примерно такая логика:

  1. Запускается главный файл
  2. Активизируется основное меню (или основная форма)
  3. По команде READ EVENTS организуется "точка останова" для ожидания действий пользователя
  4. Команда CLEAR EVENTS прекращает действие команды READ EVNETS и передает управление в главный файл на команду, непосредственно следующую за командой READ EVENTS.
  5. Завершается выполнение главного файла, что приводит к закрытию приложения FoxPro.

Аварийное прекращение приложения. Настройка ON ShutDown

До сих пор, речь шла о "штатном" завершении. Т.е. когда пользователь дисциплинированно использует все положенные пункты меню для выхода из приложения. Но ведь пользователь может закрыть приложение, нажав на крестик в правом верхнем углу основного окна FoxPro или, например, через окно "Диспетчер задач Windows" (Ctrl+Shift+Esc). Если все оставить так, как было описано до сих пор, то после такой попытки пользователя выскочит сообщение об ошибке

Quote: Cann`t quit Visual FoxPro

Или, в старших версиях FoxPro без сокращений

Quote: Cannot quit Visual FoxPro

Причина такого сообщения в том, что осталась активной команда READ EVENTS. Именно она и вызывает такое сообщение об ошибке в описанной ситуации. Чтобы перехватить описанные события закрытия приложения используется специальная настройка ON SHUTDOWN В принципе, можно просто написать

ON SHUTDOWN CLEAR EVENTS

Но обычно перед закрытием приложения надо выполнить ряд предварительных операций. Каких? Это уже зависит от Вашего приложения. Ну, например, можно спросить пользователя о том, действительно ли он хочет выйти из приложения или это у него "рука дрогнула". В общем случае одной команды недостаточно. Нужен вызов процедуры. Например:

ON SHUTDOWN DO MyExitProcedure

И вот уже в этой процедуре MyExitProcedure и надо дать команду CLEAR EVENTS. Причем эта команда должна быть самой последней, поскольку в момент ее выполнения управление будет передано на команду, следующую за командой READ EVENTS в главном файле и все то, что написано после CLEAR EVENTS в процедуре MyExitProcedure просто не будет выполнено.

Для универсальности, желательно в пункте меню "Выход", вместо простой команды CLEAR EVENTS также сделать вызов этой процедуры. Тогда у Вас будет один общий код, обрабатывающий выход из Вашего приложения при любых ситуациях.

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

Если Вы делаете приложение на базе "As Top-Level" форм, то вызов этой процедуры надо организовать еще и в событии QueryUnload главной формы. Точнее так, в событии QueryUnload главной формы надо перенаправить вызов на собственно метод, организующий закрытие всего приложения.

В результате, содержимое главного файла приобретает следующий вид

ON SHUTDOWN DO MyExitProcedure
  DO MainMenu.mpr
  READ EVENTS

PROCEDURE MyExitProcedure * Необходимые действия по "штатному" закрытию приложения CLEAR EVENTS RETURN

Надо ли давать команду QUIT для закрытия приложения

В FoxPro существует команда QUIT, которая приводит к немедленному закрытию приложения FoxPro. Правда эта команда также перехватывается настройкой ON SHUTDOWN и также по ней невозможно выйти из приложения, если активна команда READ EVENTS.

Возникает резонный вопрос, а нужна ли эта команда, если приложение FoxPro и так само закроется, когда завершится выполнение главного файла?

Ну, лично я считаю, что это команда для ленивых программистов. Т.е. для тех, кому лень отслеживать все открытые в приложении объекты. Как я уже упоминал ранее, "штатное" закрытие приложения (содержимое процедуры MyExitProcedure) в общем случае - это очень не тривиальная задача.

Ну, например, пользователь редактировал какие-либо данные в форме и нажал на крестик или пункт меню "Выход". Следует ли организовать "штатное" закрытие формы или просто "прихлопнуть" все открытые процессы?

По "правильному" логично спросить пользователя желает ли он сохранить внесенные изменения. Т.е. организовать "штатное" закрытие всех открытых объектов. А команда QUIT просто "прихлопнет" все открытые объекты без каких-либо дополнительных вопросов и все!

Есть и более тонкие моменты. Т.е. по хорошему, в процедуре MyExitProcedure надо организовать "штатное" закрытие всех открытых объектов и после команды READ EVENTS (где собственно и предполагается давать команду QUIT) вообще не должно остаться ничего такого, что следовало бы закрывать именно командой QUIT. Если все-таки что-то осталось, то это явная недоработка разработчика. И эта недоработка может очень сильно "аукнуться" в готовом приложении. Без команды QUIT Вы отловите эту проблему еще на стадии отладки приложения.

Кроме того, использование команды QUIT может осложнить отладку. Что, каждый раз после запуска главного файла заново открывать среду FoxPro? Впрочем, как это обойти, чуть ниже.

Итого, я не рекомендовал бы использовать команду QUIT.

Как скрыть главное окно FoxPro (SCREEN)

В большинстве случаев, вне зависимости от того в какой идеологии Вы разрабатываете свое приложение при загрузке среды FoxPro желательно скрыть главное окно FoxPro (SCREEN). Если Вы разрабатываете приложение в основном окне FoxPro, то потом его можно будет отобразить. Ну, а если приложение на базе "As Top-Level" форм, то отображение его и не нужно. Можно первой же командой в главном файле дать команду

_SCREEN.Visible = .F.

Окно FoxPro действительно скроется. Однако перед этим успеет все-таки "мелькнуть". Не хорошо. Чтобы подавить открытие главного окна следует использовать файл конфигурации Config.fpw. Это обычный текстовый файл. В нем должна быть строчка:

SCREEN=OFF

Более подробно о файле конфигурации рассказано в других разделах. Данная статья посвящена только главному файлу. Чтобы снова отобразить главное окно FoxPro (SCREEN) следует дать команду

_SCREEN.Visible = .T.

Если главное окно FoxPro (SCREEN) и до этой команды было отображено, то от этой команды хуже не будет.

Как скрыть системные ToolBar

Когда Вы запускаете свое приложение в режиме отладки, то системное меню заменяется Вашим меню. Но вот системный ToolBar остается "висеть", как ни в чем не бывало. Строго говоря, на системный ToolBar можно вообще не обращать внимания. Дело в том, что информация о том, какие именно системные ToolBar открыты и где именно они расположены, хранится в так называемом "ресурсном файле". По умолчанию, это файл FoxUser.dbf и связанный с ним файл FoxUser.fpt. Разумеется, Вы не потащите пользователю этот ресурсный файл. Как следствие, на машине клиента системные ToolBar вообще не появятся. Просто потому, что там не будет ресурсного файла с машины разработчика. Однако если на этапе отладки Вам все-таки необходимо скрыть системные ToolBar, то это можно сделать набором команд

HIDE WINDOW

Имя того или иного ToolBar можно посмотреть в заголовке самого ToolBar (если он не "приклеен" к меню) или через пункт меню View, подпункт ToolBars Например, скрыть стандартную панель можно командой

HIDE WINDOW "Standard"

Снова активизировать стандартную панель можно командой

SHOW WINDOW "Standard"

Проверить тот факт, что та или иная панель в настоящий момент активна можно используя команду WEXIST()

IF WEXIST("Standard") = .T.
        HIDE WINDOW "Standard"
  ENDIF

Таким образом, если Вам очень хочется скрыть системные ToolBar в режиме отладки, то несложно написать простые процедуры их закрытия в начале главного файла и восстановления после команды CLEAR EVENTS. Или же использовать класс, поставляемый с FoxPro. MODIFY CLASS _systoolbars OF (Home()+"FFC_app.vcx") Но, повторюсь, особого смысла в готовом приложении это не имеет. Поскольку там их и так не будет.

Настройка среды FoxPro

Ранее я вскользь уже упомянул тот факт, что просто запустить среду FoxPro недостаточно. Надо сделать некоторые предварительные настройки. Хотя бы настройку ON SHUTDOWN.

Дело в том, что среда FoxPro по умолчанию настроена таким образом, чтобы облегчить создание новых и модификацию старых приложений. Т.е. она настроена для удобства разработчика. Но в готовом приложении такая настройка среды FoxPro не просто некорректна. Она может оказаться недопустимой!

Вообще-то, настроек среды FoxPro много. Даже очень много. Однако на практике, для корректной работы готового приложения следует уточнить только несколько настроек. Самое большее -два ... три десятка.

Часть настроек среды FoxPro можно увидеть через пункт меню Tools, подпункт Options. А чтобы получить эти настройки в виде кодов нажмите и удерживайте клавишу "Shift" и левой кнопкой мыши нажмите на кнопку "Ok". В командное окно будет выведены все текущие настройки формы Options. В версии VFP9 список настроек будет выведен в окно Output окна Debugger.

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

Программно можно получить тот же список текущих настроек среды FoxPro, используя команду DISPLAY STATUS

Сложность в том, что на этапе отладки приложения все-таки нужны несколько другие настройки, чем в готовом приложении.

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

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

Есть несколько вариантов определения режимов работы. Самый простой - это посмотреть значение свойства

_VFP.StartMode

Если это свойство имеет значение 0, то мы находимся в режиме отладки. Т.е. получается что-то вроде:

* Общие настройки вне зависимости от режима работы
  IF _VFP.StartMode = 0
    * Настройки только для режима отладки  
  ELSE
    * Настройки только для готового приложения  
  ENDIF

Есть и еще одна проблема настроек. Дело в том, что при открытии Private DataSession часть настроек, связанных с работой с данными сбрасываются в значения по умолчанию. Полный список таких настроек Вы можете посмотреть в описании к команде "SET DataSession". Причем для некоторых настроек эти самые значения по умолчанию отличаются в зависимости от того, в какой DataSession мы находимся.

Т.е. получается, что недостаточно просто один раз сделать настройки в главном файле. Нужно еще повторить часть настроек в каждой Private DataSession.

Следовательно, выполнение настроек среды FoxPro надо вынести либо в отдельную процедуру, либо оформить как метод класса. Если как метод класса, то либо как метод некоей базовой формы, на основе которой будут созданы все формы Вашего проекта, либо как метод класса Custom, экземпляр которого будет "бросаться" на нужные формы.

Наконец, еще одна проблема настроек. По завершении работы приложения надо вернуть настройки в то состояние, в котором они были на момент запуска приложения.

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

Например, при отладке триггеров хорошо бы видеть записи помеченные как удаленные. Но в готовом приложении, даже при запуске в режиме отладки, нам видеть эти записи не надо. Это регулирует настройка SET DELETED

Да и в любом случае желательно всегда следовать принципу: намусорил, убери за собой. Для программирования это особенно важно во всех смыслах.

Таким образом, в общем случае, работа с настройками выглядит примерно так:

  1. Проверяем текущее состояние настройки
  2. Если текущее состояние настройки отличается от нужного нам значения в данном режиме работы в данном месте, то сохраняем старое значение настройки и устанавливаем новое
  3. По окончании работы восстанавливаем измененные настройки

Из этой логики следует, что использовать процедуры в принципе можно, но очень уж неудобно. Нужны две отдельные процедуры для установки и для восстановления настроек. А, кроме того, нужно где-то хранить старые настройки. Более удобным кажется использование классов. Создаются два метода одного и того же класса (возможно больше, ведь для Private DataSession надо установить только часть настроек). А для хранения старых значений настроек можно использовать свойства (Properties) класса. Тогда остается уточнить, использовать ли класс Custom или класс на базе Form? По большому счету - это абсолютно одинаковые варианты. Следует только помнить, что методы класса Custom можно запустить не ранее события Init этого класса. Но в любом случае методы собственно формы или любых ее объектов еще не существуют на момент выполнения события BeforOpenTables в DataEnvironment формы. Т.е. автоматическое открытие таблиц, включенных в DataEnvironment формы, произойдет с использованием настроек по умолчанию. Но в большинстве случаев, это не столь уж и принципиально. Почему? Это отдельный вопрос, выходящий за рамки данной статьи. Просто примите к сведению, что выполнение настроек в INIT-формы в большинстве случаев вполне уместно и не будет "слишком поздно". Возвращаясь к содержимому нашего главного файла, получаем, что его содержимое уже выглядит примерно так:

* Подключаю библиотеку классов, содержащую ряд полезных классов общего назначения  
  LOCAL loSetting, llIsClass  
  IF UPPER("MyClass.VCX ALIAS MyClass") $ UPPER(SET("ClassLib"))  
        llIsClass = .T.  
  ELSE  
        llIsClass = .F.  
        SET CLASSLIB TO MyClass ADDITIVE  
  ENDIF  
    
 * Класс, устанавливающий глобальные настройки среды (находится в MyClass.VCX)  
  loSetting = CREATEOBJECT("Setting")  
    
  ON SHUTDOWN DO MyExitProcedure  
  PUSH MENU _MSYSMENU  
  DO MainMenu.mpr  
  _SCREEN.Visible = .T.  
  READ EVENTS  
    
 ********* Восстановление исходных настроек  
  POP MENU _MSYSMENU  
  ON SHUTDOWN  
  IF m.llIsClass=.F.  
        RELEASE CLASSLIB MyClass  
  ENDIF  
    
 ******** Процедура "аварийного" выхода из программы  
  PROCEDURE MyExitProcedure  
 * Необходимые действия по "штатному" закрытию приложения  
  CLEAR EVENTS  
  RETURN

Здесь я предполагаю, что в библиотеке классов MyClass.VCX есть класс "Setting" в событии Init, которого происходит установка нужных настроек, а в событии Destroy восстановление исходных настроек. Т.е. удаление переменной m.loSetting означает автоматическое восстановление исходных настроек. Еще использованы дополнительные команды PUSH MENU и POP MENU, которые сохраняют и восстанавливают системное меню FoxPro. Обратите внимание на то, что переменные объявляются как LOCAL. Дело в том, что если не объявить переменные, то по умолчанию они получат область видимости PRIVATE. А для переменных главного файла это равнозначно объявлению их как PUBLIC, поскольку они будут видны во всех вызванных формах и процедурах. Еще один тонкий момент заключается в том, что команды, записанные после команды READ EVENTS, будут выполнены только после подачи команды CLEAR EVENTS. Т.е. все то, что обозначено, как "Восстановление исходных настроек" будет выполнено непосредственно перед закрытием приложения. Как видите, главный файл начинает разрастаться. С этим надо что-то делать. Чем больше листинг программы (код одного метода или процедуры), тем сложнее такой код отлаживать и модифицировать.

Использование глобального объекта goAPP

До сих пор, я описал лишь часть того, что в принципе должно бы быть в главном файле. Фактически, описан самый минимум, без которого сложно написать полноценное приложение. В принципе, можно оставить все "как есть". Если проект не очень большой и не предполагается его активное развитие, то того, что уже описано вполне хватит. Однако по мере развития проекта одного файла PRG для управления загрузкой и выгрузкой проекта оказывается недостаточно. "Недостаточно" не в смысле "мало", а в смысле "не удобно" Дело в том, что в большом проекте необходим целый набор различных процедур и функций, которые должны запускаться как при старте приложения, так и при его завершении. Вот некоторые из них:

  1. Контроль повторного запуска приложения на одном компьютере
  2. Контроль логина и пароля пользователя
  3. Чтение и запись настроек среды FoxPro и приложения из внешних хранилищ (реестр, ini-файл и т.п.)

Это, еще не считая ряда специфических процедур, которые необходимо запустить при старте в конкретном проекте в зависимости от конкретной задачи. Да еще добавьте к этому все то, что уже было описано ранее. Разумеется, все это можно реализовать в идеологии процедурного программирования. Т.е. написав нужное количество процедур и раскидав их по процедурным файлам. Или создать один большой процедурный файл. Но у такого подхода есть ряд недостатков. Например:

  1. Один большой процедурный файл с множеством процедур или много процедурных файлов очень трудно модифицировать. Просто тяжело найти нужное место и понять, как модификация в этом месте повлияет на все остальные процедуры.
  2. Невозможно написать "универсальную" процедуру "на все случаи жизни". Как правило, в каждом конкретном проекте, надо сделать "как там", но вот в этом месте чуть-чуть по другому.
  3. Проблематично написать парные процедуры загрузки-выгрузки, если они требуют некоторого "внешнего" по отношению к этим процедурам хранилища данных. Например, восстановление исходных настроек среды FoxPro требует где-то сохранить эти самые настройки.

Вот для решения этих проблем и еще многих других создается специальный класс на базе класса Custom. Экземпляр этого класса обычно и называют goApp. Пример реализации подобного класса можно посмотреть в стандартном проекте, поставляемом вместе с FoxPro под названием TasTrade.pjx

MODIFY PROJECT (HOME(2)+"tastrade	astrade.pjx")

По сути, если опустить некоторые детали, содержимое главного файла в этом случае будет выглядеть примерно так:

SET CLASSLIB TO MAIN, TSGEN
  PUBLIC goApp
  goApp = CREATEOBJECT("TasTrade")
  goApp.Do()

В чем тут "фокус"? А "фокус" в том, что в момент создания объекта на базе класса TasTrade выполняется его событие Init. А уже из события Init этого класса осуществляется вызов всех тех методов, которые необходимы в данном приложении в момент загрузки приложения Метод goApp.Do() содержит собственно команду READ EVENTS, что и обеспечивает "точку останова" всего приложения. Команда CLEAR EVENTS дается в одном из методов класса goApp. В данном случае - это метод goApp.CleanUp2() Методы, которые необходимо выполнить при закрытии приложения можно вызвать в том же методе goApp.Do() после команды READ EVENTS или в событии goApp.Destroy() Все те процедуры, которые были описаны в данной статье, оформляются как отдельные методы класса TasTrade. И вызываются в соответствующем месте класса. Обратите внимание, что собственно класс TasTrade из библиотеки классов Main.vcx является наследником другого класса Application из библиотеки классов TSGen.vcx. Такая "иерархия" вовсе не случайна. В этом случае Вы можете использовать класс Application во всех Ваших приложениях, создавая на его основе собственный класс, например, MyClass в собственной библиотеке классов MyClass.vcx. Т.е. Вы получите некую общую библиотеку классов вообще для всех ваших проектов, как заготовку для класса конкретного приложения. Несколько слов о том, чего не должно быть в классе goApp. Дело в том, что возникает большое искушение "впихнуть" в класс goApp вообще все глобальные методы, переменные памяти и разные обработчики. Так вот, не надо этого делать. Цель класса goApp - это организовать корректную загрузку и выгрузку вашего приложения. Все! Все что "сверх" этой задачи должно быть выделено в отдельные классы. Например:

  1. Объект - обработчик ошибок
  2. Объект - стек активных объектов (форм) приложения
  3. Объект, содержащий глобальные настройки приложения

Список можно продолжать. Но основной принцип: не смешивать в одном объекте разные задачи. Будьте осторожны с использованием класса из проекта TasTrade.pjx. Он достаточно далек от идеала. Его следует рассматривать скорее как учебное пособие, но не как объект готовый к использованию. Не надо слепо копировать его в свое приложение. Лучше напишите свой собственный класс. Да, это займет больше времени, но Вы лучше поймете что это такое и для чего это используется.

Передача параметров в EXE

Как правило, готовый файл EXE самодостаточен в том смысле, что ему не требуется передавать какие-либо параметры. Все необходимые "внешние" настойки делаются либо в файле CONFIG.FPW, либо в специальных ini-файлах. Но иногда такая необходимость все-таки возникает. Например, чтобы указать, где именно находится нужный ini-файл. В системе Windows общепринятым стандартом является указание параметров сразу за именем программы через пробел: MyProg.exe Par1 Par2 Для приема таких "внешних" параметров в главном файле в качестве первой исполняемой команды надо дать стандартную команду PARAMETRS (или LPARAMETERS) PARAMETERS tcPar1, tcPar2 Т.е. с точки зрения кода FoxPro ничего нового и необычного. Стандартный список параметров через запятую как первая исполняемая команда программного модуля. Однако есть некоторые отличия в приеме "внешних" параметров Во-первых, один параметр от другого разделяется пробелом, поэтому если Вы хотите передать в свое приложение некую символьную строку, которая сама содержит пробелы, то ее следует взять в кавычки MyProg.exe "первый параметр" "второй параметр" Во-вторых, все внешние параметры, передаваемые в приложение FoxPro, имеют символьный тип данных. Т.е. даже если Вы дадите команду: MyProg.exe 1 2 То в программе FoxPro будут приняты не числа 1 и 2, а символы "1" и "2" В-третьих, несмотря на то, что кажется, будто в момент запуска Вашего приложения сразу оно и начинает исполняться, но на самом деле это не совсем так. Сначала происходит загрузка среды FoxPro и только после этого уже из загруженной среды FoxPro начинает исполняться Ваше приложение. Применительно к передаче параметров в Ваше приложение из этого следует, что сначала переданные параметры будут приняты и обработаны собственно средой FoxPro. Но все дело в том, что сама среда FoxPro в момент загрузки может принимать несколько именованных параметров (переключателей, ключей). Именованные параметры среды FoxPro начинаются либо с символа дефиса (знак минус), либо с наклонной черты (знак деления). Полный список именованных параметров и для чего они предназначены, Вы можете посмотреть в HELP к Visual FoxPro в разделе с именем вроде "Command-line switches in Visual FoxPro" Как именно называется этот раздел, и в какой именно статье он находится, зависит от версии FoxPro. В разных версиях он находится в разных статьях. Поэтому откройте HELP к Вашей версии FoxPro, перейдите на закладку "Поиск" и введите ключевое слово "switches" для поиска. Самая первая из найденных статей, скорее всего и будет содержать список и описание "ключей". Начиная с версии Visual FoxPro 7.0 список ключей можно получить, запустив среду FoxPro с ключом "/?". Примерно так: "C:Program FilesMicrosoft Visual FoxPro 8vfp8.exe" /? Здесь я не буду описывать эти самые "ключи" и для чего они предназначены. Для нас более важным является то, как использование ключей FoxPro повлияет на передачу параметров в созданное приложение. Оказывается, эти самые "ключи" перехватываются самой средой FoxPro в момент ее запуска и исключаются из списка переданных параметров. Например, если Вы дадите команду: MyProg.exe -t 1 2 То в момент загрузки среды FoxPro будет найден ключ "-t". Этот ключ будет соответствующим образом обработан средой FoxPro, а непосредственно вызов Вашего приложения примет вид: MyProg.exe 1 2 Таким образом, "ключи" FoxPro просто исключаются из списка параметров, передаваемых в Ваше приложение. Поэтому будьте аккуратнее в выборе значений внешних параметров Вашего  приложения.

Заключение

В данной статье были рассмотрены основные идеи по содержимому главного файла проекта FoxPro. Причем, не вообще все, что только можно и нельзя "впихнуть" в главный файл, а только самый минимум. Без чего проблематично написать полноценное приложение. В статье сознательно не давалось подробное описание кода той или иной части главного файла. Опять же, только самый минимум, как общая идея того, что должно быть. Есть несколько причин, такого "поверхностного" описания:

  1. Содержимое главного файла сильно зависит от конкретной задачи и личных предпочтений программиста
  2. Достаточно часто, одну и ту же задачу в FoxPro можно решить несколькими способами. Как следствие, следовало бы привести конкретный код реализации той или иной задачи каждым из способов и провести сравнительный анализ этих способов. Это слишком обширная задача, далеко выходящая за рамки данной статьи.

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

0

Автор публикации

не в сети 24 года

Владимир Максимов

0
Комментарии: 0Публикации: 63Регистрация: 02-09-2000
Оставить комментарий
Авторизация
*
*
Генерация пароля