Xcodeへの道-CW7からXcode1.5へ、そしてCarbonとResEditとともにMach-Oへ-
CWの発売停止

これはショックだった。個人的にはパッケージ型アプリが嫌い(初期設定ファイルもない、単一ファイルのアプリが好み)でCFM形式にこだわってきたのに、これでCFM形式のアプリを作る道が閉ざされたわけだ。
今後最新OSに対応するのがXcodeだけになれば、C言語+ToolBox(Carbon)でやってきた人はMach-O形式のパッケージ型アプリを作るしかなくなる。そして必然的にClassicアプリを作ることもできなくなる。
私はC言語しかできない。しかもうまくいかないと、根拠もない怪しいキャストや*や&を適当につけて、それでコンパイラが通してくれたらラッキーと思うすさまじいレベルである。(そのあとデバッガごと落ちたりする。)おまけに頭の中はBASIC的思考なので、どんな初心者向けのOOP系教科書でも2ページも読めば頭が朦朧としてくる。これではCocoaの習得などとうてい無理である。
しかし、ネットや書籍を眺めてもどうもCocoaが優勢なようで、Carbonでやっている人間は少数派になりつつあるのかと心配になる。とはいえ、ToolBox関数とResEdit式リソースこそMacの原点であり、故郷であり、未来に伝えるべき伝統芸能である。(本当か?)
というわけで、CでCarbonでしかもnibが使えずに.rsrc一本槍で、それでXcodeの世界へ行けるものかどうかを書き記すのも案外意味があるかと思い、気づいたTipsをメモしておく。

CW7(8でも9でもない)からXcode1.5への移植

こうなってしまってはXcodeに泣く泣く移行するしかないわけだが、以下に多くの日曜プログラマーがつまづいているのではと推測される移行上の問題点をいくつか。

(1)自分自身のリソースに書き込めない
 私の作ったフリーウェアのほとんどはアプリ自身の古典的(つまりResEditで編集可能な)リソースを書き換えることで設定を保存している。(もちろん、こういう方法は推奨されないが。)
今回、Mach-O形式のアプリを作ってみると、なんと、組み込んだ.rsrcファイルはアプリ名を冠した.rsrcファイルに変換され、しかもその中身はデータフォークに変換されてしまう。そのためか、リソースのデータ(たとえば'STR ')を読み込むことはできるが書き込めないのである。ここでけっこう悩んでしまったのだが、以下の奇策でなんとか見かけ上は解決した。

Mach-O形式では、.rsrcはデータフォーク化されて(アプリ名).rsrcになり、パッケージ内のResourcesフォルダ内に格納される。このファイルはResEditで編集できないだけでなく、Resource Manager APIの従来のアクセス方法で読み込むことはできるが、書き込めない。(いや、本当は何か方法があるのかも知れないが、私はついに見つけられなかった。)
そこで、書き込みの必要なリソースデータについては、別の通常の書き込み可能な.rsrcファイルを作ってResourcesフォルダ内にファインダー操作で外から持ち込んで使うことにした。
以下にパッケージ内のResourcesフォルダ内の.rsrcファイルへのアクセス方法例を記す。
リソース書き換え用の古典的リソースファイルを例えばmyAppdata.rsrcとすると、

extern short refN;//アプリ起動時に得た主リソースファイルのreference(externとは限らないが)
extern short dataresrefN;//データ書き込み用リソースファイルのreference(externとは限らないが)
short MyOpenUseDataRes()
{
 CFURLRef theURL;
 FSRef resref;
 FSSpec resspec;
 OSErr err;

theURL=CFBundleCopyResourceURL(CFBundleGetMainBundle(),CFSTR("myAppdata"),CFSTR("rsrc"),NULL);
 CFURLGetFSRef(theURL,&resref);
 CFRelease(theURL);//データ用のファイルがないとここで爆弾が出る
 err=FSGetCatalogInfo(&resref,kFSCatInfoNone,NULL,NULL,&resspec,NULL);
 dataresrefN=FSpOpenResFile(&resspec,0);
 UseResFile(dataresrefN);
 //エラー処理は略
 return 0;
}
このあと通常のResource Manager APIで読み出しや書き込みをして、
最後に閉じるのもお忘れなく。
void MyCloseDataRes()
{
 CloseResFile(dataresrefN);
 if(CurResFile()!=refN)UseResFile(refN);
 //エラー処理は略
}
また、この方法だと、持ち込んだmyAppdata.rsrcがターゲットをcleanするたびに消去されてしまうので、そのつどバックアップコピーから持ち込みなおさねばならないのが面倒ではある。

(2) ProjectのテンプレートでCarbon Applicationを選択したのに、Projectに登録した旧来のリソースファイル(ResEditで編集できる.rsrc)をアプリ.rsrcのデータフォークに組み込んでくれない。

これはXcode 1.5 では設定をいじる必要があって、それを含めて自分が移行の際に行った具体的な手順を以下に記す。

Xcode 1.5 において、
Fileメニュー→New Project...でCarbon Application選択
Projectメニュー→New Build Phase→New ResourceManager Resources Build Phaseを選択するとはじめて.rsrcや.rがアプリに取り込み可能になる。(デフォルトではならない。)
CWのソースファイル、ヘッダファイル、リソースファイルをProjectメニュー→Add to Project ...ですべて登録。(Text Encodingは全て「日本語(MacOS)」でよい。ただし、私の.cや.h内には2バイト文字はない。)
Group&FilesのウィンドウでProject名を選択し、右のウィンドウで、テンプレート由来の不要なファイル(main.cやmain.nibなど)や不要なframeworkのチェック(右端)をはずす.(Carbon.frameworkは最低限必要)
Build & Goしたあと、buildフォルダ→アプリ→control+クリックで「パッケージの内容を表示」→Resourcesフォルダ内にリソースデータ書き込み用の.rsrcファイルを置く。

(3) 日本語が表示できない。(リソースでもっている日本語のメニューや文字列が文字化けしまくる)

以下に日本語表示させるための操作を記す。(ちょっと間違っているかも)

Group&FilesのウィンドウでmyApp(例).rsrcを選択しinfoボタンクリック
File "myApp(例).rsrc" Infoウィンドウ開く
Make File Localizableボタンクリック
Add Localization...ボタンをクリック
Enter the name of the new localization:でJapaneseを選択してAdd

これで日本語表示可能になるが、もっと簡単な方法は、アプリパッケージの中のResourcesフォルダ内のEnglish.lprojフォルダをそのまま複製してその複製したフォルダにJapanese.lprojという名前をつけただけでとりあえず日本語表示ができるようにはなる。(日本でしか使わないアプリならこれでもよいか。)ただし、このやりかたではターゲットをcleanするたびにJapanese.lprojが消えてしまうので不便ではある。

(4) 操作方法に誤りがあるのか、Resourcesフォルダの中に何故かヘッダファイルのコピー?が散らばっていることがあるが、これらは削除してもちゃんとアプリは動作する。(ヘッダのいくつかが何故かTargetのBundle Resourcesに紛れ込んで登録されていたので、それを消したら直ったが、何故紛れ込んだのか原因不明)

ソースの移植

CW7のソースコード自体はXcode1.5でほとんどエラーなくビルドできた。(#include<>でエラーになるものを消し、一部にキャストの有無が問題になる箇所があった程度。)
ただ、Project Builderでは#include <Carbon/Carbon.h>を書き忘れるとエラーが一杯出て一瞬慌てる。(CWやXcodeではコードに意図的に記述しなくてもよいようだったが。)
また、例えばQuickTimeのAPIを使うとすれば、External Frameworks and Librariesを選択してコンテキストメニューから操作してQuickTime Frameworkをaddする必要があり、ソースにも
#include <QuickTime/QuickTimeComponents.h>
の記述が必要。(自分のフリーウェアSimple ResizeXの場合。)
また、CW由来のCarbon.rでは、以下の2行はコメントアウトする必要があった。
//#include <Types.r>
//#include <SysTypes.r>
こうしないとビルド時にProject Builderでは原因が明示されずにエラーになる。Xcodeでは明示されてエラーになる。
(注: この記事中、htmlで書く関係でかっこが全角になってますが、本当はもちろん半角です。)

その他の注意

ZeroLinkは日曜プログラマーには不要の機能と思われ、トラブル回避のため、DevelopmentビルドスタイルでもZeroLinkのチェックを忘れずにはずしておく。(プロジェクト名選択→Info→styles)
(ビルドスタイルでのZeroLink設定はターゲットのZeroLink設定に優先する)
ZeroLinkをoffにして、さらにターゲットのcleanをしてから、再ビルドする。さもないと、開発したマシン上でしか動かないアプリがビルドされてしまう。
(なお、完成品を作るDeploymentビルドスタイルではoffの筈。)
Project BuilderやXcodeではビルドスタイルなどの設定を変更したり、ソースファイルの構成を変えたりしたあとは、こまめにターゲットのcleanをしてから再ビルドしないと変更が反映しないので注意。
あと、ターゲット名に「.」が入っているとエラーになる。ブランクは大丈夫のようだ。

Info.plistについて

Info.plistは、アプリケーションのアイコン、どんな種類のファイルが開けるか(ドラッグ&ドロップを受け付けるか)、ドキュメントアイコンなどを指定するUTF-8のファイルで、パッケージ内に格納されている。(内容の変更は再起動後に反映されるようだ。)

Info.plistの編集方法

ターゲット選択→Properties→Open Info.plist as Fileで開いて、デフォルトのを消して、あらかじめテキストエディットなどで作っておいたテキストエンコーディングUTF-8のInfo.plistテキストをコピーする→(閉じる時)保存

以下に実例としてSimple ResizeXのXcode移植実験に使っているInfo.plistを示す。
注: htmlでの表示のため、かっこは全角で表記していますが、本当はもちろん全て半角です。
----
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">  //このへんまではさわる必要なし
<dict>
 <key>CFBundleDevelopmentRegion</key>
 <string>Japanese</string>   //日本がメインのアプリ
 <key>CFBundleDocumentTypes</key>
 <array>
  <dict>
   <key>CFBundleTypeIconFile</key>
   <string>PICT.icns</string>   //PICTファイルのドキュメントアイコンファイルはPICT.icnsという名前

   <key>CFBundleTypeName</key>
   <string>PICT image</string>   //情報で表示される文字列
   <key>CFBundleTypeOSTypes</key>
   <array>
    <string>PICT</string>   //ファイルタイプ
   </array>
   <key>CFBundleTypeRole</key>
   <string>Editor</string>   //編集やセーブ可能ならEditor
   <key>CFBundleTypeSuffixes</key>   //読み込めるファイルの拡張子
   <array>
    <string>pict</string>
   </array>
  </dict>
  <dict>
   <key>CFBundleTypeIconFile</key>
   <string>JPEG.icns</string>
   <key>CFBundleTypeName</key>
   <string>JPEG image</string>
   <key>CFBundleTypeOSTypes</key>
   <array>
    <string>JPEG</string>
   </array>
   <key>CFBundleTypeRole</key>
   <string>Editor</string>
   <key>CFBundleTypeSuffixes</key>
   <array>
    <string>jpg</string>
    <string>jpeg</string>   //2種類ありうる
   </array>
  </dict>
              --------(中略)-------------
  <dict>
   <key>CFBundleTypeIconFile</key>
   <string>PDF.icns</string>    //Viewerなのでこれは不要か
   <key>CFBundleTypeName</key>
   <string>PDF image</string>
   <key>CFBundleTypeOSTypes</key>
   <array>
    <string>PDF </string>
   </array>
   <key>CFBundleTypeRole</key>
   <string>Viewer</string>   //PDF は開くだけなのでViewer
   <key>CFBundleTypeSuffixes</key>
   <array>
    <string>pdf</string>
   </array>
  </dict>
 </array>
 <key>CFBundleExecutable</key>
 <string>Simple ResizeX</string>   //実行ファイルの名称
 <key>CFBundleIconFile</key>
 <string>SimpleResizeX.icns</string>   //アプリのアイコンのファイル名
 <key>CFBundleIdentifier</key>
 <string>soft.deracine.ReSX211m</string>   //Cocoaでは初期設定ファイル(.plist)の同定に必要だがCarbonでは???
 <key>CFBundleInfoDictionaryVersion</key>
 <string>6.0</string>  //意味不明だがこれでよいらしい
 <key>CFBundleName</key>
 <string>Simple ResizeX</string>   //アプリの名称
 <key>CFBundlePackageType</key>
 <string>APPL</string>   //アプリケーションであることを示す
 <key>CFBundleShortVersionString</key>
 <string>2.11m</string>   //アプリのバージョンでよいらしい
 <key>CFBundleSignature</key>
 <string>ReSX</string>   //固有のクリエイタ
 <key>CFBundleVersion</key>
 <string>2.11m</string>   //まだ私には意味不明
 <key>CSResourcesFileMapped</key>   //リソースの展開様式にかかわるらしい
 <true/>
</dict>
</plist>
----------
なお、Info.plist内の記述では.icnsの拡張子まで記載する必要がある。(例 SimpleResizeX.icns)

 Info.plistの記述は、相同内容の旧式リソースが併存していてもそれらに優先するようだ。(ドキュメントアイコンの指定など)
Info.plist内に記載があれば、ResEdit式リソースの'vers','open','FREF','BNDL','kind'リソースが同時に存在していたとしても、それらに優先する。
自分の実験ではResEdit式リソースのうち、少なくとも'vers',オーナーリソース(クリエイタ名のリソース),'FREF','BNDL',アプリアイコン系のアイコンリソースはInfo.plistに記載があれば削除可能であった。
'vers'リソースで持っていたコメントを表示させる機能がどこにあるのかもう一つはっきりしないが、Resourcesフォルダ内の Japanese.lpoj内の InfoPlist.strings の記述で、
CFBundleGetInfoString = "おみくじ自作ソフト";
とか記述すると、それが「情報をみる」メニューで表示される情報ウィンドウのバージョンの位置に表示される。(日本語も可能で、再起動後に表示可能になる。)

 あと、コンパイル後にアプリの名前などを変更したいことはよくある。
CFM形式のアプリでは、ファインダー上のファイル名がアプリケーションメニューのタイトルと「...を隠す」「...を終了」のアプリ名になるが、Mach-O形式のアプリではそれらはResourcesフォルダ内のJapanese.lprojフォルダ内のInfoPlist.stringsのCFBundleName = "アプリ名";の記載で決まる。
ファイルリストでInfoPlist.stringsを選択し、infoボタンをクリックしてダイアログを開き、前述した.rsrcの日本語追加操作と同じ操作をおこなうとJapanese用のInfoPlist.stringsができるので、それをダブルクリックで開いて、たとえば、
CFBundleName = "おMyくじ(猫式)X";
と書き直せばアプリケーションメニューのタイトルと「...を隠す」「...を終了」のアプリ名をおMyくじ(猫式)Xに変えられる。(これもコンパイル後の簡便法としてはEnglish.lprojフォルダ内のinfoPlist.stringsをコピーしてJapanese.lprojフォルダに入れてテキストエディットで編集してもよいわけだが、それではcleanすると消えてしまうと思う。)

また、Info.plistのCFBundleExecutableの記述とMacOSフォルダ内の実行ファイル名とResourcesフォルダ内の.rsrcの名の三つが一致しないと起動しない。必要性はほとんどないが、これらの名をどうしても変更したい場合は、三つを同じ名前に変更すれば、再起動後、多分起動すると思う。一方、ファインダー上のアプリのファイル名(.appフォルダの名)は自由に変更可能である。

Info.plistに記述する.icnsファイルの作り方

まず128X128ドットに収まる絵を描く。
Photoshop(のお試し版など)でカンバスカラーが透明の128X128pixelファイルを作って、そこへ元絵をペーストし、アイコンの絵の周囲の部分を取り除いて透明にしアイコンの境界を持つ絵を作り.psdファイルとして保存する。
そのファイルをIcon ComposerのThumbnail(128X128)の枠内へドラッグ&ドロップして、あとはそのThumbnailの絵を上の小さいアイコン枠にドラッグ&ドロップして順次小さいアイコンとそのマスクを作り、Save As ...で任意の.icnsファイルにすればよい。

パッケージで使う.icnsファイルは、アプリケーションアイコンもドキュメントアイコンもすべてプロジェクト内に置き、Projectメニュー→Add to Projectで登録しておく。これらはビルド時に自動的にパッケージのResourcesフォルダ内へコピーされる。


Carbonへの道-An Opaque Road To Carbon-