it-swarm.com.de

IOException: Der Prozess kann nicht auf die Datei 'Dateipfad' zugreifen, da er von einem anderen Prozess verwendet wird

Ich habe etwas Code und wenn es ausgeführt wird, wirft es eine IOException, die das sagt 

Der Prozess kann nicht auf die Datei 'Dateiname' zugreifen, da sie von .__ verwendet wird. ein anderer Prozess

Was bedeutet das und was kann ich dagegen tun?

132
Adriano Repetti

Was ist die Ursache?

Die Fehlermeldung ist ziemlich eindeutig: Sie versuchen, auf eine Datei zuzugreifen, und auf sie kann nicht zugegriffen werden, da ein anderer Prozess (oder sogar derselbe Prozess) etwas damit tut (und keine Freigabe zulässt).

Debuggen

Abhängig von Ihrem spezifischen Szenario ist es möglicherweise ziemlich einfach zu lösen (oder ziemlich schwer zu verstehen). Mal sehen.

Ihr Prozess ist der einzige, der auf diese Datei zugreift
Sie sind sicher, dass der Prozess other Ihr eigener Prozess ist. Wenn Sie wissen, dass Sie diese Datei in einem anderen Teil Ihres Programms öffnen, müssen Sie zunächst prüfen, ob Sie das Datei-Handle nach jeder Verwendung ordnungsgemäß schließen. Hier ist ein Beispiel für Code mit diesem Fehler:

var stream = new FileStream(path, FileAccess.Read);
var reader = new StreamReader(stream);
// Read data from this file, when I'm done I don't need it any more
File.Delete(path); // IOException: file is in use

Glücklicherweise implementiert FileStreamIDisposable, so dass es einfach ist, Ihren gesamten Code in eine using -Anweisung zu packen:

using (var stream = File.Open("myfile.txt", FileMode.Open)) {
    // Use stream
}

// Here stream is not accessible and it has been closed (also if
// an exception is thrown and stack unrolled

Dieses Muster stellt auch sicher, dass die Datei im Falle von Ausnahmen nicht geöffnet bleibt (dies kann der Grund sein, warum die Datei verwendet wird: Es ist ein Fehler aufgetreten, und niemand hat sie geschlossen; siehe dieser Beitrag zum Beispiel).

Wenn alles in Ordnung zu sein scheint (Sie sind sicher, dass Sie immer alle Dateien schließen, die Sie öffnen, auch in Ausnahmefällen) und wenn Sie mehrere Arbeitsthreads haben, haben Sie zwei Möglichkeiten: Überarbeiten Sie Ihren Code, um den Dateizugriff zu serialisieren (nicht immer machbar und nicht immer) gesucht) oder ein Wiederholungsmuster anwenden. Dies ist ein recht häufiges Muster für E/A-Vorgänge: Sie versuchen, etwas zu tun, und im Fehlerfall warten Sie und versuchen es erneut (haben Sie sich gefragt, warum beispielsweise Windows Shell einige Zeit benötigt, um Sie darüber zu informieren, dass eine Datei verwendet wird und kann nicht gelöscht werden?). In C # ist es ziemlich einfach zu implementieren (siehe auch bessere Beispiele zu Disk I/O , Networking und Datenbankzugriff ).

private const int NumberOfRetries = 3;
private const int DelayOnRetry = 1000;

for (int i=1; i <= NumberOfRetries; ++i) {
    try {
        // Do stuff with file
        break; // When done we can break loop
    }
    catch (IOException e) when (i <= NumberOfRetries) {
        // You may check error code to filter some exceptions, not every error
        // can be recovered.
        Thread.Sleep(DelayOnRetry);
    }
}

Bitte beachten Sie einen häufigen Fehler, der bei StackOverflow sehr häufig auftritt:

var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);

In diesem Fall schlägt ReadAllText() fehl, da die Datei verwendet wird (File.Open() in der vorherigen Zeile). Das vorherige Öffnen der Datei ist nicht nur unnötig, sondern auch falsch. Gleiches gilt für alle File Funktionen, die kein handle an die Datei zurückgeben, mit der Sie arbeiten: File.ReadAllText(), File.WriteAllText(), File.ReadAllLines(), File.WriteAllLines() und andere (wie File.AppendAllXyz() Funktionen) öffnen und schließen die Datei von selbst.

Ihr Prozess ist nicht der einzige, der auf diese Datei zugreift
Wenn Ihr Prozess nicht der einzige ist, der auf diese Datei zugreift, kann die Interaktion schwieriger sein. Ein Muster wiederholen hilft (wenn die Datei von niemand anderem geöffnet werden soll, benötigen Sie ein Hilfsprogramm wie Process Explorer, um wer zu überprüfen macht was).

Möglichkeiten zu vermeiden

Verwenden Sie ggf. immer die Anweisungen using, um Dateien zu öffnen. Wie bereits im vorherigen Abschnitt erwähnt, hilft es Ihnen, viele häufige Fehler zu vermeiden (siehe dieser Beitrag für ein Beispiel zu wie man es nicht verwendet ) .

Versuchen Sie nach Möglichkeit zu entscheiden, wer Zugriff auf eine bestimmte Datei hat, und zentralisieren Sie den Zugriff mithilfe einiger bekannter Methoden. Wenn Sie zum Beispiel eine Datendatei haben, in der Ihr Programm liest und schreibt, sollten Sie den gesamten E/A-Code in eine einzige Klasse packen. Dies erleichtert das Debuggen (da Sie immer einen Haltepunkt setzen können, um zu sehen, wer was tut) und ist (falls erforderlich) ein Synchronisationspunkt für den Mehrfachzugriff.

Vergessen Sie nicht, dass E/A-Vorgänge immer fehlschlagen können.

if (File.Exists(path))
    File.Delete(path);

Wenn Jemand die Datei nach File.Exists(), aber vor File.Delete() löscht, wirft sie an einer beliebigen Stelle ein IOException fühle mich falsch sicher.

Wenden Sie nach Möglichkeit ein Wiederholungsmuster an. Wenn Sie FileSystemWatcher verwenden, sollten Sie eine Verschiebung in Betracht ziehen (da Sie benachrichtigt werden, eine Anwendung jedoch möglicherweise weiterhin ausschließlich funktioniert) mit dieser Datei).

Erweiterte Szenarien
Es ist nicht immer so einfach, daher müssen Sie möglicherweise den Zugriff mit einer anderen Person teilen. Wenn Sie beispielsweise von Anfang an lesen und bis zum Ende schreiben, haben Sie mindestens zwei Möglichkeiten.

1) teile das gleiche FileStream mit den richtigen Synchronisationsfunktionen (weil es nicht thread-sicher ist ). Ein Beispiel finden Sie unter this und this .

2) Verwenden Sie die Aufzählung FileShare, um das Betriebssystem anzuweisen, anderen Prozessen (oder anderen Teilen Ihres eigenen Prozesses) den gleichzeitigen Zugriff auf dieselbe Datei zu ermöglichen.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}

In diesem Beispiel habe ich gezeigt, wie eine Datei zum Schreiben geöffnet und zum Lesen freigegeben wird. Bitte beachten Sie, dass beim Lesen und Schreiben von Überlappungen undefinierte oder ungültige Daten entstehen. Es ist eine Situation, die beim Lesen behandelt werden muss. Beachten Sie auch, dass dies den Zugriff auf den Thread stream nicht sicher macht. Daher kann dieses Objekt nicht mit mehreren Threads geteilt werden, es sei denn, der Zugriff wird auf irgendeine Weise synchronisiert (siehe vorherige Links). Andere Freigabeoptionen sind verfügbar und eröffnen komplexere Szenarien. Weitere Informationen finden Sie unter MSDN .

Im Allgemeinen können [~ # ~] n [~ # ~] Prozesse alle zusammen aus derselben Datei lesen, aber nur einer sollte schreiben. In einem kontrollierten Szenario können Sie sogar gleichzeitiges Schreiben aktivieren, aber dies kann ' t in wenigen Absätzen in dieser Antwort verallgemeinern.

Ist es möglich, unlock eine Datei zu erstellen, die von einem anderen Prozess verwendet wird? Es ist nicht immer sicher und nicht so einfach, aber ja, es ist möglich .

240
Adriano Repetti

Mit FileShare wurde mein Problem beim Öffnen einer Datei behoben, auch wenn sie von einem anderen Prozess geöffnet wurde.

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
11
Muhammad Umar

Ich hatte ein Problem beim Hochladen eines Bildes, konnte es nicht löschen und fand eine Lösung. Viel Glück und Viel Spaß

//C# .NET
var image = Image.FromFile(filePath);

image.Dispose(); // this removes all resources

//later...

File.Delete(filePath); //now works
5
Hudson

Ich habe diese Fehlermeldung erhalten, weil ich File.Move zu einem Dateipfad ohne Dateinamen gemacht habe und den vollständigen Pfad im Ziel angeben muss.

2
live-love

Ich hatte das folgende Szenario, das den gleichen Fehler verursachte:

  • Laden Sie Dateien auf den Server hoch
  • Beseitigen Sie dann die alten Dateien, nachdem sie hochgeladen wurden

Die meisten Dateien waren klein, einige jedoch groß und der Versuch, diese zu löschen, führte zu dem Fehler kann nicht auf Datei zugreifen.

Es war nicht leicht zu finden, die Lösung war jedoch so einfach wie Waiting "für die Ausführung der Aufgabe":

using (var wc = new WebClient())
{
   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 
}
1
usefulBee

Der Fehler zeigt an, dass ein anderer Prozess versucht, auf die Datei zuzugreifen. Vielleicht haben Sie oder jemand anderes es geöffnet, während Sie versuchen, darauf zu schreiben. "Lesen" oder "Kopieren" verursacht dies normalerweise nicht, aber das Schreiben oder das Aufrufen von delete würde dies bedeuten.

Es gibt einige grundlegende Dinge, um dies zu vermeiden, wie andere Antworten erwähnt haben:

  1. Platzieren Sie es in FileStream-Vorgängen in einem using-Block mit einem FileShare.ReadWrite-Zugriffsmodus.

Ex.

using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}

Beachten Sie, dass FileAccess.ReadWrite nicht möglich ist, wenn Sie FileMode.Append verwenden.

  1. Ich bin auf dieses Problem gestoßen, als ich mit einem Eingabestrom einen File.SaveAs machte, als die Datei verwendet wurde. In meinem Fall, den ich gefunden habe, musste ich ihn eigentlich gar nicht im Dateisystem speichern, also habe ich das einfach entfernt, aber ich hätte wahrscheinlich versuchen können, einen FileStream in einer using-Anweisung mit FileAccess.ReadWrite zu erstellen, ähnlich der Code oben. 

  2. Speichern Sie Ihre Daten in einer anderen Datei und löschen Sie die alte, wenn sie nicht mehr verwendet wird. Wenn Sie die erfolgreich gespeicherte Datei in den Namen der ursprünglichen Datei umbenennen, können Sie sie umbenennen. Wie Sie testen, ob die Datei verwendet wird, wird über die List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);-Zeile in meinem Code unten ausgeführt. Dies kann in einem Windows-Dienst in einer Schleife geschehen, wenn Sie eine bestimmte Datei haben, die Sie regelmäßig anzeigen und löschen möchten, wann Sie möchten ersetze es. Wenn Sie nicht immer über dieselbe Datei verfügen, könnte eine Textdatei oder eine Datenbanktabelle aktualisiert werden, die der Dienst immer auf Dateinamen prüft und diese Überprüfung dann auf Prozesse durchführt und anschließend die Prozessabbrüche und -löschungen ausführt, wie ich es beschrieben habe in der nächsten Option. Beachten Sie, dass Sie einen Kontonutzernamen und ein Kennwort mit Administratorrechten auf dem angegebenen Computer benötigen, um das Löschen und Beenden von Prozessen durchzuführen.

    4a. Wenn Sie nicht wissen, ob eine Datei verwendet wird, wenn Sie versuchen, sie zu speichern, können Sie vor dem Speichern alle Prozesse schließen, die möglicherweise verwendet werden, z. B. Word (sofern es sich um ein Word-Dokument handelt). 

Wenn es lokal ist, können Sie Folgendes tun:

ProcessHandler.localProcessKill("winword.exe");

Wenn es sich um einen Remote-Computer handelt, können Sie Folgendes tun:

ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");

wobei txtUserName die Form von DOMAIN\user hat. 

4b. Angenommen, Sie kennen den Prozessnamen nicht, mit dem die Datei gesperrt wird. Sie können dies tun:

List<Process> lstProcs = new List<Process>();
lstProcs = ProcessHandler.WhoIsLocking(file);

foreach (Process p in lstProcs)
{
    if (p.MachineName == ".")
        ProcessHandler.localProcessKill(p.ProcessName);
    else
        ProcessHandler.remoteProcessKill(p.MachineName, txtUserName, txtPassword, p.ProcessName);
}

Beachten Sie, dass file der UNC-Pfad sein muss: \\computer\share\yourdoc.docx, damit Process herausfinden kann, auf welchem ​​Computer sich der Computer befindet, und p.MachineName gültig ist. Nachfolgend finden Sie die Klasse, die diese Funktionen verwenden. Dazu muss eine Referenz zu System.Management hinzugefügt werden. Sie stammt von https://stackoverflow.com/a/20623311 :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    using System.Management;

    namespace MyProject
    {
        public static class ProcessHandler
        {
            [StructLayout(LayoutKind.Sequential)]
            struct RM_UNIQUE_PROCESS
            {
                public int dwProcessId;
                public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
            }

            const int RmRebootReasonNone = 0;
            const int CCH_RM_MAX_APP_NAME = 255;
            const int CCH_RM_MAX_SVC_NAME = 63;

            enum RM_APP_TYPE
            {
                RmUnknownApp = 0,
                RmMainWindow = 1,
                RmOtherWindow = 2,
                RmService = 3,
                RmExplorer = 4,
                RmConsole = 5,
                RmCritical = 1000
            }

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
            struct RM_PROCESS_INFO
            {
                public RM_UNIQUE_PROCESS Process;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)]
                public string strAppName;

                [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)]
                public string strServiceShortName;

                public RM_APP_TYPE ApplicationType;
                public uint AppStatus;
                public uint TSSessionId;
                [MarshalAs(UnmanagedType.Bool)]
                public bool bRestartable;
            }

            [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
            static extern int RmRegisterResources(uint pSessionHandle,
                                                UInt32 nFiles,
                                                string[] rgsFilenames,
                                                UInt32 nApplications,
                                                [In] RM_UNIQUE_PROCESS[] rgApplications,
                                                UInt32 nServices,
                                                string[] rgsServiceNames);

            [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)]
            static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey);

            [DllImport("rstrtmgr.dll")]
            static extern int RmEndSession(uint pSessionHandle);

            [DllImport("rstrtmgr.dll")]
            static extern int RmGetList(uint dwSessionHandle,
                                        out uint pnProcInfoNeeded,
                                        ref uint pnProcInfo,
                                        [In, Out] RM_PROCESS_INFO[] rgAffectedApps,
                                        ref uint lpdwRebootReasons);

            /// <summary>
            /// Find out what process(es) have a lock on the specified file.
            /// </summary>
            /// <param name="path">Path of the file.</param>
            /// <returns>Processes locking the file</returns>
            /// <remarks>See also:
            /// http://msdn.Microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx
            /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing)
            /// 
            /// </remarks>
            static public List<Process> WhoIsLocking(string path)
            {
                uint handle;
                string key = Guid.NewGuid().ToString();
                List<Process> processes = new List<Process>();

                int res = RmStartSession(out handle, 0, key);
                if (res != 0) throw new Exception("Could not begin restart session.  Unable to determine file locker.");

                try
                {
                    const int ERROR_MORE_DATA = 234;
                    uint pnProcInfoNeeded = 0,
                        pnProcInfo = 0,
                        lpdwRebootReasons = RmRebootReasonNone;

                    string[] resources = new string[] { path }; // Just checking on one resource.

                    res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null);

                    if (res != 0) throw new Exception("Could not register resource.");

                    //Note: there's a race condition here -- the first call to RmGetList() returns
                    //      the total number of process. However, when we call RmGetList() again to get
                    //      the actual processes this number may have increased.
                    res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons);

                    if (res == ERROR_MORE_DATA)
                    {
                        // Create an array to store the process results
                        RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded];
                        pnProcInfo = pnProcInfoNeeded;

                        // Get the list
                        res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons);
                        if (res == 0)
                        {
                            processes = new List<Process>((int)pnProcInfo);

                            // Enumerate all of the results and add them to the 
                            // list to be returned
                            for (int i = 0; i < pnProcInfo; i++)
                            {
                                try
                                {
                                    processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId));
                                }
                                // catch the error -- in case the process is no longer running
                                catch (ArgumentException) { }
                            }
                        }
                        else throw new Exception("Could not list processes locking resource.");
                    }
                    else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result.");
                }
                finally
                {
                    RmEndSession(handle);
                }

                return processes;
            }

            public static void remoteProcessKill(string computerName, string userName, string pword, string processName)
            {
                var connectoptions = new ConnectionOptions();
                connectoptions.Username = userName;
                connectoptions.Password = pword;

                ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions);

                // WMI query
                var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'");

                using (var searcher = new ManagementObjectSearcher(scope, query))
                {
                    foreach (ManagementObject process in searcher.Get()) 
                    {
                        process.InvokeMethod("Terminate", null);
                        process.Dispose();
                    }
                }            
            }

            public static void localProcessKill(string processName)
            {
                foreach (Process p in Process.GetProcessesByName(processName))
                {
                    p.Kill();
                }
            }

            [DllImport("kernel32.dll")]
            public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags);

            public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4;

        }
    }
0
vapcguy

Wie andere Antworten in diesem Thread gezeigt haben, müssen Sie den Code sorgfältig untersuchen, um zu verstehen, wo die Datei gesperrt wird, um diesen Fehler zu beheben.

In meinem Fall habe ich die Datei vor dem Verschieben als E-Mail-Anhang versendet.

Daher wurde die Datei für einige Sekunden gesperrt, bis der SMTP-Client das Senden der E-Mail beendet hatte.

Die Lösung, die ich gewählt habe, war: Verschieben Sie zuerst die Datei und senden Sie dann die E-Mail. Das hat das Problem für mich gelöst.

Eine andere mögliche Lösung, auf die Hudson bereits hingewiesen hat, wäre gewesen, das Objekt nach Gebrauch zu entsorgen.

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
} 
0