高度な検索
Developer Connection
Member Login ログイン | ご入会 ADC連絡先

Technote 1070

Background-Only Applications


目次

概要

BOAになる方法

バックグラウンドのApple event処理

システムソフトウェア7.5.5以前のBOA



ックグラウンド・オンリ・アプリケーション(BOA)は名前の通り、バックグラウンドのみで動作するMacintoshアプリケーションです。起動時の処理やシステムにサービスを提供するようなデーモンを設計する場合は'INIT'、ドライバ、単独コードよりも、BOAを作成するのが望ましいです。このテックノートはBOAを開発する際の注意点やアドバイスについて解説します。

このテックノートは元々PS 02でしたが、機能拡張アプリケーションに使う'appe'コードリソースの説明、SetApplLimit ()の詳細、システムソフトウェア7.5.5以前において機能拡張アプリケーションに'INIT'コードを追加した際に発生するバグの回避策などが追加されています。このテックノートはPS 02を取って代わります。


概要

バックグラウンド・オンリー・アプリケーション(BOA、フェースレス・バックグラウンド・アプリケーション、機能拡張アプリケーションとも言う)は起動時や継続的にシステムサービスを提供する場合に利用します。BOAはシステムソフトウェア6.xとMultiFinderでも使えますが、システムソフトウェア7.xのプロセス管理やプロセス間通信機能を利用すると高度なシステムサービスが実現できます。

このテックノートはBOAを開発する際の注意点やアドバイスを含みますが、この他にdevelop 9の記事「Be Our Guest: Background-Only Applications in System 7」やDeveloper CD Seriesに含まれているCとPascalのサンプルコード「SmallDaemon」も参考にして下さい。


BOAになる方法

BOAは通常のMacintoshアプリケーションの凝縮版です。BOAは必ずInitGraf ()を呼び出します(Apple Event Managerを利用するために必要なQuickDrawグローバル変数の初期化に必要です)。しかし、InitWindows ()InitMenus ()など、描画を伴う可能性のあるツールボックス関数は呼び出さないで下さい。これは、直接的又は間接的にダイアログを表示するようなツールボックス関数を含みます(例えば、PPCBrowser ()ResolveAlias ()など)。ダイアログを表示する関数の多くについては、ダイアログを表示しないタイプの関数があります(例えばIPCListPorts ()MatchAlias ()など)。BOAからユーザへ通知する場合はNotification Managerを利用します(ノーティフィケーションについては後程解説をします)。

通常のMacintoshアプリケーションと同様、BOAは4つの必須Apple eventに対応していなければなりません。特に重要なのは、QuitApplication Apple eventへの対応です。QuitApplicationに正しく対応をしないとBOAが終了できなくなり、Mac OS自体が終了できなくなります。

アプリケーションの'SIZE'リソース(ID = -1)は必ずcanBackgroundbackgroundOnlyのフラグをセットして下さい。この2つのフラグをセットすることによって、アプリケーションがBOAであることをシステムソフトウェアに伝えます。

スタックについて

BOAに割り当てられるスタックサイズはデフォルトで2Kしかありません。これは標準的なアプリケーションに割り当てられるデフォルトスタックサイズ(カラーQuickDrawを持たないMacintoshでは8K、カラーQuickDrawを搭載したMacintoshで24K)より小さいです。PowerPCコンピュータでは2Kのスタックサイズはあまり現実的ではありません。リカーシブ関数やスタックを激しく利用するオペレーションを使うとスタックとヒープが接触してしまう恐れがあります。これはリカーシブ関数の変わりにループ、スタックの変わりにヒープを使うことで回避できます(大きな変数やパラメータの変わりにNewHandle ()NewPointer ()TempNewHandle ()などを利用します)。Str255のようなローカル変数はすぐにスタックを消費してしまうので、特に注意が必要です。

BOAのスタックサイズを大きくする必要があれば、GetApplLimit ()SetApplLimit ()でBOAのヒープスペースを小さくすることによって、スタックの利用可能なエリアが確保できます。ヒープスペースを圧縮する場合は必ずMaxApplZone ()を呼び出す前に行って下さい。

  // Increase the space allocated for the background only application stack.
  //
  // Warning: SetApplLimit always sets the stack to at least as large as the
  //    default stack for the machine (8K on machines with original QuickDraw,
  //    24K on machines with Color QuickDraw), so the application partition
  //    must be large enough to accommodate an appropriate stack and heap.
  //    Call this only once, at the beginning of the BOA.
  //
  // Another warning:
  //    Don't bother trying to set the stack size to something lower than 24K.
  //    If SetApplLimit is called to do this, it will silently lower ApplLimit
  //    to a 24K stack. (The limit is 8K on machines without Color QuickDraw.
  //    In this sample, we don't allow an increment less than 24K.)
 
  OSErr IncreaseBOAStack (Size additionalStackSize)
  {
    OSErr retCode;
 
    // Check that we aren't running with a corrupt heap. If we are,
    // fix the problem.  This was a bug with FBA's before System 7.5.5.
    // With System Software later than 7.5.5, this "fix" is harmless.

    myZone = GetZone ();
    if (myZone->bkLim != LMGetHeapEnd ())
        LMSetHeapEnd (myZone->bkLim);
 
    // Increase the stack size by lowering the heap limit.

    SetApplLimit ((Ptr) ((unsigned long) GetApplLimit () - additionalStackSize));
    retCode = MemError ();
    if (retCode == noErr)
        MaxApplZone ();
    return retCode;
  }



なぜMaxApplZone ()を呼び出す前にやるの?

MaxApplZone ()ApplLimit(ローメモリグローバル変数)で指定されているアドレスまでアプリケーションヒープを拡張します。SetApplLimit ()ApplLimitの内容を設定します(上記の場合は通常より低いアドレスに設定しています)。MaxApplZone ()を一度呼び出してしまうと、アプリケーションヒープは最高値まで拡張され、ApplLimitを変更しても再度縮小することはできません。残念ながらSetApplLimit ()は関数やMemError ()を通じてエラーを返さないので、設定が失敗した場合の通知はありません。

ノーティフィケーション

ユーザに対して異常事態などの通知を行う場合はNotification Managerを使って文字列を表示したり、サウンドを鳴らすことが可能です。しかし、BOAを全面に持ってくることが出来ない上、BOAはアプリケーションメニューにも現われないので、多くのユーザはBOAが動作していると言うことに気が付きません。突然のノーティフィケーションには驚かれるので、重要な事態やユーザが何だかの処置を取る必要がある場合に限ってノーティフィケーションを表示することを勧めます。

用途によっては、BOAの中であってもユーザから入力や操作を受け付ける必要があります。このような場合はBOAから通常のアプリケーションを立ち上げるよう勧めています。このアプリケーションはユーザインタフェースを持ち、ユーザ入力などを受け付けて、Apple eventを使ってBOAとやり取りをします。BOAの場合は描画、及び描画を伴うようなツールボックス関数は一切使えませんのでご注意下さい。

FinderとBOAの起動

BOAを起動する方法はいくつかあります。BOAのファイルタイプが'APPL'なら通常のアプリケーションと変わりなく、Finderで開くことができます。また、システムソフトウェア7.xの起動項目フォルダにBOAを入れると、Macintoshが起動する際に自動的に実行されます。

機能拡張アプリケーション型のBOAはファイルタイプが'appe'となります。機能拡張アプリケーションをシステムフォルダへドラッグすると自動的に機能拡張フォルダへ送られ、起動時に走ります。

'INIT'リソースを含む機能拡張アプリケーションが機能拡張フォルダに入っていると、'INIT'リソースは他の機能拡張の'INIT'リソースと同様、起動時に実行されます。これは起動時にアイコンを表示する場合に便利です。('INIT'のアイコン表示方法についてはDeveloper CD SeriesのShowInitサンプルコードを参照して下さい)。BOA自体は'INIT'リソースが実行し終わるまで立ち上がらないので、'INIT'で表示するアイコンはBOAの起動状況を反映できません。例えば、BOAが起動に失敗した場合はアイコンに×マークを被せるようなことはできません。しかし、単純にアイコンを表示してBOAが起動したことだけをユーザに伝える場合は、'INIT'コードリソースで実現できます。

BOAはProcess ManagerのLaunch Application ()関数を使っても起動できます。LaunchApplication ()は起動するプロセスのファイルタイプを無視しますので、BOAのファイルタイプが'APPL''appe'、その他であっても起動されます。

システムソフトウェア7.1.1以降では、機能拡張アプリケーション(ファイルタイプが'appe')に'appe'コードリソースを追加することができます。'appe'コードリソースはBOA起動直前に実行され、そのBOAを起動するかどうかを判断します。'appe'コードリソースはパラメータを持たないPascalの関数で、Booleanを返します。trueを返すとBOAはそのまま起動されますが、falseを返すとBOAは起動されません。

以下の例は、カラーQuickDrawが存在する場合だけBOAを立ち上げるための'appe'コードリソースです。

pascal Boolean ShouldThisBeLaunched (void)
{
    OSErr err;
    long response;

    err = Gestalt (gestaltQuickdrawVersion, &response);
    if (err == noErr && response >= gestalt8BitQD)
        return true;
    else
        return false;
}


System 7.xのFinderはBOAが終了しても、終了したことを関知できません。従って、ユーザがダブルクリックして起動させたBOAは終了してもアイコンが「使用中」のまま残ってしまいます。これは表示上の問題だけなので、再度ダブルクリックするとBOAは正常に立ち上がります。Mac OS 8のFinderでは実行中のBOAのみが正常に「使用中」として現われます。

BOAのタスク

いままで単独コードやドライバで実現していたほとんどの機能はBOAでも実現可能です。'INIT'を利用する理由の1つにはグローバルなトラップパッチを行うことができます。BOAはアプリケーションなので、トラップアドレスを変更しても、適応されるのはアプリケーション内のみです。

また、BOAは直接描画を行うことは一切できません。一方、'INIT'コード内から画面上に描画をするのは非常に難しいですが、可能ではあります。描画を必要とする用途にBOAは使えません。

BOAヒープ内で割り込み関数を利用するのは安全です。例えば、Time Managerのタスクや完了通知関数はBOA内部にあっても構いません。しかし、通常のアプリケーションと同様、BOAが終了するとヒープは無くなりますので、割り込みや完了通知の受け待ち中はアプリケーションを終了させないで下さい。BOAをスリープさせるにはWaitNextEvent ()に大きなスリープ値を渡します。そして、System 7.x以後では割り込み時でもWakeUpProcess ()を呼び出すことによってスリープ状態を解除できます。

定期的に処理を行わないBOAはWaitNextEvent ()呼び出し時に大きなスリープ値を渡して下さい。BOA用のイベントが発生するとスリープ状態は自動的に解除されますので、大きなスリープ値を指定することによって、BOAのパフォーマンスを損なうことなく、他プロセスへできるだけ処理時間を譲ることが可能です。定期的に処理を行うBOAは適当な値をスリープ値としてWaitNextEvent ()に渡します。

バックグラウンドのApple event処理

BOAを含むプロセス間通信にはApple eventが最適な手段です。しかし、ユーザ操作を必要とする事態を起こさないよう注意して下さい。例えば、ネットワーク上の他のコンピュータのプロセスをターゲットした場合、そのコンピュータとの通信セッションがすでに確率していなければ、プログラムリンクのダイアログが表示されてしまいます。このような事態を避けるにはApple eventのreplyパラメータを使って返信することができます。また、Apple eventの発信アドレスを保存(AEGetAttributePtr ()で送られてきたApple eventのkeyAddressAttr属性を取り出します)して、送信時に使う方法もあります。

システムソフトウェア7.5.5以前のBOA

システムソフトウェア7.5.5以前ではBOAを管理するProcess Managerにバグがありました。2つ以上のBOAがインストールされていると、2つ目に起動したBOAはヒープが壊れてしまいます。(以前、この問題はPowerTalkをインストールしている時に起こる問題としてテックノートに記述されていました。しかし、これは単純にPowerTalkがBOAをインストールしていたことが原因でした。)このバグはMaxApplZone ()を呼び出した際に発生します。

システムソフトウェア7.5.5以前でこのバグを回避するには、ローメモリのグローバル変数HeapEndをBOAのゾーンのbkLimフィールドと比較します(BOAのゾーンはGetZone ()で入手します)。比較した結果、値が違っていたら、theZone->bkLimの内容をHeapEndにコピーします(HeapEndを変更する際はLMSetHeapEnd ()及びLMGetHeapEnd ()のアクセサ関数を使って下さい)。上記の手順は必ずスタックサイズの変更やMaxApplZone ()を呼び出す前に行ってください。詳しくは上記のスタックサイズ変更用のソースコードをご覧下さい。

参考文献

更新日: 1997 年 11 月 19 日