Fichier verrouillé sans avoir été ouvert.

Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
- - Dernière réponse : vb95
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
- 25 mai 2019 à 17:56
Bonjour,

Je me heurte encore au traditionnel "Le processus ne peut pas accéder au fichier, car il est utilisé par un autre processus". Bizarre, je n'ai jamais explicitement ouvert ce fichier.
Je m'explique :

L'application est destinée à renommer des fichiers de photos selon certains critères. Pour cela, j'en charge la liste dans un ListView, soit par menu, soit par drag&drop.
Pour gérer les paramètres de renommage, j'utilise les fonctions suivantes :
Path.GetExtension(FileName);
Path.GetFileNameWithoutExtension(FileName)
Path.GetDirectoryName(FileName)
A priori, ces fonctions ne "touchent" pas au fichier car elles ne font que travailler sur une chaine de caractères.

Ensuite, j'ai besoin d'accéder à la date de prise de vue, dans le bloc EXIF de la photo. Pour cela, je charge l'image et j'en extrais la date :
private DateTime GetDate(string FileName)
{
    try
    {
        Image image = (Image)(new Bitmap(FileName)).Clone();
        DateTime dtDate = new DateTime(0);
        const int DateKey = 36867;
        if (image.PropertyIdList.Contains(DateKey))     // Bloc EXIF existant et standard
        {
            string strDate = Encoding.UTF8.GetString(image.GetPropertyItem(DateKey).Value);
            string[] strDateTime = strDate.Replace("\0", "").Split(' ');
            strDateTime[0] = strDateTime[0].Replace(':', '-');
            strDate = string.Join(" ", strDateTime);
            dtDate = DateTime.Parse(strDate);
        }
        else        // Non EXIF ou pas standard
        {
            //Console.WriteLine("---- " + FileName);
        }

        // Déverouillage du fichier et libération de la mémoire
        image.Dispose();
        image = null;

        return dtDate;
    }
    catch(Exception ex) { Console.WriteLine("{0} =>\n{1}\n", FileName, ex.Message); return new DateTime(0); }
}


Bien que ces procédures "attaquent" le fichier, j'ai pris grand soin de faire une copie de l'image avant d'y travailler, et je la détruis à la sortie. Donc en aucun cas le fichier ne devrait rester verrouillé.

Lorsque je clique sur une ligne de la liste, j'affiche la photo dans un PictureBox :
private void DisplayPicture(ListViewItem Item)
{
    try
    {
        string FilePath = Path.Combine(Item.SubItems[(int)ColNames.Path].Text, Item.Text + Item.Tag.ToString());
        if (!File.Exists(FilePath))
        {
            picPhoto.Image = null;
            picPhoto.Refresh();
            return;
        }
        Image SrceImg = Image.FromFile(FilePath);
        Image Img = SrceImg.Clone() as Image;
        SrceImg.Dispose();
        GC.Collect();           // Pour libérer le fichier et pouvoir le renommer
        GC.WaitForPendingFinalizers();
        picPhoto.Image = Img;
        PnlImage_ClientSizeChanged(null, null);
    }
    catch (Exception ex) { ErrorHandling.TraiteErreur(ex); }
}

Comme on le voit, là encore je prends soin de faire une copie de l'image, de détruire celle provenant du fichier et d'appeler le GC. Pourquoi le fichier resterait verrouillé ? En tous cas, il ne l'est pas en lecture, puisque je peux changer de photo et y revenir.

Le problème survient lors du renommage du fichier : (clic sur une ligne du ListView)
string sName = Item.SubItems[(int)ColNames.Name].Text;
string sPath = Item.SubItems[(int)ColNames.Path].Text;
string sDate = Item.SubItems[(int)ColNames.Date].Text;
string sExt = Item.Tag.ToString();
string NewName = Config.FormatName(sName, sPath, sDate, sExt);
string OldPath = Path.Combine(sPath, sName + sExt);
string NewPath =  Path.Combine(sPath, NewName);
try
{
   <gras> File.Move(OldPath, NewPath);</gras>
    Item.SubItems[(int)ColNames.Text].Text = NewName;
    Item.Checked = false;
    Item.ForeColor = lvwFiles.ForeColor;
    Item.ToolTipText = "";
}
catch(Exception ex)
{
    Item.ToolTipText = ex.Message;
    Item.ForeColor = Color.Red;
    Console.WriteLine("{0} => {1}", sName, ex.Message);
}


et là, paf ! Exception dans la ligne en gras.

Je ne comprends vraiment pas pourquoi les fichiers sont verrouillés par l'application. Et ils le sont vraiment, car tant que l'appli est ouverte, je ne peux pas non plus les renommer avec l'explorateur de fichiers. Pourtant je n'ai jamais ouvert le fichier avec Stream ou autre Open.

Quelqu'un saurait-il par quelle fonction le fichier est verrouillé et comment le déverrouiller ???

PS : Cette appli en C# est la réécriture de la même en VB6. Je n'avais pas tous ces problèmes en VB...
Afficher la suite 

14 réponses

Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
0
Merci
J'avais oublié de valider le sujet...
Entre-temps, j'ai un peu avancé : en supprimant le ".clone()" dans la fonction GetDate(), je n'ai plus d'exception.
J'en déduis que le framework garde le fichier ouvert après avoir fait le clonage #&!@$!!
En travaillant directement sur l'image du fichier et en la disposant ensuite, il semble que le framework déverrouille le fichier. Je le saurai pour les prochaines fois.

Mais maintenant j'ai un autre problème, toujours du même ordre. J'ai écrit :
DateTime OldDate = File.GetLastWriteTime(OldPath);
File.Move(OldPath, NewPath);
GC.Collect();
GC.WaitForPendingFinalizers();
File.SetLastWriteTime(NewPath, OldDate);    // Garder l'ancienne date fichier


J'ai maintenant l'exception "Accès refusé" dans la dernière ligne ci-dessus.
Pourtant je prend soin d'appeler le GC (à tout hasard) mais ça n'apporte rien.

Pourquoi on n'a pas accès à un fichier qu'on vient de renommer ??
Commenter la réponse de MGD Software
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
60
0
Merci
Bonjour
J'ai fait un test en VB Net avec ce code
Imports System.IO

Public Class Form1
    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

        Dim OldPath As String = "D:\dicoreel7.txt"
        Dim NewPath As String = "F:\dicoreel7.txt"
        Dim OldDate As DateTime = File.GetLastWriteTime(OldPath)
        File.Move(OldPath, NewPath)
        File.SetLastWriteTime(NewPath, OldDate)

    End Sub

End Class


Le fichier "D:\dicoreel7.txt" existe bien sur mon lecteur D:\
Son homologue sur F:\ n'existe pas .
Je lance le projet : le fichier se déplace bien sur F:\ avec sa date d'origine

Par contre avec le fichier D:\dicoreel7.txt avec l'attribut Readonly le projet me donne la même erreur que toi ! Le fichier s'est bien déplacé mais on ne peut y accéder pour une modification de date : une piste à creuser à mon avis avec les attributs du fichier .

Voici le même code en c#
using System.IO;

private void Form1_Load(object sender, EventArgs e)
{
    string OldPath = @"D:\dicoreel7.txt";
    string NewPath = @"F:\dicoreel7.txt";
    DateTime OldDate = File.GetLastWriteTime(OldPath);
    File.Move(OldPath, NewPath);
    File.SetLastWriteTime(NewPath, OldDate);
}


Commenter la réponse de vb95
Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
0
Merci
Dans mon cas, le move() ne fait que renommer le fichier, le chemin étant le même.
Le renommage se passe bien, donc le fichier n'est pas verrouillé à ce moment-là.
Mais tout de suite après, le changement de date échoue.
J'ai essayé d'appeler le GC entre les deux, sans succès.

Fichier verrouillé, ou interdiction de changer la date de mise à jour ?
J'avoue ne pas avoir essayé sur un fichier sur lequel il n'y aurait eu aucune autre action.

Il est possible que Windows mette l'opération en cache et l'effectue en différé (quelques millisecondes), ce qui pourrait expliquer le verrouillage. Mais s'il faut mettre un timer pour effectuer chaque action suivante, bonjour l'usine à gaz !

La gestion des dates par Windows est d'ailleurs curieuse : j'ai beaucoup de fichiers dont la date de création (CreationDate) est postérieure à la date de mise à jour (LastWriteDate) !!
Microsoft toujours égal à lui-même...
Commenter la réponse de MGD Software
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
60
0
Merci
Bonjour
Il faut dire qu'un fichier ou répertoire en NTFS contient 4 dates ce qui pourrait expliquer la fin de ton message
Voici un exemple avec un fichier sur mon PC tiré d'un de mes projets : https://codes-sources.commentcamarche.net/source/102800-acces-direct-disques-et-partitions-en-c-2019


La troisième date est plus difficile à comprendre et nécessite le fait que connaître le système de fichier NTFS . Pour faire simple chaque fichier ou répertoire est contenu dans un enregistrement comme une base de donnée . Cette "base" s'appelle la MFT ( Master file Table) . Chaque enregistrement contient ce que l'on appelle des attributs (STANDARD_INFORMATION, FILE_NAME, DATA, etc.....) . Si un de ces attributs change ( exemple le fichier passe en lecture seule ) ceci sera répercuté sur la date de modification MFT alors que si l'on change uniquement les DATA d'un fichier texte ceci sera répercuté sur la date de modification ( la seconde date)

il y a aussi à prendre en compte le déplacement d'un fichier . supposons qu'hier tu crées un fichier Toto.txt sur C:\ . Ensuite toujours hier tu le modifies et tu l'enregistres . Aujourd'hui tu déplaces ce fichier sur D:\ . Sa date de création sur D:\ est donc postérieure à sa date de modification sur C:\ .
Et si on le change de répertoire sur le même lecteur c'est identique .

L'exemple en image illustre justement ce phénomène
date création en 2018 : la première ligne
date modification en 2017 : la seconde ligne


Pour ton problème vu que ton fichier ne change pas de chemin as-tu essayé avec la fonction File.Rename https://docs.microsoft.com/fr-fr/dotnet/api/microsoft.visualbasic.fileio.filesystem.renamefile?view=netframework-4.8
Dans ce dernier cas il n'y a aucun mouvement de fichier au niveau de la fonction : juste un changement de nom de celui-ci

Sous toute réserve

Commenter la réponse de vb95
Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
0
Merci
Rename fait partie de Visual Basic, pas de Visual C#.
Bien que j'aie fait du VB pendant plus de 20 ans (plusieurs centaines de projets, dont une bonne partie professionnelle), je m'astreins absolument à n'utiliser aucune fonction VB dans mon code en C#.
D'autant que la fonction existe en C#, puisqu'elle s'appelle Move, comme en PHP, en FTP ou sous Linux. Quand le chemin est le même, il n'y a pas de mouvement non plus, le fichier n'est pas réécrit, et les dates ne sont pas modifiées (à l'exception de la date d'accès, bien sûr).

Je pense que ces fonctions font toutes deux appel aux même fonctions du kernel de Windows (voire du framework), et qu'elles auront le même résultat.
Commenter la réponse de MGD Software
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
60
0
Merci
Bonsoir !
Désolé pour mon erreur !
J'aurais appris quelque chose au moins : qu'en C# et en VB Net on ne retrouve pas forcément les mêmes noms de méthodes du Framework !

Tu dis : Je pense que ces fonctions font toutes deux appel aux mêmes fonctions du kernel de Windows (voire du framework), et qu'elles auront le même résultat.

Je le pense aussi car ceci paraît plus que logique .
Je ne puis t'en dire plus et ne vois aucune solution à mon niveau pour t'aider
Désolé
@ ++
Commenter la réponse de vb95
Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
0
Merci
Merci quand même.

J'aime bien ton slogan.
Le problème, c'est qu'actuellement je sais pas tout et que ça ne fonctionne pas...

@+
Commenter la réponse de MGD Software
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
60
0
Merci
J'ose une question : tu ouvres ton projet avec Visual Studio en administrateur ?

Moi non plus je ne sais point tout rassures-toi même si je pratique en amateur depuis plus de 30 ans !
Commenter la réponse de vb95
Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
0
Merci
J'évite de lancer Visual Studio en mode administrateur, bien que ça m'arrive parfois (par erreur).

En effet, si VS est en mode administrateur et qu'on lance le debug :
1 - On n'est pas dans le même mode qu'aura l'utilisateur, et donc on peut passer à côté d'exceptions qu'il aura et pas moi.
2 - Beaucoup de mes applications utilisent le drag&drop. Or depuis Windows 7, le drag&drop ne fonctionne pas si on tire des fichiers utilisateur dans une appli en mode administrateur. Donc si VS est mode administrateur lors du debug, je ne peux pas tester le drag&drop de fichiers qui sont dans Mes Documents...
3 - Je ne vois pas l'intérêt de lancer VS en mode administrateur. Cela m''arrive car j'ai mis des raccourcis sur certains programmes dans la section Favoris du menu démarrer (Win7) . Dans ce cas, il faut cocher l'option "Lancer en mode administrateur" du raccourci sinon on a une demande de confirmation à chaque lancement, ce qui est très ch...t (pardon, pénible). Pour VS, j'utilise le menu démarrer normal, en ayant ancré l'item.

Mais ceci n'engage que moi...
@+
Commenter la réponse de MGD Software
Messages postés
13412
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
17 juin 2019
280
0
Merci
Bonjour à tous les 2

je me suis gardé de répondre jusque là car, je n'ai pas trop le temps de tester de mon coté.

Mais VB a dit
Par contre avec le fichier D:\dicoreel7.txt avec l'attribut Readonly le projet me donne la même erreur que toi ! Le fichier s'est bien déplacé mais on ne peut y accéder pour une modification de date

Et MGD, tu n'as pas répondu sur ce point, peut-être as tu testé, ou peut-être as tu zappé cette phrase.
Commenter la réponse de Whismeril
Messages postés
142
Date d'inscription
vendredi 1 septembre 2006
Statut
Membre
Dernière intervention
25 mai 2019
0
Merci
Salut Whismeril

Je n'ai pas répondu parce qu'il me paraît possible qu'on ne puisse pas modifier un fichier en lecture seule, même pour ses dates (mais pas ses attributs). Et en plus ce n'est pas mon cas, le fichiers ne sont pas en lecture seule, et je ne les change pas de répertoire.

Par contre, je ne sais toujours pas si on a le droit de modifier la date de mise à jour ou de création d'un fichier, ce qui pourrait être la cause de l'erreur.

J'ai eu et j'ai toujours beaucoup de soucis avec le C# et le verrouillage des fichiers. J'ai fait d'autres applications d'imagerie où il faut faire des bricolages pas possibles pour réécrire le fichier après modification de l'image, car lors de l’écriture le fichier est encore verrouillé. Je n'avais pas ces problèmes avec VB6, même sous Win7. J'en déduis que c'est le framework qui garde ces fichiers verrouillés.

J'ai notamment un problème étrange : lorsque je récupère l'image d'un fichier photo avec BitmapFromFile(), le fichier reste verrouillé tant que je n'ai pas disposé l'image. Bon, c'est peut-être normal. Mais si je fais un clone() de l'image et un dispose() de l'originale, le fichier est toujours verrouillé tant que je n'ai pas disposé le clone !!
C'est plutôt gênant pour renommer le fichier dont l'image est affichée...
Commenter la réponse de MGD Software
Messages postés
13412
Date d'inscription
mardi 11 mars 2003
Statut
Contributeur
Dernière intervention
17 juin 2019
280
0
Merci
lorsque je récupère l'image d'un fichier photo avec BitmapFromFile(), le fichier reste verrouillé tant que je n'ai pas disposé l'image. Bon, c'est peut-être normal.
oui windows doit considéré que tu as ouvert le fichier et le bloque

Mais si je fais un clone() de l'image et un dispose() de l'originale, le fichier est toujours verrouillé tant que je n'ai pas disposé le clone !!
un clone ne touche pas à l'image, mais à la variable, tu as donc 2 variables distinctes qui verrouillent ton image, c'est logique qu'il faille libérer les 2 variables pour déverrouiller l'image
Commenter la réponse de Whismeril
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
60
0
Merci
Bonjour
Pour MGD_Software
Tu as écrit :"Par contre, je ne sais toujours pas si on a le droit de modifier la date de mise à jour ou de création d'un fichier, ce qui pourrait être la cause de l'erreur. "

J'ai essayé avec ceci et cela ne pose aucun problème
using System;
using System.IO;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, System.EventArgs e)
        {
            string OldPath = @"D:\Infos WIFI.txt";
            string NewPath = @"F:\Infos WIFI.txt";
            DateTime WriteDate = new DateTime(2008, 3, 1, 7, 0, 0);
            DateTime CreateDate = new DateTime(2008, 3, 1, 7, 0, 0); 
            File.Move(OldPath, NewPath);
            File.SetLastWriteTime(NewPath, WriteDate);
            File.SetCreationTime(NewPath, CreateDate); 
        }
    }
}


La modification de la date de création et celle de modification de fichier ne posent aucun problème pour les 2 dernières lignes de code
Le fichier sur le lecteur D est de 2017
Celui sur le lecteur F est bien au 01/03/2008 07:00:00 ( vérifié dans l'Explorateur Windows).
donc j'en conclu qu'on a le droit de modifier la date de mise à jour ou de création d'un fichier

Commenter la réponse de vb95
Messages postés
1886
Date d'inscription
samedi 11 janvier 2014
Statut
Contributeur
Dernière intervention
15 juin 2019
60
0
Merci
Petite précision sur mon dernier post : Visual Studio n'était pas en mode Administrateur
Commenter la réponse de vb95