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

Technote 1134

The Preferences Problem

目次

設定ファイルの問題

解説

迷った場合は

まとめ

サンプルコード

ダウンロード

定 (preferences) はほとんどすべてのアプリケーションで必要なものなのに、Mac OS API にはこの処理を支援するものがありません。このため、設定を正しく処理するにはかなりの量のコードを書く必要があります。

この TECHNOTE では、設定ファイルに伴う問題と解決方法について概要を説明し、完成度の高い製品を作成できるようにようにします。

どのような問題にも解決方法は複数あります。あらゆる場合に対応できるほどすぐれた解決方法はありません。この TECHNOTE では、適切な選択肢を上げ、それぞれの解決策について解説します。そうした情報を検討して、どの方法を実装するかを決定してください。

この TECHNOTE は、設定ファイル (ひとつまたは複数) を使用するデベロッパを対象に書きました。あらゆる可能性をカバーし、正しく設定ファイルを処理したいと考えるデベロッパが対象です。

 

設定ファイルの問題

設定ファイルとは: アプリケーションにはほぼすべてに設定ファイルがあります。本来ものごとを簡単にしてくれるべきところが、非常な苦痛とフラストレーションの元になってしまうことがよくあります。

設定ファイルにまつわる主な問題とは何でしょうか。

  1. 設定ファイルにはどんな名前を付けるべきでしょうか。
  2. 設定ファイルはどこに置きますか。
  3. 設定ファイルにはどんなファイルタイプとクリエータを使うべきですか。
  4. 設定ファイルはどうやって見つけますか。
  5. 設定ファイルには 'vers' リソースを含めるべきですか。
  6. 設定ファイルに正しいアイコンを関連付けるにはどうしたらよいでしょう。
  7. ユーザが設定ファイルをダブルクリックしたらどうしますか。
  8. 設定ファイルが存在しない場合はどうしますか。
  9. 設定ファイルに書き込めない場合はどうしますか。
  10. 設定ファイルが壊れたらどうしますか。
  11. 別のアプリケーションと設定ファイルがコンフリクトを起こしたらどうしますか。

実際にはこれらすべての問題を、ひとつの方法で解決し、それが常に正しいということはありえません。このことを心に留めて、個別のアプリケーションにとってできるだけ理想的な解決に近づくために必要な情報を提供しようと思います。

develop 18』に「The Write Way to Implement Preferences Files」という記事が載っています。Developer Technical Support (DTS) はデベロッパがこの記事のすすめに必ずしたがうべきだと考えているわけではありません。この TECHNOTE はその記事に代わるものです。単に資料として、その中で述べられている禁止事項を知りたい場合は、記事をダウンロードして確認してください。


 

解説

設定をファイル内に保存する方法は自由です。フラットな構造で保存してアプリケーションの起動時にハンドルに直接読み込むことも、もっと複雑な形式で保存することもできます。

 

設定ファイルにはどんな名前を付けるべきですますか。

設定ファイルに付ける名前は完全に自由です。DTS からの唯一のお願いは、終りを "Prefs" にしないことです。代わりに "Preferences" を使ってください。そのファイルに入っているものが、削除すると危険な謎の重要データでなく、単に例えば SurfWriter の設定であることをユーザによくわかるようにするためです。

これは些細な事柄と思われるかもしれませんが、アプリケーションの名前が長すぎて "Preferences" という部分が最後の文字列にならない場合を除き、省略は避けるべきです。これをタイプするのは一度だけなので (おそらく 'STR#' リソース内に)、名前が長くても問題になることはないはずです。

 

設定ファイルはどこに置きますか。

簡単な答えとしては、FindFolder への呼び出しで返される“初期設定”フォルダ内のひとつまたは複数のファイルに入れてください。

設定ファイルがひとつしかない場合は、“初期設定”フォルダ (FindFolder から返される) に入れてください。設定ファイルが複数の場合や、ログファイルなど“初期設定”フォルダに入れたい他のファイルがある場合は、“初期設定”フォルダ内にフォルダを作成して、そこにすべてのファイルを保存します。そうすれば、“初期設定”フォルダの中を整理することができます。

アプリケーションが複数の設定ファイルを必要とする場合は、“初期設定”フォルダ内に新規フォルダを作成し、設定ファイルをその追加したフォルダに入れるようにしてください。しかし、あらゆるファイルを“初期設定”フォルダ内の階層に保存するのがすべてのアプリケーションにとって最良の方法というわけではありません。例えば、複数ユーザが対象になる場合、多くのファイルを同じ“初期設定”フォルダに入れるのではなく、各ユーザ個人のフォルダに設定ファイルを保存したい場合もあるでしょう。

 

設定ファイルにはどんなファイルタイプとクリエータを使うべきですか。

ファイルタイプについて

設定ファイルのファイルタイプは 'pref' にするのがよいでしょう。このタイプを使うと、Finder の将来のバージョンが設定ファイルを“初期設定”フォルダから自動的に認識します。'pref' というファイルタイプを使うべきではない理由として思いつくのは、このファイルタイプを使うと次のような Finder のバルーンヘルプメッセージが出てしまうことだけです。

Finder の設定ファイル
このファイルには Finder の設定が保存されています。

ほとんどすべての設定ファイルが 'pref' のタイプを持っているため、これではユーザが混乱してしまいます。DTS では、アップルのエンジニアリング部門が Finder を修正すべきだと考えています。われわれはこの問題についてバグレポートを提出しました (ID は 2241857)。

“初期設定”フォルダ (または、“初期設定”フォルダ内のフォルダ) に設定ファイルの他に保存するファイルがある場合は、任意のタイプを使ってかまいません。例えば、“初期設定”フォルダにログファイルを入れる場合、'TEXT' タイプにするのが最も適切でしょう。設定ファイルでなく、開いても大丈夫だということがわかるからです。

つまり、設定ファイルであればタイプは 'pref' にするべきです。設定ファイルでなければ、“初期設定”フォルダに保存するファイルでも 'pref' タイプにしてはいけません。

クリエータコードについて

この問題については 3 つの説があります。最初の説は、設定ファイルにはアプリケーションのシグネチャに一致するクリエータコードを付けるべきだというもの。2 番目は、'????' というクリエータを付けるべきだというもの。3 番目は '????' でもなく、アプリケーションのシグネチャでもない、クリエータコード (登録されたもの) を付けるべきだというものです。

アプリケーションのクリエータコードを使用する場合

設定ファイルにアプリケーションのシグネチャと同じクリエータを与えるということには、次のようなもっともな理由があります (順不同)。

  • ユーザがファイル名を変更した場合でも、クリエータコードによって簡単に設定ファイルを探すことができます。
  • 「親のない」設定ファイルを削除するユーティリティアプリケーションが、そのファイルがどのアプリケーションに付属しているかがわかります。
  • アプリケーションをローカライズしてもクリエータコードは変わらないため、アプリケーションコードは設定ファイルの名前を知る必要がなく、ローカライズが簡単になります。

ところが、次のように、設定ファイルのクリエータコードとしてアプリケーションのシグネチャを使わない方がよい理由もあります。

  • ユーザが設定ファイルを開くとアプリケーションが起動します。この場合アプリケーション側ではこれに対応して何らかの処理をしなくてはなりません。これについては後で説明します。

'????' のクリエータコードを使用する場合

アプリケーションのクリエータコードを使わないで、'????' を使うことは次のような利点があります。

  • ファイルを名前で探すのが非常に簡単です (ここをクリックするとこれを示すコードの断片にジャンプします)。
  • '????' をクリエータコードとして登録する必要がありません。(実際には登録できません。どのアプリケーションにも属さないシグネチャとして明確に定義されています。)
  • ユーザが設定ファイルを開こうとしてもアプリケーションは起動されません。
  • ユーザに設定ファイルが開けない理由を伝えるため「アプリケーションが見つかりませんでした」メッセージ文字列を指定できます。

しかし、'????' というクリエータコードを使うと次のような不都合もあります。

  • 設定ファイルを名前で探さなければならず、ローカライズの際に問題となる可能性があります。
  • ユーティリティアプリケーションが、ユーザのシステムにもう存在しないアプリケーションの設定ファイルが残っていることを警告し、ユーザがそのファイルを削除してしまう可能性があります。逆に、同じようなユーティリティが、アプリケーションが削除されたにもかかわらず、設定ファイルを消さずに残してしまうこともあります。
  • '????' のクリエータコードを使うと、同じクリエータコードを持つファイルが複数あるため、クリエータコードによって設定ファイルの場所を探すことができなくなります。

異なるクリエータコードを使う場合

アプリケーションのシグネチャでも '????' でもないクリエータコードを使うことには、次のような利点があります。

  • 他では使われていないクリエータコードを付けることで、当然、設定ファイルを簡単に探すことができます。
  • クリエータコードについては、ローカライズの問題は起こりません。
  • ユーザが設定ファイルを開こうとしてもアプリケーションは起動されません。
  • 「アプリケーションが見つかりませんでした」メッセージの文字列を指定して、ユーザに設定ファイルが開けない理由を伝えることができます。

しかし、この方法には他の場合とは多少異なる次のような不都合があります。

  • 他のアプリケーションとのコンフリクトを避けるため、設定ファイルのクリエータコードを 2 番目のアプリケーションシグネチャとして登録する必要があります。
  • 前例と同様、ユーティリティアプリケーションが親のない設定ファイルと認識します。

注意: この TECHNOTE の最後に、クリエータコードかファイル名 (またはその両方) で設定ファイルを探す方法を示すコードを掲載しました。これを基にして自分で検索コードを作成することができます。

設定のローカライズについて

アプリケーションが現在動作しているシステムとは違う言語コードで作成された設定ファイルをユーザが使用する可能性があります。例えば、米国人のユーザが日本人の同僚に設定ファイルを送ることがあります。

アプリケーションがファイル名でなくクリエータコードで設定ファイルを探すようになっていれば、日本のアプリケーションと米国で名前を付けた設定ファイルの組み合わせでもユーザの期待通り動作します。アプリケーションが設定ファイルを常に名前で開いたり、名前が特定の文字列であることを想定している場合は、設定ファイルを単純にコピーすることはできません。ユーザが設定ファイルのコピーを作り、複数の設定ファイル間ですばやく切り替えを行うようなことがしにくくなります。

「アプリケーションが見つかりませんでした」メッセージについて

「アプリケーションが見つかりませんでした」メッセージの文字列は 'STR ' リソース ID -16397 として、アプリケーションが作成するファイルに入れておくと、Finder がそのファイルが開けない理由をユーザに伝えるために使用します。このメッセージには、そのファイルを作成したアプリケーション名と、そのファイルの目的をユーザに伝えるものでなければなりません。この文字列は、設定ファイル (他のファイルも同様) に、アプリケーションのシグネチャとは違うクリエータコードを与える場合に使用する必要があります。こうして、ユーザが設定ファイルを開こうとすると次のようなメッセージが表示されます。

「このファイルには SurfWriter アプリケーションのユーザ設定が記述されています。このファイルを開いたり印刷することはできません。このファイルを有効にするには、システムフォルダ内の“初期設定”フォルダに保存する必要があります。」

「存在しないアプリケーションの名前」を表す文字列、'STR ' リソース ID -16396 もあります。このリソースはそのファイルを作成したアプリケーションの名前 (例えば”SurfWriter”) です。あるファイルを作成したアプリケーションが見つからないとき、ユーザにどのアプリケーションかを伝えるために Finder が使います。通常、この文字列はユーザが開く可能性のあるファイルに対してのみ使われます。

重要: 「アプリケーションが見つかりませんでした」メッセージ文字列と「存在しないアプリケーションの名前」文字列の両方を使用してはいけません。どちらかひとつを使ってください。「アプリケーションが見つかりませんでした」メッセージの詳細については『Inside Macintosh: Macintosh Toolbox Essentials』の第 7 章「Finder Interface, using the Finder Interface Displaying Messages When the Finder Can't Find Your Application」を参照してください。

 

設定ファイルはどうやって見つけますか。

名前で設定ファイルを見つけるのはとても簡単です。そこにあるかないかの問題だからです。クリエータコードで設定ファイルを見つける場合は、“初期設定”フォルダ全体から正しいタイプのファイルを探す必要があります。同じクリエータコードを持つ複数ファイルが見つかった場合は、名前もチェックする必要があるでしょう。クリエータコードが '????' の場合がこれに該当します。

特定のクリエータコードを持つファイルを探すには、PBHGetFInfo にインデックスを指定して呼び出し、ディレクトリ内の各ファイルについての Finder 情報を取得してください。PBHGetFInfo への各呼び出しの後、パラメータブロックの ioDirID フィールドを再設定しなければならないことを覚えておいてください。PBHGetFInfo がこのフィールドの値を変更するからです。

 

設定ファイルには 'vers' リソースを含めるべきですか。

はい、含めなければなりません。アプリケーションを構成するファイルはすべて、ID 1 および 2 の 'vers' リソースを持たなければならず、当然、設定ファイルも例外ではありません。

'vers' リソースの直接の利点は、Finder の“情報を見る”ダイアログからユーザがより完全な情報を得ることができることです。ダイアログにはそのファイルが属するアプリケーション名とバージョンが表示されます。'vers' の他の利点は、プログラムの新バージョンが古い設定ファイルのバージョンを認識して新しい書式に変換する際に便利だということです。

 

設定ファイルに正しいアイコンを関連付けるにはどうしたらよいでしょう。

これは簡単です。設定ファイルに 'pref' のタイプを与えれば、自動的に正しいアイコン (設定ファイルのデフォルトアイコン) が付きます。アプリケーションのバンドルリソースに 'pref' エントリを作成してはいけません。設定ファイルに汎用アイコンを与えると、“初期設定”フォルダが開くのがずっと速くなります。

設定ファイルに汎用ではないアイコン (カスタムアイコン) を持たせることはおすすめしません。しかし、カスタムアイコンを使いたいならば、Finder カスタムアイコンを使って、アプリケーションが削除されても設定ファイルがアイコンを持つようにしてください。ユーザが設定ファイルを削除してもかまわないかを判断することができます。Finder カスタムアイコンを使用する別の理由は、Finder は場所のわかっている 1 個のファイル (設定ファイル) を開きさえすればよく、すべてのボリューム上のデスクトップデータベースを探す必要がなくなるからです。Apple Remote Access を通じてマウントされている AppleShare のボリュームがあると非常に遅くなってしまいます。

Finder カスタムアイコンの作成方法については『Inside Macintosh: Macintosh Toolbox Essentials』の第 7 章「Finder Interface, using the Finder Interface, Creating Customized Document Icons」を参照してください。

 

ユーザが設定ファイルをダブルクリックしたらどうしますか。

これは、設定ファイルにアプリケーションのシグネチャと同じクリエータコードを与えている場合にだけ起こる問題です。アプリケーションが設定ファイルを開くよう求められたときにどうするかについては、次のような判断が可能です。

  1. 設定ファイルは開くことができないというダイアログを表示する。
    • 設定ファイルを開くことができないというダイアログを出すことで、ユーザはアプリケーションを起動したつもりだったが、自分が「開いた」のが何のファイルだったのかを理解します。
    • ユーザによってはこのようなダイアログで混乱する人もいます。
  2. 設定ダイアログを開く。
    • 設定ダイアログを開くというのは素晴らしい考えで、Mac にとてもふさわしく思われますが、解決しなければならない次のような問題もあり、それはこの TECHNOTE のような一般目的の文書の枠を超えるものです。
      • どのファイルから設定値を引き出すか。現在のファイルからか、それとも新しく開いたファイルからか。
      • ユーザが「OK」をクリックしたとき変更をどこに保存するのか。既存の設定ファイルに保存するのか、新しく開いた設定ファイルにするのか、それとも両方か。
      • 新しく開いた設定ファイルがサーバにあって、書き込みができない場合はどうするか。
      • ファイルに保存した設定のいくつかまたはすべてが明示的なダイアログで設定されなかった場合、ユーザに設定ファイルが変更されたことをどうやって伝えるか。
  3. 何もしない。
    • 何もしないことがデベロッパにできる一番簡単なことです。
    • クラッシュしてはいけません (アプリケーションの「文書を開く」アップルイベントのハンドラから 'pref' タイプのファイルを取り除く必要があります)。
    • しかしこれはすぐれたユーザインタフェースとはいえません。ユーザがファイルをダブルクリックしたことを感知したことを何らかの方法で示すべきです。

ユーザインタフェースの問題を除けば、どの方法にも実際の不都合はありません。ユーザインタフェースをどうするかはデベロッパが決めてください (アップルにはこの件に関する公的見解はありません)。

 

設定ファイルが存在しない場合はどうしますか。

これは簡単そうに思えますが、2 つの違う意見があります。

2 つの意見は次の通りです。

  1. ファイルは作成しない。アプリケーションの設定はすべてデフォルト設定のファイルが存在するように設定する。ユーザが何か設定を変更したら変更を含む設定ファイルを作成する。
  2. 設定ファイルを作成し、デフォルトの値を入れる。

最初の方法の明らかな利点は、ユーザが設定をデフォルトの値から変更しなければ、設定ファイルが作成されないのでハードディスクが消費されないということです。また、アプリケーションをインストールして、試し、削除することが簡単にでき、システムから親のいなくなったファイルを探す必要がありません。このことは、ソフトウェアの試用版を配布するデベロッパのために特筆しておきます。

常に設定ファイルを作成する方が少しだけコードは簡単になるでしょう。ただ、設定ファイルが作成できない場合もあるわけで、設定ファイルなしでもアプリケーションが動作できるようにしておかなければならない点は同じです。実際に必要になるまで設定ファイルの作成を先延ばしにしたほうがよいでしょう。

設定ファイルが作成できない場合というのは、ひとつにはボリュームやシステムフォルダがロックされている場合です。この場合どう対処すべきかについては、次の「設定ファイルに書き込めない場合はどうしますか」を参照してください。

設定ファイルやフォルダを保存したい場所に、他のファイルやフォルダがすでに存在するという場合もあります。この場合どう対処すべきかは「別のアプリケーションと設定ファイルがコンフリクトを起こしたらどうしますか」のセクションを参照してください。

 

設定ファイルに書き込めない場合はどうしますか。

アプリケーションが設定ファイルの保存または更新ができない場合はどうしたらよいでしょうか。

  • アプリケーションの実行をやめる。これは最悪の解決策です。最後の手段であり、最初に選択すべき方法ではありません。
  • アプリケーションは続行する。ただユーザの設定は保存しない。

しかし、ユーザがプログラムを新しい設定で使いたい場合はどうなるのでしょう。

  • ユーザが行った設定はすべて有効にする。ただし保存はできないことを警告する。この場合、プログラムは新しい設定で動作しなければなりません。
  • 別のフォルダに保存する。プログラムが設定ファイルを任意のフォルダに保存できる場合は、ユーザに別の場所を問い合わせることで、設定ファイルの保存をキャンセルする必要はなくなります。

 

設定ファイルが壊れたらどうしますか。

アプリケーションの多くは、独自のリソースデータ構造体を含むリソースフォークのみの設定ファイルを用いて、起動時にそれを読み込みます。Resource Manager に基づいて設定ファイルを使う場合はその制約を知っておかなければなりません。重要なのは、現在の Resource Manager はリソースファイルを開く際にあまり強力なチェックを行わないことです。リソースファイルが壊れていて、Resource Manager がクラッシュしたりエラーを出すと、ユーザには問題を発見する方法はありません。ユーザがこれに気付いて壊れた設定ファイルを削除することはほとんど期待できません。

このような理由で、Resource Manager で設定ファイルを開く前に、設定ファイルのリソースマップをチェックしてください。このためのコードが「Internet Config」の一部にパブリックドメインとして提供されています。IC Programmers Kit の ICResourceForkSanity.c というファイルを探してください。

設定ファイルが壊れたら、ユーザにその事実を警告しなければならず、簡単に元に戻せないような処理をすぐに行ってはいけません。他のアプリケーションが設定ファイルを開いており、一時的に壊れているように見えるだけという可能性もあります。その場合はファイルを消したくはないでしょう。アプリケーションから最良と思われる対処方法をユーザに提案することはかまいませんが、問題解決の最終決断はユーザが行えるようにしてください。

他のアプリケーションが設定ファイルを開いているわけではなく、本当に壊れてしまったのなら、デフォルトの設定か、バックアップされた設定のいずれかで実行を続けるべきです。アプリケーションを終了して設定ファイルを削除するよう強いることはユーザを失望させます。アプリケーションはデフォルトの設定で完全に使用できる可能性があり、ユーザは急いで簡単な仕事を片づけてしまいたいのかもしれません。そのような場合にユーザに即座に問題を解決することを強いることは得策ではありません。

良い例として、Internet Config は設定ファイルのリソースフォークのコピーを同じ設定ファイルのデータフォークに保存します。リソースフォークが壊れたら、リソースフォークを消し、データフォーク内のデータから、保存されている設定をコピーして、実行を続けます。これは複雑な方法で、設定ファイルのサイズも倍になります。ただ、普通はたいしたディスクスペースを消費するわけではありません。

自分のアプリケーションでもこの方法を試してみたいなら、Internet Config のコードはパブリックドメインなので、無料でアプリケーションに取り込むことができます。

 

別のアプリケーションと設定ファイルがコンフリクトを起こしたらどうしますか。

設定を保存したい場所にすでに他のファイルやフォルダがある場合は、この事実をユーザに警告しなければなりませんが、そのまま実行できるようにします。

可能であれば、設定ファイルを別の場所に保存してください。そうすれば、コンフリクトが一時的でない場合、とりあえずユーザはどちらのアプリケーション使うことができます。別の場所に保存できないと、正しい設定ファイルがないアプリケーションを使うことになります。

この問題を一番簡単に回避するには、固有のクリエータコード (アプリケーションのシグネチャ) で設定ファイルを探すようにすることです。そうすればユーザが設定ファイルの名前を変更してもアプリケーションには影響がありません。

設定ファイルを名前で探すことにした場合、名前の中に登録済みの文字列 ("Apple" など) を使うと、コンフリクトを起こす可能性を減らす助けになります。


 

迷った場合は

選択肢がたくさんあって迷う場合は、DTS は次のことをおすすめします。

  • 単一の設定ファイルを使い、アプリケーションのシグネチャと同じクリエータコードを持たせます。アプリケーションに付属するファイルはすべてそのアプリケーションのシグネチャと一致するクリエータコードを持つべきだからです。(他のアプリケーションが使用するファイルを作成する場合は、もちろんそのアプリケーションのシグネチャを使わなくてはなりません。)
  • 設定ファイルは (FindFolder が返す) “初期設定”フォルダに保存します。
  • 設定ファイルのタイプは 'pref' にします。
  • 設定ファイルには汎用アイコンを使います。
  • ユーザが設定ファイルを開こうとしたら、アプリケーションは設定ダイアログを表示します。ダブルクリックされたファイルの設定ではなく、現在の設定を使用するようにします。
  • 設定が変更されるまでは、アプリケーションは設定ファイルを作成してはいけません。
  • 設定ファイルに書き込みができない場合は、どれでも、読むことのできた設定を使います。ユーザには現在の設定を変更させてかまいませんが、新しい設定を保存することができないことを警告してください。
  • 設定ファイルが壊れたら、デフォルトの値を使って実行を続けます。できれば、別の場所から設定を復元してみてください。
  • 設定ファイルが別のアプリケーションとコンフリクトを起こしたら、デフォルトの値を使って実行を続けます。ユーザに設定を変更させてもかまいません。できれば、設定を別の場所か、“初期設定”フォルダに違う名前で保存してください。
  • 設定ファイルに「アプリケーションが見つかりませんでした」メッセージ文字列を与えてください。

 

まとめ

設定ファイルのメンテナンスは見た目ほど簡単ではありません。検討すべき選択肢と比較すべきトレードオフがありますが、ほとんどのアプリケーションは設定ファイルを持っているはずなので、正しいメンテナンスはきわめて重要なことです。

この TECHNOTE では、考えられる可能性を挙げ、その利点と欠点を示しました。ですから、自分とユーザのために正しい方法を選ぶことができるようになっているはずです。


 

付録 A: サンプルコード

次のコードは、設定ファイルをファイル名かクリエータコード (またはその両方) で探す方法を表すものです。

FindFolder は、指定のフォルダが見つからないと fnfErr (-43) を、フォルダがあるべき場所にファイルがあると dupFNErr (-48) を返します。FindPrefsFile の最後のパラメータは long の値で、これは詳細エラーを表す enum のインデックスです。エラーが返されたとき (ときに noErr が返されたときでも) 何が起こったかを詳しく示します。この値を使うと、エラーについてユーザにもっとよく説明することができます (ユーザにエラーの意味を伝えるとして)。例えば、この値を 'STR#' リソースへのインデックスとして使い、ユーザにエラーメッセージを示すことができます。

/*
**  Apple Macintosh Developer Technical Support
**
**  設定ファイルをファイル名かクリエータコード (またはその両方) で
**  探す方法を表すルーチン
**  ファイル名:   FindPrefsFile.c
**  このサンプルコードはご自分のアプリケーションで制限なく使用して
**  かまいませんが、このサンプルコードは「そのままで」提供されるも
**  のであり、使用する責任も 100% かかります。ただし、このソース
**  コードに変更を加えた後で「アップルのサンプルコード」として再配布
**  することは許されません。ソースを再配布するのであれば、「アップルの
**  サンプルコード」に変更を加えたものであることを必ず明記してください。
*/
 
#include <Types.h>
#include <Folders.h>
#include <Files.h>
#include <Errors.h>
#include <TextUtils.h>
 
#include <assert.h>
 
enum {
    noError                 = 0,
    noFileExists            = 1,
    notRightName            = 2,
    fileWithNameNotCreator  = 3,
    folderInsteadOfFile     = 4,
    tooManyFiles            = 5,
    findFolderErr           = 6
};
 
OSErr   FindPrefsFile (OSType creatorCode, Str63 prefsName,
  FSSpec * prefsFSSpec, long *result);
 
/*
     この関数は、クリエータコードとファイル名を使って“Preferences”
     フォルダからからファイルを探します。
 
     * 概要:
 
     やりかたはまずクリエータコードで設定ファイルを探し、ファイル名は
     予備の検索方法として使用します。
 
     正しいクリエータコードを持つファイルがひとつだけ見つかった場合は、
     名前には関係なく、そのファイルの FSSpec を返します。
 
     同じクリエータコードを持つファイルが複数見つかった場合、その中から正しい
     名前のファイルを探し、見つかったら、そのファイルの FSSpec を返します。
 
     正しいクリエータコードを持つファイルが見つからない場合、もし dupFNErr と
     問題のファイルの FSSpec が返されたら、正しい名前のファイルがあるか
     チェックします。そうでなければ、fnfErr とファイルを作成すべき場所の
     FSSpec を返します。
 
     この関数は何が起こったかをより詳しく伝える enum も返します。この値を
     使って、ユーザに何がうまくいかなかったのかを (必要なら) 詳しく伝え
     てください。例えば、この値を STR#リソースへのインデックスとして使う
     ことができます。
 
     * 実装:
 
     まず簡単なテストを行います。正しい名前を持つファイルを探してみて、
     ファイルがあれば、ファイルタイプが正しいかをチェックします。
     この 2 つが正しければ、それ以上の検索は行わず、このファイルの
      FSSpec を返します。
 
     指定された名前とクリエータを持つファイルが見つからない場合は、
    “Preferences”
     フォルダ内のすべてのファイルを検索してクリエータコードを調べます。
     一致するクリエータコードのファイルが見つかったら、テンポラリの FSSpec
     にコピーして検索を続けます。同じクリエータコードの別のファイルが
     見つかると検索を中止し、fnfErr を返します。FSSpec は正しい名前を
     持つファイルのあるべき場所に設定して返します。
 
     正しいクリエータコードのファイルがひとつだけ見つかった場合は、それを
     FSSpec で返し、関数はファイル名が何であろうが、noErr を返します。
*/
 
OSErr   FindPrefsFile (OSType creatorCode, Str63 prefsName, FSSpec *
   prefsFSSpec, long *result) {
    HParamBlockRec      hpb;
    FSSpec              matchFSSpec;
    SInt32              foundPrefDirID      = 0;
    UInt32              numMatches          = 0;
    SInt16              foundPrefVRefNum    = 0;
    OSErr               err                 = noErr;
    Str63               fileName            = "\p";
    Boolean             shortCircuit        = false,
                        foundFileName       = false,
                        foundDirectory      = false;
 
    assert (prefsName != nil);
    assert (prefsName[0] <= 63);
    assert (prefsFSSpec != nil);
 
    // 検索をはじめる“Preferences ”フォルダを検索
    err = FindFolder (kOnSystemDisk, kPreferencesFolderType,
    kDontCreateFolder, &foundPrefVRefNum, &foundPrefDirID);
 
    // まず簡単な検索を行う。正しい名前、クリエータコードのファイルを探す。
    if (err == noErr) {
        BlockMoveData (prefsName, fileName, prefsName[0] + 1);
        hpb.fileParam.ioCompletion = nil;
        hpb.fileParam.ioNamePtr = fileName;
        hpb.fileParam.ioVRefNum = foundPrefVRefNum;
        hpb.fileParam.ioDirID = foundPrefDirID;
        hpb.fileParam.ioFDirIndex = 0;
 
        // 見つからなかった場合に返す、ファイルのあるべき場所を示す FFSpec を
        // 組み立てる
        err = FSMakeFSSpec (foundPrefVRefNum, foundPrefDirID, prefsName, prefsFSSpec);
        if (err == noErr) {
            foundFileName = true;
            err = PBHGetFInfoSync (&hpb);
 
            if (err == noErr) {
                if (hpb.fileParam.ioFlFndrInfo.fdCreator == creatorCode) {
                    // 探しているファイルがあった
                    shortCircuit = true;
                    numMatches = 1;
                    if (result != nil) {
                        *result = noError;
                    }
                }
            } else {
                // PBHGetFInfoSync がエラーを返したら、
                // それはおそらくディレクトリを検索したから。
                foundDirectory = true;
                err = noErr;    // クリエータタイプで検索を続行
                if (result != nil) {
                    *result = folderInsteadOfFile;
                }
            }
        } else {
            err = noErr;    // クリエータタイプで検索を続行
        }
    } else {
        if (result != nil) {
            *result = findFolderErr;
        }
    }
 
    // 名前で合致するファイルはないのでクリエータタイプで検索する
    if (err == noErr && shortCircuit == false) {
        // “初期設定”フォルダ内のすべてのファイルで、指定のクリエータ
        // タイプを持つものがないか調べる
        do {
            // ファイルのクリエータコードを調べる
            hpb.fileParam.ioFDirIndex += 1;
            hpb.fileParam.ioDirID = foundPrefDirID;
            err = PBHGetFInfoSync (&hpb);
 
            // ファイルがないと仮定する
            if (result != nil) {
                *result = noFileExists;
            }
 
            if (err == noErr && hpb.fileParam.ioFlFndrInfo.fdCreator == creatorCode) {
                // ファイルは正しいクリエータコードを持つので、記録する
                if (numMatches == 0) {
                    matchFSSpec.vRefNum = foundPrefVRefNum;
                    matchFSSpec.parID = foundPrefDirID;
                    BlockMoveData (fileName, matchFSSpec.name, fileName[0] + 1);
                    numMatches = 1;
                } else {
                    // 同じクリエータコードのファイルがもうひとつあったが、
                    // ファイル名は違う
 
                    // どのファイルを返したらいいかわからないので検索を中止
 
                    numMatches = 2;
                }
            }
        } while (err != fnfErr && numMatches < 2);
    }
 
    if (numMatches == 0) {
        if (foundDirectory == true) {
            err = notAFileErr;
            if (result != nil) {
                *result = folderInsteadOfFile;
            }
        } else if (foundFileName == true) {
            err = dupFNErr;     // 名前は正しいがクリエータコードが違う
            if (result != nil) {
                *result = fileWithNameNotCreator;
            }
        } else {
            err = fnfErr;
        }
    } else if (numMatches == 1 && shortCircuit == false) {
        // 正しいものが 1 個見つかった。したがってこれが正しい設定ファイルだ
        BlockMoveData (&matchFSSpec, prefsFSSpec, sizeof (FSSpec));
        err = noErr;
        if (result != nil) {
            *result = notRightName;
        }
    } else if (numMatches > 1) {
        // 同じクリエータコードのファイルが複数見つかった。
        // つまりどれも正しい名前ではない。
        //  (正しければ最初の簡単なテストで見つかっているはずだから)
        // だから fnfErr を返す。
        err = fnfErr;
        if (result != nil) {
            *result = tooManyFiles;
        }
    }
 
    return err;
}

次のコードの断片は、“初期設定”フォルダからファイルを名前で探す方法を示します。

FindFolder はシステムフォルダか“初期設定”フォルダが見つからないと (そして、このコードのように kDontCreateFolder を使っている場合)、fnfErr (-43) を返します。FSMakeFSSpec は、指定された名前のファイルまたはフォルダがないと、fnfErr を返します。ファイルがない場合は、設定ファイルをまず作成してからでないと開くことができません。

注意: FSMakeFSSpec は、指定された名前のフォルダが存在すれば、有効な FSSpec を返します。返された FSSpec を開くときには、ファイルオープンの呼び出しが失敗したときに対応できるようにしておいてください。

#include <Types.h>
#include <Folders.h>
#include <Files.h>
#include <Errors.h>
#include <TextUtils.h>
 
#include <assert.h>
 
enum {
    // 呼び出し側が識別可能な未使用のエラー番号を使う
    findFolderErr   = 128
};
 
OSErr FindPrefsByName (FSSpec * prefsFSSpec) {
    OSErr               err                 = noErr;
    SInt32              foundPrefDirID      = 0;
    SInt16              foundPrefVRefNum    = 0;
    Str255              prefsName;
 
    assert (prefsFSSpec != nil);
 
    // 設定ファイル名を調べる
    GetIndString (prefsName, kPreferencesSTRRes, kPrefsNameSTRIndex);
 
    if (prefsName == nil) {
        // 設定ファイル名の nil なら、おそらくリソースは存在しない
        err = resNotFound;
    }
 
    if (err == noErr) {
        // 検索を始める“初期設定”フォルダを探す
        err = FindFolder (kOnSystemDisk, kPreferencesFolderType,
        kDontCreateFolder, &foundPrefVRefNum, &foundPrefDirID);
    }
 
    if (err == noErr) {
        // 設定ファイルがあるべき場所の FFSpec を作成する
        err = FSMakeFSSpec (foundPrefVRefNum, foundPrefDirID, prefsName, prefsFSSpec);
    } else {
        err = findFolderErr;
    }
 
    return err;
}

参考文献


ダウンロード

Internet Config source

更新日: 1998 年 6 月 198日