Visual C++で開発中にリソースが消えてしまう問題

作成:2005年12月4日

吉田誠一のホームページ   >   ソフトウェア工学   >   技術コラム   >   プログラミング

Visual C++ (VC++) を使ったWindowsソフトウェアの開発では、ダイアログ画面やアイコン、文字列などは、リソースとして作成します。

リソースは、.rcという拡張子を持つリソースファイルと、resource.hというヘッダファイルに保存されます。どちらもテキストファイルなので、メモ帳でも見ることができます。

しかし、これらのファイルを入れ換えたり、メモ帳で修正したりすると、せっかく作ったリソースが消えてしまうといった問題が発生することがあります。

C++言語のソースコードと違って、リソースは、1つのリソースファイルで一元管理するのが一般的です。チームで開発している時は、他の人が修正したリソースファイルをコピーしたり、お互いの修正内容をマージしたりすることが良くあります。このようなケースでは、特に問題が起きやすくなります。

ここでは、リソースが消えてしまう具体的な事例を紹介します。また、問題が起こる原因と、リソースを消さないための対策を示します。

目次

  1. リソースが消えてしまうケース
    1. 古いリソースに戻す時
    2. 他の人が修正したリソースで置き換える時
    3. リソースIDを書き換える時
  2. リソースを消さないための対策
    1. 何故リソースが消えるのか
    2. リソースを消さないためには、どうすれば良いのか
    3. リソースを正しく扱っているか、どうすれば分かるのか

リソースが消えてしまうケース

古いリソースに戻す時

リソースエディタで作業していると、たまに失敗してしまうことがあります。

バックアップを取っていれば、いったん元に戻してから、もう一度作業をやり直すことができます。しかし、取っておいたリソースファイルをコピーするだけでは、実は、リソースは元に戻りません。

例えば、次のような画面を作ってみましょう。

2つのボタンのある画面
2つのボタンを置いた画面を作った

とりあえず、この状態でバックアップを残しておきましょう。.rcファイルとresource.hファイルを、どこかにコピーしておきます。

次に、画面に置いてある2つのボタンを削除してしまいます。

2つのボタンのない画面
2つのボタンを削除してしまった

保存もしてしまいましたが、やっぱり2つのボタンはあった方が良いと思い直しました。

そこで、いったんワークスペースを閉じます。そして、さっき保存しておいた.rcファイルとresource.hファイルをコピーしてから、再びワークスペースを開きます。

するとどうでしょう。元に戻したはずなのに、2つのボタンが消えたままのダイアログが表示されます。

2つのボタンのない画面
元に戻しても、2つのボタンは元に戻らなかった

このまま作業を続けると、2つのボタンは消えたままになってしまいます。

この例では、画面を見てすぐに気づくことができましたが、実際の開発では、たくさんのリソースを作るので、気がつかないことも良くあります。ファイルをコピーしただけで、元に戻ったように思い込むのは危険です。

他の人が修正したリソースで置き換える時

2人のチームで1つのソフトウェアを開発しているとしましょう。あなたと、もう1人のチームメイトが、それぞれ1つずつ画面を作ります。

リソースファイルを同時に修正するのは危険なので、まず、チームメイトが先に画面のリソースを作ることにしました。あなたは、チームメイトがリソースを作り終わるのをただ待つのではなく、リソースエディタを使って、自分が作る画面のレイアウトを、いろいろと試してみることになるでしょう。

しばらくして、チームメイトの作業が終わりました。そこであなたは、チームメイトが修正したリソースファイルをもらって、自分の開発環境にコピーします。そしてワークスペースを開きます。

・・・これは、チームでWindowsソフトウェアを開発するケースは、たいへん良くある場面です。しかしこれは、前述のバックアップをコピーして元に戻す場合と、まったく同じ状況になっています。

そのため、ワークスペースを開いてみても、画面には、チームメイトが作った画面は表示されないでしょう。代わりに、さっきまで自分が試していた作りかけの画面が、そのまま表示されてしまうでしょう。

もちろん、このまま作業を続けたら、あなたは、チームメイトがせっかく作った画面を消してしまうことになってしまいます。

チームでリソースを修正する流れ

たとえ、チームメイトの作業が終わるまで、自分ではリソースをいじらないつもりでも、油断はできません。

C++言語のソースファイルであれば、誤って書き換えてしまっても、編集の「やり直し」をすれば、ファイルは更新されません。しかしリソースは、「やり直し」をしても、修正をしなかったことにはなりません。閉じる時に「保存しますか?」と訊かれて、無意識のうちに保存してしまっているかもしれません。

リソースIDを書き換える時

resource.hには、リソースエディタで作ったリソースの識別子(リソースID)と、対応する番号が書かれています。

Visual C++は、リソースIDに自動的に番号を割り当ててくれます。しかし、この番号を自分で書き換えたいこともあります。

例えば、メッセージマップでON_COMMAND_RANGE、ON_UPDATE_COMMAND_UI_RANGE、ON_CONTROL_RANGEを使って、複数のリソースに対するメッセージ処理を1つの関数で記述したい場合は、リソースIDに連続した番号を割り当てます。これは、自分で書き換える必要があります。

resource.hを書き換えて保存すると、たいてい、次のようなメッセージが表示されます。

resource.h
このファイルはMicrosoft Developer Studio以外で修正されています。○○.rcを再ロードしますか?

このメッセージはたいへん分かりにくいです。まず、Developer Studioを使っているのに、なぜか「Developer Studio以外で修正されています」と言われてしまいます。また、書き換えたのはresource.hであり、.rcファイルは修正していないので、「○○.rcを再ロード」する必要は無いように思えます。

ここで「はい」を選んで、.rcファイルを再ロードすると、せっかく開いていたダイアログ画面などはすべて閉じられてしまいます。.rcファイルは修正していないので、ここは「いいえ」を選んでも良さそうに思えます。

「いいえ」を選ぶと、リソースエディタでの作業をそのまま続けることができます。作業が終わったら、リソースファイルを保存してみて下さい。

するとどうでしょう。さきほど書き換えたはずのresource.hの番号が、また元に戻ってしまっています。

リソースを消さないための対策

何故リソースが消えるのか

リソースが消えてしまう問題は、どうして起こったのでしょうか?

リソースは、.rcという拡張子を持つリソースファイルと、resource.hというヘッダファイルに保存されます。しかし、これだけではありません。実は、.apsという拡張子を持つ、Visual C++がワークスペースを管理するためのファイルの1つにも、リソースの内容は保存されているのです。

.rcファイルやresource.hファイルを置き換えると、.apsファイルと内容が食い違ってしまいます。

.rcファイルやresource.hファイルの方が新しければ、Visual C++はその内容を.apsファイルに上書きするので、問題は起こりません。しかし、古いファイルに戻した場合のように、.apsファイルの方が新しい場合は、Visual C++は、.apsファイルの内容を読み込んでしまいます。

.rcファイルやresource.hファイルを修正した時は、.apsファイルも作り直さなくてはいけないのです。

リソースIDを書き換える時も同様です。resource.hを修正するだけではなく、.apsファイルも修正する必要があります。実は、「○○.rcを再ロードしますか?」というメッセージは、「.apsファイルの内容も書き換えますか?」という意味だったのです。「はい」を選ぶと、古い.apsファイルは削除されます。

リソースを消さないためには、どうすれば良いのか

.rcファイルやresource.hファイルを置き換えた時に、これらのファイルのタイムスタンプが、.apsファイルのタイムスタンプより新しくなるようにすれば、問題は起こりません。

ですが、ファイルを置き換えた時に、.apsファイルを削除してしまうのが、最も確実です。

リソースIDを書き換えた時は、「○○.rcを再ロードしますか?」というメッセージに対して、必ず「はい」を選ぶようにして下さい。

リソースを正しく扱っているか、どうすれば分かるのか

これまで述べたように、間違った方法でリソースファイルをコピーしたり、リソースIDを書き換えたりしていると、いつかリソースを消してしまいかねません。

リソースファイルの扱い方を間違えている時は、その兆候は、resource.hの番号に表れます。

resource.hには、リソースIDの番号が書かれています。この番号は、リソースを作るたびに1ずつ増えていきます。ふつうは、番号は重複することはありません。

#define IDC_BUTTON1             1000
#define IDC_BUTTON2             1001
#define IDC_BUTTON3             1002
#define IDC_BUTTON_A            1003
#define IDC_BUTTON_B            1004
          

しかし、.rcファイルやresource.hファイルの内容と、.apsファイルの内容に食い違いが生じると、複数のリソースIDに重複する番号が割り当てられることが多くなります。

#define IDC_BUTTON1             1000
#define IDC_BUTTON_A            1000
#define IDC_BUTTON2             1001
#define IDC_BUTTON_B            1001
#define IDC_BUTTON3             1002
          

resource.hの番号が重複している時は、リソースファイルの扱い方を間違えていると思って、注意する必要があります。

「てるぼう」と「やん茶姫」の著作権は「まる」氏にあります。

Copyright(C) Seiichi Yoshida ( comet@aerith.net ). All rights reserved.