Contextual Menu PlugIn for Mac OS X (2002/12/31)


 MacTech の記事に触発され、Mac OS X 用の Contextual Menu PlugIn を作ってみることを思い立ちました。ぶっちゃけた話、かつて作った MacOS 7.6〜9.x 上で動作する FSSpecCMMPlugIn の移植をしてみよう、と思ったわけです。
 まぁ当初から Mac OS X 上では FSSpecCMPlugIn は殆ど役に立たないとは思ってはいましたが、やっぱり役に立ちませんでしたね。まず Carbon では FSSpec ではなくて FSRef を使うこと、Full Path や Touch の機能は Terminal があれば事足りますし。
 Full Path を取得する方法も、従来の方式では /Volumes を含む正確な Full Path は取得出来ないみたいです。Carbon では FSRefMakePath() を使えば取得できるのですが、これは全 Path 名の長さに制限があるみたいですし。旧来の MacOS 用では完全無制限に動作したのですが、今回は1つのパスの長さは 1024 文字までに制限することにしました。無念。
 成果物は近いうちにここで公開する予定です。

 Contextual Menu PlugIn の作り方自体はおおよそ理解できたので、その時の作業内容を私自身の備忘録として、そしてこれから Contexutual Menu PlugIn を作ろうという人の参考になればと思い、作成方法を書いてみました。大抵の場合コード自体は見れば何をすれば良いかわかるのに対し Project の設定はわかりづらいと思うのでその辺を中心に書いてみました。
 みなさんのご参考になれば。。。


    ■プロジェクトの作成■

  1. 新規プロジェクトで、Bundle->CFPluginBundle を選択
  2. プロジェクト名を適当につける

    ■Info.plist の情報の修正■

  3. 「プロジェクトメニュー」から「アクティブターゲットを編集」を選択
  4. 「Info.plist のエントリ」の「詳細設定ビュー」を選択
  5. プロパティーリストの「CFPlubInFactories」を開く(▲をクリック)
  6. Terminal を起動し、コマンドラインから「uuidgen (/usr/bin/uuidgen)」を実行する。
  7. 6 で表示された XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX をコピーする
  8. 7 でコピーした値を、5 で開いた所のプロパティーリストにペーストする (もともと 00000000-0000-0000-000000000000 になっている欄にペーストして上書きする)。ちなみに、この欄の「値」に入っている「MyFactoryFunction」が、プラグインのエントリポイント(関数名)になります。
  9. プロパティーリストの「CFPlugInTypes」を開く(▲をクリック)
  10. 9 で開いたプロパティーリストの値に「2F6522E9-3E66-11D5-80A7-003065B300BC」入れる。この値は /System/Library/Frameworks/Carbon.framework/Frameworks/HIToolbox.framework/Headers/Menus.h で kContextualMenuTypeID として定義されている定数で Contexutual Menu PlugIn 固有の UUID であり、全ての Contexutal Menu PlugIn で共通です。
  11. 10 で入力したプロパティを更に開く (▲をクリック)
  12. プロパティリストに「0」が入っているのを確認し、その値に 7 でコピーした UUID をペーストする (もともと 00000000-0000-0000-000000000000 になっている欄にペーストして上書きする)。
  13. ここまでの作業で、Info.plist は以下のようになります。
    	(中略)
    	▼ CFPlugInFactories                辞書      X 1 キー/値ペア(グレー表示)
    	       XXXXXXXX-XXXX-XXXX-XXXX.... 文字列    X MyFactoryFunction
    	▼ CFPlugInFactories                辞書      X 1 キー/値ペア(グレー表示)
    	    ▼ 2F6522E9-3E66-11D5-80A7.... 配列      X 1 個の順序付きオブジェクト(グレー表示)
    	          0                         文字列    X XXXXXXXX-XXXX-XXXX-XXXX....
    	   CFPlugInUnloadFuntion            文字列    X
    	   		
  14. その他のプロパティリストの設定をする。左の「Info.plistのエントリ」の「シンプルビュー」を選択(クリック)する。
  15. 基本情報の識別子に <ドメイン名の逆順>.<プロジェクト名> を書き込む。(例 : jp.somedomain.CMPlugInName) まぁ、「ドメイン名の逆順」の所だけ合っていれば、後はドメイン内で重複がないようにすればいいだけなので、何でも構わないです。
    え?ドメイン名持っていないんですか?この際ですから持ちましょう(笑)
  16. 「????」になっているシグネチャに「Plug」を書き込む
  17. 「情報を表示」の「表示名」にプロジェクト名を書き込む
  18. 「情報文字列」にプロジェクト名とバージョンを半角スペースで区切って書き込む (例 : CMPlugInName 0.0.1d1)
  19. 「簡潔な形式のバージョン」に、18 と同じ文字列を書き込む

    ■ビルドの設定の修正■

  20. ビルドの設定を修正するため、左側の「設定」をクリックする。
  21. 「インストール先」を「なし(インストールしない)」にする
  22. 「GCCコンパイラの設定」の「その他の C コンパイラフラグ」の欄に「-fpascal-strings」を追加する。これで Pascal String を使用できるようになります。
  23. 最下段の「ビルド設定」の「WRAPPER_EXTENSION」の値を「bundle」から「plugin」に修正する
  24. ターゲットの設定を保存して閉じる

    ■Framework の追加■

  25. 「プロジェクト」メニューから「フレームワークを追加...」を選択
  26. CarbonFramewok を追加

    ■ソースの修正■

  27. main.c を開く
  28. 7 でコピーした UUID を main.c の中の適当な場所にコピーする。
  29. 26 でペーストした値を、8bit の 16 進数の値をカンマで区切った形になるように編集する。
  30. 27 で編集した値が CFUUIDGetConstantUUIDWithBytes() の引数になるようにし、更にそれをマクロとして定義する例えば UUID が 12345678-9ABC-DEF0-1234-56789ABCDEF0 の場合、以下のようにする。
    	//	12345678-9ABC-DEF0-1234-56789ABCDEF0
    	#define	kMyFactoryFunctionID	(CFUUIDGetConstantUUIDWithBytes( NULL, 	0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0 ) )
    			
  31. ContextualMenuInterfaceStruct 構造体のインスタンスをグローバル領域に作成する。この構造体には QueryInterface, AddRef, Release, ExamineContext, HandleSelection, PostMenuCleanup のメンバがありますが、これらはいずれも関数ポインタであり、それぞれ適合する関数を自前で定義し、その関数ポインタをメンバの値として設定する必要があります。
    ContextualMenuInterfaceStruct 構造体は Menus.h で定義されている構造体で、Microsoft の COM の IUnknown インターフェイスに適合させるための構造体のようです。MacOS 9 までの Contextual Menu PlugIn は IBM の SOM Object でしたが、Mac OS X では COM (OLE?) になったようです。
    また、Mac OS 9 までは全てを C++ で記述することが出来ましたが、Mac OS X の Contextual Menu では、現状 (少なくとも MacOS 10.2.2) の Project Builder を用いる限り、インターフェイスの部分は必ず C で記述しなければならないようです。
    CoreFoundation.framework/Headers/CFPlugInCOM.h には、IUnknown のベースクラスの宣言がありますが、ProjectBuilder からはうまく使えないみたいです。
  32. 以下の構造体を作成する。構造体自体の名前は何でも良い。UInt32 refCount メンバの後に、必要に応じてプラグイン内で使うメンバ変数を追加する (昔の MacOS 用のソースを Mac OS X 用に移植している場合は、SOM のインタフェイスになっているクラスのメンバ変数をここに追加する)
    	typedef	struct	myCMPlugInType	myCMPlugInType;
    	struct	myCMPlugInType
    	{
    		ContextualMenuInterfaceStruct *myCMInterfaceTable;
    		CFUUIDRef                     factoryID;
    		UInt32                        refCount;
    		PrivateDataType               *privateDataBuf;
    		UInt32                        privateObjCount;
    	};
    			
  33. QueryInterface, AddRef, Release, ExamineContext, HandleSelection, PostMenuCleanup の各関数と、プラグインのエントリポイントとなる関数となる MyFactoryFunction 関数(8 を参照) を実装する。
  34. その他、プラグインの実際の仕事内容をインプリメントする
  35. プラグインのビルドが終わったら、build フォルダの中に出来ている <Bundle名>.plugin フォルダを ‾/Library/Contextual Menu Items にコピーし、Finder を再起動する
  36. 使ってみて OK ならお終い

 ただ、Contextual Menu から Dialog を出すと OK ボタンをクリックした後に Finder が異常終了することがあるみたいです。何が悪いのかなぁ?詳しくはわかりません。

以上


戻る