J'ai du code et quand il s'exécute, il lance un IOException
, disant que
Le processus ne peut pas accéder au fichier 'filename' car il est utilisé par un autre processus
Qu'est-ce que cela signifie et que puis-je faire?
J'ai du code et quand il s'exécute, il lance un IOException
, disant que
Le processus ne peut pas accéder au fichier 'filename' car il est utilisé par un autre processus
Qu'est-ce que cela signifie et que puis-je faire?
Réponses:
Le message d'erreur est assez clair: vous essayez d'accéder à un fichier, et il n'est pas accessible car un autre processus (ou même le même processus) fait quelque chose avec lui (et il n'a permis aucun partage).
Cela peut être assez facile à résoudre (ou assez difficile à comprendre), selon votre scénario spécifique. Voyons en voir.
Votre processus est le seul à accéder à ce fichier.
Vous êtes sûr que l' autre processus est votre propre processus. Si vous savez que vous ouvrez ce fichier dans une autre partie de votre programme, vous devez d'abord vérifier que vous fermez correctement le descripteur de fichier après chaque utilisation. Voici un exemple de code avec ce bogue:
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
Heureusement FileStream
implémente IDisposable
, il est donc facile d'encapsuler tout votre code dans une using
instruction:
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
Ce modèle garantira également que le fichier ne sera pas laissé ouvert en cas d'exceptions (cela peut être la raison pour laquelle le fichier est utilisé: quelque chose s'est mal passé, et personne ne l'a fermé; voir cet article pour un exemple).
Si tout semble bien (vous êtes sûr de toujours fermer tous les fichiers que vous ouvrez, même en cas d'exceptions) et que vous avez plusieurs threads de travail, alors vous avez deux options: retravailler votre code pour sérialiser l'accès aux fichiers (pas toujours faisable et pas toujours voulu) ou appliquez un modèle de nouvelle tentative . C'est un modèle assez courant pour les opérations d'E / S: vous essayez de faire quelque chose et en cas d'erreur, vous attendez et réessayez (vous êtes-vous demandé pourquoi, par exemple, Windows Shell prend un certain temps pour vous informer qu'un fichier est en cours d'utilisation et ne peut pas être supprimé?). En C #, c'est assez facile à implémenter (voir également de meilleurs exemples sur les E / S disque , le réseau et l' accès aux bases de données ).
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);
}
}
Veuillez noter une erreur courante que nous voyons très souvent sur StackOverflow:
var stream = File.Open(path, FileOpen.Read);
var content = File.ReadAllText(path);
Dans ce cas ReadAllText()
échouera car le fichier est en cours d'utilisation ( File.Open()
dans la ligne précédente). Ouvrir le fichier au préalable n'est pas seulement inutile mais également erroné. De même pour toutes les File
fonctions qui ne renvoient pas une poignée dans le fichier que vous travaillez avec: File.ReadAllText()
, File.WriteAllText()
, File.ReadAllLines()
, File.WriteAllLines()
et d' autres (comme les File.AppendAllXyz()
fonctions) seront tous ouverts et fermez le fichier par eux - mêmes.
Votre processus n'est pas le seul à accéder à ce fichier
Si votre processus n'est pas le seul à accéder à ce fichier, l'interaction peut être plus difficile. Un modèle de nouvelle tentative aidera (si le fichier ne doit pas être ouvert par quelqu'un d'autre mais qu'il l'est, alors vous avez besoin d'un utilitaire comme Process Explorer pour vérifier qui fait quoi ).
Le cas échéant, utilisez toujours des instructions using pour ouvrir les fichiers. Comme indiqué dans le paragraphe précédent, cela vous aidera activement à éviter de nombreuses erreurs courantes (voir cet article pour un exemple sur la façon de ne pas l'utiliser ).
Si possible, essayez de décider à qui appartient l'accès à un fichier spécifique et centralisez l'accès grâce à quelques méthodes bien connues. Si, par exemple, vous avez un fichier de données dans lequel votre programme lit et écrit, vous devez insérer tout le code d'E / S dans une seule classe. Cela facilitera le débogage (car vous pouvez toujours y mettre un point d'arrêt et voir qui fait quoi) et aussi ce sera un point de synchronisation (si nécessaire) pour un accès multiple.
N'oubliez pas que les opérations d'E / S peuvent toujours échouer, voici un exemple courant:
if (File.Exists(path))
File.Delete(path);
Si quelqu'un supprime le fichier après File.Exists()
mais avant File.Delete()
, il jettera un IOException
dans un endroit où vous pourriez vous sentir mal en sécurité.
Chaque fois que c'est possible, appliquez un modèle de nouvelle tentative , et si vous utilisez FileSystemWatcher
, envisagez de reporter l'action (car vous serez notifié, mais une application peut toujours travailler exclusivement avec ce fichier).
Scénarios avancés
Ce n'est pas toujours aussi simple, vous devrez peut-être partager l'accès avec quelqu'un d'autre. Si, par exemple, vous lisez du début et écrivez jusqu'à la fin, vous avez au moins deux options.
1) partager la même chose FileStream
avec les fonctions de synchronisation appropriées (car il n'est pas thread-safe ). Voir ceci et ces messages pour un exemple.
2) utilisez une FileShare
énumération pour indiquer au système d'exploitation d'autoriser d'autres processus (ou d'autres parties de votre propre processus) à accéder au même fichier simultanément.
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.Read))
{
}
Dans cet exemple, j'ai montré comment ouvrir un fichier pour l'écriture et le partager pour la lecture; Veuillez noter que lors de la lecture et de l'écriture de chevauchements, il en résulte des données non définies ou invalides. C'est une situation qui doit être gérée lors de la lecture. Notez également que cela ne permet pas d'accéder au stream
thread-safe, donc cet objet ne peut pas être partagé avec plusieurs threads à moins que l'accès ne soit synchronisé d'une manière ou d'une autre (voir les liens précédents). D'autres options de partage sont disponibles et ouvrent des scénarios plus complexes. Veuillez consulter MSDN pour plus de détails.
En général, N processus peuvent lire tous ensemble à partir du même fichier, mais un seul doit écrire.Dans un scénario contrôlé, vous pouvez même activer les écritures simultanées, mais cela ne peut pas être généralisé en quelques paragraphes de texte dans cette réponse.
Est-il possible de déverrouiller un fichier utilisé par un autre processus? Ce n'est pas toujours sûr et pas si facile mais oui, c'est possible .
File.Create(path)
, vous devez ajouter .Close()
la fin de celui-ci avant d'écrire dessus. Il y a des pièges comme ça, en plus des using
instructions pour écrire les fichiers, puis pour les supprimer après eux. Vous devriez publier le code dans votre question pour savoir comment vous créez et supprimez votre fichier. Mais cela correspond probablement à quelque chose mentionné ci-dessus.
Directory.SetCreationTimeUTC()
mais il échoue lorsque l'Explorateur de fichiers est ouvert, affirmant que le répertoire est accédé par un autre processus. Comment dois-je gérer cette situation?
L'utilisation de FileShare a résolu mon problème d'ouverture de fichier même s'il est ouvert par un autre processus.
using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
J'ai rencontré un problème lors du téléchargement d'une image, je n'ai pas pu la supprimer et j'ai trouvé une solution. GL hf
//C# .NET
var image = Image.FromFile(filePath);
image.Dispose(); // this removes all resources
//later...
File.Delete(filePath); //now works
J'ai eu cette erreur parce que je faisais File.Move vers un chemin de fichier sans nom de fichier, je dois spécifier le chemin complet dans la destination.
L'erreur indique qu'un autre processus tente d'accéder au fichier. Peut-être que vous ou quelqu'un d'autre l'avez ouvert pendant que vous essayez de lui écrire. "Lire" ou "Copier" ne cause généralement pas cela, mais y écrire ou appeler delete le ferait.
Il y a quelques éléments de base pour éviter cela, comme d'autres réponses l'ont mentionné:
En FileStream
opération, placez-le dans un using
bloc avec un FileShare.ReadWrite
mode d'accès.
Par exemple:
using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}
Notez que ce FileAccess.ReadWrite
n'est pas possible si vous utilisez FileMode.Append
.
J'ai rencontré ce problème lorsque j'utilisais un flux d'entrée pour faire un File.SaveAs
lorsque le fichier était en cours d'utilisation. Dans mon cas, j'ai trouvé que je n'avais pas du tout besoin de le sauvegarder dans le système de fichiers, donc j'ai fini par le supprimer, mais j'aurais probablement pu essayer de créer un FileStream dans une using
instruction avec FileAccess.ReadWrite
, un peu comme le code au dessus.
Enregistrer vos données dans un fichier différent et revenir en arrière pour supprimer l'ancien lorsqu'il s'avère qu'il n'est plus utilisé, puis renommer celui qui a été enregistré avec succès au nom de l'original est une option. La façon dont vous testez le fichier en cours d'utilisation est effectuée via le
List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);
ligne dans mon code ci-dessous, et pourrait être fait dans un service Windows, en boucle, si vous avez un fichier particulier que vous souhaitez regarder et supprimer régulièrement lorsque vous souhaitez le remplacer. Si vous n'avez pas toujours le même fichier, un fichier texte ou une table de base de données pourrait être mis à jour pour que le service vérifie toujours les noms de fichiers, puis effectue cette vérification des processus et effectue ensuite le processus tue et supprime, comme je le décris dans l'option suivante. Notez que vous aurez besoin d'un nom d'utilisateur et d'un mot de passe de compte disposant des privilèges d'administrateur sur l'ordinateur donné, bien sûr, pour effectuer la suppression et la fin des processus.
Lorsque vous ne savez pas si un fichier sera en cours d'utilisation lorsque vous essayez de l'enregistrer, vous pouvez fermer tous les processus qui pourraient l'utiliser, comme Word, s'il s'agit d'un document Word, avant l'enregistrement.
S'il est local, vous pouvez le faire:
ProcessHandler.localProcessKill("winword.exe");
S'il est distant, vous pouvez le faire:
ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");
où txtUserName
est sous la forme de DOMAIN\user
.
Disons que vous ne connaissez pas le nom du processus qui verrouille le fichier. Ensuite, vous pouvez faire ceci:
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);
}
Notez qu'il file
doit s'agir du chemin UNC: \\computer\share\yourdoc.docx
pour que le Process
puisse savoir sur quel ordinateur il se trouve et p.MachineName
être valide.
Vous trouverez ci-dessous la classe à laquelle ces fonctions utilisent, ce qui nécessite l'ajout d'une référence System.Management
. Le code a été écrit à l'origine par Eric J .:
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;
}
}
Comme d'autres réponses de ce fil l'ont souligné, pour résoudre cette erreur, vous devez inspecter attentivement le code pour comprendre où le fichier est verrouillé.
Dans mon cas, j'envoyais le fichier sous forme de pièce jointe à un e-mail avant d'effectuer l'opération de déplacement.
Le fichier a donc été verrouillé pendant quelques secondes jusqu'à ce que le client SMTP ait fini d'envoyer l'e-mail.
La solution que j'ai adoptée était de déplacer d'abord le fichier, puis d'envoyer l'e-mail. Cela a résolu le problème pour moi.
Une autre solution possible, comme indiqué précédemment par Hudson, aurait été de jeter l'objet après utilisation.
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()
ne fonctionnera pas et donne la même erreur. Si vous ajoutez simplement un fichier à un e-mail, je ne pense pas que ce soit une erreur lors de son utilisation pendant l' Attachments.Add()
opération car il ne s'agit que d'une opération de copie. Si c'est le cas pour une raison quelconque, vous pouvez le copier dans un répertoire Temp, joindre la copie et supprimer le fichier copié par la suite. Mais je ne pense pas, si l'OP veut modifier un fichier et l'utiliser, que ce genre de solution (dont vous n'avez pas montré le code, seule la partie jointe) fonctionnerait. .Dispose()
est toujours une bonne idée, mais pas pertinente ici à moins que le fichier ne soit ouvert dans une opération antérieure.
J'ai eu le scénario suivant qui causait la même erreur:
La plupart des fichiers étaient de petite taille, cependant, quelques-uns étaient volumineux et toute tentative de suppression entraînait une erreur de fichier impossible d'accès .
Ce n’était pas facile à trouver, cependant, la solution était aussi simple que d’ attendre «que la tâche soit terminée»:
using (var wc = new WebClient())
{
var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
tskResult.Wait();
}
Mon code ci-dessous résout ce problème, mais je suggère que vous devez d'abord comprendre ce qui cause ce problème et essayer la solution que vous pouvez trouver en changeant le code
Je peux donner un autre moyen de résoudre ce problème, mais la meilleure solution est de vérifier votre structure de codage et d'essayer d'analyser ce qui fait que cela se produit, si vous ne trouvez aucune solution, vous pouvez utiliser ce code ci-dessous
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.*()
vous avez probablement d'autres problèmes avec votre code. 2) Le message est localisé et fragile , utilisez plutôt HRESULT. 3) Vous voudrez peut-être dormir avec Task.Delay()
(et dix secondes sont en quelque sorte excessives dans de nombreux cas). 4) Vous n'avez pas de condition de sortie: ce code pourrait se bloquer pour toujours. 5) Vous n'avez certainement pas besoin goto
ici. 6) Attraper Exception
est généralement une mauvaise idée, dans ce cas aussi parce que ... 6) Si autre chose se produit, vous avalez l'erreur.
GC.Collect()
a été celui de traiter certains objets COM.