928

1 つの画像ファイルに繰り返しアクセスする必要があるプログラムを C# で作成しています。ほとんどの場合は機能しますが、コンピューターが高速で実行されている場合、ファイルシステムに保存される前にファイルにアクセスしようとし、エラーをスローします。

「別のプロセスで使用中のファイル」

これを回避する方法を見つけたいのですが、私のグーグル検索では、例外処理を使用してチェックを作成するだけで済みました。これは私の宗教に反するので、誰かがそれを行うためのより良い方法があるかどうか疑問に思っていましたか?

4

20 に答える 20

612

このソリューションに関する注記を更新: でのチェックFileAccess.ReadWriteは読み取り専用ファイルでは失敗するため、ソリューションは でチェックするように変更されましたFileAccess.Read

オリジナル: 私はこのコードを過去数年間使用してきましたが、問題はありませんでした。

例外の使用を躊躇することは理解できますが、例外を常に回避することはできません。

protected virtual bool IsFileLocked(FileInfo file)
{
    try
    {
        using(FileStream stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None))
        {
            stream.Close();
        }
    }
    catch (IOException)
    {
        //the file is unavailable because it is:
        //still being written to
        //or being processed by another thread
        //or does not exist (has already been processed)
        return true;
    }

    //file is not locked
    return false;
}
于 2009-06-02T01:20:19.047 に答える
595

セキュリティの脆弱性として使用されている例が文書化されているため、スレッドの競合状態が発生する可能性があります。ファイルが利用可能であることを確認してから使用しようとすると、その時点でスローされる可能性があり、悪意のあるユーザーがコードを強制して悪用するために使用する可能性があります。

あなたの最善の策は、ファイルハンドルを取得しようとする try catch / finally です。

try
{
   using (Stream stream = new FileStream("MyFilename.txt", FileMode.Open))
   {
        // File/Stream manipulating code here
   }
} catch {
  //check here why it failed and ask user to retry if the file is in use.
}
于 2009-05-18T06:57:05.370 に答える
97

これを使用して、ファイルがロックされているかどうかを確認します。

using System.IO;
using System.Runtime.InteropServices;
internal static class Helper
{
const int ERROR_SHARING_VIOLATION = 32;
const int ERROR_LOCK_VIOLATION = 33;

private static bool IsFileLocked(Exception exception)
{
    int errorCode = Marshal.GetHRForException(exception) & ((1 << 16) - 1);
    return errorCode == ERROR_SHARING_VIOLATION || errorCode == ERROR_LOCK_VIOLATION;
}

internal static bool CanReadFile(string filePath)
{
    //Try-Catch so we dont crash the program and can check the exception
    try {
        //The "using" is important because FileStream implements IDisposable and
        //"using" will avoid a heap exhaustion situation when too many handles  
        //are left undisposed.
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None)) {
            if (fileStream != null) fileStream.Close();  //This line is me being overly cautious, fileStream will never be null unless an exception occurs... and I know the "using" does it but its helpful to be explicit - especially when we encounter errors - at least for me anyway!
        }
    }
    catch (IOException ex) {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex)) {
            // do something, eg File.Copy or present the user with a MsgBox - I do not recommend Killing the process that is locking the file
            return false;
        }
    }
    finally
    { }
    return true;
}
}

パフォーマンス上の理由から、同じ操作でファイルの内容を読み取ることをお勧めします。ここではいくつかの例を示します。

public static byte[] ReadFileBytes(string filePath)
{
    byte[] buffer = null;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
                sum += count;  // sum is a buffer offset for next reading

            fileStream.Close(); //This is not needed, just me being paranoid and explicitly releasing resources ASAP
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }
    return buffer;
}

public static string ReadFileTextWithEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0)
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            //Depending on the encoding you wish to use - I'll leave that up to you
            fileContents = System.Text.Encoding.Default.GetString(buffer);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    { }     
    return fileContents;
}

public static string ReadFileTextNoEncoding(string filePath)
{
    string fileContents = string.Empty;
    byte[] buffer;
    try
    {
        using (FileStream fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
        {
            int length = (int)fileStream.Length;  // get file length
            buffer = new byte[length];            // create buffer
            int count;                            // actual number of bytes read
            int sum = 0;                          // total number of bytes read

            // read until Read method returns 0 (end of the stream has been reached)
            while ((count = fileStream.Read(buffer, sum, length - sum)) > 0) 
            {
                sum += count;  // sum is a buffer offset for next reading
            }

            fileStream.Close(); //Again - this is not needed, just me being paranoid and explicitly releasing resources ASAP

            char[] chars = new char[buffer.Length / sizeof(char) + 1];
            System.Buffer.BlockCopy(buffer, 0, chars, 0, buffer.Length);
            fileContents = new string(chars);
        }
    }
    catch (IOException ex)
    {
        //THE FUNKY MAGIC - TO SEE IF THIS FILE REALLY IS LOCKED!!!
        if (IsFileLocked(ex))
        {
            // do something? 
        }
    }
    catch (Exception ex)
    {
    }
    finally
    {
    }

    return fileContents;
}

自分で試してみてください:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
于 2012-06-16T02:31:09.753 に答える
7

おそらく、 FileSystemWatcherを使用して Changed イベントを監視できます。

私はこれを自分で使用したことはありませんが、試してみる価値があるかもしれません。この場合、filesystemwatcher が少し重いことが判明した場合は、try/catch/sleep ループを使用します。

于 2009-05-18T06:52:09.807 に答える
7

ストリームが利用可能になるとすぐに、ストリームを提供するタスクを返すことができます。これは単純化されたソリューションですが、出発点としては適切です。スレッドセーフです。

private async Task<Stream> GetStreamAsync()
{
    try
    {
        return new FileStream("sample.mp3", FileMode.Open, FileAccess.Write);
    }
    catch (IOException)
    {
        await Task.Delay(TimeSpan.FromSeconds(1));
        return await GetStreamAsync();
    }
}

このストリームは通常どおり使用できます。

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
于 2015-10-20T12:33:01.467 に答える
6

上記の受け入れられた回答には、ファイルが FileShare.Read モードで書き込み用に開かれている場合、またはファイルに読み取り専用属性がある場合、コードが機能しないという問題があります。この変更されたソリューションは最も確実に機能しますが、次の 2 つの点に注意する必要があります (受け入れられたソリューションにも当てはまります)。

  1. 書き込み共有モードで開かれたファイルでは機能しません
  2. これはスレッド化の問題を考慮していないため、ロックダウンするか、スレッド化の問題を個別に処理する必要があります。

上記を念頭に置いて、これはファイルが書き込み用にロックされている、読み取りを防ぐためにロックされているかを確認します。

public static bool FileLocked(string FileName)
{
    FileStream fs = null;

    try
    {
        // NOTE: This doesn't handle situations where file is opened for writing by another process but put into write shared mode, it will not throw an exception and won't show it as write locked
        fs = File.Open(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None); // If we can't open file for reading and writing then it's locked by another process for writing
    }
    catch (UnauthorizedAccessException) // https://msdn.microsoft.com/en-us/library/y973b725(v=vs.110).aspx
    {
        // This is because the file is Read-Only and we tried to open in ReadWrite mode, now try to open in Read only mode
        try
        {
            fs = File.Open(FileName, FileMode.Open, FileAccess.Read, FileShare.None);
        }
        catch (Exception)
        {
            return true; // This file has been locked, we can't even open it to read
        }
    }
    catch (Exception)
    {
        return true; // This file has been locked
    }
    finally
    {
        if (fs != null)
            fs.Close();
    }
    return false;
}
于 2015-10-15T13:34:59.510 に答える
6

私は最近この問題に遭遇し、これを見つけました: https://docs.microsoft.com/en-us/dotnet/standard/io/handling-io-errors .

IOExceptionここで、Microsoft は、ロックされたファイルが原因であったかどうかを確認するための次の方法について説明しています。

catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32 ) {
    Console.WriteLine("There is a sharing violation.");
}
于 2021-04-18T00:40:14.207 に答える
5
static bool FileInUse(string path)
    {
        try
        {
            using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate))
            {
                fs.CanWrite
            }
            return false;
        }
        catch (IOException ex)
        {
            return true;
        }
    }

string filePath = "C:\\Documents And Settings\\yourfilename";
bool isFileInUse;

isFileInUse = FileInUse(filePath);

// Then you can do some checking
if (isFileInUse)
   Console.WriteLine("File is in use");
else
   Console.WriteLine("File is not in use");

お役に立てれば!

于 2011-08-22T16:01:58.190 に答える
4

これは、私が知る限り、受け入れられた回答と同じことを行いますが、コードが少ないコードです。

    public static bool IsFileLocked(string file)
    {
        try
        {
            using (var stream = File.OpenRead(file))
                return false;
        }
        catch (IOException)
        {
            return true;
        }        
    }

ただし、次の方法で行う方がより堅牢だと思います。

    public static void TryToDoWithFileStream(string file, Action<FileStream> action, 
        int count, int msecTimeOut)
    {
        FileStream stream = null;
        for (var i = 0; i < count; ++i)
        {
            try
            {
                stream = File.OpenRead(file);
                break;
            }
            catch (IOException)
            {
                Thread.Sleep(msecTimeOut);
            }
        }
        action(stream);
    }
于 2016-08-22T20:41:25.453 に答える
4

私が知っている唯一の方法は、あまり高速ではない Win32 排他的ロック API を使用することですが、例は存在します。

ほとんどの人は、これに対する簡単な解決策として、try/catch/sleep ループを実行するだけです。

于 2009-05-18T06:42:31.010 に答える
-1

ファイルを一時ディレクトリに移動/コピーしてみてください。可能であれば、ロックがなく、ロックを取得せずに一時ディレクトリで安全に作業できます。それ以外の場合は、x 秒後にもう一度移動してみてください。

于 2009-05-18T09:11:04.440 に答える
-2

私はこの回避策を使用しますが、IsFileLocked 関数でファイルのロックをチェックしてからファイルを開くまでの間にタイムスパンがあります。このタイムスパンでは、他のスレッドがファイルを開くことができるため、IOException が発生します。

そのため、これに追加のコードを追加しました。私の場合、XDocument をロードしたい:

        XDocument xDoc = null;

        while (xDoc == null)
        {
            while (IsFileBeingUsed(_interactionXMLPath))
            {
                Logger.WriteMessage(Logger.LogPrioritet.Warning, "Deserialize can not open XML file. is being used by another process. wait...");
                Thread.Sleep(100);
            }
            try
            {
                xDoc = XDocument.Load(_interactionXMLPath);
            }
            catch
            {
                Logger.WriteMessage(Logger.LogPrioritet.Error, "Load working!!!!!");
            }
        }

どう思いますか?私は何かを変えることができますか?IsFileBeingUsed 関数をまったく使用する必要がなかったのでしょうか。

ありがとう

于 2012-01-17T15:45:31.450 に答える