質問

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

これを回避する方法を見つけたいのですが、グーグルで調べても、例外処理を使用してチェックを作成することしか見つかりませんでした。これは私の宗教に反するので、もっと良い方法がある人はいないだろうかと思いました。

役に立ちましたか?

解決

このソリューションに関する注意を更新しました:で確認中 FileAccess.ReadWrite 読み取り専用ファイルの場合は失敗するため、ソリューションは次のように変更されました。 FileAccess.Read. 。で確認しようとしているため、このソリューションは機能しますが、 FileAccess.Read ファイルに書き込みまたは読み取りのロックがある場合、この解決策は失敗しますが、ファイルに書き込みまたは読み取りのロックがない場合、この解決策は機能しません。FileShare.Read または FileShare.Write アクセスで (読み取りまたは書き込みのために) 開かれています。

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

例外を使用することについて躊躇する気持ちは理解できますが、常に例外を避けることはできません。

protected virtual bool IsFileLocked(FileInfo file)
{
    FileStream stream = null;

    try
    {
        stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
    }
    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;
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }

    //file is not locked
    return false;
}

他のヒント

あなたはセキュリティ上の脆弱性として使用されている。この例が記載されています。この上のスレッド競合状態に苦しむことができます。あなたは、ファイルが利用可能であることを確認しますが、それを試してみて使用する場合は、悪意のあるユーザーがあなたのコード内で強制的に活用するために使用することができ、その点、で投げることができます。

あなたの最善の策は、ファイルハンドルを取得しようとしている最後に/トライキャッチされます。

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.
}

ファイルがロックされているかどうかを確認するために使用します:

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");

おそらく、あなたは FileSystemWatcher と時計を使用することができますChangedイベントのために。

私はこれを自分自身を使用していないが、それはショットの価値があるかもしれません。 filesystemwatcher、この場合のために少し重いことが判明した場合、私はのtry / catch /スリープループのために行くだろう。

意図したとおりに

ただ、例外を使用します。ファイルが使用中であることを受け入れて、あなたの行動が終了するまで繰り返し、もう一度試してください。あなたが行動する前の状態をチェックする任意のサイクルを無駄にしてはいけないので、これはまた、最も効率的である。

例えば、以下の機能を使用して

TimeoutFileAction(() => { System.IO.File.etc...; return null; } );

再利用可能な方法その2秒後にタイムアウトします。

private T TimeoutFileAction<T>(Func<T> func)
{
    var started = DateTime.UtcNow;
    while ((DateTime.UtcNow - started).TotalMilliseconds < 2000)
    {
        try
        {
            return func();                    
        }
        catch (System.IO.IOException exception)
        {
            //ignore, or log somewhere if you want to
        }
    }
    return default(T);
}

私の知っている唯一の方法はあまりにもスピーディーでないWin32の排他ロックAPIを使用することであるが、例が存在します。

ほとんどの人は、これに対する簡単な解決策のために、単にキャッチ/スリープループ/試してます。

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");

この情報がお役に立てば幸い!

あなたは、すぐにそれが利用可能になったあなたにストリームを与えるタスクを返すことができます。それは単純化されたソリューションですが、それは良い出発点です。それは、スレッドセーフです。

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);
}

上記の受け入れられた回答には、ファイルが 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;
}

動作するスリーライナーとは別に、参考までに:ご希望の場合は、 本格的な 情報 - Microsoft Dev Center に小さなプロジェクトがあります。

https://code.msdn.microsoft.com/windowsapps/プロセスを知る方法-704839f4

はじめに:

.NETフレームワーク4.0で開発されたC#サンプルコードは、ファイルにロックを持っているプロセスを見つけるのに役立ちます。 RmStartSession RSTRTMGR.DLLに含まれる関数は、RestArt Managerセッションの作成に使用されており、RETURN結果に従って、Win32Exceptionオブジェクトの新しいインスタンスが作成されます。リソースを再起動マネージャーセッションに登録した後 RmRegisterリソース 関数、 RmGetList 関数は呼び出されて、アプリケーションが何を列挙しているかを確認するために呼び出されます RM_PROCESS_INFO 配列。

「Restart Manager Session」に接続することで動作します。

Restart Managerは、セッションに登録されたリソースのリストを使用して、どのアプリケーションとサービスをシャットダウンして再起動する必要があるかを決定します。 リソースは、ファイル名、サービスショートネーム、またはrm_unique_process構造によって識別できます 実行中のアプリケーションについて説明します。

少しかもしれない 過剰設計された あなたの特定のニーズに合わせて...でも、そういうことなら あなた さあ、vs-project を手に入れてください。

ここで限り私ができるように最善のtellが受け入れ答えとしてではなく、より少ないコードで同じことを行い、いくつかのコードがあります

    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);
    }

あなたは、複数のアプリケーションからファイルにアクセスするための私のライブラリを使用することができます。

あなたはnugetからそれをインストールすることができます。インストール・パッケージXabe.FileLock

あなたはそれについての詳細な情報が必要な場合は、

をチェック https://github.com/tomaszzmuda/Xabe.FileLockする

ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
    using(fileLock)
    {
        // file operations here
    }
}
このオブジェクトのための排他的なファイルをロックすることができた場合にのみ、

fileLock.Acquireメソッドはtrueを返します。 しかし、アップロードファイルも、ファイルロックでそれを行う必要がありますアプリ。 オブジェクトがアクセスできないmetodある場合はfalseを返します。

私の経験では、通常、その後、空想何かをした後、「保護された」ファイルを使用してファイルを「守る」、これをやりたいです。あなたはこのように使用する1つのファイルだけを持っている場合は、ジェレミー・トンプソンの回答で説明していますトリックを使用することができます。あなたは(たとえば、たとえばインストーラを書いているとき)のファイルの多くにこれを実行しようとした場合しかし、あなたは傷のかなりのためにしています。

これを解決することができる非常にエレガントな方法は、いずれかのファイルが存在し、それが使われている場合は、ファイルシステムを使用して、フォルダ名を変更することはできないという事実を使用することです。同じファイルシステム内のフォルダを維持し、それが魅力のように動作します。

あなたは、これが悪用される可能性が明白な方法を知っておく必要があることに注意してください。すべての後に、ファイルがロックされません。また、失敗するあなたのMove操作につながることができ、他の理由があることに注意してください。明らかに(MSDN)を取り扱う適切なエラーはここに助けることができます。

var originalFolder = @"c:\myHugeCollectionOfFiles"; // your folder name here
var someFolder = Path.Combine(originalFolder, "..", Guid.NewGuid().ToString("N"));

try
{
    Directory.Move(originalFolder, someFolder);

    // Use files
}
catch // TODO: proper exception handling
{
    // Inform user, take action
}
finally
{
    Directory.Move(someFolder, originalFolder);
}

個々のファイルについては、私はジェレミー・トンプソンによって投稿ロックの提案に固執すると思います。

私は、これは任意のWTFの反射をトリガーするかどうかを確認するには興味があります。私が作成し、その後、コンソールアプリケーションからPDFドキュメントを起動するプロセスを持っています。しかし、私は、ユーザーが最初に、以前に生成されたファイルを閉じずに同じファイルを生成し、プロセスを複数回実行した場合、アプリは例外をスローし、死ぬもろさを扱いました。ファイル名が販売見積番号に基づいているので、これはかなり頻繁に発生しました。

というよりも、このような無様なやり方で失敗し、私は自動インクリメントのファイルのバージョンに依存することを決定します:

private static string WriteFileToDisk(byte[] data, string fileName, int version = 0)
{
    try
    {
        var versionExtension = version > 0 ? $"_{version:000}" : string.Empty;
        var filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"{fileName}{versionExtension}.pdf");
        using (var writer = new FileStream(filePath, FileMode.Create))
        {
            writer.Write(data, 0, data.Length);
        }
        return filePath;
    }
    catch (IOException)
    {
        return WriteFileToDisk(data, fileName, ++version);
    }
}

おそらくいくつかのより多くのケアは、私が正しいのIOException(複数可)を引くよ確実にするためにcatchブロックに与えることができます。これらのファイルはとにかく一時的であることを意図しているので、私はおそらくまた、起動時にアプリケーションのストレージをクリアします。

私は、これはファイルが使用中であるが、これは確かに私はので、おそらくそれは他の誰かに有用であろうここに到着したときに解決するために探していた問題であれば、単純にチェックするのOPの質問の範囲を超えて実現しています。

このヘルプたい何か?

var fileWasWrittenSuccessfully = false;
while (fileWasWrittenSuccessfully == false)
{
    try
    {
        lock (new Object())
        {
            using (StreamWriter streamWriter = new StreamWriter(filepath.txt"), true))
            {
                streamWriter.WriteLine("text");
            }
        }

        fileWasWrittenSuccessfully = true;
    }
    catch (Exception)
    {

    }
}

私はかつて、オンライン・バックアップ・アーカイブにPDFファイルをアップロードする必要がありました。ユーザーが別のプログラム(たとえば、PDFリーダーなど)で開いているファイルを持っていた場合でも、バックアップは失敗します。私の急いで、私はこのスレッドでトップの答えのいくつかを試みたが、それらを動作させることができませんでした。私はのを独自のディレクトリにPDFファイルを移動しようとしていたために仕事をした何か。私は、ファイルが別のプログラムで開いた場合、これは失敗することを発見し、移動が成功した場合は何も、リストア操作は、それが別のディレクトリに移動された場合があることになるとして必要ありませんが存在することになります。私はそれが他人の具体的な使用例のために有用である可能性がある場合には、私の基本的な解決策を投稿したい。

string str_path_and_name = str_path + '\\' + str_filename;
FileInfo fInfo = new FileInfo(str_path_and_name);
bool open_elsewhere = false;
try
{
    fInfo.MoveTo(str_path_and_name);
}
catch (Exception ex)
{
    open_elsewhere = true;
}

if (open_elsewhere)
{
    //handle case
}

試してみて、一時ディレクトリにファイルをコピー/移動します。あなたができるならば、それは何のロックを持っていない、あなたは安全にロックを取得せずに一時ディレクトリで作業することができます。そうでなければ、単にx秒で再びそれを移動しようとします。

私はこの回避策を使用しますが、私は、ファイルを開いたとき、私は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機能を使用する必要はありませんでした?

おかげ

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top