2008年11月28日金曜日

[VC++ 2008 Express] FormのresXにリソースを追加しても消されてしまう

VC++のフォームアプリケーションで、フォームとは無関係なリソース(例えばイメージ)をそのフォームのresXに追加することはできるのですが、何かの拍子に消えてしまいます(おそらく、フォームデザイナがヘッダーファイルを生成ときのタイミングだと思います)。

VC++のフォームプロジェクトにあるフォームは、ソリューションエクスプローラー内においては、ソースコードは「<フォーム名>.h」でその下の階層に「<フォーム名>.resX」という表示になっています。このresXを開くとリソースの一覧が表示されて、編集可能となっています。そこで、イメージなどのリソースを追加できるのですが、何故か消えてしまうのです。

そこで、フォームとは別なところにリソースを定義して、プログラムで読み取ろうということですが、少なくとも「rcファイル」はmanagedなプロジェクトでは意味がありません。なぜ、Formアプリケーションのプロジェクトに存在しているかはわかりませんのは、おそらくアプリケーションのアイコンの設定するためなのだが、.NETのドキュメントを読むとrcファイルのリソースは利用できませんと書いてあります。

managedなモジュール(Assemblyといったほうが適切か…)にこの手のリソースを組み込む方法はいくつか存在するようですが、新たにresXファイルを追加してそこにリソースを定義するという方法をやってみました。

空のresXファイルを作る

プロジェクトに組み込むresXファイルを用意します。resXといっても所詮XMLファイルなので、ベタな方法として「メモ帳」で作ることにします。ただし、中身がresXの定義に則してないとIDE上で編集できないですし、ビルド時にエラーになるので、最低限の定義だけを書いておきます。

必要な部分だけ、プロジェクト内にあるresXからコピペするのですが、まず、プロジェクトにある(エラーになっていない)resXファイルをXMLエディタで開きます(コンテキストメニューから「ファイルを開くアプリケーションの選択」というのを選ぶ)。そのresXファイルから、最初の「<?xml version="1.0" encoding="utf-8"?>」から、「</resheader>」の部分まで(たぶん、「<resheader name="writer">」の対になっているのが最後のなので、そこまでをコピー、ペーストします。そのあとの行に「</root>」でrootを閉じればOKです。
ファイルは、拡張子をresXにしてUTF-8で保存します。拡張子よりも前の部分(いわゆるbasename)はプログラムで読み込むときに指定するので、わかり易いものにしておいたほうがよいでしょう。

プロジェクトに追加する

作った空のresXファイルをプロジェクトのコンテキストメニューで「追加→既存の項目」で追加すると、ソリューションエクスプローラー上でプロジェクトの下の「リソースファイル」というツリーの中に現れるはずです。そうしたら、そのresXファイルを開くと、resX用のリソース定義用のビュー(マネージリソースエディタ)が表示されます。
そこで、好きなリソースを追加してください。

リソースの読み込み

ManagedなC++のコードの中でリソースを読み込む方法ですが、まずResourceManagerでresXに定義されたリソースを読み込みます。resXファイル名のbasenameを「リソース名」ということにすると(「hoge.resX」なら「hoge」)。

//アセンブリを取得する
System::Reflection::Assembly^ assembly = System::Reflection::Assembly::GetExecutingAssembly();
//リソースを読み込む
System::Resources::ResourceManager^ resources = 
 gcnew System::Resources::ResourceManager("<アセンブリ名>.<リソース名>", assembly);

ここでの「アセンブリ名」といっているのは(この言い方が正しいかは不明)、普通はプロジェクト名になっているのでプロジェクト名にします。

あとは、ResourceManagerなどのドキュメント、サンプルを見ると使い方がわかりますが、例として、イメージを読み込む場合は以下のような感じになります。

//リソースからイメージを読み込む
System::Drawing::Bitmap^ image = (System::Drawing::Bitmap^)(resources->GetObject(L"<リソースオブジェクト名>"));

ここでの「リソースオブジェクト名」は、resXの定義で命名したものを指定します。マネージリソースエディタでイメージファイルを読み込むとファイル名のbasenameになりますが、エディタ上で変更できます。

この場合、リソースファイルを手動で追加しましたが、いわゆる国際化(カルチャー毎の設定)も対応しているはずなので、ファイル名にカルチャー名を付加したresXファイルを用意しておけば、ResourceManagerで適切に処理されるはずです。

0 件のコメント: