Gibt es eine Möglichkeit zu überprüfen, ob eine Datei verwendet wird?

846

Ich schreibe ein Programm in C #, das wiederholt auf 1 Bilddatei zugreifen muss. Meistens funktioniert es, aber wenn mein Computer schnell läuft, versucht er, auf die Datei zuzugreifen, bevor sie wieder im Dateisystem gespeichert wurde, und gibt den Fehler "Datei wird von einem anderen Prozess verwendet" aus .

Ich würde gerne einen Weg finden, um dies zu umgehen, aber mein ganzes Googeln hat nur dazu geführt, dass Schecks mithilfe der Ausnahmebehandlung erstellt wurden. Das ist gegen meine Religion, also habe ich mich gefragt, ob jemand einen besseren Weg hat, es zu tun?

Dawsy
quelle
30
In Ordnung, Sie können es testen, indem Sie alle offenen Griffe im System untersuchen. Da es sich bei Windows jedoch um ein Multitasking-Betriebssystem handelt, besteht die Möglichkeit, dass unmittelbar nach dem Ausführen des Codes festgestellt wird, ob die Datei geöffnet ist und dies nicht der Fall ist, und dass ein Prozesscode diese Datei verwendet, sobald Sie dies versuchen Verwenden Sie es, Sie erhalten eine Fehlermeldung. Es ist jedoch nichts Falsches daran, zuerst zu überprüfen. Gehen Sie einfach nicht davon aus, dass es nicht verwendet wird, wenn Sie es tatsächlich benötigen.
BobbyShaftoe
3
Aber nur für dieses spezielle Thema; Ich würde empfehlen, die Dateihandles nicht zu untersuchen und nur einige voreingestellte Male zu versuchen, z. B. 3-5, bevor Sie fehlschlagen.
BobbyShaftoe
Wie wird diese Bilddatei generiert? Können Sie Ihr Programm anhalten / schlafen / anhalten, bis die Generierung abgeschlossen ist? Das ist bei weitem eine überlegene Art, mit der Situation umzugehen. Wenn nicht, können Sie die Ausnahmebehandlung nicht vermeiden.
Catchwa
Ist nicht jede Verwendung von Ausnahmen eine Überprüfung einer Annahme, indem etwas potenziell Gefährliches getan wird, ohne die Möglichkeit eines Ausfalls absichtlich auszuschließen?
JWG
26
Ihre Philosophie hat ein schlechtes Verständnis für Ausnahmen. Die meisten Leute denken, Ausnahmen bedeuten heiligen Mist aus dem Untergang, etwas ist falsch, stirbt, stirbt. Wenn Ausnahme bedeutet .... Ausnahme. Es bedeutet, dass etwas Außergewöhnliches passiert ist, das Sie "behandeln" (oder berücksichtigen) müssen. Vielleicht möchten Sie den Datenzugriff erneut versuchen, vielleicht muss der Benutzer wissen, dass Sie keine Verbindung herstellen können. Wie geht's? Sie behandeln die ConnectionFailedException und benachrichtigen den Benutzer. Vielleicht hören sie nach einer Stunde auf, es zu versuchen, und stellen fest, dass das Kabel abgezogen ist.
Lee Louviere

Antworten:

542

Aktualisiert HINWEIS zu dieser Lösung : Die Überprüfung mit FileAccess.ReadWriteschlägt bei schreibgeschützten Dateien fehl, sodass die Lösung für die Überprüfung geändert wurde FileAccess.Read. Diese Lösung funktioniert zwar, da der Versuch, mit zu prüfen FileAccess.Read, fehlschlägt, wenn die Datei über eine Schreib- oder Lesesperre verfügt. Diese Lösung funktioniert jedoch nicht, wenn die Datei keine Schreib- oder Lesesperre aufweist, dh geöffnet wurde (zum Lesen oder Schreiben) mit FileShare.Read- oder FileShare.Write-Zugriff.

ORIGINAL: Ich habe diesen Code in den letzten Jahren verwendet und hatte keine Probleme damit.

Verstehen Sie Ihr Zögern bei der Verwendung von Ausnahmen, aber Sie können sie nicht immer vermeiden:

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;
}
ChrisW
quelle
60
Dies ist eine großartige Lösung, aber ich habe einen Kommentar: Möglicherweise möchten Sie die Datei nicht mit dem Zugriffsmodus FileAccess.Read öffnen, da ReadWrite immer fehlschlägt, wenn die Datei schreibgeschützt ist.
adeel825
220
-1. Dies ist eine schlechte Antwort, da die Datei möglicherweise von einem anderen Thread / Prozess gesperrt wird, nachdem sie in IsFileLocked geschlossen wurde und bevor Ihr Thread die Möglichkeit erhält, sie zu öffnen.
Polyfun
16
Ich denke, das ist eine großartige Antwort. Ich benutze dies als Erweiterungsmethode á la public static bool IsLocked(this FileInfo file) {/*...*/}.
Manuzor
54
@ ChrisW: Sie fragen sich vielleicht, was los ist. Sei nicht beunruhigt. Sie sind nur dem Zorn der täglichen WTF-Community ausgesetzt
Pierre Lebeaupin
16
@ChrisW Warum ist das eine schlechte Sache? Diese Community ist hier, um auf gute und schlechte Antworten hinzuweisen. Wenn eine Reihe von Fachleuten bemerkt, dass dies eine schlechte Sache ist, und sich der Abstimmung anschließen, dann ist die Website WAI. Und bevor Sie negativ werden, wenn Sie diesen Artikel lesen, sagen sie, "die richtige Antwort zu stimmen", nicht die falsche zu stimmen. Möchten Sie, dass sie ihre Gegenstimmen auch in Kommentaren erklären. Vielen Dank, dass Sie mir eine andere gute Seite vorgestellt haben!
Lee Louviere
569

Sie können unter einer Thread-Race-Bedingung leiden, für die es dokumentierte Beispiele gibt, die als Sicherheitslücke verwendet werden. Wenn Sie überprüfen, ob die Datei verfügbar ist, aber dann versuchen, sie zu verwenden, können Sie an dieser Stelle einen Fehler auslösen, den ein böswilliger Benutzer verwenden könnte, um Ihren Code zu erzwingen und auszunutzen.

Ihre beste Wette ist ein try catch / finally, der versucht, das Dateihandle zu erhalten.

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.
}
Spence
quelle
124
+1. Es gibt keine 100% sichere Methode, um zu erfahren, ob eine Datei verwendet wird, da die Datei Millisekunden nach der Überprüfung möglicherweise nicht mehr verwendet wird oder umgekehrt. Stattdessen öffnen Sie einfach die Datei und verwenden sie, wenn es keine Ausnahmen gibt.
Sedat Kapanoglu
8
Schade, dass .NET CAS nicht unterstützt. So etwas wie TryOpenFile (Ref FileHandle), das Erfolg / Misserfolg zurückgibt. Es sollte immer eine Problemumgehung geben, die nicht nur auf der Ausnahmebehandlung beruht. Ich frage mich, wie Microsoft Office das macht.
TamusJRoyce
2
Das Wichtigste dabei ist, dass diese API einfach die Windows-API verwendet, um ein Dateihandle zu erhalten. Daher müssen sie den von der C-API empfangenen Fehlercode übersetzen und in eine Ausnahme einschließen, die ausgelöst werden soll. Wir haben eine Ausnahmebehandlung in .Net. Warum also nicht? Auf diese Weise können Sie einen sauberen Vorwärtspfad in Ihren Code schreiben und die Fehlerbehandlung in einem separaten Codepfad belassen.
Spence
36
Die using-Anweisung soll sicherstellen, dass der Stream geschlossen wird, nachdem ich fertig bin. Ich denke, Sie werden feststellen, dass using () {} weniger Zeichen enthält als try {} finally {obj.Dispose ()}. Sie werden auch feststellen, dass Sie Ihre Objektreferenz jetzt außerhalb der using-Anweisung deklarieren müssen, die mehr typisiert. Wenn Sie eine explizite Schnittstelle haben, müssen Sie auch umwandeln. Schließlich möchten Sie ASAP entsorgen, und die finally-Logik verfügt möglicherweise über eine Benutzeroberfläche oder andere lang laufende Aktionen, die wenig mit dem Aufruf von IDispose zu tun haben. </ rant>
Spence
2
Das negiert nicht die Tatsache, dass Sie Ihr Objekt außerhalb des Versuchs deklarieren und explizit dispose aufrufen müssen, was using für Sie bedeutet und dasselbe bedeutet.
Spence
92

Verwenden Sie diese Option, um zu überprüfen, ob eine Datei gesperrt ist:

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

Aus Leistungsgründen empfehle ich, den Dateiinhalt im selben Vorgang zu lesen. Hier sind einige Beispiele:

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

Probieren Sie es selbst aus:

byte[] output1 = Helper.ReadFileBytes(@"c:\temp\test.txt");
string output2 = Helper.ReadFileTextWithEncoding(@"c:\temp\test.txt");
string output3 = Helper.ReadFileTextNoEncoding(@"c:\temp\test.txt");
Jeremy Thompson
quelle
8
Ich würde positiv stimmen, wenn es nicht so viele "magische Zahlen" gäbe ( en.wikipedia.org/wiki/Magic_number_(programming)
Kris
3
Ich bezog mich auf die errorCode-Vergleiche, nicht auf die Bitverschiebungen. obwohl jetzt erwähnen Sie es ...
Kris
1
Ihr Fang sollte eingeschaltet sein IOException, nicht allgemein Exceptionund dann ein Typentest.
Askolein
3
@JeremyThompson leider setzen Sie das spezifische IOExceptionnach dem allgemeinen. Der Allgemeine wird alles fangen, was vorbeigeht, und der Spezifische IOExceptionwird immer einsam sein. Tauschen Sie einfach die beiden aus.
Askolein
Ich mag diese Lösung. Ein weiterer Vorschlag: Innerhalb des Catch als ein anderes zum if (IsFileLocked (ex)) würde ich ex werfen. Dies behandelt dann den Fall, in dem die Datei nicht vorhanden ist (oder eine andere IOException), indem die Ausnahme ausgelöst wird.
Shindigo
7

Verwenden Sie einfach die Ausnahme wie vorgesehen. Akzeptieren Sie, dass die Datei verwendet wird, und versuchen Sie es wiederholt, bis Ihre Aktion abgeschlossen ist. Dies ist auch am effizientesten, da Sie keine Zyklen damit verschwenden, den Status zu überprüfen, bevor Sie handeln.

Verwenden Sie zum Beispiel die folgende Funktion

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

Wiederverwendbare Methode, die nach 2 Sekunden abläuft

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);
}
Kernowcode
quelle
6

Vielleicht könnten Sie einen FileSystemWatcher verwenden und auf das geänderte Ereignis achten.

Ich habe das selbst nicht benutzt, aber es könnte einen Versuch wert sein. Wenn sich herausstellt, dass der Dateisystemwatcher in diesem Fall etwas schwer ist, würde ich mich für die try / catch / sleep-Schleife entscheiden.

Karl Johan
quelle
1
Die Verwendung eines FileSystemWatcher hilft nicht, da die Ereignisse "Erstellt" und "Geändert" zu Beginn einer Dateierstellung / -änderung ausgelöst werden. Selbst kleine Dateien benötigen mehr Zeit, um vom Betriebssystem geschrieben und geschlossen zu werden, als die .NET-Anwendung über den FileSystemEventHandler-Rückruf ausführen muss. Das ist so traurig, aber es gibt keine andere Möglichkeit, als die Wartezeit vor dem Zugriff auf die Datei zu schätzen oder auf Ausnahmeschleifen zu
FileSystemWatcher verarbeitet jedoch nicht viele Änderungen gleichzeitig sehr gut. Seien Sie also vorsichtig damit.
Ben F
1
Übrigens, habt ihr beim Debuggen und Ansehen von Threads bemerkt, dass MS ihren eigenen FSW "FileSystemWather" nennt? Was ist überhaupt ein Wasser?
Devlord
Genau dieses Problem ist aufgetreten, weil ein Windows-Dienst mit FileSystemWatcher versucht, die Datei zu lesen, bevor der Prozess sie geschlossen hat.
Freedeveloper
6

Sie können eine Aufgabe zurückgeben, die Ihnen einen Stream gibt, sobald dieser verfügbar ist. Es ist eine vereinfachte Lösung, aber ein guter Ausgangspunkt. Es ist threadsicher.

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

Sie können diesen Stream wie gewohnt verwenden:

using (var stream = await FileStreamGetter.GetStreamAsync())
{
    Console.WriteLine(stream.Length);
}
Ivan Branets
quelle
3
Wie viele Sekunden, bis ein Stapel von der Rekursion in überläuft GetStreamAsync()?
CAD Kerl
@CADbloke, Sie haben einen sehr guten Punkt angesprochen. In der Tat kann es bei meinem Beispiel zu einer Stapelüberlaufausnahme kommen, falls eine Datei längere Zeit nicht verfügbar ist. Im Zusammenhang mit dieser Antwort kann stackoverflow.com/questions/4513438/… die Ausnahme in 5 Stunden auslösen .
Ivan Branets
In Bezug auf Ihre Anwendungsfälle ist es vorzuziehen, eine E / A-Ausnahme auszulösen, wenn beispielsweise 10 Versuche, die Datei zu lesen, fehlgeschlagen sind. Die andere Strategie könnte darin bestehen, die Wartezeit für eine Sekunde zu verlängern, sobald 10 Versuche fehlgeschlagen sind. Sie können auch eine Mischung aus beiden verwenden.
Ivan Branets
Ich würde (und tue) einfach den Benutzer benachrichtigen, dass die Datei gesperrt ist. Sie haben es im Allgemeinen selbst abgeschlossen, damit sie wahrscheinlich etwas dagegen unternehmen können. Oder nicht.
CAD Kerl
In einigen Fällen müssen Sie die Wiederholungsrichtlinie verwenden, da eine Datei möglicherweise noch nicht bereit ist. Stellen Sie sich eine Desktop-Anwendung vor, um Bilder in einem temporären Ordner herunterzuladen. Die Anwendung wird heruntergeladen und gleichzeitig öffnen Sie diesen Ordner im Datei-Explorer. Windows möchte sofort eine Miniaturansicht erstellen und die Datei sperren. Gleichzeitig versucht Ihre App, das gesperrte Bild an einem anderen Ort zu ersetzen. Sie erhalten eine Ausnahme, wenn Sie keine Wiederholungsrichtlinie verwenden.
Ivan Branets
4

Die einzige mir bekannte Möglichkeit ist die Verwendung der exklusiven Win32-Sperr-API, die nicht zu schnell ist, aber es gibt Beispiele.

Die meisten Menschen versuchen für eine einfache Lösung einfach, Schleifen zu fangen / zu fangen / zu schlafen.

Luke Schafer
quelle
1
Sie können diese API nicht verwenden, ohne zuvor die Datei geöffnet zu haben. Zu diesem Zeitpunkt müssen Sie dies nicht mehr tun.
Harry Johnston
1
Schrödingers Akte.
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");

Hoffe das hilft!

julianisch
quelle
10
Die tatsächliche Überprüfung, die Sie durchführen, ist in Ordnung. es in eine Funktion zu setzen ist irreführend. Sie möchten eine solche Funktion NICHT verwenden, bevor Sie eine Datei öffnen. Innerhalb der Funktion wird die Datei geöffnet, überprüft und geschlossen. Dann geht der Programmierer davon aus, dass die Datei NOCH in Ordnung ist, und versucht, sie zur Verwendung zu öffnen. Dies ist schlecht, da es von einem anderen Prozess verwendet und gesperrt werden kann, der zum Öffnen dieser Datei in die Warteschlange gestellt wurde. Zwischen dem ersten Öffnen (zur Überprüfung) und dem zweiten Öffnen (zur Verwendung) könnte das Betriebssystem Ihren Prozess geplant und einen anderen Prozess ausgeführt haben.
Lakey
3

Die oben akzeptierten Antworten weisen ein Problem auf, bei dem der Code nicht funktioniert, wenn die Datei zum Schreiben mit einem FileShare.Read-Modus geöffnet wurde oder wenn die Datei ein schreibgeschütztes Attribut hat. Diese modifizierte Lösung funktioniert am zuverlässigsten, wobei zwei Dinge zu beachten sind (wie dies auch für die akzeptierte Lösung gilt):

  1. Es funktioniert nicht für Dateien, die mit einem Schreibfreigabemodus geöffnet wurden
  2. Dies berücksichtigt keine Threading-Probleme, sodass Sie es sperren oder Threading-Probleme separat behandeln müssen.

Unter Berücksichtigung der obigen Punkte wird überprüft, ob die Datei entweder zum Schreiben gesperrt oder gesperrt ist, um das Lesen zu verhindern :

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;
}
Junge
quelle
Hat immer noch das gleiche Problem wie die akzeptierte Antwort - es zeigt Ihnen nur, ob die Datei zu einem bestimmten Zeitpunkt von einem anderen Prozess gesperrt wurde , was keine nützliche Information ist. Zum Zeitpunkt der Rückgabe der Funktion ist das Ergebnis möglicherweise bereits veraltet!
Harry Johnston
1
Das stimmt, man kann nur zu einem bestimmten Zeitpunkt prüfen (oder Ereignisse abonnieren). Der Vorteil dieses Ansatzes gegenüber der akzeptierten Lösung besteht darin, dass nach einem schreibgeschützten Attribut und einer Schreibsperre gesucht werden kann und kein falsches Positiv zurückgegeben wird.
Junge
3

Abgesehen von der Arbeit mit 3-Linern und nur als Referenz: Wenn Sie die vollständigen Informationen wünschen, gibt es ein kleines Projekt im Microsoft Dev Center:

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

Aus der Einleitung:

Der in .NET Framework 4.0 entwickelte C # -Beispielcode hilft dabei, herauszufinden, welcher Prozess eine Datei sperrt. Die in rstrtmgr.dll enthaltene RmStartSession- Funktion wurde zum Erstellen einer Neustart-Manager-Sitzung verwendet. Entsprechend dem Rückgabeergebnis wird eine neue Instanz des Win32Exception-Objekts erstellt. Nach dem Registrieren der Ressourcen für eine Restart Manager-Sitzung über die Funktion RmRegisterRescources wird die Funktion RmGetList aufgerufen, um zu überprüfen, welche Anwendungen eine bestimmte Datei verwenden, indem das Array RM_PROCESS_INFO aufgelistet wird.

Es funktioniert durch Herstellen einer Verbindung mit der "Restart Manager Session".

Der Neustart-Manager verwendet die Liste der in der Sitzung registrierten Ressourcen, um zu bestimmen, welche Anwendungen und Dienste heruntergefahren und neu gestartet werden müssen. Ressourcen können durch Dateinamen, Dienstkurznamen oder RM_UNIQUE_PROCESS-Strukturen identifiziert werden , die laufende Anwendungen beschreiben.

Es könnte ein wenig überarbeitet für Ihre speziellen Bedürfnisse sein ... Aber wenn es das ist, was Sie wollen, gehen Sie voran und greifen Sie zum vs-Projekt.

Bernhard
quelle
2

Nach meiner Erfahrung möchten Sie dies normalerweise tun, dann Ihre Dateien "schützen", um etwas Besonderes zu tun, und dann die "geschützten" Dateien verwenden. Wenn Sie nur eine Datei haben, die Sie so verwenden möchten, können Sie den Trick verwenden, der in der Antwort von Jeremy Thompson erläutert wird. Wenn Sie jedoch versuchen, dies für viele Dateien zu tun (z. B. wenn Sie ein Installationsprogramm schreiben), werden Sie ziemlich verletzt.

Eine sehr elegante Möglichkeit, dies zu lösen, besteht darin, dass Sie in Ihrem Dateisystem keinen Ordnernamen ändern können, wenn eine der Dateien dort verwendet wird. Behalten Sie den Ordner im selben Dateisystem und es wird wie ein Zauber funktionieren.

Beachten Sie, dass Sie sich der offensichtlichen Möglichkeiten bewusst sein sollten, wie dies ausgenutzt werden kann. Schließlich werden die Dateien nicht gesperrt. Beachten Sie auch, dass es andere Gründe gibt, die dazu führen können, dass Ihr MoveVorgang fehlschlägt. Offensichtlich kann hier eine ordnungsgemäße Fehlerbehandlung (MSDN) hilfreich sein.

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

Für einzelne Dateien würde ich mich an den Sperrvorschlag von Jeremy Thompson halten.

atlaste
quelle
Hallo, da sich die Reihenfolge der Antworten ändert, können Sie klarstellen, welchen Beitrag Sie oben für Leser dieser beliebten Qualitätssicherung bedeuten. Vielen Dank.
Jeremy Thompson
1
@JeremyThompson Du hast recht, danke, ich werde den Beitrag bearbeiten. Ich würde die Lösung von Ihnen verwenden, hauptsächlich wegen Ihrer korrekten Verwendung FileShareund Überprüfung auf ein Schloss.
Atltaste
2

Hier ist ein Code, der, soweit ich das am besten beurteilen kann, dasselbe tut wie die akzeptierte Antwort, jedoch mit weniger Code:

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

Ich denke jedoch, dass es robuster ist, dies auf folgende Weise zu tun:

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

Sie können meine Bibliothek für den Zugriff auf Dateien aus mehreren Apps verwenden.

Sie können es von nuget installieren: Install-Package Xabe.FileLock

Wenn Sie weitere Informationen dazu wünschen, besuchen Sie https://github.com/tomaszzmuda/Xabe.FileLock

ILock fileLock = new FileLock(file);
if(fileLock.Acquire(TimeSpan.FromSeconds(15), true))
{
    using(fileLock)
    {
        // file operations here
    }
}

Die Methode fileLock.Acquire gibt nur dann true zurück, wenn die für dieses Objekt exklusive Datei gesperrt werden kann. Aber App, welche Datei hochladen muss, muss es auch in der Dateisperre tun. Wenn auf das Objekt nicht zugegriffen werden kann, gibt metod false zurück.

Tomasz Żmuda
quelle
1
Bitte posten Sie nicht einfach ein Tool oder eine Bibliothek als Antwort. Zeigen Sie zumindest in der Antwort selbst , wie das Problem gelöst wird .
paper1111
Demo hinzugefügt :) Sorry @ paper1111
Tomasz Żmuda
1
Erfordert alle Prozesse, die die Datei zur Zusammenarbeit verwenden. Es ist unwahrscheinlich, dass dies auf das ursprüngliche Problem des OP anwendbar ist.
Harry Johnston
2

Ich musste einmal PDFs in ein Online-Backup-Archiv hochladen. Die Sicherung schlägt jedoch fehl, wenn der Benutzer die Datei in einem anderen Programm (z. B. einem PDF-Reader) geöffnet hat. In meiner Eile habe ich versucht, einige der wichtigsten Antworten in diesem Thread zu finden, konnte sie jedoch nicht zum Laufen bringen. Was für mich funktioniert hat, war der Versuch, die PDF-Datei in ein eigenes Verzeichnis zu verschieben . Ich stellte fest, dass dies fehlschlagen würde, wenn die Datei in einem anderen Programm geöffnet wäre, und wenn die Verschiebung erfolgreich wäre, wäre kein Wiederherstellungsvorgang erforderlich, wie dies der Fall wäre, wenn sie in ein separates Verzeichnis verschoben würde. Ich möchte meine Basislösung veröffentlichen, falls sie für bestimmte Anwendungsfälle anderer nützlich sein kann.

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
}
Benjamin Curtis Drake
quelle
0

Ich bin gespannt, ob dies WTF-Reflexe auslöst. Ich habe einen Prozess, der ein PDF-Dokument über eine Konsolen-App erstellt und anschließend startet. Ich hatte es jedoch mit einer Schwachstelle zu tun, bei der die App eine Ausnahme auslöste und starb, wenn der Benutzer den Prozess mehrmals ausführen und dieselbe Datei generieren würde, ohne zuvor die zuvor generierte Datei zu schließen. Dies kam häufig vor, da Dateinamen auf Angebotsnummern basieren.

Anstatt auf solch unanständige Weise zu scheitern, habe ich mich für die automatisch inkrementierte Dateiversionierung entschieden:

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

Wahrscheinlich kann dem catchBlock etwas mehr Sorgfalt gewidmet werden, um sicherzustellen, dass ich die richtigen IOException (s) abfange. Ich werde wahrscheinlich auch den App-Speicher beim Start löschen, da diese Dateien sowieso nur vorübergehend sein sollen.

Mir ist klar, dass dies über den Rahmen der Frage des OP hinausgeht, einfach zu überprüfen, ob die Datei verwendet wird, aber dies war tatsächlich das Problem, das ich lösen wollte, als ich hier ankam, sodass es vielleicht für jemand anderen nützlich sein wird.

Vinney Kelly
quelle
0

Würde so etwas helfen?

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

    }
}
Tadej
quelle
-2

Versuchen Sie, die Datei in ein temporäres Verzeichnis zu verschieben / kopieren. Wenn Sie können, hat es keine Sperre und Sie können sicher im temporären Verzeichnis arbeiten, ohne Sperren zu bekommen. Andernfalls versuchen Sie einfach, es in x Sekunden erneut zu verschieben.

Carra
quelle
@jcolebrand sperrt was? die, die du kopiert hast? Oder die, die Sie in das temporäre Verzeichnis eingegeben haben?
Cullub
5
Wenn Sie die Datei kopieren und erwarten, dass niemand anderes daran arbeitet, und Sie die temporäre Datei verwenden und jemand sie direkt nach dem Kopieren sperrt, haben Sie möglicherweise Daten verloren.
Jcolebrand
-3

Ich benutze diese Problemumgehung, aber ich habe eine Zeitspanne zwischen dem Überprüfen der Dateisperrung mit der IsFileLocked-Funktion und dem Öffnen der Datei. In dieser Zeitspanne kann ein anderer Thread die Datei öffnen, sodass ich eine IOException erhalte.

Also habe ich zusätzlichen Code dafür hinzugefügt. In meinem Fall möchte ich XDocument laden:

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

Was denken Sie? Kann ich etwas ändern? Vielleicht musste ich die IsFileBeingUsed-Funktion überhaupt nicht verwenden?

Vielen Dank

zzfima
quelle
4
Was ist IsFileBeingUsed? Quellcode über IsFileBeingUsed?
Kiquenet