ファイルを開いているプロセスを調べる
以前『ファイルの使用者を調べる PowerShell Cmdlet』というエントリを書いたせいか,明らかに「ファイルを開いているプロセスを今すぐ調べたい」という感じの検索語でうちの日記に来られる方が多いようです.
『ファイルの使用者を調べる PowerShell Cmdlet』で示した方法は,技術的興味から作ってみただけの,不完全かつ使いにくいものなので,本当に「ファイルを開いているプロセスを今すぐ調べたい」という方にはおすすめできるものではありません.
参考までに,そういうときに私がどうしているかについて軽く書いておきます.
handle.exe
sysinternals 社の Mark Russinovich 氏によって作成・公開され,同社の買収によって Microsoft サイトで公開されるようになった handle.exe というコマンドラインツールがあります.
このツールを使用すると,実行中のプロセスが所有しているハンドルの一覧をダンプすることができます.そして,そのリストの中にファイルハンドルも含まれています.
削除したり移動したりできなくなったファイルやフォルダがあって困っているときは,まずこの handle.exe の実行結果を見てみます.もし問題のファイルやフォルダのハンドルを所有しているプロセスがあれば,そのプロセスを終了することでファイル操作ができるようになるでしょう.
あるいは,handle.exe にはプロセスの開いている特定のハンドルを外部から強制的に閉じるという機能もあるので,それを使って閉じるということも可能です.
Process Explorer
同じく Mark Russinovich 氏によって作成された Process Explorer を使っても,ファイルやフォルダを開いているプロセスを調べることができます.
Process Explorer を起動し,メニューから Find → Find Handle or DLL を選びます.すると以下のようなダイアログが表示されるので,問題のファイル名またはフォルダ名を入力してください.関連するハンドル一覧が表示されます.
なぜ Directory が開かれたままになるのか? ―― ひとつの解
ついでにもう一点.
『ファイルの使用者を調べる PowerShell Cmdlet』に対し,こういった質問をいただきました.
bear.mini 『ソースコード、ダウンロードさせていただきました。(中略) ところで、ファイルだけでなく、ディレクトリを Lock しているプロセスを識別できるようにはなりませんでしょうか?ディレクトリ内のファイルを再帰的にチェックさせようとすると、どうもディレクトリ自体のチェックの際にエラーが発生しているようですので・・・。』
あとからよく考えてみると,確かに Explorer から「特定ディレクトリだけ操作できなくなること」というのは少なからず経験があります.中のファイルは削除できたのに,なぜか大元のディレクトリを削除しようとすると,「別のプログラムがこのフォルダを開いているので、操作を完了できません」というダイアログが表示されてしまうという経験,皆さんもありませんか?.
ディレクトリのファイルハンドル
さて,何らかのプロセスが何らかの形でフォルダを開いているのは確からしいとして,普段 Win32 プログラミングを行っている身からすると,「そもそもディレクトリのファイルハンドルって開けたっけ?」という疑問もわいてきます.
実際には MSDN Library の CreateFile の頁にあるように,「開ける」が正解なのですが,どうも FILE_FLAG_BACKUP_SEMANTICS を指定して特殊な開き方をする必要があるようです.
HANDLE handle = CreateFile( _T("c:\\test"), READ_CONTROL, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
しかし,バックアップソフトが裏で動いているならまだしも,こんなへんてこな動作をするプロセスがそんなにいるものでしょうか?
ディレクトリのファイルハンドル (再考)
一方,id:NyaRuRu:20070808:p1 の実験から,どうも全てのプロセスは最低ひとつのディレクトリを開いているらしいことも分かります.そして多くのプロセスは,自身の exe ファイルが置かれているディレクトリのハンドルを開いています.どうやらこれは FILE_FLAG_BACKUP_SEMANTICS とは関係ない理由で,プロセスがディレクトリのハンドルを所有する仕組みがありそうです.
ちょっとしたクイズですが,これらの状況証拠から,どうもプロセスのカレントディレクトリが怪しそうに見えてきます.実験してみるとこの直感は正しく,SetCurrentDirectory API によってカレントディレクトリを変更すると,プロセスがそのディレクトリのファイルハンドルをオープンすることが,Process Explorer や handle.exe によって確かめられました.実験環境は Windows Vista Ultimate Edition 日本語版です.
// c:\test のファイルハンドルが開かれる BOOL ret = SetCurrentDirectory( L"c:\\test" );
悪魔のシナリオ
ここまで来れば,いったいどういったシナリオで「フォルダが削除できなくなる」か,勘のいい人ならいくつか想像が付いているのではないでしょうか.
たとえば Windows のバッドノウハウに,「ファイル選択ダイアログでカレントディレクトリが変わる」というものがあります.ならばシナリオはこんな感じ.
- c:\test\hoge.txt をあるプロセスのファイル選択ダイアログで開く (hoge.txt と c:\test の 2 つのファイルハンドルが開かれる)
- hoge.txt を閉じる (hoge.txt のファイルハンドルが閉じられる)
- hoge.txt を Explorer から削除 (これは可能)
- c:\test を Explorer から削除しようとしてエラー (あるプロセスのカレントディレクトリになったままなので削除できない)
他にも,c:\test\hoge.pptx をダブルクリックで開いたところ,PowerPoint 2007 のカレントディレクトリが c:\test になってしまったとか,conime.exe のような常駐プロセスのカレントディレクトリが何かの拍子でデスクトップ上の特定フォルダになってしまったとか,色々とバリエーションはありそうです.
以上,夏の夜のちょっと怖い話でした.