フォルダ削除処理は本当に注意が必要!

C++でWindowsのファイル操作を行うとき、意外と困るのが「フォルダの削除」です。

 

 

まず、Windowsの一番基本的なAPIから、以下の機能を組み合わせて使用します。

  • 指定フォルダ内のフォルダ、ファイルを1つずつ取り出す(FindFile()、FindNextFile())
  • ファイルを削除する(DeleteFile())
  • "空の"フォルダを削除する(RemoveDirectory())

ここで重要なのは、フォルダ削除に使用するAPIが、"空の"フォルダしか扱えないということです。

そこで、基本的なプログラミングの技術である再帰呼び出しがわかっている人は、

「フォルダの中にある全てのファイルを削除してから親フォルダを削除する」ということは

すぐに思いつくでしょう。

 

フォルダ削除()という関数にして、中にある要素がファイルなら削除、フォルダならそのフォルダパスを引数にしてフォルダ削除()と呼ぶだけです。

 

あとちょっと面倒くさそうなのは、「指定フォルダ内のフォルダ、ファイルを1つずつ取り出す

という部分だと思います。FindFile()の引数にフォルダパスをそのまま渡してもファイルを列挙してくれません。

"\\*.*"というワイルドカードをフォルダパスの後ろに付けることで、そのフォルダ内の全ファイルを列挙してくれるようになります。

 

ここまでの部分を理解したうえで、「こういうパターン化された処理は、誰かがソースコードを公開しているだろう」と

検索をするのはよくあることではないでしょうか。そして、そのほうが自分でひとつずつコーディングするよりも

はるかに速くできるということを、経験している人も多いと思います。

 

そこで、Googleで「C++ フォルダ削除」と検索すると、そのようなことをしているソースコードが

すぐに見つかります。

 

ただし、ここで見つかったソースコードを手元でそのまま使用しては、絶対にいけません。

 

呼び出し元のプログラムがしっかりと作られているときは、いらないフォルダを削除するために

このフォルダ削除処理を呼び出し、問題なく指定したフォルダの削除に成功するでしょう。

 

しかし、プログラムの開発段階において、そういつも正しく実行できるとは限りません。

 

もし、このフォルダ削除呼び出しの引数に「空文字」を指定したらどうなるでしょうか?

フォルダの削除を目的とする処理なのに引数にフォルダパスになる文字列が来なければ

何もせずに処理を終わればいいと思うのですが、検索して見つかるソースコードにそのような

チェック処理はほとんど入っていません。

 

FindFile()の引数が"\\*.*"だけとなるのですが、これはどこかのフォルダ内ではなく、

ドライブの最上位パスから検索を開始するという動作になります。

 

というわけで、単にフォルダ削除処理の呼び出しをミスしただけのことが、

ドライブ内全ファイル削除という暴走処理になってしまうことがあるのでした。

 

私ではないのですが、これをやらかしたメンバーは、動作確認中に応答待ちになって待っていて

「なんとなくファイルが減っていっている気がする」と言い出して気づいたときには

数割のファイルが消えた後だったということです。

 

みなさん本当に気をつけましょう。