開発環境
Win XP Pro SP3
VC++ 2005 SP1 MFC
ツリービューに、指定したフォルダのツリーを表示させ、
選択すると絶対パスやファイルサイズを表示させるアプリを作成しています。
サイズ表示時、選択したフォルダの全ファイルのサイズを調べて
加算するという形をとっているのですが、その方法ですとCドライブなどを選択した際、
異常に時間がかかってしまい、アプリが止まったようになってしまいます。
処理の軽量化を考えているのですが良い方法が思いつきません。
APIなどでフォルダのサイズを一括取得してくるようなものがあれば・・・
と思いましたが良いものが見当たりません。
Windowsのエクスプローラのように、ファイルサイズを素早く表示させる方法を
どなたかご教授頂けないでしょうか?
ちなみに、現在は以下のようなコードでフォルダサイズを取得しています。
ここをこうすれば軽くなる、というようなアドバイスでも良いので頂けたら助かります。
ULONGLONG GetFolderSize(LPCTSTR strPath)
{
CFileFind find;
int i = 1;
CString file;
ULONGLONG FileSize = 0;
CFileStatus statusAf;
//全ファイル検索
file = strPath;
file += _T(*);
if (find.FindFile(file))
{
while(i)
{
i = find.FindNextFile();
// ファイル名が.(カレントDir)と..(親Dir)の場合は
次へ
if(find.IsDots()) continue;
if(find.IsDirectory())
{
//再起呼び出し
CString path = strPath + find.GetFileName
() + _T(\\);
FileSize += GetFolderSize(path);
}
else{
CString path = strPath + find.GetFileName
();
CFile::GetStatus(path, statusAf);
FileSize += statusAf.m_size;
}
}
}
return FileSize;
}
> Windowsのエクスプローラのように、ファイルサイズを素早く表示させる方法を
> どなたかご教授頂けないでしょうか?
どこで表示している内容を指しているのでしょうか?
ステータスバーに表示されているサイズは選択したフォルダにあるファイルサイズの
合計であり、さらに下のフォルダのサイズは加えていない(つまり再帰呼び出しを
行っていない)と思いますが。エクスプローラでもフォルダのプロパティを表示する
とそれなりに時間はかかりますよ。まぁ集計途中のデータを表示しているので、アプ
リが止まっているようには見えませんが。
ステータスバーの左側の空きディスク領域はGetDiskFreeSpace()等を使えば計算可能
です。
すいません、フォルダのプロパティでの表示でした。
フォルダサイズの取得にある程度時間がかかってしまうのは
どうしようも無い事なのでしょうか?
起動時にツリーのCドライブのアイテムが選択されるため、
そこでファイルサイズ取得が始まって起動が遅くなってしまうので、
何とかならないものかと思っていたのですが・・・
基本的に一気に取ってくるようなAPIは無い筈です。
maruさんも書かれていますが、エクスプローラーでも時間が掛かりますから
地道にカウントして積み上げるしか無いと思います。
問題点をもう少し整理しましょう。
サイズのカウントに時間が掛かるのは物理的に仕方ないとして
現状困っているのはどうしてでしょう?
カウントが始まると操作ができなくなることでしょうか?
では、処理が重いのはどうしようもないとして
カウント中でも操作ができるようにする事が出来れば良いのでしょうか?
その場合に、重い処理はどのように実装すれば良いでしょう?
> すいません、フォルダのプロパティでの表示でした。
ということは、
> Windowsのエクスプローラのように、ファイルサイズを素早く表示
が勘違いであったということでよろしいでしょうか?
> フォルダサイズの取得にある程度時間がかかってしまうのは
> どうしようも無い事なのでしょうか?
ご要望の機能はOSの機能というよりFATやNTFSなどのファイルシステムの機能である
ように思います。で、FATにはディレクトリ下のファイルサイズを保存する機構がな
いので、少なくともFAT(FAT32を含む)では、そのような機能はないでしょう。
多分、NTFSでも同様でしょう。
> 起動時にツリーのCドライブのアイテムが選択されるため、
> そこでファイルサイズ取得が始まって起動が遅くなってしまうので、
> 何とかならないものかと思っていたのですが・・・
起動時には選択されたものがない状態にする。
ファイルサイズ取得中も操作可能にする(PATIOさんが問いかけている件)。
etc.
PATIOさんは既に解決策をお持ちと見ましたが、教育的配慮により...
というところでしょうね。
>PATIOさん
現状困っている事は、カウントが始まると操作出来なくなる、と言う点です。
処理を軽くする事が出来ないのならば、カウント中に操作が出来ると良いなと思います
が、
方法としてはマルチスレッドのようなものを使うのでしょうか?
それとも、他に簡単な方法などがあるのでしょうか?
一応、現状は苦肉の策でWaitカーソルを表示させるという方法をとっています。
>maruさん
>勘違いであったということでよろしいでしょうか?
はい、マイコンピュータのドライブの横に表示されるものがファイルサイズと思っていま
した・・・
起動時に選択されるので選択されたものが無い状態にする、と言う方法も
考えたのですが、それだと根本的な解決になった気がしませんよね・・・
参考までに聞いておきたいのですが、起動時に選択しない方法として
OnInitialUpdate に TreeView.SelectItem(NULL);
の処理を入れてみたんですが自動的に選択されてしまいます。
ボタンクリックでこの処理を行った際は非選択にすることが出来たので
処理自体は間違ってないと思うのですが、起動時にこの処理を行うには
どこに記述すれば良いのでしょうか?
> 方法としてはマルチスレッドのようなものを使うのでしょうか?
ユーザインタフェースを動かしながら、他の処理を実行するには通常マルチスレッドを
使用します。
http://msdn.microsoft.com/ja-jp/library/172d2hhw(v=VS.90).aspx
> 参考までに聞いておきたいのですが、起動時に選択しない方法として
> OnInitialUpdate に TreeView.SelectItem(NULL);
> の処理を入れてみたんですが自動的に選択されてしまいます。
まぁアイディアとして提案しただけで、実装まで考えていませんので。
自分で必要になれば、実装を考えますが。(ちょっと無責任モード)
あと、このアプリケーションの目的が、「任意のフォルダのサイズを表示する」
というものであるなら、起動パラメータにパスを渡すしてそのパスを最初の選択先
にするというのもありではないでしょうか。そうすれば、毎回Cドライブのルートか
ら検索するというのを避けられます。また、最後に選択されたフォルダをレジストリ
に保持して引数なしで起動された場合はその最後に選択されたフォルダを最初の選択
先にするというのもあるでしょう。もちろん一番最初の起動(レジストリがない状態
)や前回のフォルダがなくたっていた時はデフォルトからの選択(Cドライブルート)
になってしまうでしょうけど。
既にレスがついているみたいですが、
maruさんが書かれている通り、最近の定石としてはマルチスレッド化が
一番ストレートな方法だと思います。
他にも泥臭い手法もありますが、デュアルコアのCPUが当たり前に
出回っている昨今であればマルチスレッド化の方がスマートでしょう。
操作ができなくなる原因をきちんと認識出来ていれば、
なぜマルチスレッド化する事が解決策なのかはわかると思います。
イベントドリブン型のアプリケーションではイベント毎の処理が
十分短い処理で終わらないと操作不能に陥ります。
従ってイベント毎の処理は極力短い時間で終わるように考慮する
必要があるわけです。
どうしても重い処理がある場合はマルチスレッドで対応します。
重い処理の中に独自のメッセージループを入れるという方法も
ありますが、個人的にはこの方法は邪道だと思っているので
お勧めしません。使う上でイベントの処理順番についてきちんと
理解した上で問題ない事がわかっていないとこの方法では
問題が出るからです。
今後のアプリケーションの進む方構成としてマルチスレッドは
避けられないと思いますし、これを機会に勉強されてはどうでしょう。
アドバイスありがとうございます。
AfxBeginThread() を使ったマルチスレッド化が簡単そうなので
まずはそこから勉強して作成してみようと思います。
他にもいくつかマルチスレッド化の方法があるようですが、
時間があるときにまた勉強してみます。
またわからない点があれば、別途質問させてください。