MASATOの開発日記


前の開発日記 次の開発日記 一覧

2002/09/07

Windowsアプリケーションの開発環境を、Visual C++ 6.0からVisual C++.NETに移しつつあります。 Visual C++.NETは、重いということを除けば、悪くないです。 不具合は皆無では無いですが、Visual C++ 6.0も不具合はあったので、 そんな不自由になった気はしません。使い勝手はどちらかというと向上しています。

人柱も兼ねて、プロジェクトも6.0から.NETに移行しています。 すんなり移行できたらネタは無かったのですが、実際には色々と問題が発生しました。 それが今日の話題です。 正直、人柱になっても良い覚悟が無ければ、移行は止めたほうが良いです。気をつけましょう。

(2002/09/12) Visual C++.NETではポインタが64bitになっていたというのは誤りでしたので削除しました。

Win9x環境で実行したときにファイルダイアログが表示されない不具合について

環境Visual C++.NET

Visual C++.NETで作成したMFCアプリケーションにおいて、 CFileDialogを用いてファイルダイアログを表示しようとしているとき、 Win2000やWinXPでは正常に表示されるのですが、Win9X系では表示されないという問題が発生することがあります。
特に、Visual C++ 6.0時代のソースコードをVisual C++.NETで使いまわしている場合によく発生します。

色々MFC内のソースを追いかけていって分かった結果ですが、 以下の条件が両方ともそろったときに、Win9X系でファイルダイアログが表示されないという 問題が発生するようです。

最初からVisual C++.NETで作成したMFCアプリケーションは、StdAfx.hにおいて WINVER等の定数が0x0400台に設定されています。 つまり(条件1)が満たされていないので、問題は発生しません。

しかし、Visual C++ 6.0で作成したMFCアプリケーションでは、問題が発生する可能性は高いです。 まず、手動で設定していないかぎり、WINVER等の定数は何も設定されていません。結果、(条件1)は満たされます。
次に、Visual C++ 6.0ではCFileDialogのコンストラクタの引数は6つしまありません。 よって、第7引数はデフォルト値が使用され、(条件2)も満たされます。

尚、MFCフレームワーク内のCFileDialog(例えばメニューから開くを選択すると使用されるような)は、 第7引数にデフォルト値が使われていないので、(条件2)が満たされず、問題は発生しません。

次に、上の(条件1)と(条件2)が満たされていると、何故ファイルダイアログが表示されないか説明します。

最後に、ファイルダイアログを正常に表示する方法を説明します。

1つ目の方法は、条件1を崩します。
つまり、全てのコンパイル単位の先頭でWINVERを0x0400に設定してしまえば良いです。 よって、StdAfx.hに、

#ifndef WINVER
#define WINVER 0x0400
#endif

と入れれば完了です。
_WIN32_WINNTは、WINVERを設定しておけば勝手に設定されるようなので、 個別に設定しなくとも構いません。(もちろんしても構いません)

これは、いつも古いファイルダイアログを表示することによって、 どんな環境でもファイルダイアログを表示しようというアプローチです。 これにより、ファイルダイアログが表示されないという最悪の問題は回避できます。
しかし、新しいファイルダイアログの方が多機能ですので、新しい方が使える環境ならば使いたいと思うかもしれません。 そういったときは、2つ目の方法を使いましょう。

2つ目の方法は、条件2を崩します。
CFileDialogのコンストラクタの第7引数に、デフォルト値ではなく適切な値を設定します。 これは、ソースコード中の全てのCFileDialogのコンストラクタ呼び出しを修正する必要があるので、 1つ目の方法と比べて多少面倒です。
適切な値は0です。0に設定することにより、CFileDialogはOSの自動判別を行い、 Win9xならば古いファイルダイアログを表示し、Win2000以降ならば、新しいファイルダイアログを 表示します。

以下に一例を示します。

CFileDialog(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, NULL, NULL, 0);

但し、第7引数を0にすれば自動判別されるという記述は、私が探した範囲ではドキュメント内にありませんでした。 この情報は、CFileDialogのコンストラクタのソースからしか得られません。 というわけでちょっと不安ですが、現状これがベストの方法ではないかと思います。

以下、延々と愚痴が続きます。 CFileDialogがしっかりと機能しないと、ファイルを指定して何かするようなアプリケーションの機能の一つが潰れる可能性があります。 ファイルを指定するところから全てが始まるアプリケーションであれば、アプリケーション自体が機能しないことさえあるでしょう。 私も、この問題が原因となってWin9xでは役に立たないアプリケーションをリリースしてしまったことがあります。
しかし、例えばファイルダイアログが表示されるのが少しくらい遅れても、(数十ミリ秒程度であれば) ユーザーにとってほとんど気になりません。 また、ファイルダイアログは、数百回も表示されるようなものではないので、 アプリケーション全体の処理時間にもほとんど影響しないでしょう。 つまり、ファイルダイアログは、少しくらい表示されるのに時間がかかっても、 確実に表示するべきものであるはずです。それであれば、CFileDialogのデフォルト動作として 最善の案は、自動判別であり、次善の案は、古いバージョンの強制使用になると思います。 しかし、何故デフォルト動作が、新しいバージョンの強制使用になっているのでしょうか・・・。 実は自動判別に欠点があるとか、自動判別に異様に時間がかかるのか・・・ でも、MFCのフレームワーク内でCFileDialogを使うときは、普通にコンストラクタの第7引数を0にして使っています。 ですので、第7引数を0にしてもそれほど問題があるとも思えません。 それともWin9X系はMicrosoftに切り捨てられる運命にあるのでしょうか。これが一番説得力がありそうです。ううむ・・・
(もちろん実際の所はVisual C++ 6.0のプロジェクトをVisual C++.NETに変換する機能の検討不足・テスト不足でしょう)

(2003/05/02) 推敲

前の開発日記 次の開発日記 一覧