プログラム系統備忘録ブログ

記事中のコードは自己責任の下でご自由にどうぞ。

IFileOperationで異なる種類の操作を登録した時のUI表示の話

確認環境: Windows 7 SP1 64bit
IFileOperation関係の前の記事: IFileOperationで進行ダイアログ付きでディレクトリ操作を行う (.NETで必要な宣言や、本記事でも使っているCreateShellItem補助メソッドについて記述しています)
IFileOperation関係の次の記事: IFileOperationでZIPファイルを展開する方法 (次からC++を使っています)

今回の記事も、簡単のためCOMオブジェクトの解放処理を一切記述していません。ご了承ください。

背景

IFileOperation interface のRemarksにあるように、PerformOperationsを実行すると、それまでに登録していた操作が全て実行されます。
進行ダイアログの表示も、以下のようにまとめて表示されます。

var fo = (IFileOperation)new FileOperation();
var dest = CreateShellItem(@"F:\");
fo.CopyItem(CreateShellItem(@"C:\test\a.dat"), dest, null, null);
fo.CopyItem(CreateShellItem(@"C:\test\b.dat"), dest, null, null);
fo.PerformOperations();

全体を通じての進行状態が表示されますし、「キャンセル」ボタンクリックで操作全体をキャンセルできます。Explorerで複数選択した場合と同様の表示を実現できます。
さてそれでは、MoveItemやDeleteItemなどと同時に行うとダイアログ表示はどうなるのか、と実験した結果がこの記事になります。

CopyItemとMoveItem

1つ目はコピー、2つ目は移動にしてみます。

var fo = (IFileOperation)new FileOperation();
var dest = CreateShellItem(@"F:\");
fo.CopyItem(CreateShellItem(@"C:\test\a.dat"), dest, null, null);
fo.MoveItem(CreateShellItem(@"C:\test\b.dat"), dest, null, null);
fo.PerformOperations();



進行ダイアログは1回だけ表示され、a.datを処理している間は「コピー中」、b.datを処理している間は「移動中」となります。
そこまではいいのですが、どちらも「2個の項目」と表示されるせいで「2つともコピーしているのか?それとも2つとも移動しているのか?」と混乱を招きます。

CopyItemとDeleteItem

前の記事で触れたようにFOF_ALLOWUNDOが規定値に含まれているので、DeleteItemではゴミ箱に送られます。

var fo = (IFileOperation)new FileOperation();
var dest = CreateShellItem(@"F:\");
fo.CopyItem(CreateShellItem(@"C:\test\a.dat"), dest, null, null);
fo.DeleteItem(CreateShellItem(@"C:\test\b.dat"), null);
fo.PerformOperations();


コピーが始まる前から「複数ファイルの削除」ダイアログが表示されました。それもa.datも削除されるとしか思えない表示内容です。
「はい」をクリックすると、a.datはコピーされb.datはゴミ箱へ移動と、宣言通りの動作は行われました。「いいえ」をクリックするとコピーすら行われません。

SetOperationFlagsでFOF_ALLOWUNDOを外した場合を試しますと、同様に「これら2個の項目を完全に削除しますか?」と誤解間違いなしの表示でした。

まとめ

異なる種類の操作を一度に行おうとすると、確認ダイアログや進行ダイアログの表示が不自然極まりないものなります。
SetOperationFlagsで FOF_SILENT や FOF_NOCONFIRMATION を指定する場合はともかく、そうでない場合は操作種類ごとにPerformOperationsを呼び出したほうが良さそうです。

2017/03/12(日)追記 自前実装の道

IFileOperation::SetProgressDialog method というメソッドがあり、 IOperationsProgressDialog interface を指定できます。
自前でインターフェースを実装することで、異なる種類の操作を一度に行う場合でも自然に表示できる可能性があります。
CLSID_ProgressDialog で作成できるインスタンスも IOperationsProgressDialog を実装しているので、ほとんどの処理を委譲してごく一部だけ乗っ取ることも可能かもしれません(未確認)。

ただし IOperationsProgressDialog::StartProgressDialog method のRemarksに以下の内容があります。

The progress dialog should be created on a separate thread than the file operation on which the dialog is reporting.
If the dialog is running in the same thread as the file operation, progress messages are, at best, only sent as resources allow.
Progress messages on the same thread as the file operation might not be sent at all.

IFileOperationはSTAスレッドでしか扱えないため、UIスレッドで作成するでしょう。
IOperationsProgressDialogはワーカースレッドで作成する必要があるようです。