今回は、navMyPutFile()ルーチンから呼ばれているNavPutFile()を詳しく解説します。それに加え、Carbon Frameworkで使われているファイル保存場所を認識するため(ファイルアクセス用)の「4つの仕組み」を紹介したいと思います。
以下が、ドキュメント保存用の「ファイル名入力ダイアログ」を表示するnavMyPutFile()ルーチンです。ファイルメニューから「保存...」を選択した時か、まだ保存していないカタログウィンドウを閉じようとした時に実行されます。

NavPutFile()で表示されるダイアログは、前回解説したNavAskSaveChanges()と同様に、NavDialogOptions構造体の内容を操作することで色々なオプション機能を追加できます。まずはNavGetDefaultDialogOptions()を実行し、NavDialogOptions構造体の内容を初期化します。次に、preferenceKeyに使用形態に応じた識別子となる値(任意の値)を代入します。この操作により、Navigation Service側は、初期表示されるファイル階層などの情報を識別子ごとに保持します(次回利用する時に前回と同じ階層を表示する)。本サンプルでは、dialogOptionFlagsのkNavNoTypePopupビットを立てることで、ダイアログに配置される「ファイル種類選択用ポップアップメニュー」を使用できないように制限しています。savedFileNameには、オープン時に入力カラムに表示されるディフォルトファイル名を、clientNameにはサービスを利用するアプリケーション名をセットします。
NavDialogOptionsを設定し終わりNavPutFile()を実行すると、画面にファイル名入力ダイアログがオープンされます。ユーザによりファイル名が入力され、OKボタンがクリックされた場合にのみ、AEGetNthPtr()で保存先ファイルのFSSpec構造体を得ることができます。Mac OS X 10.3環境では、表示されたファイル名入力ダイアログはマウスドラッグにより移動可能(Movable Modeless Dialog)なのですが、10.2環境では移動不可な状態となります。この現象を回避するには、以下のようにダミーのeventProcを定義しておき、それをNavPutFile()の4番目の引数として代入します。これにより、どちらのバージョン環境でもダイアログを移動することが可能となります。
pascal void navEventProc( NavEventCallbackMessage sel,NavCBRecPtr parm,NavCallBackUserData ud )
{
}
ret=NavPutFile( NULL,&reply,&opt,navEventProc,MY_DOC,MY_SIG,NULL );
本アプリケーションでは、上記のnavMyPutFile()ルーチンを含め、ファイルアクセス用としてFSSpec構造体を採用しています。FSSpecを用いるのは、Mac OS 9時代から使われている手頃な方法ですが、Carbon Frameworkには、ファイルアクセス用としてAliasHandle、FSRef、CFURLRefという別の定義も用意されています。Mac OS X環境でのファイルアクセスにおいて、32文字より長いファイル名やUnicodeファイル名への対応が必須であるならば、FSSpecの代わりにFSRefを使う必要があります。AliasHandleは、ファイル保存場所を追跡するような処理に活用すると大変便利です。また、Mac OS Xから導入されたQuartz 2DやCore Foundationに含まれる多くのAPIは、ファイルアクセス時にCFURLRefを使う場合が多いようです。
アプリケーションによっては、そのドキュメント内に処理のターゲットとなるファイルの保存場所情報を埋め込みたい場合があります。例えば、データベースがアサインしているファイルとか、ワープロにリンクされている画像の保存場所情報などです。4つの定義を用いたファイルアクセスにはそれぞれに特徴、利点、欠点があり、選択を間違えると後から色々な不都合が生じ、状況によってはファイルが認識できなくなるケースに遭遇することもあります。ちなみに、この4つのファイルアクセス用パラメータは相互に変換することが可能です。まず最初に、その方法のいくつかを紹介しておきたいと思います。
以下はFSSpecを作成する例です。FindFolder()によりHome/Library/Preferencesフォルダのボリュームリファレンス番号(vref)とディレクトリID(did)を得ています。これらをアクセスしたいファイル名(fname)と一緒にFSMakeFSSpec()に渡すことにより、目的のFSSpec構造体を作成します。
Str255 fname
short vref;
FSSpec fsc;
long did;
FindFolder(kOnSystemDisk,kPreferencesFolderType,kCreateFolder,&vref,&did );
FSMakeFSSpec( vref,did,fname,&fsc );
次は、同様にFSFindFolder()を使いPreferencesフォルダのFSRefを得る方法です。
FSRef fsref;
FSFindFolder( kOnSystemDisk,kPreferencesFolderType,kCreateFolder,&fref );
FSSpecからFSRefを得るのにはFSpMakeFSRef()を利用します。FSSpecはアサイン先のファイルが存在していなくても定義できますが、FSRefの方はファイルが存在していないと定義できませんので注意しましょう。もし存在しないファイルのFSSpecをFSpMakeFSRef()に渡すとエラーが返ります。この特徴をうまく利用すると、FSpMakeFSRef()はファイルの存在を判断する処理に使えます。逆にFSRefからFSSpecを得るのには、FSGetCatalogInfo()を利用します。
FSpMakeFSRef( &fsc,&fsref );
FSGetCatalogInfo( &fsref,kFSCatInfoGettableInfo,NULL,NULL,&fsc,NULL );
次にFSSpecからAliasHandleを作成する場合です。この作業にはNewAlias()を用い、作成したAliasHandleからFSSpecを導くにはResolveAlias()を用います。この処理のFSRef版APIはFSNewAlias()とFSResolveAlias()です。
Boolean chg=0;
AliasHandle ahd;
FSSpec fsc;
NewAlias( NULL,&fsc,&ahd );
ResolveAlias( NULL,ahd,&fsc,&chg );
最後にCFURLCreateFromFSRef()を使い、FSRefからCFURLRefを得る方法を紹介します。その逆はCFURLGetFSRef()で実現します。
FSRef fsref;
CFURLRef url;
url=CFURLCreateFromFSRef( NULL,&fsref );
CFURLGetFSRef( url,&fsref );
Apple社には、ファイルアクセスはCFURLRefを用いる方法に統一したいという雰囲気が漂っているのですが、なにせまだ多くのファイル関連APIがFSSpecやFSRefに依存している状態でして(FSSpec依存はかなり減った)早期実現は不可能だと思われます。ちなみに、Mac OS Xが登場したばかりのころ、CFURLRefの使用のみを推薦し、FSSpecやFSRefの使用を否定すテックノート(Technical Note)がデベロッパーサイトに登録されたことがありました。しかし、多くの開発者の「時期尚早である!」という大反論に見舞われ、あえなく引っ込められた経緯があります。だいたい、当時のQuickTime APIはFSSpecしか受け取らなかったわけですし...いったい何を考えていたのやら(笑)。
次回は、FSSpec、AliasHandle、FSRef、CFURLRefを用いたファイルアクセスの特徴や仕組み、利点や欠点について、もう少し詳しく解説したいと思います。そして、その例題としてFSSpecの代わりにFSRefを得るシートウィンドウ(Sheet Window)タイプのファイル名入力ダイアログ(本サンプルでは未使用)を紹介したいと思います。
copyright 2005 Ottimo, Inc. All rights reserved
無断転載・引用禁止
Contact us: koike@ottimo.co.jp