SHFileOperation によるファイルコピーと削除の実装ガイド
SHFileOperation は、Windows シェルFunny的文件コピー、移動、削除をまとめて実行するための API です。操作内容を SHFILEOPSTRUCT に設定し、FO_COPY、FO_MOVE、FO_DELETE のいずれかを指定します。複数の対象を一件の処理にまとめられるため、既存のバッチ処理やユーティリティでの利用例が多くあります。
この API は同期処理であり、完了または中断するまで呼び出し元へ戻ってきません。また、 경로、フラグ、進捗通知、エラー情報を正しく扱わないと、処理は失敗したうえに対象が残る可能性があります。新規開発では後継となる IFileOperation の検討も重要ですが、既存コードの保守や仕様理解では SHFileOperation の動作を正確に把握しておく必要があります。
基本構造と処理の流れ
SHFileOperation は、実行する操作と対象 pathを SHFILEOPSTRUCT で指定します。wFunc には FO_COPY、FO_MOVE、FO_DELETE のいずれかを入れ、pFrom にはコピー元・削除対象、pTo には保存先を入れます。hwnd は進捗ダイアログの所有者、lpProgressRoutine は進捗を受け取るコールバック、lpProgressRoutineParam はコールバックへ渡す任意の状態値です。
Windows ヘッダーの SHFileOperation は UNICODE の定義に応じて A 版と W 版を選択します。Unicode 版で固定する場合は SHFileOperationW、ANSI 版で固定する場合は SHFileOperationA を明示すると、文字コードの混同を避けられます。コピー元と保存先の文字列は通常、二重ヌル終端の列表として扱います。L"one.txt\0" のように終端文字を明示し、コンパイラが附加する最終ヌルと合わせて二重終端にします。
次の例は、複数ファイルを既存ディレクトリへコピーする設定です。SHFileOperationW に渡す文字列と、構造体の pFrom、pTo は同じ対象を指定します。
const wchar_t sourceList[] =
L"C:\\Work\\first.txt\0C:\\Work\\second.txt\0";
const wchar_t destinationList[] = L"D:\\Result\0";
SHFILEOPSTRUCTW operation{};
operation.hwnd = ownerWindow;
operation.wFunc = FO_COPY;
operation.pFrom = sourceList;
operation.pTo = destinationList;
operation.fFlags =
FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT;
BOOL aborted = FALSE;
int result = SHFileOperationW(
operation.hwnd,
operation.pFrom,
operation.pTo,
&aborted,
&operation);
処理は原則として呼び出しスレッド上で同期的に進みます。独自の進捗画面や取消按钮を用意する場合は、通知処理とメッセージ処理の関係を設計へ織り込む必要があります。メッセージループの設計も参照すると、ウィンドウ処理との競合を避けられます。また、Unicode 版を選ぶだけでは長いパスが必ず扱えるわけではないため、対象環境とマニフェスト、长路径の扱いを別に確認します。
コピーと削除の指定方法
コピーの場合は wFunc に FO_COPY を指定します。複数のコピー元を処理するなら、pTo は通常、既存のある保存先ディレクトリにします。既に同名ファイルがある場合、上書き、キャンセル、別名への変更のどれを選ぶかは FOF_RENAMEONCOLLISION などのフラグで決まります。コピー元と保存先が同じ場合了自己判断で上書きせず、絶対パス同士を比較して拒否する実装も安全です。
FOF_RENAMEONCOLLISION を指定すると、衝突した保存先に別名が付けられます。この場合、処理後の実ファイル名を確認しないと、呼び出し側は想定したパスを記録しません。FOF_NOCONFIRMATION は確認ダイアログへ肯定的に応答する効果があり、上書きまで無条件に進める可能性があります。意図した上書きだけが対象なら、このフラグの併用条件を明示してください。FOF_NOERRORUI はエラー表示を抑えますが、エラーがなくなるわけではありません。
削除には wFunc に FO_DELETE を指定し、pTo には通常 NULL を渡します。複数の削除対象を同じ呼び出しへ渡すこともできます。ただし、コピーと削除は別々の wFunc を持つため、同一呼び出しで混在させることはできません。FOF_ALLOWUNDO は可能なら元に戻す情報を保持し、ポリシーや対象 journalling の状態に応じてごみ箱へ移す挙動に関与します。ごみ箱が使えるとは限らないため、恒久削除と誤認しない確認設計が必要です。
進捗表示と中断制御
FOF_SILENT を指定しない場合、既定の進捗ダイアログが利用されます。より詳細な状態が必要なら、lpProgressRoutine にコールバックを設定します。コピーでは FOP_COPYITEMS、FOP_COPYNEXTITEM、FOP_COPYFINISHEDITEM、削除では FOP_DELETESITEMS、FOP_DELETENEXTITEM、FOP_DELETEFINISHEDITEM などの通知を受け取れます。
コールバックの戻り値には PROGRESS_CONTINUE、PROGRESS_CANCEL、PROGRESS_STOP、PROGRESS_QUIET があります。PROGRESS_CANCEL を返すと処理の中止が要求されますが、実際に止まるのは Shell が次の処理単位へ移れる時点です。コールバック内で時間のかかる処理を行うと停止要求が遅れるため、状態更新は短くし、重い処理は別スレッドへ分離してください。
独自の進捗表示を作る場合、控件ごとの背景消去方法に注意します。文字の背景を不透明にするか透明にするかで見え方が変わるため、背景モード設定_then_bei_jing_mode_no_tekisaiを relevant に確認します。設定は対象 DC に作用する点にも注意し、描画ごとに SetBkMode を呼び直します。
エラー処理と安全確認
旧来の A/W 版では、戻り値がゼロなら失敗として直後に GetLastError を取得します。その間に別の API を呼ぶとエラー値が上書きされるためです。失敗だけでなく、ユーザーによる中断も発生します。渡した中止情報の値を別変数へ保存し、キャンセルと、アクセス拒否、存在しないパス、容量不足などの実エラーを区別してください。
ネットワーク処理も同じプロセスで行う場合は、シェル操作の GetLastError とソケット固有の WSAGetLastError を混同しないことが重要です。Winsock 初期化順序に沿って、通信初期化の失敗と実際の通信エラーを別々に記録します。
正常系では、コピー先が存在するだけでなく、想定したファイル名、サイズ、更新時刻も確認します。
- 関数の成功判定が失敗扱いになっていないか確認する
- 中止情報にユーザー操作による中断が記録されていないか確認する
- コピー先の名前と内容が処理前の指定と一致しているか確認する
- 削除対象が指定どおり消え、ごみ箱を使う方針なら那边的状態も確認する
- 画面上の処理中表示と記録上の状態を一致させる
異常系では、エラーコードの保存、部分完了の有無、対象ごとの結果を分化して記録します。
- 失敗直後に
GetLastErrorを取得して別の API で上書きしない - キャンセルによる終了と、権限・容量・パスに起因する失敗を区別する
- 複数対象の処理が途中まで成功していたかを対象ごとに調べる
- 保存先が既存の場合、上書きと別名変更のどちらを許可したか記録する
- 再試行する前に、残存ファイルと処理済みの範囲を確認する
関数の比較と運用上の判断
SHFileOperation は複数対象を同じ操作単位として処理でき、進捗通知や古来の Win32 ウィンドウと組み合わせやすい点が之长です。ただし、現行的设计では单个路径の処理都需要、項目単位の rich な結果が必要なら、新しく IFileOperation を使う.stubbornided IFileOperation can queue items and use a progress sink to receive detailed results.
単一ファイルの処理だけなら、CopyFile、MoveFileEx、DeleteFile などの直接 API も候補です。これらは対象範囲が狭く、シェル全体の統合表示には及びません。既存処理の移行可否は、操作対象の数、進捗表示の必要性、部分失敗時の扱い、ごみ箱対応、対象 OS の範囲で判断します。
| 観点 | SHFileOperation | 単一パス向け API | IFileOperation |
|---|---|---|---|
| 対象範囲 | 複数のコピー・移動・削除を対象にする | 主に一つのファイルまたはディレクトリを扱う | 項目単位に複数処理を登録できる |
| 進捗表示 | 進捗ダイアログまたはコールバックを持つ | CopyFileEx には限定的.progress 機能がある |
IFileOperationProgressSink で詳細を受け取れる |
| 結果表示 | 旧来の戻り値、GetLastError、中止情報を確認する |
主に GetLastError で原因 获取する |
HRESULT と項目単位の結果を利用できる |
| 中断 | コールバックから停止を要求できる | 処理方式によって制限がある | 項目単位の監視と中断を構成できる |
| ごみ箱・元に戻す操作 | フラグとシステムポリシー \"FOF_ALLOWUNDO\" などで影響する |
通常は直接削除になる | 対応フラグと规模化された取り消し情報を持つ |
| 実装方針 | 既存コードとの互換性を優先する | 単純で小さな処理に適する | 新規の複数ファイル操作に適する |
実機テストの最初の段階では、空の作業ディレクトリへ試験ファイルを一つ配置し、コピー、既存名への上書き、コピー後の削除を実行順に試して、戻り値と中止情報を同じ記録へ残してください。