Palm Programmer's Laboratory
Palm OS Programmer's Companion Volume I/2-2
2-2 起動コードへの応答
アプリケーションは、PilotMain という名前の関数で起動コードに応答しなければなりません。PilotMain は全てのアプリケーションのエントリポイントです。
アプリケーションが起動コードを受信した際、最初にしなければならないことは、渡された起動コードを処理できるかどうか確認することです。例えば、文字列の検索をリクエストする起動コードに応答しなければならないのはテキストデータを保有しているアプリケーションだけです。アプリケーションが起動コードを処理できなければ、そのまま終了します。あるいは、なんらかのアクションをしてすぐに終了します。
リスト2.1 に、例として Datebook アプリケーションの PilotMain 関数の一部を示します。完全な内容を参照したければ、Palm OS SDK の examples フォルダにある Datebook.c を参照して下さい。
リスト 2.1 Datebook.c の PilotMain 関数
UInt32 PilotMain (UInt16 cmd, void *cmdPBP, UInt16 launchFlags) { return DBPilotMain(cmd, cmdPBP, launchFlags); } static UInt32 DBPilotMain (UInt16 cmd, void *cmdPBP, UInt16 launchFlags) { UInt16 error; Boolean launched; // This app makes use of PalmOS 2.0 features.It will crash // if run on an earlier version of PalmOS. Detect and warn // if this happens, then exit. error = RomVersionCompatible (version20, launchFlags); if (error) return error; // Launch code sent by the launcher or the datebook // button. if (cmd == sysAppLaunchCmdNormalLaunch) { error = StartApplication (); if (error) return (error); FrmGotoForm (DayView); EventLoop (); StopApplication (); } // Launch code sent by text search. else if (cmd == sysAppLaunchCmdFind) { Search ((FindParamsPtr)cmdPBP); } // This launch code might be sent to the app when it's // already running if the user hits the "Go To" button in // the Find Results dialog box. else if (cmd == sysAppLaunchCmdGoTo) { launched = launchFlags & sysAppLaunchFlagNewGlobals; if (launched) { error = StartApplication (); if (error) return (error); GoToItem ((GoToParamsPtr) cmdPBP, launched); EventLoop (); StopApplication (); else GoToItem ((GoToParamsPtr) cmdPBP, launched); } // 訳注:上記ブロックは、インデントがズレている上にブレースの // の対応がとれていませんが、ひとまず原文のままとします。 // Launch code sent by sync application to notify the // datebook application that its database has been synced. // ... // Launch code sent by Alarm Manager to notify the // datebook application that an alarm has triggered. // ... // Launch code sent by Alarm Manager to notify the // datebook application that is should display its alarm // dialog. // ... // Launch code sent when the system time is changed. // ... // Launch code sent after the system is reset. We use this // time to create our default database if this is a hard // reset // ... // Launch code sent by the DesktopLink server when it // creates a new database. We will initialize the new // database. return (0); }
通常起動に対する応答
アプリケーションが sysAppLaunchCmdNormalLaunch 起動コードを受け取ると、まずスタートアップルーチンが起動します。次にイベントループに入り、最終的には終了ルーチンに入って終了します。(イベントループについては 「 3 イベントループ 」 を、終了ルーチンについてはこの章の終わりの方にある 「 2-5 アプリケーションの終了 」 を参照して下さい。)
スタートアップルーチンでは、アプリケーションは以下に示す処理をしなければなりません。
- システム全体のプリファレンス(例えば数値や日付・時刻の表示書式など)を取得し、アプリケーションが実行中に参照するグローバル変数を初期化します。
- クリエータID とデータベースタイプを使用してアプリケーションのデータベースを探します。見つからなければ新規に作成して初期化します。
- アプリケーション固有のプリファレンスを取得し、関連するグローバル変数を初期化します。
- その他のグローバル変数を初期化します。
リスト2.1 を見ると、Datebook アプリケーションの例では sysAppLaunchCmdNormalLaunch に応答して StartApplication という名前の関数をコールしています。リスト2.2 に StartApplication 関数を示します。
リスト 2.2 Datebook.c の StartApplication 関数
static UInt16 StartApplication (void) { UInt16 error = 0; Err err = 0; UInt16 mode; DateTimeType dateTime; DatebookPreferenceType prefs; SystemPreferencesType sysPrefs; UInt16 prefsSize; // Step 1: Get system-wide preferences. PrefGetPreferences (&sysPrefs); // Determime if secret records should be // displayed. HideSecretRecords = sysPrefs.hideSecretRecords; if (HideSecretRecords) mode = dmModeReadWrite; else mode = dmModeReadWrite | dmModeShowSecret; // Get the time formats from the system preferences. TimeFormat = sysPrefs.timeFormat; // Get the date formats from the system preferences. LongDateFormat = sysPrefs.longDateFormat; ShortDateFormat = sysPrefs.dateFormat; // Get the starting day of the week from the system // preferences. StartDayOfWeek = sysPrefs.weekStartDay; // Get today's date. TimSecondsToDateTime (TimGetSeconds(), &dateTime); Date.year = dateTime.year - firstYear; Date.month = dateTime.month; Date.day = dateTime.day; // Step 2. Find the application's data file. If it // doesn't exist, create it. ApptDB = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode); if (! ApptDB) { error = DmCreateDatabase (0, datebookDBName, sysFileCDatebook, datebookDBType, false); if (error) return error; ApptDB = DmOpenDatabaseByTypeCreator(datebookDBType, sysFileCDatebook, mode); if (! ApptDB) return (1); error = ApptAppInfoInit (ApptDB); if (error) return error; } // Step 3. Get application-specific preferences. // Read the preferences/saved-state information. There is // only one version of the DateBook preferences so don't // worry about multiple versions. prefsSize = sizeof (DatebookPreferenceType); if (PrefGetAppPreferences (sysFileCDatebook, datebookPrefID, &prefs, &prefsSize, true) != noPreferenceFound) { DayStartHour = prefs.dayStartHour; DayEndHour = prefs.dayEndHour; AlarmPreset = prefs.alarmPreset; NoteFont = prefs.noteFont; SaveBackup = prefs.saveBackup; ShowTimeBars = prefs.showTimeBars; CompressDayView = prefs.compressDayView; ShowTimedAppts = prefs.showTimedAppts; ShowUntimedAppts = prefs.showUntimedAppts; ShowDailyRepeatingAppts = prefs.showDailyRepeatingAppts; } // Step 4. Initialize any other global variables. TopVisibleAppt = 0; CurrentRecord = noRecordSelected; // Load the far call jump table. FarCalls.apptGetAppointments = ApptGetAppointments; FarCalls.apptGetRecord = ApptGetRecord; FarCalls.apptFindFirst = ApptFindFirst; FarCalls.apptNextRepeat = ApptNextRepeat; FarCalls.apptNewRecord = ApptNewRecord; FarCalls.moveEvent = MoveEvent; return (error); }
その他の起動コードに対する応答
sysAppLaunchCmdNormalLaunch 以外の起動コードを受信した場合、アプリケーションはその起動コードに応答すべきかどうかを判断する必要があります。もし応答するのであれば、その起動コードに対応するハンドラ関数を実装し、それを PilotMain 関数から呼び出します。
その他の起動コードに応答するほとんどのケースでは、グローバル変数にアクセスすることはできません。グローバル変数領域は通常アプリケーションが sysAppLaunchCmdNormalLaunch( リスト2.2 を参照 )か sysAppLaunchCmdGoTo を受け取った場合にのみ確保されます。ですから、アプリケーションがそれ以外の起動コードを受け取った場合、グローバル変数は準備されておらず、アクセスすることはできません。さらに、マルチセグメントアプリケーションの場合、グローバル変数にアクセスできなければ、segment 0(最初のセグメント)以外のコードセグメントにもアクセスできません。
グローバル領域(と 0 番以外のコードセグメント)にアクセス可能な状況がもう1つあります。それはアプリケーションが起動コード sysAppLaunchCmdURLParams で起動された場合です。この起動コードが palm URL から発行されたものであればグローバル領域が利用できます。この起動コードが palmcall URL から発行されたものであれば、グローバル領域は利用できません。URL は起動パラメータブロックに格納されてアプリケーションに渡されます。
- NOTE
- スタティックなローカル変数はグローバル変数とともにシステムのダイナミックヒープに置かれます。ですから、グローバル変数にアクセスできなければ同様にアクセスできません。
起動コードをチェックするのは、アプリケーションがグローバル変数領域にアクセスできるかどうかを確認する良い方法といえます。しかし、実はグローバル領域にアクセスできるかどうかは、起動コードとともに渡される起動フラグによって決まっています。もし sysAppLaunchFlagNewGlobals フラグがセットされていれば、アプリケーションのためのグローバル領域はその起動において確保されていることになります。このフラグはシステムによってセットされるものであり、起動コードの発行元によってセットされるものではありません(し、されるべきものでもありません)。
Boolean appHasGlobals = launchFlags & sysAppLaunchFlagNewGlobals;
このフラグがセットされていないにもかかわらず、グローバル領域にアクセスできる場合があります。それは、アプリケーションが既にカレントアプリケーションとして実行中だった場合です。この場合、グローバル領域は以前の(通常)起動時に確保されています。
アプリケーションが sysAppLaunchCmdNormalLaunch と sysAppLaunchCmdGoTo 以外の起動コードを受け取った場合、起動コードとともに渡される起動フラグをチェックすることでカレントアプリケーションとして実行中かどうかを確認することができます。アプリケーションがカレントアプリケーションとして実行中であれば、sysAppLaunchFlagSubCall フラグがセットされています。このフラグはシステムによってセットされるものであり、起動コードの発行元によってセットされるものではありません(し、されるべきものでもありません)。
Boolean appIsActive = launchFlags & sysAppLaunchFlagSubCall;