Windows 秘話ハンドルを強制的に閉じる

Raymond Chen

別のプログラムがファイルを開いているためにファイルを削除できなかったという経験はありませんか。この場合の適切な対応は、ファイルを開いているプログラムにファイルを閉じてもらうことです。たとえば、削除するドキュメントを編集しているユーザーに、ドキュメントを閉じるように依頼します。しかし、焦って、ファイル ハンドルを強制的に閉じることができるユーティリティを使用してしまうことがあります。ハンドルを強制的に閉じると、問題は一時的に解決されますが、それと引き換えに、長期にわたるデータ破損の問題が発生することになります。

ハンドルを強制的に閉じるということは、プログラムにアクセスして、メモリを解放することです。プログラムでは、ハンドル (メモリ) が有効だと認識されているため、引き続きこれを使用します。しかし、ハンドルは実際には解放されているため、別の目的で再利用されます。

たとえば、学校での出来事を考えてみましょう。305 番のロッカーに欲しい本が入っているとします。そこで、ロッカーをこじ開けて、その本を取り出します。これでロッカーが空いたので、別の生徒がやってきて自分のブックバックをロッカーに入れます。しばらくして、元の 305 番のロッカーの持ち主が昼食から戻ってきて、このブックバッグを取り出し、教室に持って行ってしまいました。

ここでは 2 つの問題が発生しています。1 点目は、元の 305 番のロッカーの持ち主は間違ったブックバッグを持って行ったため、他人のノートに書き込む可能性があることです。2 点目は、2 番目にやってきた生徒のブックバッグが盗まれてしまったことです。

強運の持ち主であれば、プログラムの次の処理で強制的に閉じられたハンドルが正しく閉じられるでしょう。プログラムにはハンドルが無効であることを示すエラーが返されますが、対応のしようがないので、プログラムでは、ハンドルが正常に閉じられたものとして処理を続行します。しかし、このようなことはめったにありません。

しかし、プログラムがハンドルに再びアクセスし、ハンドルを使用しようとして、ハンドルが無効であることを示すエラーが返されるという状況に陥る可能性の方が高いでしょう。さらに可能性が高いのは、強制的に閉じられたハンドルが、別の目的で使用されることです。こうなると、事態は完全におかしくなり始めます。たとえば、ハンドルが別のファイルに再利用されている場合を考えてみましょう。プログラムでは、元のファイルに対して読み取りや書き込みを行っていることを想定していますが、実際には新しいファイルを操作していることになります。その結果、元のファイルと新しいファイルでデータ破損が発生します。元のファイルでは、書き込まれるはずのデータが書き込まれず、新しいファイルでは、元のファイルに書き込まれるはずのデータが書き込まれるという問題です。

プログラムが元のファイルを閉じるとき、再利用されたハンドルも閉じられます。つまり、芋づる式の問題を発生させています。元のファイルを強制的に閉じた結果、別のハンドルが強制的に閉じられます。

このデータ破損は延々と続きます。ハンドルを要求したコンポーネントでは、その前にハンドルを使用していたコンポーネントによって意図せず破損されているハンドルを受け取ることになるためです。前のコンポーネントが有効なハンドルであると思い込んでいるハンドルを閉じるとき、実際には現在のコンポーネントが使用しているハンドルが閉じられることになり、これは次にハンドルを要求するコンポーネントの破損につながります。

また、検索インデックス サービスがインデックスを作成するためにファイルを開いているときに、一時的に処理が保留されたため、ユーザーがファイルを削除したいと考えて、(賢明な選択肢ではありませんが) ハンドルを強制的に閉じたとします。検索インデックス サービスで、情報を記録するためにログ ファイルを開くと、削除されたファイルへのハンドルが、このログ ファイルへのハンドルとして再利用されます。しかし、保留されていた操作が完了すると、検索インデックス サービスでは、インデックス作成時に開いたハンドルを閉じる処理に取り掛かりますが、知らずにログ ファイルのハンドルを閉じることになります。

また、検索インデックス サービスで、別のファイル (たとえば、継続状態を更新できるように構成ファイルを書き込み用に) 開いたとします。ログ ファイルのハンドルは、構成ファイルのハンドルとして再利用されます。検索インデックス サービスでは、情報を記録するため、ログ ファイルに書き込みを行います。残念ながら、ログ ファイルのハンドルは閉じられていて、この時点では、構成ファイルで再利用されています。その結果、ログ情報は構成ファイルに書き込まれ、構成ファイルが破損することになります。

しばらくして、強制的に閉じられた別のハンドルがミューテックス ハンドルとして再利用されたとします。このミューテックス ハンドルは、データ破損を防ぐために使用されるものですが、元のファイル ハンドルが閉じられると、ミューテックス ハンドルも閉じられ、データ破損に対する保護が失われます。サービスの実行時間が長くなるほど、インデックスの破損の程度も大きくなります。いずれ、だれかがインデックスから正しくない結果が返されることに気が付くでしょう。そこで、検索インデックス サービスを再起動しようとすると、構成ファイルが破損しているためにエラーが発生します。

検索インデックス サービスを提供している会社に、この問題を報告すると、インデックスが破損していること、原因は特定できないがログ ファイルへの記録が停止していること、および構成ファイルが不要なデータで上書きされていることが判明します。不運な技術者は、サービスがインデックス ファイルと構成ファイルが破損した原因を突き止めるという解決の目処が立たない作業を担当することになります。この破損の原因は、あるユーザーがハンドルを強制的に閉じたことであるとも知らずに。

Raymond Chen は、自分の Web サイト「The Old New Thing」(古くて新しいもの) および同じタイトルの著書 (Addison-Wesley、2007 年発行) で、Windows の歴史、Win32 プログラミング、そして新聞の見出しの読み間違いについて触れています。