95 votes

System.IO.IOException : "Le fichier existe" lors de l'utilisation de System.IO.Path.GetTempFileName() - résolutions ?

Un de mes clients a eu une exception à chaque fois qu'il a essayé d'utiliser mon produit. J'ai obtenu le callstack de l'exception qui s'était produite, dont le sommet est :

at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
   at System.IO.__Error.WinIOError()
   at System.IO.Path.GetTempFileName()
   at System.Windows.Input.Cursor.LoadFromStream(Stream cursorStream)
   at System.Windows.Input.Cursor..ctor(Stream cursorStream)

En cherchant sur Google, j'ai trouvé beaucoup de articles de blog indiquant que cette exception est levée lorsqu'il y a plus de 65535 fichiers temporaires dans le dossier %TEMP%, et que la solution consiste simplement à effacer les anciens fichiers temporaires. Je peux demander au client de le faire, mais cela pourrait n'être qu'une solution temporaire. Que faire si le client utilise régulièrement un autre logiciel qui fait de fréquents appels à GetTempFileName, ce qui fera réapparaître le problème encore et encore ?

Je ne peux pas simplement effacer par programme le dossier %TEMP%, car cela pourrait endommager quelque chose d'autre, et je ne peux pas éviter d'appeler GetTempFileName (et utiliser mon propre dossier temporaire à la place) car ce n'est pas moi mais le code WPF qui l'appelle.

Existe-t-il une solution permanente à ce problème ?

UPDATE : J'ai confirmé que le problème où le dossier %TEMP% déborde de fichiers journaux n'est pas causé par mon propre code, et doit être causé par une autre application tierce sur la machine du client. J'ai également examiné l'implémentation de Cursor.LoadFromStream et il n'est sûrement pas en cause - il génère un fichier temporaire, mais le supprime ensuite en finally bloc.

48voto

Gerardo Grignoli Points 782

Si cela vous arrive sur un environnement de production ou avec une application que vous ne pouvez pas modifier, la solution rapide consiste à vider le dossier Temp.

En fonction de l'utilisateur qui exécute l'application, vous devez soit

  • Vide C:\Windows\Temp (pour IIS ou les services fonctionnant sous LocalSystem compte)
  • Ou %temp% pour les utilisateurs connectés localement (ce qui pour moi est C:\Users\MyUserName\AppData\Local\Temp ).

D'un autre côté, si c'est votre propre code qui déclenche ce problème et que vous voulez éviter que cela ne se reproduise :

  1. N'utilisez pas System.IO.Path.GetTempFileName() !

GetTempFileName() est une enveloppe de l'élément Api Win32 vieux de deux décennies . Il génère des noms de fichiers qui vont très facilement entrer en collision. Il contourne ces collisions en bouclant fortement sur le système de fichiers, en itérant les noms de fichiers possibles de "%temp%\tmp0000.tmp" a "tmpFFFF.tmp" et en sautant celles qui existent déjà. Il s'agit d'un algorithme très gourmand en E/S, lent et franchement terrible. De plus, l'utilisation de seulement 4 caractères hexagonaux est à l'origine de la limite artificielle de 65536 fichiers avant l'échec.

L'alternative est de générer des noms de fichiers qui n'entreront pas en collision. Par exemple, réutilisons GUID's logique : 32 chiffres hexadécimaux n'entreront presque jamais en collision.

private string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
}
// Sample: c:\Windows\Temp\2e38fe87-f6bb-4b0d-90b3-2d07016324c1

Cela fait passer la limite de 65k à 4k millions de fichiers maximum (théoriquement)... Bien sûr, avoir une fuite de 65k fichiers est déjà terrible, donc...

  1. Ne faites pas fuir les fichiers temporaires !

Vérifiez à nouveau votre application pour tous les chemins heureux et malheureux (comme les exceptions inattendues). Assurez-vous que chaque FileStream est correctement éliminé et que les fichiers temporaires sont supprimés dans les blocs Finally.

  1. Nettoyer le dossier temporaire

Nettoyez-le maintenant, et apprenez à l'administrateur système à le nettoyer périodiquement, car vous ne pouvez pas faire confiance à toutes les applications dans la nature. Sur mes propres serveurs, j'automatiserais cette tâche en utilisant :

  • Pour Windows global \Temp

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete Global Temp Files" /sc WEEKLY /ST 12:00 /ru system

  • Pour l'utilisateur actuel :

schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete %username% Temp Files" /sc WEEKLY /ST 12:00

16voto

Sayse Points 9275

Comme je l'ai mentionné dans mon dernier commentaire, je pense que la seule façon sûre de le faire est de demander à l'utilisateur s'il veut que vous supprimiez les fichiers et de réessayer. Il est impératif que vous obteniez la contribution des utilisateurs à ce sujet, de cette façon, c'est à leurs risques et périls. Dans ma tête, c'est quelque chose de similaire à.

public Stream GetStream(Stream cursorStream)
{
    try
    {
       //getting stream
    }
    catch(IOE)
    {
        MessageBox.Show(this, "Unable to get stream, your temporary
                              folder may be full, do you want to try deleting 
                                some and try again?");
         if(yes)
         try
         {
             //delete and try again
             return GetStream(cursorStream);
         }
         catch(IOE)
          {
                //no luck
           }
          else
              return null;
    }

}

Une vérification facultative pour s'en assurer pourrait être,

Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
  .Count() == ushort.MaxValue;

5voto

Omer Raviv Points 4100

Voici le code que j'ai utilisé à la fin, et que j'ai placé tôt dans le chemin de code d'initialisation de mon application, avant tout appel à Cursor.LoadFromStream pourrait se produire :

    private void WarnUserIfTempFolderFull()
    {
        string tempFile = null;
        try
        {
            tempFile = Path.GetTempFileName();
        }
        catch (IOException e)
        {
            string problem = "The Temporary Folder is full.";

            string message = "{ProductName} has detected that the Windows Temporary Folder is full. \n" + 
                             "This may prevent the {ProductName} from functioning correctly.\n" + 
                             "Please delete old files in your temporary folder (%TEMP%) and try again.";

            Logger.Warn(problem);

            MessageBox.Show(message, caption: problem);
        }
        finally
        {
            if (tempFile != null) File.Delete(tempFile);
        }
    }

2voto

Dennis Points 14573

Solutions :

  1. Le bon. Déterminez quelle application produit tant de fichiers temporaires et ne les supprime pas. Des utilitaires comme Process monitor devrait vous aider. Ensuite, soit vous réparez l'application, soit vous la jetez. Et oui, cela pourrait être votre application. C'est pourquoi je vous recommande de détecter la source du mal.
  2. Le plus facile. Utilisez votre propre répertoire temporaire. Cela ne sera pas utile si les fichiers sont créés à partir de votre code.
  3. Le plus laid. Effacez le répertoire temporaire de votre application. Vous avez tout à fait raison au sujet des conséquences - vous pourriez casser une autre application.

2voto

Fredrik Fahlman Points 29

Si quelqu'un d'autre a rencontré ce problème et ne trouve pas de dossier temporaire débordant Vérifiez le dossier "C:/Windows/Temp". Le nettoyage de ce dossier a résolu mes problèmes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X