FSSpec vs. FSRef Part 2

前文

Apple は FSRef の構造を公開していないので推測の域ではあるけれど、おそらく指しているアイテムの存在するボリュームフォーマットに依存した内容になっていると思う。現在のところ、HFS/HFS+ ボリュームに対する FSRef は移動やリネーム操作が起こっても追従して正しいファイルを指す。おそらく、VRefNum と file/directry ID を保持しているためです。この点では、FSRef は FSSpec よりも有益です。ただ、FSRef の構造が公開されていないことを勘案すると、この特徴はあくまで「現時点における」ものであることに留意しておくべきです。つまりいつ変更されるともしれないので、これに依存したコードは避けるべきでしょう。こういった追従が必要なら alias を使いましょう。

AppleEvent 経由で FSRef を渡してはいけません。例えば Finder への AppleEvent などですね。Mac OS 9 ではうまく動くかもしれませんが、Mac OS X において FSRef はプロセス内でのみ有効なものなのです。この場合は alias を使いましょう。George Warner 氏の MoreFinderEvents の中にこのワナにはまるコードをいくつか見てとれます。

FSSpec と同様、FSRef は Mac OS 9 / Mac OS X の同じブート内でのみ有効であるだけでなく、Mac OS X では同一プロセス内でのみ有効なのです。(たとえ同じアプリケーションを別々に起動した場合でも、それぞれのプロセス内でのみ有効になります)したがって、この値をストレージに記録しておくようなことはしてはいけないです。そういった場合には alias を使いましょう。(Aliases.h を参照)

Q&A

FSSpec を使い続けることはできますか?

私の知る限り、答えは Yes です。FSSpec はこれからも正しいファイル参照方法であり続けるでしょう。けれども、FSSpec の name メンバは有効な値にならないことがあるから、記録用/表示用のいずれにも使うべきではありません。本当の名前が Pascal String で表現できない場合や、31文字以上の場合には name メンバは有効な値にならないのです。後者の場合「A really, really longfile#23A4」のような文字列が取得されます。FSSpec は現在も機能しますが、本当の名前を含まないだけです。

FSSpec を全て FSRef に置き換えることはできますか?

それはアプリケーション毎にケース・バイ・ケースです。QuickTime API 群は現在も FSSpec を要求します。私は Toolbox の全てを使っているわけではないので、他にも FSRef を使用するバージョンのものが用意されていない API が残っているかもしれません。

Drag Manager が直接 FSRef をサポートしていないようなので、私は Drag を作ったり受け取ったりする場面では、いまだに FSSpec を使っています。

自分自身のものも含め、私が見たことのある Navigation Services の filter proc は、いずれも次のようなコードを含んでいました。

if ( theItem->descriptorType == typeFSS )

なので、filter に渡される AEDesc には FSSpec が入っていると仮定しています。

ファイルシステム・オブジェクトのドラッグをサポートしたり、QuickTime を使ったりしないのなら、ひょっとすると FSSpec を全部なくすことができるかもしれません。いずれにせよ、ケース・バイ・ケースだけどね。

どうやったら Open/Save ダイアログで FSRef と Unicode のロングファイル名をサポートできますか?

Navigation.h にある NavCreateXXX API を使う必要がある。これらのダイアログをシートとして実装する場合にも同様です。(注意:新しい NavCreateXXX API で kWindowModalityAppModal を指定した場合、NavDialogRun() はユーザがダイアログを閉じるまで処理を戻しません。シートを実装するために kWindowModalityWindowModal を使った場合には、Mac OS X の NavDialogRun() はすぐに処理を戻します。つまり NavDialogRun は処理を続けているけど、通常の Window のように振る舞うということです。これは Navigation.h に記述されてはいますが、十分には浸透してないようです)

自アプリケーションの FSRef を得る方法は?

CFM バイナリアプリケーションの場合、GetProcessInformation() で FSSpec を取得して FSRef に変換することができます。バンドル形式の場合、この方法を使うとパッケージの FSRef ではなく、実行ファイルの FSRef が得られます。そのような場合には GetProcessBundleLocation() を使いましょう。これは CFM バイナリでも同じように動作するようです。

Tip

もし C++ で書かれているコードを FSRef を利用するものに更新していく場合には、FSSpec と FSRef を相互変換するキャスト演算子をオーバーロードするという方法が使えます。これにより移行のコストを小さくできます。

LaunchServices

LaunchServices は Mac OS X のみで使える API 群です。Mac OS X のファイル処理での最新情報を知りたかったら、LaunchServices.h を読むことを断然おすすめします。例えば、バンドルアプリケーションに関することや、表示名のこと、書類とアプリケーションの結びつけの新しいルールのこと、などなどです。

いくつかの例

パッケージはファイルとフォルダの両方の特性を持っています。フォルダがパッケージかどうかは LSCopyItemInfoForRef() を使えば調べられるし、もしパッケージがアプリケーションの場合にはクリエータ/ファイルタイプも取得可能です。LSGetApplicationForItem() を使うと、Finder がファイルを開く際に利用するアプリケーションを決定できます。LSGetApplicationForInfo() を使うと、特定の拡張子/ファイルタイプ/クリエータをもつファイルを開くのに使われるアプリケーションを調べることができます。Mac OS X では書類とアプリケーションを結びつけるのに複雑なルールが存在し、さらにユーザが設定を変更することもできるので、こういった機能をもつ API が必要になるのです。

Unicode ファイル名を扱う際のいくつかのポイントについては、別の投稿に書くことにします。

訳出メモ

原文作者の Laurence Harris 氏の要請により、元のメールの AppleEvent に関する記述のパラグラフを以下のものに変更しています。

Don't pass FSRefs in AppleEvents. For example, in AppleEvents to the
Finder. They'll work in OS 9, but not in OS X because FSRefs aren't valid
across processes in Mac OS X. Uses aliases. I believe George Warner's
MoreFinderEvents code has some examples if you find yourself lost in this
black art. ;-)

訳文に対してのフィードバックは amn@mugiwara.jp まで

[index] [Part 1] [Part 2] [Part 3]