it-swarm.asia

Bir dosyanın kullanımda olup olmadığını kontrol etmenin bir yolu var mı?

C # 'da sürekli olarak 1 resim dosyasına erişmesi gereken bir program yazıyorum. Çoğu zaman çalışır, ancak bilgisayarım hızlı çalışıyorsa, dosya sistemine geri kaydedilmeden önce dosyaya erişmeye çalışır ve bir hata verir: "Başka bir işlem tarafından kullanılan dosya" .

Bununla ilgili bir yol bulmak isterdim, ancak tüm Googling'im yalnızca istisna işlemeyi kullanarak kontroller üretti. Bu benim dinime aykırı, bu yüzden birinin daha iyi yapmasının bir yolu olup olmadığını merak ediyordum.

743
Dawsy

Bu çözüm hakkında güncellenmiş NOT : Salt Okunur dosyalar için FileAccess.ReadWrite ile kontrol etmek başarısız olur, böylece çözüm FileAccess.Read ile denetlenecek şekilde değiştirilir. Bu çözüm işe yarıyor çünkü FileAccess.Read ile kontrol etmeye çalışmak, dosyada Yazma veya Okuma kilidi varsa başarısız olur, ancak dosyada Yazma veya Okuma kilidi yoksa, bu çözüm işe yaramaz. FileShare.Read veya FileShare.Write erişimi ile açıldı (okuma veya yazma için).

ORİJİNAL: Bu kodu son birkaç yıldır kullanıyorum ve onunla herhangi bir sorun yaşamadım.

İstisnaları kullanma konusundaki tereddütünüzü anlayın, ancak her zaman onlardan kaçınamazsınız:

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

Bunun bir güvenlik açığı olarak kullanıldığına dair belgelenmiş örneklerin bulunduğu bir konu yarış durumundan muzdarip olabilirsiniz. Dosyanın kullanılabilir olduğunu kontrol ederseniz, ancak daha sonra kullanmayı deneyin ve kullanmaya çalışın, kötü niyetli bir kullanıcının kodunuzu zorlamak ve istismar etmek için kullanabileceği bir noktaya atabilirsiniz.

En iyi bahis, bir tanıtıcı yakalama/nihayet dosya tanıtıcısını elde etmeye çalışır.

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

Bir dosyanın kilitli olup olmadığını kontrol etmek için bunu kullanın:

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

Performans nedeniyle, aynı işlemdeki dosya içeriğini okumanızı tavsiye ederim. İşte bazı örnekler:

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

Kendin dene:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
82
Jeremy Thompson

Belki bir FileSystemWatcher kullanabilir ve Değiştirilmiş olayı izleyebilirsiniz.

Bunu kendim kullanmadım, ancak denemeye değer olabilir. Dosya sistemi izleyicisinin bu durumda biraz ağır olduğu ortaya çıkarsa, dene/yakala/uyku döngüsüne giderdim.

6
Karl Johan

Sadece istisnayı istediğiniz gibi kullanın. Dosyanın kullanımda olduğunu kabul edin ve işleminiz tamamlanıncaya kadar tekrar tekrar deneyin. Bu aynı zamanda en verimli olanıdır çünkü harekete geçmeden önce durumu kontrol eden herhangi bir çevrimi boşa harcamazsınız.

Aşağıdaki işlevi kullanın, örneğin

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

2 saniye sonra zaman aşımına uğrayan yeniden kullanılabilir yöntem

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

Kullanılabilir olur olmaz size bir akış veren bir görev döndürebilirsiniz. Basitleştirilmiş bir çözüm, ancak iyi bir başlangıç ​​noktasıdır. İplik güvenli.

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

Bu akışı her zamanki gibi kullanabilirsiniz:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
4
Ivan Branets
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");

Bu yardımcı olur umarım!

4
Julian

bildiğim tek yol çok hızlı olmayan ama özel örnekler veren Win32 özel kilit API'sini kullanmak.

Çoğu insan, bunun için basit bir çözüm bulmak için/yakalamak/uyku döngüleri denemek için.

4
Luke Schafer

Birden fazla uygulamadan dosyalara erişmek için kitaplığımı kullanabilirsiniz.

Nuget'ten yükleyebilirsiniz: Install-Package Xabe.FileLock

Bu konuda daha fazla bilgi istiyorsanız, check 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 yöntemi yalnızca bu nesne için özel bir dosyayı kilitleyebiliyorsa true değerini döndürür .Ama dosya yükleyen uygulamanın da dosya kilidinde yapması gerekir . Nesne erişilemezse, metod false döndürür.

2
Tomasz Żmuda

Yukarıdaki kabul edilen cevaplar, dosyanın bir FileShare.Read moduyla yazmak için açılmış olması durumunda veya dosyanın Salt Okunur bir özniteliği varsa, kodun çalışmadığı bir soruna neden olur. Bu değiştirilmiş çözüm, akılda tutulması gereken iki şeyle (güvenilir çözüm için de geçerlidir) en güvenilir şekilde çalışır:

  1. Yazma paylaşım moduyla açılan dosyalar için çalışmaz
  2. Bu, diş açma sorunlarını dikkate almaz, bu nedenle kilitlemeniz veya diş açma sorunlarını ayrı ayrı ele almanız gerekir.

Yukarıdakileri akılda tutarak, dosyanın yazma için kilitli veya okumayı önlemek için kilitli olup olmadığını kontrol eder:

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

En iyi söyleyebileceğim kadarıyla, kabul edilen cevapla aynı şeyi yapan, ancak daha az kod içeren bir kod:

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

Ancak, aşağıdaki şekilde yapmanın daha sağlam olduğunu düşünüyorum:

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

Tecrübelerime göre, genellikle bunu yapmak istersiniz, daha sonra fantezi bir şeyler yapmak için dosyalarınızı 'koruyun' ve 'korunan' dosyaları kullanın. Bu şekilde kullanmak istediğiniz tek bir dosyanız varsa, cevabında Jeremy Thompson tarafından açıklanan hileyi kullanabilirsiniz. Ancak, bunu birçok dosyada yapmaya çalışırsanız (örneğin, bir yükleyici yazarken), biraz incinmişsiniz demektir.

Bunu çözmenin çok zarif bir yolu, dosya sisteminizin, eğer oradaki dosyalardan biri kullanılıyorsa, bir klasör adını değiştirmenize izin vermeyeceği gerçeğini kullanmaktır. Klasörü aynı dosya sisteminde tutun, böylece bir cazibe gibi çalışacaktır.

Bunun istismar edilebileceği bariz yolların farkında olmanız gerektiğini unutmayın. Sonuçta, dosyalar kilitlenmeyecek. Ayrıca Move işleminizin başarısız olmasına neden olabilecek başka nedenlerin olduğunu unutmayın. Açıkçası doğru hata işleme (MSDN) burada yardımcı olabilir.

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

Bireysel dosyalar için, Jeremy Thompson tarafından gönderilen kilitleme önerisine sadık kaldım.

1
atlaste

3-liner çalışmak dışında ve sadece referans olarak: tam üfleme bilgisini istiyorsanız - Microsoft Dev Center'da küçük bir proje var:

https://code.msdn.Microsoft.com/windowsapps/How-to-know-the-process-704839f4

Giriş'den:

.NET Framework 4.0'da geliştirilen C # örnek kodu, Bir dosya üzerinde kilitleme olan işlemin hangisi olduğunu bulmak . Rstrtmgr.dll dosyasına dahil edilen RmStartSession işlevi bir yeniden başlatma yöneticisi oturumu oluşturmak için kullanılır ve iadesine göre. Sonuç yeni bir Win32Exception nesnesi örneği oluşturulur. Sonra kaynakların bir Restart Manager oturumuna .__ ile kaydedilmesi. RmRegisterRescources işlevi, RmGetList işlevi kontrol etmek için çağrılır. uygulamalar ne numaralandırılarak belirli bir dosyayı kullanıyor? RM_PROCESS_INFO dizisi.

"Yeniden Başlat Yöneticisi Oturumu" na bağlanarak çalışır.

Yeniden Başlatma Yöneticisi, oturum için kayıtlı olan kaynakların listesini hangi uygulamaların ve hizmetlerin kapatılıp yeniden başlatılması gerektiğini belirler. Kaynaklar dosya isimleri, servis kısa isimleri veya .__ ile tanımlanabilir. Çalışan uygulamaları tanımlayan RM_UNIQUE_PROCESS yapılar.

Özel ihtiyaçlarınız için biraz yenili olabilir .. Ama eğer [ istediğiniz buysa, devam edin ve vs projesini alın.

1
Bernhard

Bunun herhangi bir WTF refleksini tetikleyip tetiklemediğini görmek istiyorum. Konsol uygulamasından bir PDF belgesi oluşturan ve sonra başlatan bir işlemim var. Bununla birlikte, eğer kullanıcının işlemi birden çok kez çalıştırması, daha önce oluşturulan dosyayı kapatmadan aynı dosyayı oluşturması durumunda uygulamanın bir istisna atacağı ve öleceği bir zayıflıkla uğraşıyordum. Bu oldukça sık rastlanan bir durumdu çünkü dosya isimleri satış teklif numaralarına dayanıyordu.

Böylesine nezaketsiz bir şekilde başarısız olmak yerine, otomatik artan dosya sürümüne güvenmeye karar verdim:

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

Muhtemelen, doğru IOException (ları) yakaladığımdan emin olmak için catch bloğuna biraz daha özen gösterilebilir. Muhtemelen de başlangıçta uygulama depolama alanını temizleyeceğim, çünkü bu dosyalar zaten geçici olacak şekilde tasarlandı.

Bunun OP'nin basitçe dosyanın kullanımda olup olmadığını kontrol etme konusundaki sorunun ötesine geçtiğini biliyorum ama bu gerçekten de buraya geldiğimde çözmek istediğim bir problemdi, bu yüzden belki de başkası için faydalı olacaktır.

0
Vinney Kelly

Böyle bir şey yardımcı olur mu?

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)
    {

    }
}
0
Tadej

Bir keresinde PDF'leri çevrimiçi bir yedekleme arşivine yüklemem gerekiyordu. Ancak kullanıcı dosyayı başka bir programda (PDF okuyucu gibi) açarsa yedekleme başarısız olur. Acelemce, bu konudaki en iyi yanıtların birkaçını denedim ama işe yaramadı. Benim için iş, PDF dosyasını kendi dizini konumuna getirmeye çalışıyordu. Dosya başka bir programda açık olsaydı, bunun başarısız olacağını ve taşıma başarılı olursa, ayrı bir dizine taşındığında olduğu gibi geri yükleme işlemi yapılmayacağını öğrendim. Başkalarının özel kullanım durumlarında faydalı olabileceği için temel çözümümü göndermek istiyorum.

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