壊れかけたハードディスクからコピーできるファイルだけ取り出すため、エラーログ採取機能が付いたコピーソフトをでっち上げました。
この手のソフトはたくさんあります。なぜわざわざ作ったかというと、コピーしながら例外のログを採取するだけなので、イメージ通りのものを探すよりも作った方が手っ取り早かったからです。
マルチスレッド化により複雑になってしまいました。C#で書いてあるというだけで、中身はよくあるやっつけスクリプトと大差ないレベルです。そんな感じでC#は結構気軽に使っているという例です。
ライセンス | パブリックドメイン |
ソース | ![]() |
備考 | マルチスレッド、進捗表示改善、中止サポート |
ソース | ![]() | シングルスレッド、進捗表示が止まる、中止不可能 |
実装で使っているテクニックを紹介します。書式は煩雑な印象がありますが過渡的なもので、C# 3.0ではもっと簡潔なものになるようです。
最初にファイルサイズをチェックしてからコピーを開始します。チェックとコピーの処理はどちらも再帰的にディレクトリをたどっているため、再帰処理は共通化して、異なる部分だけを匿名メソッドで渡すようにしました。今流行のクロージャ的な書き方です。ローカル変数がそのまま使えて便利です。
long size = 0, totalSize = 0, count = 0, totalCount = 0; CompareFolder(di1, di2, "", delegate(FileInfo fi1, FileInfo fi2) { SetProgressText("--- / {0:#,##0} MB , --- / {1:#,##0} 個", totalSize / 1024 / 1024, totalCount); if (fi1 == null) return; totalSize += fi1.Length; totalCount++; }); if (totalSize == 0) totalSize = 1; CompareFolder(di1, di2, "", delegate(FileInfo fi1, FileInfo fi2) { CopyFile(fi1, fi2, delegate(long pos) { SetProgressText( "{0:#,##0} / {1:#,##0} MB ({2} %) , {3:#,##0} / {4:#,##0} 個", (size + pos) / 1024 / 1024, totalSize / 1024 / 1024, size * 100 / totalSize, count, totalCount); }, delegate { size += fi1.Length; count++; }); });
メソッドにブロックを渡すような感じです。元々あった委譲の概念を拡張(この場合は定義のインライン化)して実装されているため、関数型言語のようにアプリオリに実装されているよりも理解しやすいと思いました。
C# 3.0ではラムダ式という簡単な書式が使用できるようになりました。
[C# 2.0] delegate(FileInfo fi1, FileInfo fi2) {} [C# 3.0] (fi1, fi2) => {}
既存のメソッドをdelegateとして渡すときにキャストをすると簡潔に記述できます。delegateを汎用的に表現しているAction<>を使うと、いちいちdelagateを定義する必要がないため便利です。
private void SetLabelText(Label label, string t) { label.Text = t; } private void SetFolderLabel(string t) { Invoke((Action<Label, string>)SetLabelText, label3, t); }