8

次のコードを使用して、以前にユーザーの %TEMP% フォルダーに作成したファイルを読み取り用に開いています。

new FileStream(cacheFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete);

一部のユーザーのコンピューターでは、「パスへのアクセスが拒否されました」というメッセージとともに UnauthorizedAccessException がスローされることがあります。これを再現できませんでした。私の最初の推測では、ウイルス対策エンジンまたはインデックス作成エンジンが奇妙なことをしているのですが、このコードが "FileShare.Delete" を使用していることにも気付きました。

「FileShare.Delete」を使用すると UnauthorizedAccessException が発生するシナリオはありますか?

4

2 に答える 2

18

はい、FileShare.Delete はこの問題を引き起こす傾向があります。バックグラウンドで実行され、ファイルをスキャンするすべてのプログラムで使用され、ファイル インデクサーとウイルス スキャナーが一般的な例です。

FileShare.Delete は、バックグラウンド プロセスがまだファイルを開いて読み取り中であっても、別のプロセスがファイルを削除できるようにします。その他のプロセスは、ファイルが実際に削除されたことを知っているため、ファイルが実際に消えなかったことを認識しません。

問題は、他のプロセスが実際に削除されるファイルに依存し、何か他のことを行うときに始まります。通常、同じ名前の新しいファイルを作成することによってトリガーされます。保存に失敗すると、バックアップなしで完全なデータが失われるため、特にファイルを保存する方法は非常に賢明ではありませんが、この間違いは非常に一般的です。

ファイルのディレクトリエントリがまだ存在するため、これは失敗します。ファイルを開いた最後のプロセスがハンドルを閉じるまで、エントリは消えません。ファイルを再度開こうとする他のプロセスは、エラー 5「アクセスが拒否されました」で平手打ちされます。ファイルを削除して再作成しようとしたプロセスを含めます。

回避策は、常に「トランザクション」保存を使用し、上書きする前にファイルの名前を変更することです。.NET では File.Replace() で、ネイティブの winapi では ReplaceFile() で利用できます。手動でも簡単に実行できるワークフローは次のとおりです。

  1. バックアップファイルを削除し、失敗した場合は停止します
  2. 古いファイルの名前をバックアップ ファイル名に変更し、失敗した場合は停止します
  3. 元のファイル名を使用して新しいファイルを書き込み、失敗した場合はバックアップの名前を元に戻します
  4. バックアップ ファイルを削除し、失敗を無視します

ステップ 2 により、データが失われることはなく、何か問題が発生した場合でも元のファイルはそのまま残ります。手順 4 により、FileShare.Delete が意図したとおりに動作し、他のプロセスがハンドルを閉じると、最終的にバックアップ ファイルが消えることが保証されます。

于 2013-11-09T13:01:13.213 に答える
3

これを再現するシナリオを見つけました:

    static void Main(string[] args)
    {
        string cacheFileName = @"C:\temp.txt";
        using (var filestream = new FileStream(cacheFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, 4096, FileOptions.SequentialScan))
        {
            filestream.Read(new byte[100], 1, 1);
            Console.ReadLine();
            GC.KeepAlive(filestream);
        }
        Console.WriteLine("Done!");
    }
}

「C:\temp.txt」ファイルを作成し、このプログラムを実行します。Explorer/TotalCommander でファイルを削除してみてください。エラーは出ませんが、ファイルも削除されません。次に、プログラムを再度実行すると、UnauthorizedAccessException がスローされます。両方の .exe を閉じると、最終的にファイルが削除されたように見えます。

「FileShare.Delete」を削除すると、使用中のファイルを削除できなくなるため、この問題は解決します。

于 2013-11-09T11:19:25.507 に答える