문제

1 이미지 파일에 반복적으로 액세스 해야하는 C#에 프로그램을 작성하고 있습니다. 대부분의 시간은 작동하지만 내 컴퓨터가 빠르게 실행되면 파일이 파일 시스템에 저장되어 파일에 액세스하고 오류를 던지려고합니다. "다른 프로세스에서 사용중인 파일".

이 주변의 방법을 찾고 싶습니다. 그러나 모든 인터넷 검색은 예외 처리를 사용하여 수표를 만들었습니다. 이것은 내 종교에 반대하는 것이므로 누군가 더 나은 방법이 있는지 궁금해하고 있습니까?

도움이 되었습니까?

해결책

이 솔루션에 대한 업데이트 된 메모: 확인 FileAccess.ReadWrite 읽기 전용 파일에 실패하여 솔루션이 다음과 같이 수정되었습니다. FileAccess.Read. 이 솔루션은 확인하려고 노력하기 때문에 작동합니다 FileAccess.Read 파일에 쓰기 또는 읽기 잠금 장치가 있으면 실패하지만 파일에 쓰기 또는 읽기 잠금 장치가 없으면이 솔루션이 작동하지 않습니다. 즉, Fileshare와 함께 읽거나 쓰여진 경우 (읽기 또는 쓰기). 읽기 또는 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;
}

다른 팁

보안 취약점으로 사용되는 문서화 된 사례가 문서화 된 사례가있는 스레드 레이스 조건으로 고통받을 수 있습니다. 파일을 사용할 수 있는지 확인하지만 시도하고 사용해 보면 해당 시점에서 던질 수 있습니다.

최선의 방법은 파일 핸들을 얻으려고 시도하는 시도 Catch / MARCH입니다.

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

아마도 당신은 a를 사용할 수 있습니다 FileSystemWatcher 변경된 이벤트를 조심하십시오.

나는 이것을 직접 사용하지 않았지만, 그것은 가치가있을 것입니다. 이 경우 FileSystemWatcher가 약간 무겁게 판명되면 시도/캐치/수면 루프로 이동합니다.

의도 한대로 예외를 사용하십시오. 파일이 사용 중임을 수락하고 동작이 완료 될 때까지 반복적으로 다시 시도하십시오. 행동하기 전에 상태를 점검하는주기를 낭비하지 않기 때문에 이것은 또한 가장 효율적입니다.

예를 들어 아래 기능을 사용하십시오

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 모드로 작성하기 위해 파일이 열린 경우 또는 파일에 읽기 전용 속성이있는 경우 코드가 작동하지 않는 문제가 발생합니다. 이 수정 된 솔루션은 가장 안정적으로 작동하며 두 가지를 염두에 두어야합니다 (허용 솔루션의 경우에도 적용).

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

3 라이너를 제외하고 참조를 위해 : 원하는 경우 완전히 날아 갔다 정보 - Microsoft Dev Center에는 작은 프로젝트가 있습니다.

https://code.msdn.microsoft.com/windowsapps/how-to-know-the-process-704839f4

소개에서 :

.NET Framework 4.0에서 개발 된 C# 샘플 코드는 파일에 잠금이있는 프로세스인지 알아내는 데 도움이됩니다. rmstartsession RSTRTMGR.DLL에 포함 된 함수는 다시 시작 관리자 세션을 작성하는 데 사용되었으며 리턴 결과에 따라 Win32Exception 객체의 새로운 인스턴스가 작성되었습니다. 재시작 관리자 세션에 리소스를 등록한 후 RMREGISTERRESCOURCES 기능, rmgetList 기능이 호출되어 호출됩니다. rm_process_info 정렬.

"다시 시작 관리자 세션"에 연결하여 작동합니다.

재시작 관리자는 세션에 등록 된 리소스 목록을 사용하여 종료 및 다시 시작 해야하는 응용 프로그램 및 서비스를 결정합니다. 리소스는 파일 이름, 서비스 짧은 이름 또는 rm_unique_process 구조로 식별 할 수 있습니다. 실행중인 응용 프로그램을 설명합니다.

조금 일 수 있습니다 과도하게 당신의 특별한 요구에 대해 ...하지만 그게 만약 그렇다면 원하고, 가서 vs-project를 잡아라.

다음은 내가 가장 잘 말할 수있는 한 허용 된 답변과 동일한 일을하지만 코드가 적은 코드입니다.

    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 : PACKAGE 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를 반환합니다.

내 경험에 따르면, 당신은 일반적으로 이것을하고, 파일을 '보호'하여 멋진 일을하고 '보호 된'파일을 사용합니다. 이와 같이 사용하려는 파일이 하나만 있으면 Jeremy Thompson의 답변에 설명 된 트릭을 사용할 수 있습니다. 그러나 많은 파일 (예 : 설치자를 작성할 때) 에서이 작업을 시도하면 상당히 상처를 입을 수 있습니다.

이를 해결할 수있는 매우 우아한 방법은 파일 시스템을 사용하여 사용되는 파일 중 하나가 폴더 이름을 변경할 수 없다는 사실을 사용하는 것입니다. 폴더를 동일한 파일 시스템에 유지하면 매력처럼 작동합니다.

이것이 악용 될 수있는 명백한 방법을 알고 있어야합니다. 결국 파일이 잠겨 있지 않습니다. 또한, 당신의 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);
}

개별 파일의 경우 Jeremy Thompson이 게시 한 잠금 제안을 고수합니다.

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

아마도 더 많은주의를 기울일 수 있습니다 catch 올바른 ioexception (들)을 잡을 수 있도록 블록. 이 파일은 어쨌든 일시적인 것이기 때문에 시작시 앱 스토리지를 지울 것입니다.

나는 이것이 파일이 사용 중인지 간단히 확인하는 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