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


846

C # 'da tekrar tekrar 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 atar: "Başka bir işlem tarafından kullanılan dosya" .

Bu konuda bir yol bulmak istiyorum, ancak tüm Google'ım sadece istisna işleme kullanarak kontroller oluşturdu. Bu benim dinime aykırı, bu yüzden daha iyi bir yolu olup olmadığını merak ediyordum?


30
Pekala, sistemdeki tüm açık tutamaçları inceleyerek test edebilirsiniz. Ancak, Windows çok görevli bir işletim sistemi olduğundan, kodu çalıştırdıktan hemen sonra dosyanın açık olup olmadığını belirlemek için bir olasılık vardır, bir işlem kodu bu dosyayı kullanmaya başlar, sonra denediğiniz zamana kadar kullanın, bir hata alırsınız. Ancak, önce kontrol etmekle ilgili yanlış bir şey yoktur; sadece gerçekten ihtiyacınız olduğunda kullanılmadığını varsaymayın.
BobbyShaftoe

3
Ama sadece bu özel konu için; Dosya tanıtıcılarını incelemenizi ve önceden belirlenmiş sayıda denemenizi öneririm, başarısız olmadan önce 3-5 deyin.
BobbyShaftoe

Bu görüntü dosyası nasıl oluşturulur? Nesil tamamlanana kadar programınızı durdurabilir / uyku / duraklatabilir misiniz? Bu durumun üstesinden gelmenin en üstün yoludur. Değilse, istisna işlemeyi kullanmaktan kaçınabileceğinizi sanmıyorum.
Catchwa

İstisnaların tümü, kasıtlı olarak başarısızlık olasılığını ortadan kaldırmazken potansiyel olarak tehlikeli bir şey yaparak bazı varsayımları kontrol etmiyor mu?
jwg

26
Felsefenizin istisnalar hakkında kötü bir anlayışı vardır. Çoğu insan istisnaların kıyamet-kutsal-şey-yanlış-ölmek-ölmek-ölmek demek olduğunu düşünür. İstisna demek ... istisna. Bu, "işlemeniz" (veya hesaba katmanız) gereken olağanüstü bir şey olduğu anlamına gelir. Belki veri erişimi için yeniden denemeye devam etmek istersiniz, belki kullanıcının bağlantı kuramayacağınızı bilmesi gerekir. Ne yaparsın? ConnectionFailedException özel durumunu işler ve kullanıcıyı uyarırsınız, bu nedenle belki bir saat sonra denemeyi bırakacaklar ve kablonun fişinin çekildiğini fark edeceklerdir.
Lee Louviere

Yanıtlar:


542

Bu çözümde güncellenen NOT : ile FileAccess.ReadWriteokuma işlemi, Salt Okunur dosyalar için başarısız olur, böylece çözüm denetlenecek şekilde değiştirilir FileAccess.Read. Bu çözüm FileAccess.Read, dosyanın üzerinde bir Yazma veya Okuma kilidi varsa kontrol etmeye çalışmak başarısız olacağından, ancak dosyanın üzerinde Yazma veya Okuma kilidi yoksa bu çözüm çalışmaz, yani açılmışsa (okuma veya yazma için) FileShare.Read veya FileShare.Write erişimi ile.

ORİJİNAL: Son birkaç yıldır bu kodu kullandım ve herhangi bir sorun yaşamadım.

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

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

60
Bu harika bir çözüm, ancak bir yorumum var - Dosyayı salt okunur hale gelirse ReadWrite her zaman başarısız olacağından FileAccess.Read erişim moduyla Dosyayı açamayabilirsiniz.
adeel825

220
-1. Bu kötü bir yanıttır, çünkü dosya IsFileLocked içinde kapatıldıktan sonra ve iş parçacığınız dosyayı açma şansı elde etmeden önce başka bir iş parçacığı / işlem tarafından kilitlenebilir.
Polyfun

16
Bence bu harika bir cevap. Bir şekilde bunu kullanıyorum uzatma yöntemi la á public static bool IsLocked(this FileInfo file) {/*...*/}.
Manuzor

54
@ChrisW: neler olduğunu merak ediyor olabilirsiniz. Telaşlanma. Sadece Günlük WTF topluluğunun gazabına maruz davranıyorsun: thedailywtf.com/Comments/...
Pierre Lebeaupin

16
@ChrisW Neden bu kötü bir şey. Bu topluluk iyi ve kötü yanıtlara dikkat çekmek için burada. Bir grup profesyonel bunun kötü bir şey olduğunu fark ederse ve aşağı oylamaya katılırsa, site WAI'dir. Negatif olmadan önce, bu makaleyi okuduğunuzda, yanlış cevabı küçümsememek için "doğru cevabı onayla" derler. Onların yorumlarını açıklamalarda da açıklamalarını ister misiniz? Beni başka bir iyi siteye tanıttığın için teşekkürler!
Lee Louviere

569

Bu konuda, güvenlik açığı olarak kullanılan belgelenmiş örneklerin olduğu bir iş parçacığı yarış koşulundan muzdarip olabilirsiniz. Dosyanın kullanılabilir olduğunu kontrol ederseniz, ancak denemeyi ve kullanmayı denerseniz, kötü niyetli bir kullanıcının kodunuzu zorlamak ve kullanmak için kullanabileceği bir noktaya atabilirsiniz.

En iyi bahis, dosya tanıtıcısını almaya çalışan bir try catch / nihayet.

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

124
+1. "Bir dosyanın kullanımda olup olmadığını öğrenmek" için% 100 güvenli bir yol yoktur, çünkü kontrolü yaptıktan milisaniye sonra dosya artık kullanımda olmayabilir veya tam tersi. Bunun yerine, dosyayı açmanız ve istisna yoksa kullanmanız yeterlidir.
Sedat Kapanoglu

8
Çok kötü .NET, CAS'ı desteklemiyor. Başarı / başarısızlık döndüren TryOpenFile (Ref FileHandle) gibi bir şey. Her zaman yalnızca istisna işlemeye dayanmayan bir çözüm bulunmalıdır. Microsoft Office'in bunu nasıl yaptığını merak ediyorum.
TamusJRoyce

2
Burada anlaşılması gereken en önemli şey, bu API'nin sadece bir dosya tanıtıcısı almak için windows API'sini kullanmasıdır. Bu nedenle, C API'sinden alınan hata kodunu çevirmeleri ve atmak için bir istisnaya sarmaları gerekir. .Net'te istisna işleme sahibiz, bu yüzden neden kullanmıyorsunuz? Bu şekilde, kodunuza temiz bir ileri yol yazabilir ve hata işlemeyi ayrı bir kod yolunda bırakabilirsiniz.
Spence

36
Using ifadesi, işimi bitirdikten sonra akışın kapalı olmasını sağlamaktır. Sanırım () {} kullanmanın {} nihayet {obj.Dispose ()} denemekten daha az karakter olduğunu göreceksiniz. Ayrıca, nesne başvurunuzu daha fazla yazım kullanan using deyiminin dışında bildirmeniz gerektiğini de göreceksiniz. Açık bir arayüzünüz varsa, yayınlamanız da gerekir. Son olarak en kısa sürede imha etmek istersiniz ve son olarak mantığın UI veya IDispose çağrısı ile ilgisi olmayan uzun süren başka eylemleri olabilir. </rant>
Spence

2
bu, nesnenizi denemenin dışında bildirmeniz ve açıkça imha çağırmanız gerektiği gerçeğini ortadan kaldırmaz, bu da sizin için yapar ve aynı şey anlamına gelir.
Spence

92

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, dosya içeriğini aynı işlemde 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;
}

Kendiniz deneyin:

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

8
Orada çok fazla "sihirli numara" olmasaydı oylarım olurdu. En.wikipedia.org/wiki/Magic_number_(programming )
Kris

3
Biraz vardiya değil, errorCode karşılaştırmalarına atıfta bulunuyordum. şimdi de bundan bahsediyorsun ...
Kris

1
Yakalamanız IOExceptiongenel olarak yerine açık olmalı Exceptionve ardından tür üzerinde bir test olmalıdır.
Askolein

3
@JeremyThompson ne yazık ki IOExceptiongenel olandan sonra belirli koymak . Genel olan, geçen her şeyi yakalayacak ve belirli olan IOExceptionher zaman yalnız olacaktır. Sadece ikisini değiştirin.
Askolein

Bu çözümü seviyorum. Başka bir öneri: Eğer (IsFileLocked (ex)) if için başka bir yakalama içinde eski atmak. Bu daha sonra istisna atarak dosyanın mevcut olmadığı durumu (veya başka bir IOException) ele alır.
shindigo

7

İstisnayı istendiği gibi kullanın. Dosyanın kullanımda olduğunu kabul edin ve işleminiz tamamlanana kadar tekrar tekrar deneyin. Bu aynı zamanda en verimlidir çünkü harekete geçmeden önce durumu kontrol eden hiçbir döngüyü 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);
}

6

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

Bunu kendim kullanmadım, ama denemeye değer olabilir. Dosya sistemi izleyicisi bu durum için biraz ağır olursa, try / catch / sleep loop'u seçerdim.


1
Oluşturulan ve Değiştirilen olaylar bir dosya oluşturma / değiştirme işleminin başlangıcında arttığından, bir FileSystemWatcher kullanmak yardımcı olmaz. Küçük dosyaların bile işletim sistemi tarafından yazılması ve kapatılması için .NET uygulamasının FileSystemEventHandler Geri Arama ile çalışması gerekenden daha fazla zamana ihtiyacı vardır. Bu çok üzücü, ancak dosyaya erişmeden veya istisna döngülerine girmeden önce bekleme süresini tahmin etmekten başka bir seçenek yok ...

FileSystemWatcher aynı anda çok fazla değişikliği işlemez, bu yüzden dikkatli olun.
Ben F

1
BTW, hata ayıklama ve MS kendi FSW "FileSystemWather" dediği iş parçacıkları izlerken fark ettiniz mi? Zaten ne var?
devlord

FileSystemWatcher ile bir Windows hizmeti, işlem kapatmadan önce dosyayı okumaya çalıştığından, tam olarak bu sorunu var.
freedeveloper

6

Bir akış hazır olur olmaz size veren bir görevi geri verebilirsiniz. Bu basitleştirilmiş bir çözüm, ama iyi bir başlangıç ​​noktası. İ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ışı normal şekilde kullanabilirsiniz:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}

3
Bir yığın özyinelemeden taşması kaç saniye sürer GetStreamAsync()?
CAD bloke

@CADbloke, çok iyi bir noktaya değindin. Aslında, bir dosya uzun süre kullanılamıyorsa, örneğimde yığın taşması istisnası olabilir. Bu cevapla ilgili stackoverflow.com/questions/4513438/… , 5 saat içinde istisnayı artırabilir.
Ivan Branets

Kullanım durumlarınızla ilgili olarak, dosyayı okumak için 10 denemenin başarısız olduğunu varsayalım, G / Ç istisnası atmak tercih edilir. Diğer strateji, 10 deneme başarısız olduktan sonra bir saniye daha bekleme süresini artırabilir. Her ikisinin bir karışımını da kullanabilirsiniz.
Ivan Branets

Ben sadece dosyayı kilitli kullanıcı uyarmak (ve yapmak). Genellikle kendileri kilitlediler, bu yüzden muhtemelen bu konuda bir şeyler yapacaklar. Ya da değil.
CAD bloke

Bazı durumlarda, bir dosya henüz hazır olmayabileceğinden yeniden deneme politikasını kullanmanız gerekir. Geçici bir klasördeki görüntüleri indirmek için bir masaüstü uygulaması düşünün. Uygulama indirilmeye başlar ve aynı zamanda bu klasörü dosya gezginde açarsınız. Windows hemen bir küçük resim oluşturmak ve dosyayı kilitlemek istiyor. Aynı zamanda, uygulamanız kilitli görüntüyü başka bir yere değiştirmeye çalışır. Yeniden deneme politikasını kullanmazsanız bir istisna alırsınız.
Ivan Branets

4

bildiğim tek yol, çok hızlı olmayan Win32 özel kilit API'sini kullanmaktır, ancak örnekler vardır.

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


1
Bu API'yi önce dosyayı açmadan kullanamazsınız; bu noktada artık ihtiyacınız yoktur.
Harry Johnston

1
Schrodinger'in dosyası.
TheLastGIS

4
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!


10
Gerçekte yaptığınız kontrol gayet iyi; bir işlevin içine yerleştirmek yanıltıcıdır. Bir dosyayı açmadan önce böyle bir işlevi kullanmak istemezsiniz. Fonksiyonun içinde dosya açılır, kontrol edilir ve kapatılır. Sonra programcı dosyayı kullanmak için hala iyi olduğunu varsayar ve kullanım için açmaya çalışır. Bu kötü çünkü bu dosyayı açmak için sıraya alınmış başka bir işlem tarafından kullanılabilir ve kilitlenebilir. İlk kez açıldığı (kontrol için) ve ikinci kez açıldığı (kullanım için) arasında, işletim sistemi işleminizin planını bozmuş olabilir ve başka bir işlem yürütüyor olabilir.
Lakey

3

Yukarıdaki kabul edilen yanıtlar, bir dosyanın FileShare.Read moduyla yazmak için açılmışsa veya dosyanın Salt Okunur özniteliğine sahip olması durumunda kodun çalışmayacağı bir sorunla karşılaşır. Bu değiştirilmiş çözüm en güvenilir şekilde çalışır ve akılda tutulması gereken iki şey vardır (kabul edilen çözüm için de geçerlidir):

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

Yukarıdakileri göz önünde bulundurarak, dosyanın yazmaya karşı kilitli olup olmadığını 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;
}

Hala kabul edilen cevapla aynı soruna sahip - sadece dosyanın belirli bir zamanda başka bir işlem tarafından kilitlenip kilitlenmediğini söyler , bu da yararlı bilgi değildir. Fonksiyon geri döndüğünde, sonuç zaten güncel olmayabilir!
Harry Johnston

1
bu doğru, sadece herhangi bir zamanda kontrol edilebilir (veya olaylara abone olabilir), bu yaklaşımın kabul edilen çözüm üzerindeki avantajı, salt okunur bir özelliği ve yazma kilidini kontrol edebilmesi ve yanlış bir pozitif döndürmemesi.
rboy

3

3 gömlekli çalışmanın yanı sıra ve sadece referans için: Tam bilgi istiyorsanız - Microsoft Dev Center'da küçük bir proje var:

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

Girişten:

.NET Framework 4.0'da geliştirilen C # örnek kodu, bir dosya üzerinde kilit olan işlemin hangisinin olduğunu bulmaya yardımcı olur. Rstrtmgr.dll içinde bulunan RmStartSession işlevi, yeniden başlatma yöneticisi oturumu oluşturmak için kullanılmıştır ve sonuç sonucuna göre yeni bir Win32Exception nesnesi örneği oluşturulur. Kaynakları RmRegisterRescources işlevi aracılığıyla Restart Manager oturumuna kaydettikten sonra , RM_PROCESS_INFO dizisini numaralandırarak uygulamaların belirli bir dosyayı ne kullandığını kontrol etmek için RmGetList işlevi çağrılır .

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

Yeniden Başlatma Yöneticisi, hangi uygulamaların ve hizmetlerin kapatılması ve yeniden başlatılması gerektiğini belirlemek için oturumla kayıtlı kaynakların listesini kullanır. Kaynaklar dosya adları, hizmet kısa adları veya çalışan uygulamaları tanımlayan RM_UNIQUE_PROCESS yapılarıyla tanımlanabilir.

Biraz olabilir overengineered belirli ihtiyaçlarınız için ... Ama bu da ne olursa sen istemek, devam edip vs-proje yakala.


2

Deneyimlerime göre, genellikle bunu yapmak, ardından süslü bir şey yapmak için dosyalarınızı 'korumak' ve ardından 'korunan' dosyaları kullanmak istiyorsunuz. Eğer böyle kullanmak istediğiniz sadece bir dosyanız varsa, Jeremy Thompson'ın cevabında açıklanan hileyi kullanabilirsiniz. Ancak, bunu bir çok dosyada yapmaya çalışırsanız (örneğin, bir yükleyici yazarken), biraz zarar görürsünüz.

Bunun çözülebilmesinin çok zarif bir yolu, dosya sisteminizin, kullanılan dosyalardan biri varsa bir klasör adını değiştirmenize izin vermemesidir. Klasörü aynı dosya sisteminde tutun ve bir cazibe gibi çalışacaktır.

Bunun istismar edilebileceği açık yollardan haberdar olmanız gerektiğini unutmayın. Sonuçta, dosyalar kilitlenmeyecek. Ayrıca, Moveişleminizin başarısız olmasına neden olabilecek başka nedenlerin de olduğunu unutmayın . Açıkça 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);
}

Tek tek dosyalar için Jeremy Thompson tarafından gönderilen kilitleme önerisine sadık kalacağım.


Merhaba, Cevapların sırası değiştiğinden , bu popüler KG'nin okurları için yukarıda hangi yayını ifade ettiğinizi netleştirebilirsiniz . Teşekkürler.
Jeremy Thompson

1
@JeremyThompson Haklısın, teşekkürler, yazıyı düzenleyeceğim. Çözümü, temel FileShareolarak bir kilidi doğru şekilde kullanmanız ve kontrol etmeniz nedeniyle kullanacağım .
atlaste

2

İşte en iyi söyleyebildiğim kadarıyla kabul edilen cevapla aynı şeyi yapar ama daha az kod ile bazı 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 yapmak 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

Kitaplığımı birden çok uygulamadan dosyalara erişmek için kullanabilirsiniz.

Nuget'ten kurabilirsiniz: Install-Package Xabe.FileLock

Bu konuda daha fazla bilgi edinmek için https://github.com/tomaszzmuda/Xabe.FileLock adresini kontrol edin

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ı kilitleyebilirse true değerini döndürür. Ancak dosya yükleme uygulaması da dosya kilidinde yapmalıdır. Nesneye erişilemiyorsa metod false değerini döndürür.


1
Lütfen cevap olarak sadece bir araç veya kütüphane yayınlamayın. En azından sorunun cevabın kendisinde nasıl çözdüğünü göster .
paper1111

Demo eklendi :) Üzgünüz @ paper1111
Tomasz Żmuda

1
İşbirliği yapmak için dosyayı kullanan tüm işlemleri gerektirir. OP'nin orijinal sorununa uygulanamaz.
Harry Johnston

2

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çmışsa yedekleme başarısız olur. Acelemde, bu konudaki en iyi cevaplardan birkaçını denedim ama işe yaramadım. Benim için işe yarayan PDF dosyasını kendi dizinine taşımaya çalışıyordu . Dosya başka bir programda açık olsaydı bu başarısız olur ve taşıma başarılı olsaydı, ayrı bir dizine taşınmış gibi olacak herhangi bir geri yükleme işlemi gerekli olmayacağını buldum. Başkalarının özel kullanım durumları için yararlı olması durumunda 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

Bunun herhangi bir WTF refleksini tetikleyip tetiklemediğini görmek istiyorum. Bir konsol uygulamasından bir PDF belgesi oluşturan ve daha sonra başlatan bir işlemim var. Ancak, kullanıcı daha önce oluşturulan dosyayı kapatmadan aynı dosyayı üreten işlemi birden çok kez çalıştırırsa, uygulamanın bir istisna fırlatacağı ve öleceği bir zayıflıkla uğraşıyordum. Dosya adları satış teklifi numaralarına dayandığından, bu oldukça sık görülen bir durumdu.

Böyle nankör bir şekilde başarısız olmak yerine, otomatik olarak artırılan dosya sürümlendirmesine 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 catchdoğru IOException (lar) ı yakaladığımdan emin olmak için bloğa biraz daha özen gösterilebilir . Bu dosyalar yine de geçici olarak tasarlandığından, muhtemelen başlangıçta uygulama deposunu temizleyeceğim.

Bunun OP'nin sadece dosyanın kullanımda olup olmadığını kontrol etme sorununun ötesine geçtiğini fark ettim ama bu gerçekten buraya geldiğimde çözmek istediğim problemdi, bu yüzden belki de başka biri için yararlı olacaktır.


0

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

    }
}

-2

Dosyayı geçici bir dizine taşımaya / kopyalamaya çalışın. Yapabiliyorsanız, kilidi yoktur ve geçici direkte kilit almadan güvenli bir şekilde çalışabilirsiniz. Else sadece x saniye içinde tekrar hareket ettirmeye çalışın.


@jcolebrand neyi kilitler? kopyaladığınız kişi? Yoksa temp dir'e koyduğun mu?
Cullub

5
dosyayı kopyalar, başka kimsenin üzerinde çalışmasını beklemezseniz ve geçici dosyayı kullanacaksınız ve birileri kopyaladıktan hemen sonra kilitliyorsa, potansiyel olarak veri kaybettiniz.
jcolebrand

-3

Bu geçici çözümü kullanıyorum, ancak ne zaman ıFileLocked işlevi ile dosya kilitleme kontrol ve ben dosyayı açtığınızda arasında bir zaman aralığı var. Bu zaman aralığında diğer bazı iş parçacığı dosyayı açabilir, bu yüzden IOException alırsınız.

Yani, bunun için ekstra kod ekledim. Benim durumumda XDocument'i yüklemek istiyorum:

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

Ne düşünüyorsun? Bir şeyi değiştirebilir miyim? Belki IsFileBeingUsed işlevini hiç kullanmak zorunda kalmadım?

Teşekkürler


4
IsFileBeingUsed nedir? kaynak kodu IsFileBeingUsed?
Kiquenet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.