Настоящая статья обсуждает аспекты запуска программ в среде PalmOS. Статья обсуждает нюансы использования функций SysAppLaunch, SysUIAppSwitch и некоторые внутренние механизмы PalmOS, исполюзующиеся для запуска.
В этой главе кратко обсуждаются функции PalmOS API, имеющие отношение к запуску программ.
Начнем с основы - с формата главной функции любой программы.
UInt32 PilotMain(UInt16 cmd, void *cmdPBP, UInt16 launchFlags);
Параметры:
#define sysAppLaunchFlagNewThread 0x01 // create a new thread for application sysAppLaunchFlagNewStack
#define sysAppLaunchFlagNewStack 0x02 // create separate stack for application
#define sysAppLaunchFlagNewGlobals 0x04 // create new globals world for application ID for Memory chunks
#define sysAppLaunchFlagUIApp 0x08 // notifies launch routine that this is a UI app being
#define sysAppLaunchFlagSubCall 0x10 // notifies launch routine that the app is calling it's
// entry point as a subroutine call. This tells the launch
// code that it's OK to keep the A5 (globals) pointer valid
// through the call.
// IMPORTANT: This flag is for internal use by
// SysAppLaunch only!!! It should NEVER be set
// by the caller.
#define sysAppLaunchFlagDataRelocated 0x80 // global data (static ptrs) have been "relocated"
// by either SysAppStartup or StartupCode.c
// IMPORTANT: This flag is for internal use by
// SysAppLaunch only!!! It should NEVER be set
// by the caller.
// The set of private, internal flags that should never be set by the caller
#define sysAppLaunchFlagPrivateSet (sysAppLaunchFlagSubCall | sysAppLaunchFlagDataRelocated)
Флаги стоит рассматривать в двух контекстах - в контексте вызывающего приложения и в контексте вызываемого приложения. Некоторые флаги нельзя задавать при запуске приложения, другие флаги не рекомендуется задавать при запуске, а третьи флаги пользовательские программы никогда не получат.
Err SysAppLaunch( UInt16 cardNo, LocalID dbID, UInt16 launchFlags, UInt16 cmd, MemPtr cmdPBP, UInt32 *resultP);
Эта функция используется для запуска приложений. PalmOS использует ее для запуска программ в новых потоках, для запуска программ с UI. Прикладному программисту стоит использовать ее только для sub-launch, для запуска прочих приложений, выполняющих отдельные задачи, в духе вызова функций.
Подобно вызову функции, вызов SysAppLaunch запускает программу и по ее окончании продолжает исполнение вызываемой функции. Исключением является запуск в новом потоке, но поскольку он недоступен программисту, то мы его не рассматриваем.
Параметры у функции очевидные:
Err SysUIAppSwitch(UInt16 cardNo, LocalID dbID, UInt16 cmd, MemPtr cmdPBP);
Функция SysUIAppSwitch используется для передачи управления другой программе с интерфейсом. Эта функция традиционно понимается неправильно. Она не запускает приложение немедленно. Вместо этого она сохраняет параметры во внутренних переменных PalmOS и добавляет в очередь сообщений событие sysEventAppStopEvent.
Что происходит дальше? Дальше функция возвращается в прикладную программу, которая продолжает исполнение. Когда исполнение дойдет до обработки события sysEventAppStopEvent, цикл обработки завершится. Программа вызовет StopApplication и выйдет из PilotMain. PalmOS после выхода из текущей программы считывает сохраненные значения следующей программы и запускает ее.
Обратите внимание на то, что программа запускается не сразу, только после выхода из текущей программы. Обычно вызов SysUIAppSwitch стоит последним в обработчике события, следующим извлеченным событием будет sysEventAppStopEvent, и выход произойдет автоматом. Но если программа использует нестандартный цикл выборки, то могут возникнуть проблемы.
Нажатие на кнопки Datebook / Memo / Address / ToDo / Home / Calc генерируют неявный вызов SysUIAppSwitch с программами, заданными своими создателями в свойствах. Так, нажатие на home вызывает запуск программы с кодом создателя из PrefGetPreference(prefLauncherAppCreator).
Параметры у функции понятны:
Обратите внимание, что флаги не передаются. Они неявно формируются как sysAppLaunchFlagNewStack | sysAppLaunchFlagNewGlobals | sysAppLaunchFlagUIApp .
В этой главе мы обсудим механизм работы потока, в котором исполняются приложения с UI. Описание базируется на коде PalmOS 4, но вполне применимо к версиям PalmOS 2-5.
Основой графического потока является приложение UIAppShell.prc. Это приложение запускается из функции SysUILaunch. Запуск происходит с помощью функции SysAppLaunch. Указывается флаг sysAppLaunchFlagNewThread, который указывает на необходимость запуска нового потока. Этот поток становится единственным доступным для прикладных программ.
Инициализация графического потока состоит из следующих этапов:
Самой важной функцией UIAppShell является запуск приложений. Именно UIAppShell отвечает за последовательный запуск программ, указанных при вызове SysUIAppSwitch.
Программа оперирует переменными PalmOS GNextUIAppCardNo, GNextUIAppDBID, GNextUIAppCmd, GNextUIAppCmdPBP. Это именно те самые переменные, в которые SysUIAppSwitch сохраняет переданные параметры.
Рассмотрим действия, выполняемые в цикле. Первая задача - выбор приложения, которое будет запущено первым.
Панели - это редкий случай возможности запуска приложения с интерфейсом и возврата обратно. Вызов панели реализуется следующим способом:
SysAppLaunch запускает следующее приложение с возвратом обратно, а SysUIAppSwitch переключается на следующее приложение без возврата (исключение - механизм панелей). Если вам нужно запустить приложение с интерфейсом, то нужно использовать SysUIAppSwitch. Если вам нужно запросить у приложения данные, например запустить Pocket Tunes, то нужно использовать SysAppLaunch. Не пытайтесь поступать наоборот!
Все зависит от кода команды. В SDK описаны флаги, передаваемые при вызове стандартных кодов. Если же описания флагов нет, то стоит рассчитывать на худший случай.
Если мы получаем код, которые подразумевает показ UI (sysAppLaunchCmdNormalLaunch, sysAppLaunchCmdGoTo), то такой вызов был сделан с помощью SysUIAppSwitch, так что мы располагаем своим стеком и глобальными данными. Обратите внимание, что в случае с sysAppLaunchCmdGoTo есть одно исключение - подвызов с выставленным sysAppLaunchFlagSubCall. В этом случае переменные уже проинициализированы предыдущей копией программы.
Если мы получаем код, требующий только действия (например, sysAppLaunchCmdAlarmTriggered), то его нужно обрабатывать при отсутствии глобальным переменных и большого стека.
Лучше всего не использовать никаких. Правильный принцип - посылаемая команда должна всегда передавать одни и те же флаги, не больше и не меньше. То есть для вызова с кодом sysAppLaunchCmdNormalLaunch не стоит использовать
flags ===== 0
, а для вызова sysAppLaunchCmdAlarmTriggered попросту излишне указывать
flags ===== sysAppLaunchFlagNewStack | sysAppLaunchFlagNewGlobals
, поскольку обработчик должен уметь обходиться и без глобальных переменных.
Если доводы вас не убедили - попробуйте запустить сами. Возможно у Вас такой запуск заработает.
Кое-какие детали такого запуска описаны здесь: http://forum.sources.ru/index.php?showtopic=47808
Предлагается два неопробованных способа. Кто попробует - присылайте результаты.
Нужно прописать создателя программы в двух местах preferences: PrefSetPreference(prefDefaultAppCreator, 'хххх'); PrefSetPreference(prefLauncherAppCreator, 'хххх');
Первая строка будет запускать программу после ресета и в сомнительных случаях, а вторая будет запускать программу вместо лончера.
Также можно блокировать переключение на стандартные приложения по кнопкам: PrefSetPreference(prefHard1CharAppCreator , 'хххх');