Core Foundation Plug-in (CFPlugIn) API
2007/09/06 第一版
★ はじめに
CFPlugIn は Mac OS X の標準的なプラグインアーキテクチャです。CFPlugIn は一部 Microsoft 社 の COM(Component Object Model)に基づいており、C 言語または C++ 言語で開発出来ますが、このドキュメントでは C 言語での開発方法のみ説明します。
注釈:Objective-C クラスを利用したプラグインを開発するには NSBundle API が適しています。このドキュメントでは NSBundle API については説明していません。
注釈:このドキュメントは個人によって独自に作成された非公式なドキュメントです。公式なドキュメントが必要な場合は、巻末の参考資料等の Apple Developer Connection サイトをご覧下さい。
★ CFPlugIn の概要
CFPlugIn のプラグインは、ファイルシステム上ではバンドル形式として存在します。通常のバンドルとの見分け方は、Info.plist ファイルに CFPlugIn 特有のキーが設定されている事です。
・CFPlugInFactories キー
・CFPlugInTypes キー、等…
各キーについては後ほど説明します。
プラグインはコンポーネントと呼ばれる、部品として働く機能の塊を実装します(QuickTime 等で使われている Component Manager とは別物です)。コンポーネントにはタイプがあり、これによってホストアプリケーションとプラグイン内のコンポーネント間の互換性が特定されます。タイプは通常ホストアプリケーションによって定義され、これに従ってプラグインが開発されます。

プラグインに実装されたコンポーネントは、必要時にプラグイン自身が持つファクトリ関数によってインスタンス化されます。しかしインスタンス自体は外部には渡されず、インターフェイスと呼ばれるアクセッサへの参照がホストアプリケーションに渡されます。

インターフェイスでのアクセッサの呼び出し方法はインターフェイスの型によって定められますが、この型はタイプに準じて定義される為、同じタイプのインスタンスであれば、同じインターフェイスの型を想定してアクセス出来ます。また、インターフェイスの型はタイプに一種類とは限らず、拡張等の為に複数の型が用意されている場合もあり、一つのインスタンスから複数の異なる型のインターフェイスを取得する事も出来ます。

インターフェイスはアクセッサとして以外にもインスタンスの生存期間を管理する基準にもなります。インスタンスの生存期間は参照カウント方式で管理されており、インターフェイスが外部から取得/解放される度に参照カウントが増減し、全てのインターフェイスが解放され、参照カウントがゼロに至った時点でインスタンスが解放されます。
また Microsoft 社の COM に基づき、全てのインターフェイスは COM で用いられる IUnknown と呼ばれる基礎インターフェイスをベースにしなければなりません。IUnknown にはインターフェイスを取得/解放する為のアクセッサ関数がいくつか含まれており、全てのインターフェイスはこれらのアクセッサ関数を実装します。
まとめ
- プラグインはバンドルである
- プラグインにはコンポーネントが実装される
- コンポーネントにはタイプがある
- コンポーネントはファクトリ関数でインスタンス化される
- インスタンスは直接外部に渡されず、インターフェイスが渡される
- インターフェイスの型はタイプによって決まっている
- インターフェイスの取得と解放の回数でインスタンスが保持される
- 全てのインターフェイスは COM の IUnknown に基づいている
★ サンプルコード
CFPlugIn API を利用したホストアプリケーションとプラグインの実装例です。次章の実装ツアーで説明される内容に倣っています。Mac OS X 10.4.10 以降、Xcode 2.4 以降の環境を推奨します。
CFPlugInTestHost.zip(220 KB)
★ 実装ツアー

まずはホストアプリケーション側でコンポーネントのタイプと、インターフェイスの型を定義する所から始めてみましょう。タイプは UUID で表されます。
〜タイプの定義文〜
#define kFooPlugInTypeID (CFUUIDGetConstantUUIDWithBytes(NULL, 0x39, 0x99, 0xDE, 0x05, 0xF6, 0x29, 0x4B, 0xB1, 0xB5, 0x2A, 0x3C, 0xED, 0xB2, 0xAC, 0xC9, 0x56))
UUID とは Universally Unique IDentifier の略であり、世界で一意の 128 ビット長の識別子です。GUID(Globally Unique IDentifier)とも呼ばれます。UUID を生成するにはターミナルからコマンドラインツールの uuidgen を呼び出すか、Core Foundation フレームワーク内の CFUUID.h の命令を使います。説明中に登場する UUID は全てサンプル用の物であり、自分で新たに実装する場合は転用せずに新しい UUID を生成して下さい。
注釈:上記の様な CFUUIDGetConstantUUIDWithBytes を使った定数コードを生成する AppleScript をサンプルコードに同封しています。お使い下さい。
インターフェイスの型は COM の IUnknown に基づきます。IUnknown に基づかせるには、CFPlugInCOM.h に定義されたマクロを使います。また、外部からインターフェイスの型を識別する為の UUID も定義します。今回はインターフェイスの型は一つだけ定義します。
〜IUnknown のマクロ〜
#define IUNKNOWN_C_GUTS \
void *_reserved; \
HRESULT (STDMETHODCALLTYPE *QueryInterface)(void *thisPointer, REFIID iid, LPVOID *ppv); \
ULONG (STDMETHODCALLTYPE *AddRef)(void *thisPointer); \
ULONG (STDMETHODCALLTYPE *Release)(void *thisPointer)
〜インターフェイスの型と UUID の定義文〜
typedef struct {
IUNKNOWN_C_GUTS; // IUnknown のマクロ
CFStringRef (*Hello)(void *thisPointer); // 独自アクセッサ 1
CFStringRef (*ProcessString)(void *thisPointer, CFStringRef string); // 独自アクセッサ 2
} FooPlugInInterface;
#define kFooPlugInInterfaceID (CFUUIDGetConstantUUIDWithBytes(NULL, 0xD5, 0xFA, 0xF6, 0x43, 0xE8, 0x55, 0x40, 0x22, 0x92, 0x89, 0xA8, 0xBE, 0x2A, 0xC1, 0x23, 0x9A))

ホストアプリケーション側の定義が出来たら、プラグイン側の作成に移ります。プラグインにはコンポーネントが実装されると説明しましたが、C 言語の場合、コンポーネントはオブジェクト指向言語でのクラスのように特別な文法で記述される物ではなく、通常のコードの集合に過ぎません。具体的にはファクトリ関数、インスタンスの型、インターフェイスの関数テーブル、そしてインターフェイスのアクセッサ関数の一式がコンポーネントを形成します。
まずはプラグインのヘッダを先に書いてしまいましょう。ファクトリ関数の宣言は CFPlugIn.h に定義された呼び出し型に倣います。また、外部からファクトリ関数を識別する為の UUID も定義します。
〜ファクトリ関数の宣言文と UUID の定義文〜
void* MyPlugInFactory(CFAllocatorRef allocator, CFUUIDRef typeID);
#define kMyPlugInFactoryID (CFUUIDGetConstantUUIDWithBytes(NULL, 0xF9, 0x5E, 0x64, 0x62, 0xED, 0x98, 0x43, 0x51, 0xB7, 0x81, 0x3F, 0xF9, 0x1E, 0x68, 0x15, 0x4D))
ファクトリ関数はインスタンスを生成します。インスタンスの型は実は何でも構わないのですが、一般的には構造体として定義します。
〜インスタンスの型の定義文〜
typedef struct {
FooPlugInInterface *interface;
int referenceCount;
CFUUIDRef factoryID;
} MyPlugInInstance;
インスタンスの構造体にはインターフェイスの関数テーブルへのポインタが含まれています。この関数テーブルも定義しましょう。
〜関数テーブルの定義文〜
static FooPlugInInterface gMyPlugInInterface = {
0, // IUnknown の予約値
MyQueryInterface,
MyAddRef,
MyRelease,
MyHello,
MyProcessString
};
関数テーブル内の関数ポインタの並び順は、ホストアプリケーション側で定義したインターフェイスの型に準じています。IUnknown の先頭に予約値メンバがあるのでゼロを代入しているのに注意してください。
そして関数テーブルにはもちろん関数ポインタが入っているので、それらアクセッサ関数の宣言をします。アクセッサ関数の宣言では、IUnknown のアクセッサ関数とタイプ独自のアクセッサ関数を宣言しています。
〜アクセッサ関数の宣言文〜
// IUnknown アクセッサ
HRESULT MyQueryInterface(void *thisPointer, REFIID iid, LPVOID *ppv);
ULONG MyAddRef(void *thisPointer);
ULONG MyRelease(void *thisPointer);
// 独自アクセッサ
CFStringRef MyHello(void *thisPointer);
CFStringRef MyProcessString(void *thisPointer, CFStringRef string);

ヘッダが書けたら、外部からファクトリ関数へアクセス出来るように Info.plist ファイルに必要なキーを設定します。CFPlugInFactories キーにはファクトリ関数の UUID と名前をペアにして設定します。CFPlugInTypes キーにはコンポーネントのタイプと先ほどのファクトリ関数の UUID を関連付けるように設定します。これで、特定のタイプのコンポーネントを探しているホストアプリケーションから、CFPlugIn API を使って、このプラグインバンドルとファクトリ関数を見つけ出せるようになります。
〜Info.plist ファイルのキー設定文〜
<key>CFPlugInFactories</key>
<dict>
<key>F95E6462-ED98-4351-B781-3FF91E68154D</key> <!-- ファクトリの UUID -->
<string>MyPlugInFactory</string> <!-- ファクトリ関数の名前 -->
</dict>
<key>CFPlugInTypes</key>
<dict>
<key>3999DE05-F629-4BB1-B52A-3CEDB2ACC956</key> <!-- タイプの UUID -->
<array>
<string>F95E6462-ED98-4351-B781-3FF91E68154D</string> <!-- ファクトリの UUID -->
</array>
</dict>

ヘッダと Info.plist ファイルを書き終えたら、実装ファイルに移ります。ファクトリ関数の実装では、指定されたコンポーネントタイプのインスタンスを生成し、そのインターフェイスを返します。
CFPlugIn でインターフェイスを受け渡しする際には、関数テーブルのポインタのポインタとして扱うのがルールです。なぜ二重にポインタを参照しているかというと、関数テーブルは静的にあるいはシングルトンとしてメモリ上に割り当てて、インスタンスには関数テーブルのポインタのみを格納した方が効率が良いのです。この場合単なる関数テーブルのポインタでは、インスタンス毎に同じ値になってしまいますが、関数テーブルのポインタのポインタであれば、インスタンス毎に異なるアドレス(インスタンス内のメモリアドレス)を指す事になります。
〜ファクトリ関数の実装文〜
void* MyPlugInFactory(CFAllocatorRef allocator, CFUUIDRef typeID) {
if (CFEqual(typeID, kFooPlugInTypeID)) {
MyPlugInInstance *instance = calloc(1, sizeof(MyPlugInInstance));
instance->interface = &gMyPlugInInterface;
instance->referenceCount = 1;
instance->factoryID = kMyPlugInFactoryID;
CFPlugInAddInstanceForFactory(kMyPlugInFactoryID); // インスタンス生成を通知
return &instance->interface;
} else {
return NULL; // 対応していないタイプを要求されたら NULL を返す
}
}
ファクトリ関数ではインスタンスの生成と同時に、インスタンスの参照カウントを 1 に初期化します。また、ファクトリ関数でインスタンスが生成された事をシステムに通知する事で、プラグインのバイナリコードのロード状態をシステムに維持させます。
ファクトリ関数が実装出来たら、インスタンスの駆動部となるアクセッサ関数を実装します。全てのアクセッサ関数の第一引数には必ず、呼び出しに使われたインターフェイスが渡されます。
IUnknown のアクセッサ関数は 3 つあります。1 つめの QueryInterface 関数は、指定された型のインターフェイスを取得して返す関数です。同時にインスタンスの参照カウンタを増加させます。
〜QueryInterface 関数の実装文〜
HRESULT MyQueryInterface(void *thisPointer, REFIID iid, LPVOID *ppv) {
CFUUIDRef requestIID = CFUUIDCreateFromUUIDBytes(NULL, iid);
if (CFEqual(requestIID, IUnknownUUID) || CFEqual(requestIID, kFooPlugInInterfaceID)) {
(*(IUnknownVTbl**)thisPointer)->AddRef(thisPointer); // 参照カウンタを増加
*ppv = thisPointer; // インターフェイスは一つしかないので、そのまま返します
CFRelease(requestIID);
return S_OK;
} else {
*ppv = NULL;
CFRelease(requestIID);
return E_NOINTERFACE; // 対応していないインターフェイスを要求された
}
}
QueryInterface 関数はタイプで決められたインターフェイスの型に対応しますが、全てのインターフェイスの型に対応する必要はありません。ただし、どのインターフェイスの型が必須対応であるか、あるいは任意対応であるかはタイプの仕様策定時に決めておく必要があるでしょう。
IUnknown の 2 つめのアクセッサ関数は、AddRef 関数です。この関数はインスタンスの参照カウントを増加させます。関数として独立していますが、もっぱら QueryInterface 関数から呼ばれます。ここでは参照カウントを返り値として返していますが、これは必須ではなく、AddRef 関数が何の値を返すべきかは仕様として決められていません。
〜AddRef 関数の実装文〜
ULONG MyAddRef(void *thisPointer) {
return ++((MyPlugInInstance*)thisPointer)->referenceCount;
}
実装文をみて変だと思われた方も居るのではないでしょうか。渡されたインターフェイスをインスタンスへのポインタとして扱っているからです。これは、関数テーブルのポインタがインスタンスの先頭に配置されている為、関数テーブルのポインタのポインタが、すなわちインスタンスのポインタと同等になる事を利用した実装方法です。こういったアクセッサ関数からインスタンス内部へアクセスする方法については、後ほど掘り下げて説明します。
IUnknown の 3 つめのアクセッサ関数は、Release 関数です。インターフェイスの解放を担当し、インスタンスの参照カウントを減少させます。参照カウントがゼロに達した場合はインスタンスを解放し、同時にインスタンスの解放をシステムに通知し、プラグインのバイナリコードのロード状態を緩和します。Release 関数の返り値も AddRef 関数と同様に仕様上未定義です。
〜Release 関数の実装文〜
ULONG MyRelease(void *thisPointer) {
--((MyPlugInInstance*)thisPointer)->referenceCount;
if (((MyPlugInInstance*)thisPointer)->referenceCount == 0) {
CFPlugInRemoveInstanceForFactory(((MyPlugInInstance*)thisPointer)->factoryID); // インスタンス解放を通知
free(thisPointer);
return 0;
} else {
return ((MyPlugInInstance*)thisPointer)->referenceCount;
}
}
IUnknown のアクセッサ関数の実装が終わったら、タイプ独自のアクセッサ関数を実装しましょう。
〜タイプ独自のアクセッサ関数の実装文〜
CFStringRef MyHello(void *thisPointer) {
return CFRetain(CFSTR("Hello, I am MyPlugIn!"));
}
CFStringRef MyProcessString(void *thisPointer, CFStringRef string) {
CFMutableStringRef newString = CFStringCreateMutableCopy(NULL, 0, string);
CFStringUppercase(newString, NULL);
return newString;
}
◆◆◆
今回の例では、アクセッサ関数からインスタンスの構造体内部へアクセスする際に、インターフェイスの参照値がインスタンスの先頭のアドレスと同一である事を利用しています。これはインスタンスに格納されている関数テーブルのポインタが一つだけの時に使えるテクニックで、二つ以上の場合は工夫が必要になります。例えば、三つの関数テーブルをインスタンスに格納しなければならない場合には、次の様にします。
〜インスタンスの型の定義文〜
typedef struct {
void *topOfInstance; // ★修正箇所★
FooPlugInInterfaceA *interfaceA; // ★修正箇所★
FooPlugInInterfaceB *interfaceB; // ★修正箇所★
FooPlugInInterfaceC *interfaceC; // ★修正箇所★
int referenceCount;
CFUUIDRef factoryID;
} MyPlugInInstance;
〜ファクトリ関数の実装文〜
void* MyPlugInFactory(CFAllocatorRef allocator, CFUUIDRef typeID) {
if (CFEqual(typeID, kFooPlugInTypeID)) {
MyPlugInInstance *instance = calloc(1, sizeof(MyPlugInInstance));
instance->topOfInstance = instance; // ★修正箇所★
instance->interfaceA = &gMyPlugInInterfaceA; // ★修正箇所★
instance->interfaceB = &gMyPlugInInterfaceB; // ★修正箇所★
instance->interfaceC = &gMyPlugInInterfaceC; // ★修正箇所★
instance->referenceCount = 1;
instance->factoryID = kMyPlugInFactoryID;
CFPlugInAddInstanceForFactory(kMyPlugInFactoryID);
return &instance->interfaceA; // ★修正箇所★
} else {
return NULL;
}
}
そして、アクセッサ関数からインスタンスの先頭のアドレスを取得するには、次の様なローカル関数を作って行えます。
〜先頭アドレスを取得するローカル関数の実装文〜
static MyPlugInInstance* GetInstanceFromInterface(void *interface) {
void **address = interface;
while (*address != address) { // topOfInstance に当たるまで繰り返す
address--;
}
return (MyPlugInInstance*)address;
}
〜アクセッサ関数の実装文〜
ULONG MyAddRef(void *thisPointer) {
MyPlugInInstance *instance = GetInstanceFromInterface(thisPointer); // ★修正箇所★
return ++instance->referenceCount; // ★修正箇所★
}
これは一つの実装例に過ぎません。状況に応じた対処方法を探してみてください。
◆◆◆

プラグインを完璧に実装出来たら、それをホストアプリケーションで読み込んでみましょう。プラグインを読み込むには、まずプラグインバンドルの CFPlugInRef を取得します。
〜CFPlugInRef 取得の実装文〜
CFPlugInRef plugIn = CFPlugInCreate(NULL, (CFURLRef)plugInURL);
そして、コンポーネントタイプを指定してファクトリを検索します。
〜ファクトリの検索の実装文〜
CFArrayRef factoryIDs = CFPlugInFindFactoriesForPlugInTypeInPlugIn(kFooPlugInTypeID, plugIn);
CFUUIDRef factoryID = CFArrayGetValueAtIndex(factoryIDs, (int)factoryIndex);
タイプに対応したファクトリが見つかったら、ファクトリにインスタンスを生成させます。するとインスタンスのインターフェイスが返されます。
〜インスタンス生成の実装文〜
IUnknownVTbl **iUnknown = CFPlugInInstanceCreate(NULL, factoryID, kFooPlugInTypeID);
このインターフェイスは型が何か分かりません。ただし全てのインターフェイスは IUnknown ベースですから、IUnknown アクセッサ関数の QueryInterface 関数を呼び出して、特定の型のインターフェイスを取得しましょう。
〜インターフェイス取得の実装文〜
FooPlugInInterface **iFoo;
CFUUIDBytes iid = CFUUIDGetUUIDBytes(kFooPlugInInterfaceID);
HRESULT queryResult = (*iUnknown)->QueryInterface(iUnknown, iid, (LPVOID*)&iFoo);
インターフェイスの取得に失敗した場合は、エラーコード E_NOINTERFACE が返されます。取得に成功したら、目当てのアクセッサ関数を呼び出しましょう。
〜アクセッサ関数の呼び出しの実装文〜
CFStringRef helloString = (*iFoo)->Hello(iFoo);
CFStringRef processedString = (*iFoo)->ProcessString(iFoo, CFSTR("Quick brown fox"));
インスタンスが不要になったら、インターフェイスを解放します。インターフェイスの解放順は、取得順に従わなくても構いません。
〜インターフェイスの解放の実装文〜
(*iFoo)->Release(iFoo);
(*iUnknown)->Release(iUnknown);
プラグインそのものが不要になったら CFPlugInRef を解放します。ただし CFPlugInRef は、プラグインのバイナリコードをアンロード出来ない状況では解放出来ません。CFPlugInRef を解放する前には正しく全てのインターフェイスを解放し、インスタンスを解放しましょう。
〜CFPlugInRef の解放の実装文〜
CFRelease(plugIn);
★ おわりに
CFPlugIn は Spotlight で採用されていたりと結構重要な技術なのですが、ドキュメントは英語のみで、国内にも情報はあまり出ていませんでした。また、英語圏でもあまり使いやすいという評判はなく、理解が難しい(扱いが面倒な?)技術という事で、実際私も英語の苦手さも加えて理解に手間取ったので、改めてドキュメントを書いてみました。非公式なドキュメントと言う事で多少の不都合は我慢してもらいたいのですが、明らかな誤りや、説明の不十分な所があれば指摘してくださると幸甚です。
また、このドキュメントでは C++ 言語での記述方法や、ファクトリ関数とタイプの動的な関連付け等については、あまり一般的ではない為、説明していません。これらについて詳しく知りたい方は、巻末の参考資料にある Plug-in ドキュメントや、Apple Mailing Lists で調べてみてください。
★ おまけ
CFPlugIn API はプラグインの動的な読み込みを行えます。しかし、アプリケーションの起動中にプラグインフォルダでプラグインを出し入れしても、その変化を動的に検出する機能は持っていません。そこで、カーネルの監視機能である kernel queue(kqueue)を使って、フォルダ内の変化を検出する方法を説明します。kqueue は Mac OS X 10.3 以降で利用出来ます。
kqueue はキュー(待ち行列)であり、その中には kevent というカーネルからのイベントの情報が並べられます。kqueue でイベントを監視して通知を受け取るには、監視対象の基準とする kevent を kqueue に登録し、時間制限付きのポーリング命令によりイベントを受信します。
〜kqueue の監視の実装文〜
// キューを作成
int queue = kqueue();
// 監視基準のイベントを作ります
struct kevent events[8];
int fileDescriptor = open((const char*)folderPath,
O_EVTONLY); // kevent 専用のファイルディスクリプタを取得します
EV_SET(&events[0], // events[0] に代入
fileDescriptor, // このフォルダを監視してね
EVFILT_VNODE, // ファイルシステム(VFS VNODE)のイベントを監視してね
EV_ADD | EV_CLEAR, // 必要あればキューでイベントを追加/消去してね
NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB |
NOTE_LINK |NOTE_RENAME | NOTE_REVOKE, // ファイルシステムイベントの全種類が対象
0,
NULL);
// 初回ポーリングします
struct timespec timeout = {10, 0}; // 10 秒の時間制限
int count = kevent(queue,
events, // 監視基準の配列
1, // 監視基準の数
events, // 受信用の配列(監視基準と同じでも良い)
sizeof(events) / sizeof(struct kevent), // 受信用の配列の容量
&timeout);
while (true) {
// ポーリング結果を処理します
if (count < 0) {break;} // カウントが負の数ならエラー
int i;
for (i = 0; i < count; i++) {
if (events[i].flags & EV_ERROR) {break;} // イベントのフラグがエラーを示す場合もあります
MyFolderDidChanged(&events[i]); // 受信した通知を処理
}
// 二回目以降のポーリングでは、監視基準を追加する必要はありません
count = kevent(queue, events, 0, events, sizeof(events) / sizeof(struct kevent), &timeout);
}
// キューとファイルディスクリプタを閉じます
close(fileDescriptor);
close(queue);
kqueue でのイベントの受信にはポーリングを行いますが、ほとんどの場合メインスレッドで常時ポーリングを行うのは非実用的です。そこで、サブスレッドを作り、そこでポーリングを行います。
〜サブスレッドの作成の実装文〜
[NSThread detachNewThreadSelector:@selector(observeMyFolderByThread:) toTarget:self withObject:(id)folderPath];
〜サブスレッドでの kqueue の監視の実装文〜
- (void)observeMyFolderByThread:(id)argument {
NSAutoreleasePool *arp = [[NSAutoreleasePool alloc] init]; // スレッド用自動解放プール
// キューの作成、監視基準の作成、初回ポーリングは
// 同じなので省略します
while (true) {
if (count < 0) {break;}
int i;
for (i = 0; i < count; i++) {
if (events[i].flags & EV_ERROR) {break;}
NSData *eventData = [[NSData alloc] initWithBytes:&events[i] length:sizeof(struct kevent)]; // ★修正箇所★
[self performSelectorOnMainThread:@selector(myFolderDidChanged:) withObject:eventData waitUntilDone:NO]; // ★修正箇所★ 受信した通知をメインスレッドで処理させます
[eventData release]; // ★修正箇所★
}
count = kevent(queue, events, 0, events, sizeof(events) / sizeof(struct kevent), &timeout);
}
close(fileDescriptor);
close(queue);
[arp release];
}
〜メインスレッドでの通知の受信の実装文〜
- (void)myFolderDidChanged:(id)argument {
struct kevent event;
[(NSData*)argument getBytes:&event length:sizeof(event)];
char folderPath[1024];
if (fcntl(event.ident, F_GETPATH, &folderPath) != -1) {
NSLog(@"%s", folderPath); // 内容が変更されたフォルダのパスを表示
}
}
★ おまけの補足
プラグインが置かれるフォルダは技術的にはどこでも構わないのですが、標準的なプラグインフォルダの一つとして、アプリケーションバンドル内の Contents/PlugIns フォルダがあります。このフォルダはバンドル内にあるのでユーザからは普段見えませんが、Finder を使ってアプリケーションの情報を見ると、「プラグイン」ペインからこのフォルダに格納されたプラグインの構成を管理出来ます。このペインは PlugIns フォルダが存在する場合のみ現れ、デフォルトでは PlugIns フォルダは存在しない事に注意してください。
〜アプリケーションバンドル内プラグインフォルダ取得の実装文〜
NSString *plugInsFolderPath = [[NSBundle mainBundle] builtInPlugInsPath];
アプリケーションバンドルとは別離にプラグインを管理させたい場合は、/Library/Application Support/アプリケーション名/PlugIns のような、Application Support 内のフォルダパスの利用が推奨されています。ユーザ毎にプラグインを管理させたい場合のフォルダパスは、~/Library/Application Support/アプリケーション名/PlugIns のようになります。
〜アプリケーションサポート内プラグインフォルダ取得の実装文〜
NSArray *appSupportFolderPaths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSAllDomainsMask, YES);
int i;
for (i = 0; i < [appSupportFolderPaths count]; i++) {
NSString *plugInsFolderPath = [[appSupportFolderPaths objectAtIndex:i] stringByAppendingPathComponent:@"FooApp/PlugIns"];
}
★ 参考資料
制作者:北島敦記 Library@K