.NETでファイルへのアクセスが拒否されているかどうかを簡単に確認するにはどうすればよいですか?


100

基本的に、実際にファイルを開こうとする前に、ファイルを開く権限があるかどうかを確認したいと思います。必要がない限り、このチェックにtry / catchを使用したくありません。事前に確認できるファイルアクセスプロパティはありますか?


2
タグを変更したときのキャプション:「修正中」。冗談抜き。
Joel Coehoorn、2008年

6
同意-TryOpen(つまり、Try-Parseパターン)があったらいいのに。
トリスタン

回答:


157

私は過去にこれまでに数え切れないほどの回数をやりました、そして私がそれをするたびに、私は試みをすることさえ間違っていました。

ファイルのパーミッション(でもファイルの存在)がある揮発性 -彼らはいつでも変更することができます。特にマーフィーの法則のおかげで、ファイルをチェックしてから開こうとするまでの短い期間が含まれます。最初に確認する必要があることがわかっている地域にいる場合、変更はさらに起こりやすくなります。しかし、奇妙なことに、かなり静的である傾向があるテスト環境や開発環境では決して起こりません。これにより、問題を後で追跡することが困難になり、この種のバグが本番環境に移行しやすくなります。

これが意味することは、チェックにもかかわらず、ファイルのアクセス許可または存在が悪い場合でも、例外を処理できる必要があるということです。事前にファイルの権限を確認するかどうかに関係なく、例外処理コードが必要です。例外処理コードは、存在または権限チェックのすべての機能を提供ます。さらに、このような例外ハンドラーは遅いことがわかっていますが、ディスクI / Oがさらに遅いことを覚えておくことが重要です... はるかに遅い...そして.Exists()関数を呼び出すか、アクセス許可をチェックすると、追加のトリップが強制されますファイルシステムから。

要約すると、ファイルを開こうとする前の最初のチェックは冗長であり、無駄です。例外処理に追加の利点はありません。実際にはパフォーマンスに悪影響を及ぼし、助けにはなりません。維持する必要のあるコードが増えるという点でコストが追加され、コードに微妙なバグが発生する可能性があります。最初のチェックを行うメリットはまったくありません。代わりに、ここでの正しいことは、ファイルを開いて、失敗した場合に優れた例外ハンドラーに努力することです。ファイルが存在するかどうかを確認するだけでも同じです。この推論は、すべての揮発性リソースに適用されます


5
丁度。これは、競合状態の典型的な例です。
Powerlord 2008年

3
korro:とにかく、失敗した場合の不正なアクセス許可を処理できる必要があります。これにより、初期チェックが冗長で無駄になります。
Joel Coehoorn、2008年

2
初期チェックは、一般的な特定のエラーを適切に処理するのに役立ちます。多くの場合、特定の例外属性を特定の原因と照合するよりも、先を見通す方が簡単です。try / catchは依然として必須です。
peterchen 2010年

5
この回答は、ファイルを開こうとする前に、「ファイルを開く権限があるかどうかを確認する方法」という質問には答えません。許可がそのインスタンスで許可されていない場合、許可がチェックされた直後に許可が十分に付与されていても、ソフトウェアがファイルを読み取ろうとしない可能性があります。
Triynko

5
アクセス許可が揮発性であるかどうかは関係ありません。その瞬間に何が必要かを気にするだけの場合です。失敗は常に処理する必要がありますが、読み取り権限を確認してもそこにない場合は、1秒後にアクセスできる可能性がある場合でも、ファイルの読み取りをスキップできます。どこかに線を引く必要があります。
Triynko

25

同様の問題でここに来る他の人のための簡単なヒント:

DropBoxなどのWeb同期アプリに注意してください。.NETで「using」ステートメント(Disposeパターン)が壊れていると思って2時間費やしました。

最終的に、Dropboxがファイルを同期するために、バックグラウンドで継続的にファイルの読み取りと書き込みを行っていることに気付きました。

Visual Studio Projectsフォルダーがどこにあると思いますか?もちろん「My Dropbox」フォルダの中。

そのため、アプリケーションをデバッグモードで実行すると、アプリケーションが読み書きしていたファイルもDropBoxから継続的にアクセスされ、DropBoxサーバーと同期されていました。これにより、ロック/アクセスの競合が発生しました。

したがって、少なくとも今では、より堅牢なファイルを開く関数(つまり、複数回試行するTryOpen())が必要であることを知っています。フレームワークの組み込み部分ではないことに驚いています。

[更新]

これが私のヘルパー関数です:

/// <summary>
/// Tries to open a file, with a user defined number of attempt and Sleep delay between attempts.
/// </summary>
/// <param name="filePath">The full file path to be opened</param>
/// <param name="fileMode">Required file mode enum value(see MSDN documentation)</param>
/// <param name="fileAccess">Required file access enum value(see MSDN documentation)</param>
/// <param name="fileShare">Required file share enum value(see MSDN documentation)</param>
/// <param name="maximumAttempts">The total number of attempts to make (multiply by attemptWaitMS for the maximum time the function with Try opening the file)</param>
/// <param name="attemptWaitMS">The delay in Milliseconds between each attempt.</param>
/// <returns>A valid FileStream object for the opened file, or null if the File could not be opened after the required attempts</returns>
public FileStream TryOpen(string filePath, FileMode fileMode, FileAccess fileAccess,FileShare fileShare,int maximumAttempts,int attemptWaitMS)
{
    FileStream fs = null;
    int attempts = 0;

    // Loop allow multiple attempts
    while (true)
    {
        try
        {
            fs = File.Open(filePath, fileMode, fileAccess, fileShare);

            //If we get here, the File.Open succeeded, so break out of the loop and return the FileStream
            break;
        }
        catch (IOException ioEx)
        {
            // IOExcception is thrown if the file is in use by another process.

            // Check the numbere of attempts to ensure no infinite loop
            attempts++;
            if (attempts > maximumAttempts)
            {
                // Too many attempts,cannot Open File, break and return null 
                fs = null;
                break;
            }
            else
            {
                // Sleep before making another attempt
                Thread.Sleep(attemptWaitMS);

            }

        }

    }
    // Reutn the filestream, may be valid or null
    return fs;
}

3
@アッシュ私はあなたが質問を適切に読んだと思います彼はキャッチを避けたいです。
ラビシャ

10
@ラビシャ、あなたはジョエルのトップ投票の答えさえ読んだ?Joelが言うように、「代わりに行うのは、ファイルを開いて、失敗した場合に例外を処理することだけです」です。何かが避けられないという事実が気に入らないからといって、反対票を投じないでください。
Ash

コードをありがとう!一つのこと、それは参照などを使用して使用する方がよいかもしれませんここTazeemの答えを
チェル

あなたがファイルストリームを返すusingとすると、呼び出し元はを使用する必要があります...
Cel

@Cel- usingここでは機能しません。使用ブロックの最後で、fs強制的に閉じられます。呼び出し元に閉じられた(役に立たない)ファイルストリームを与えます!
ToolmakerSteve

4

ここにあなたが探している解決策があります

var fileIOPermission = new FileIOPermission(FileIOPermissionAccess.Read,
                                            System.Security.AccessControl.AccessControlActions.View,
                                            MyPath);

if (fileIOPermission.AllFiles == FileIOPermissionAccess.Read)
{
    // Do your thing here...
}

これは、すべてのファイルのパスのビューに基づいて読み取りの新しいアクセス許可を作成し、それがファイルアクセス読み取りと等しいかどうかを確認します。


3

最初に、Joel Coehoornが言ったこと。

また、必要がない限り、try / catchの使用を避けたいという根底にある仮定を検討する必要があります。例外に依存するロジックを回避する一般的な理由(Exceptionオブジェクトの作成のパフォーマンスが低い)は、ファイルを開いているコードとは関係ありません。

List<FileStream>ディレクトリサブツリーのすべてのファイルを開いてにデータを入力するメソッドを作成していて、それらの多数にアクセスできないことが予想される場合は、ファイルを開く前にファイルのアクセス許可を確認して、ファイルを開かないようにするとよいでしょう。例外が多すぎます。しかし、それでも例外は処理します。また、これを行うメソッドを作成している場合は、プログラムの設計にひどい問題がある可能性があります。


-1
public static bool IsFileLocked(string filename)
        {
            bool Locked = false;
            try
            {
                FileStream fs =
                    File.Open(filename, FileMode.OpenOrCreate,
                    FileAccess.ReadWrite, FileShare.None);
                fs.Close();
            }
            catch (IOException ex)
            {
                Locked = true;
            }
            return Locked;
        }

-3
public static FileStream GetFileStream(String filePath, FileMode fileMode, FileAccess fileAccess, FileShare fileShare, ref int attempts, int attemptWaitInMilliseconds)
{            
    try
    {
         return File.Open(filePath, fileMode, fileAccess, fileShare);
    }
    catch (UnauthorizedAccessException unauthorizedAccessException)
    {
        if (attempts <= 0)
        {
            throw unauthorizedAccessException;
        }
        else
        {
            Thread.Sleep(attemptWaitInMilliseconds);
            attempts--;
            return GetFileStream(filePath, fileMode, fileAccess, fileShare, ref attempts, attemptWaitInMilliseconds);
        }
    }
}

8
-1:「throw;」を使用します 「unauthorizedAccessExceptionをスローする」ではありません。スタックトレースが失われています。
John Saunders

なぜattemptsリファレンスによって渡されるのですか?それは意味がありません。<=だけでなく、のテストも行いません==
Konrad Rudolph

1
@John:まあ、この場合、再帰呼び出しの(深くネストされた)スタックトレースを失うことが望ましいので、このインスタンスでthrow exは実際に正しいことだと思います。
Konrad Rudolph

2
@Konrad:@Rudzitis:-1の理由を変更します。「throw ex」でスタックをねじ込むよりも悪いです。スタックの深さが実際に重要なときに、再帰によって追加のスタックレベルを人為的に誘導することにより、スタックをねじ込みます。これは反復的な問題であり、再帰的な問題ではありません。
John Saunders
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.