Ich habe einen Code und wenn er ausgeführt wird, wirft er einen IOException
und sagt das
Der Prozess kann nicht auf die Datei 'Dateiname' zugreifen, da sie von einem anderen Prozess verwendet wird
Was bedeutet das und was kann ich dagegen tun?
Ich habe einen Code und wenn er ausgeführt wird, wirft er einen IOException
und sagt das
Der Prozess kann nicht auf die Datei 'Dateiname' zugreifen, da sie von einem anderen Prozess verwendet wird
Was bedeutet das und was kann ich dagegen tun?
Antworten:
Die Fehlermeldung ist ziemlich klar: Sie versuchen, auf eine Datei zuzugreifen, und sie ist nicht zugänglich, weil ein anderer Prozess (oder sogar derselbe Prozess) etwas damit macht (und keine Freigabe zuließ).
Abhängig von Ihrem spezifischen Szenario kann es ziemlich einfach zu lösen (oder ziemlich schwer zu verstehen) sein. Mal sehen.
Ihr Prozess ist der einzige, der auf diese Datei
zugreift. Sie sind sicher, dass der andere Prozess Ihr eigener Prozess ist. Wenn Sie wissen, dass Sie diese Datei in einem anderen Teil Ihres Programms öffnen, müssen Sie zunächst überprüfen, ob Sie das Dateihandle 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 FileStream
implementiert IDisposable
, so ist es einfach, 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; ein Beispiel finden Sie in diesem Beitrag ).
Wenn alles in Ordnung zu sein scheint (Sie sind sicher, dass Sie jede geöffnete Datei auch in Ausnahmefällen immer schließen) und 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 wenden Sie ein Wiederholungsmuster an . Es ist ein ziemlich 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 die Implementierung ziemlich einfach (siehe auch bessere Beispiele für Festplatten-E / A , Netzwerk 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, den wir sehr oft bei StackOverflow sehen:
var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);
In diesem Fall ReadAllText()
schlägt dies 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. Das gleiche gilt für alle File
Funktionen, die keinen Rückgriff Sie arbeiten mit an der Datei: File.ReadAllText()
, File.WriteAllText()
, File.ReadAllLines()
, File.WriteAllLines()
und andere (wie File.AppendAllXyz()
Funktionen) werden alle öffnen und schließen Sie die Datei 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 Wiederholungsmuster hilft (wenn die Datei nicht von jemand anderem geöffnet werden sollte, aber dies ist der Fall, benötigen Sie ein Dienstprogramm wie Process Explorer, um zu überprüfen, wer was tut ).
Verwenden Sie gegebenenfalls immer using- Anweisungen, um Dateien zu öffnen. Wie im vorherigen Absatz erwähnt, hilft es Ihnen aktiv, viele häufige Fehler zu vermeiden (in diesem Beitrag finden Sie ein Beispiel, wie Sie es nicht verwenden können ).
Versuchen Sie nach Möglichkeit zu entscheiden, wem der Zugriff auf eine bestimmte Datei gehört, und zentralisieren Sie den Zugriff mithilfe einiger bekannter Methoden. Wenn Sie beispielsweise eine Datendatei haben, in der Ihr Programm liest und schreibt, sollten Sie den gesamten E / A-Code in einer einzigen Klasse einschließen. Dies erleichtert das Debuggen (da Sie dort immer einen Haltepunkt setzen und sehen können, wer was tut) und ist ein Synchronisationspunkt (falls erforderlich) für den Mehrfachzugriff.
Vergessen Sie nicht, dass E / A-Vorgänge immer fehlschlagen können. Ein häufiges Beispiel ist Folgendes:
if (File.Exists(path))
File.Delete(path);
Wenn jemand die Datei nach, File.Exists()
aber vorher löscht File.Delete()
, wird sie IOException
an einen Ort geworfen, an dem Sie sich möglicherweise fälschlicherweise sicher fühlen.
Wenden Sie nach Möglichkeit ein Wiederholungsmuster an. Wenn Sie es verwenden FileSystemWatcher
, sollten Sie die Aktion verschieben (da Sie benachrichtigt werden, eine Anwendung jedoch möglicherweise weiterhin ausschließlich mit dieser Datei arbeitet).
Erweiterte Szenarien
Es ist nicht immer so einfach, daher müssen Sie möglicherweise den Zugriff für andere Personen freigeben. Wenn Sie beispielsweise von Anfang an lesen und bis zum Ende schreiben, haben Sie mindestens zwei Möglichkeiten.
1) teilen Sie das gleiche FileStream
mit den richtigen Synchronisationsfunktionen (weil es nicht threadsicher ist ). Ein Beispiel finden Sie in diesem und diesen Beiträgen.
2) Verwenden Sie die FileShare
Aufzählung, 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 Überschneidungen undefinierte oder ungültige Daten entstehen. Es ist eine Situation, die beim Lesen behandelt werden muss. Beachten Sie auch, dass dies keinen Zugriff auf den stream
Thread sicher macht, sodass dieses Objekt nicht für mehrere Threads freigegeben werden kann, es sei denn, der Zugriff ist irgendwie synchronisiert (siehe vorherige Links). Andere Freigabeoptionen sind verfügbar und eröffnen komplexere Szenarien. Weitere Informationen finden Sie in 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 gleichzeitige Schriften aktivieren, dies kann jedoch nicht in wenigen Textabschnitten in dieser Antwort verallgemeinert werden.
Ist es möglich, eine von einem anderen Prozess verwendete Datei zu entsperren ? Es ist nicht immer sicher und nicht so einfach, aber ja, es ist möglich .
File.Create(path)
, sollten Sie sie .Close()
am Ende hinzufügen , bevor Sie darauf schreiben. Es gibt solche Fallstricke, zusätzlich zu den using
Anweisungen zum Schreiben der Dateien und zum anschließenden Löschen. Sie sollten den Code in Ihrer Frage veröffentlichen, wie Sie Ihre Datei erstellen und löschen. Aber wahrscheinlich stimmt mit etwas oben Erwähntem überein.
Directory.SetCreationTimeUTC()
, schlägt jedoch fehl, wenn der Datei-Explorer geöffnet ist und behauptet, dass ein anderer Prozess auf das Verzeichnis zugreift. Wie soll ich mit dieser Situation umgehen?
Die Verwendung von FileShare hat mein Problem beim Öffnen von Dateien behoben, auch wenn diese von einem anderen Prozess geöffnet wurden.
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Hatte ein Problem beim Hochladen eines Bildes und 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
Ich habe diesen Fehler erhalten, weil ich File.Move zu einem Dateipfad ohne Dateinamen ausgeführt habe. Sie müssen den vollständigen Pfad im Ziel angeben.
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 Aufrufen von "Löschen" würde dies bewirken.
Es gibt einige grundlegende Dinge, um dies zu vermeiden, wie andere Antworten erwähnt haben:
In FileStream
Betrieb, legen Sie sie in einem using
Block mit einem FileShare.ReadWrite
Zugriffsmodus.
Beispielsweise:
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Beachten Sie, dass FileAccess.ReadWrite
dies bei Verwendung nicht möglich ist FileMode.Append
.
Ich bin auf dieses Problem gestoßen, als ich einen Eingabestream verwendet habe, um zu tun, File.SaveAs
wann die Datei verwendet wurde. In meinem Fall musste ich es eigentlich gar nicht im Dateisystem speichern, also habe ich es letztendlich nur entfernt, aber ich hätte wahrscheinlich versuchen können, einen FileStream in einer using
Anweisung mit zu erstellen FileAccess.ReadWrite
, ähnlich wie der Code über.
Speichern Sie Ihre Daten als eine andere Datei und löschen Sie die alte Datei, wenn festgestellt wird, dass sie nicht mehr verwendet wird. Anschließend können Sie die erfolgreich gespeicherte Datei in den Namen der ursprünglichen Datei umbenennen. Wie Sie testen, ob die Datei verwendet wird, erfahren Sie über die
List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);
Zeile in meinem Code unten, und könnte in einem Windows-Dienst in einer Schleife erfolgen, wenn Sie eine bestimmte Datei haben, die Sie regelmäßig ansehen und löschen möchten, wenn Sie sie ersetzen möchten. Wenn Sie nicht immer dieselbe Datei haben, kann eine Textdatei oder Datenbanktabelle aktualisiert werden, die der Dienst immer auf Dateinamen überprüft und dann diese Prüfung auf Prozesse durchführt und anschließend die Prozessabbrüche und -löschungen darauf durchführt, wie ich beschreibe in der nächsten Option. Beachten Sie, dass Sie einen Benutzernamen und ein Kennwort für das Konto benötigen, die auf dem angegebenen Computer über Administratorrechte verfügen, um das Löschen und Beenden von Prozessen durchführen zu können.
Wenn Sie nicht wissen, ob eine Datei beim Speichern verwendet wird, können Sie vor dem Speichern alle Prozesse schließen, die sie möglicherweise verwenden, z. B. Word, wenn es sich um ein Word-Dokument handelt.
Wenn es lokal ist, können Sie dies tun:
ProcessHandler.localProcessKill("winword.exe");
Wenn es remote ist, können Sie dies tun:
ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");
wo txtUserName
ist in Form von DOMAIN\user
.
Angenommen, Sie kennen den Prozessnamen, der die Datei sperrt, nicht. Dann können Sie 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
um das Process
herauszufinden, was Computer ist es auf und um p.MachineName
gültig zu sein.
Nachfolgend finden Sie die Klasse, die diese Funktionen verwenden und für die ein Verweis hinzugefügt werden muss System.Management
. Der Code wurde ursprünglich von Eric J geschrieben :
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;
}
}
Wie andere Antworten in diesem Thread gezeigt haben, müssen Sie den Code sorgfältig untersuchen, um diesen Fehler zu beheben und zu verstehen, wo die Datei gesperrt wird.
In meinem Fall habe ich die Datei als E-Mail-Anhang gesendet, bevor ich den Verschiebevorgang ausgeführt habe.
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, bestand darin , zuerst die Datei zu verschieben und dann die E-Mail zu senden. Dies löste das Problem für mich.
Eine andere mögliche Lösung, auf die Hudson bereits hingewiesen hat, wäre gewesen, das Objekt nach der Verwendung 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
}
}
File.Move()
wird, funktioniert sie nicht und gibt den gleichen Fehler aus. Wenn Sie einer E-Mail nur eine Datei hinzufügen, glaube ich nicht, dass sie bei der Verwendung während des Attachments.Add()
Vorgangs fehlerhaft ist, da dies nur ein Kopiervorgang ist. Wenn dies aus irgendeinem Grund der Fall ist, können Sie es in ein temporäres Verzeichnis kopieren, die Kopie anhängen und die kopierte Datei anschließend löschen. Aber ich denke nicht, dass, wenn das OP eine Datei ändern und diese verwenden möchte, diese Art von Lösung (für die Sie nicht den Code angezeigt haben, sondern nur den anhängenden Teil) funktionieren würde. .Dispose()
ist immer eine gute Idee, aber hier nicht relevant, es sei denn, die Datei wurde in einer vorherigen Operation geöffnet.
Ich hatte das folgende Szenario, das den gleichen Fehler verursachte:
Die meisten Dateien waren klein, einige jedoch groß. Der Versuch, diese zu löschen, führte zu dem Fehler, dass nicht auf Dateien zugegriffen werden kann.
Es war nicht leicht zu finden, die Lösung war jedoch so einfach wie das Warten "auf die Ausführung der Aufgabe":
using (var wc = new WebClient())
{
var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
tskResult.Wait();
}
Mein unten stehender Code löst dieses Problem, aber ich schlage vor. Zunächst müssen Sie verstehen, was dieses Problem verursacht, und die Lösung ausprobieren, die Sie durch Ändern des Codes finden können
Ich kann einen anderen Weg zur Lösung dieses Problems angeben, aber eine bessere Lösung besteht darin, Ihre Codierungsstruktur zu überprüfen und zu analysieren, was dies bewirkt. Wenn Sie keine Lösung finden, können Sie diesen Code unten verwenden
try{
Start:
///Put your file access code here
}catch (Exception ex)
{
//by anyway you need to handle this error with below code
if (ex.Message.StartsWith("The process cannot access the file"))
{
//Wait for 5 seconds to free that file and then start execution again
Thread.Sleep(5000);
goto Start;
}
}
GC.*()
Sie wahrscheinlich andere Probleme mit Ihrem Code. 2) Die Nachricht ist lokalisiert und zerbrechlich . Verwenden Sie stattdessen das HRESULT. 3) Vielleicht möchten Sie mit schlafen Task.Delay()
(und zehn Sekunden sind in vielen Fällen irgendwie zu lang). 4) Sie haben keine Exit-Bedingung: Dieser Code könnte für immer hängen bleiben. 5) Sie brauchen goto
hier definitiv nicht . 6) Fangen Exception
ist normalerweise eine schlechte Idee, in diesem Fall auch, weil ... 6) Wenn etwas anderes passiert, schlucken Sie den Fehler.
GC.Collect()
war der Umgang mit einigen COM-Objekten.