System.IO.IOException: "Die Datei existiert" bei Verwendung von System.IO.Path.GetTempFileName () - Auflösungen?

84

Einer meiner Kunden bekam eine Ausnahme, wenn er versuchte, mein Produkt zu verwenden. Ich habe den Callstack der aufgetretenen Ausnahme erhalten, deren oberste lautet:

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)

Beim Googeln habe ich viele Blog-Beiträge gefunden, die besagen, dass diese Ausnahme ausgelöst wird, wenn sich mehr als 65535 temporäre Dateien im Ordner% TEMP% befinden und die Lösung darin besteht, die alten temporären Dateien einfach zu löschen. Ich kann den Kunden bitten, dies zu tun, aber dies ist möglicherweise nur eine vorübergehende Lösung. Was ist, wenn er regelmäßig eine andere Software ausführt, die häufig GetTempFileName aufruft, wodurch das Problem immer wieder auftritt?

Ich kann den Ordner% TEMP% nicht einfach programmgesteuert löschen, da dies etwas anderes beschädigen könnte, und ich kann es nicht vermeiden, GetTempFileName aufzurufen (und stattdessen meinen eigenen temporären Ordner zu verwenden), da nicht ich, sondern WPF-Code ihn aufruft.

Gibt es dafür eine dauerhafte Lösung?

UPDATE : Ich habe bestätigt, dass das Problem, bei dem der Ordner% TEMP% mit Protokolldateien überfüllt ist, nicht durch meinen eigenen Code verursacht wird und von einer anderen Drittanbieteranwendung auf dem Computer des Kunden verursacht werden muss. Ich habe mich auch mit der Implementierung von befasst Cursor.LoadFromStreamund es ist sicherlich kein Fehler - es generiert eine temporäre Datei, löscht sie dann aber im finallyBlock.

Omer Raviv
quelle
3
Sie könnten Ihren eigenen "Temp" -Ordner erstellen, der gelöscht wird (in Anwendungsdaten "), aber wahrscheinlich wäre es ein Problem, alle Referenzen zu ändern, gute Frage
Sayse
Die Frage bezieht sich nicht auf WPF, entferntes Tag. Warum nicht einfach den Code reparieren, der so viele temporäre Dateien ohne Löschung erzeugt?
Dennis
1
@Sayse Ich kann das nicht tun, weil es WPFs sind Cursor.LoadFromStream, die die temporäre Datei generieren. @ Tennis Es hängt mit der Cursor.LoadFromStreamKlasse von WPF zusammen . Der fehlerhafte Code, der so viele temporäre Dateien ohne Löschung erzeugt, ist möglicherweise nicht einmal mein eigener, und ich müsste die Ausnahme noch beheben.
Omer Raviv
Können Sie herausfinden, welche Anwendung all diese temporären Dateien zurücklässt? Ist es Ihre Bewerbung? Wenn WPF diese temporären Dateien selbst erstellt, haben Sie bestätigt, dass sie gelöscht werden, wenn sie nicht mehr benötigt werden?
Ashigore
2
@OmerRaviv Ich denke, Ihre einzige Option ist dann, die IOE zu versuchen / zu fangen und den Benutzer zu fragen, ob er möchte, dass Sie
temporäre

Antworten:

15

Wie ich in meinem letzten Kommentar erwähnt habe, besteht Ihre einzige sichere Möglichkeit darin, den Benutzer zu fragen, ob er möchte, dass Sie Dateien löschen und es erneut versuchen. Es ist unbedingt erforderlich, dass Sie die Benutzer dazu auffordern, auf diese Weise auf eigene Gefahr. In meinem Kopf ist es etwas Ähnliches.

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;
    }

}

Eine optionale Überprüfung, um sicherzustellen, könnte sein,

Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
  .Count() == ushort.MaxValue;
Sayse
quelle
2
Ich werde versuchen / fangen, wie du vorgeschlagen hast. Der von Ihnen vorgeschlagene boolesche Test ist tatsächlich falsch. Es ist wahrscheinlich, dass sich im Ordner% TEMP% noch andere Dateien im Format "tmpXXXX.tmp" befinden, die GetTempFileName () verwendet. Daher kann Ihr Test true zurückgeben, wenn tatsächlich kein Problem vorliegt false, wenn ein Problem vorliegt.
Omer Raviv
Der Test wurde entwickelt, um alle Dateien in einem temporären Ordner zu finden (obwohl ich zugebe, dass es möglicherweise nicht der richtige temporäre Ordner ist). Wenn dies 65535 entspricht, "*"werden alle Dateien gefunden (unabhängig davon, ob sie eine Erweiterung haben oder nicht). Ich hoffe, es hilft
Sayse
Sie können alle Dateien löschen, die älter als beispielsweise ein Tag sind. Es ist unwahrscheinlich, dass eine temporäre Datei, die älter als ein Tag ist, von einer anderen Anwendung verwendet wird.
JT Taylor
2
Nur ein weiterer Punkt auf der optionalen Prüfung. Als ich auf einem Build-Server auf dieses Problem stieß, hatte das temporäre Verzeichnis mehr als 65535 Dateien. Diese Zählung funktioniert nur, wenn die einzige Möglichkeit zum Erstellen temporärer Dateien die Path-Hilfsklasse ist.
Rshadman
36

Wenn Ihnen dies in einer Produktionsumgebung oder mit einer App passiert, die Sie nicht ändern können, besteht die schnelle Lösung darin, den Ordner Temp zu leeren.

Abhängig vom Benutzer, auf dem die Anwendung ausgeführt wird, sollten Sie dies auch tun

  • Leer C:\Windows\Temp(für IIS oder Dienste, die unter LocalSystemKonto ausgeführt werden)
  • Oder %temp%für lokal angemeldete Benutzer (was für mich ist C:\Users\MyUserName\AppData\Local\Temp).

Auf der anderen Seite, wenn Ihr eigener Code dies auslöst und Sie verhindern möchten, dass dies jemals wieder passiert:

  1. Verwenden Sie nicht System.IO.Path.GetTempFileName ()!

GetTempFileName()ist ein Wrapper der zwei Jahrzehnte alten Win32-API . Es werden Dateinamen generiert, die sehr leicht kollidieren. Es umgeht diese Kollisionen, indem es das Dateisystem stark durchläuft, mögliche Dateinamen von "%temp%\tmp0000.tmp"bis wiederholt "tmpFFFF.tmp"und bereits vorhandene überspringt. Dies ist ein E / A-intensiver, langsamer und ehrlich gesagt schrecklicher Algorithmus. Durch die Verwendung von nur 4 Hex-Zeichen wird das künstliche Limit von 65536 Dateien festgelegt, bevor ein Fehler auftritt.

Die Alternative besteht darin, Dateinamen zu generieren, die nicht kollidieren. GUID'sLassen Sie uns zum Beispiel die Logik wiederverwenden : 32 hexadezimale Ziffern kollidieren fast nie.

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

Dies erweitert das Limit von 65.000 auf maximal 4.000 Millionen Dateien (theoretisch) ... Natürlich ist es schon schrecklich, 65.000 Dateien durchgesickert zu haben, also ...

  1. Keine temporären Dateien verlieren!

Überprüfen Sie Ihre App auf alle glücklichen und unglücklichen Pfade (wie unerwartete Ausnahmen). Stellen Sie sicher, dass jeder FileStream korrekt entsorgt und die temporären Dateien in finally-Blöcken gelöscht werden.

  1. Bereinigen Sie den temporären Ordner

Bereinigen Sie es jetzt und weisen Sie den Systemadministrator an, es regelmäßig zu bereinigen, da Sie nicht jeder App in freier Wildbahn vertrauen können. Auf meinen eigenen Servern würde ich diese Aufgabe automatisieren, indem ich:

  • Für globales Windows \ Temp

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

  • Für den aktuellen Benutzer:

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

Gerardo Grignoli
quelle
5

Hier ist der Code, den ich am Ende verwendet und früh in den Initialisierungscodepfad meiner App eingefügt habe, bevor Aufrufe Cursor.LoadFromStreamauftreten können:

    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);
        }
    }
Omer Raviv
quelle
2

Lösungen:

  1. Das richtige. Ermitteln Sie, welche Anwendung so viele temporäre Dateien erstellt, und löschen Sie sie nicht. Dienstprogramme wie Process monitorsollten Ihnen helfen. Korrigieren Sie dann entweder die Anwendung oder werfen Sie sie weg. Und ja, dies könnte Ihre Anwendung sein. Deshalb würde ich Ihnen empfehlen, die Quelle des Bösen zu entdecken.
  2. Der einfachste. Verwenden Sie Ihr eigenes temporäres Verzeichnis. Dies hilft nicht, wenn die Dateien aus Ihrem Code erstellt werden.
  3. Der hässlichste. Löschen Sie das temporäre Verzeichnis aus Ihrer Anwendung. Sie haben absolut Recht mit den Konsequenzen - Sie könnten eine andere Anwendung beschädigen.
Dennis
quelle
Die Verwendung eines eigenen temporären Verzeichnisses ist nicht unbedingt eine Lösung. Ich verwende die API, um einen temporären Namen zu generieren, schreibe ihn aber in mein eigenes Verzeichnis. Leider scheitert auch DAS.
George Mauer
2
// one more implementation
string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
Volody
quelle
1

Wie von Sayse vorgeschlagen, können Sie versuchen, die Umgebungsvariable% TEMP% beim Start Ihrer App festzulegen.

Environment.SetEnvironmentVariable("TEMP", "<dir>");
Ed Chapel
quelle
Das ist eine großartige Idee, aber leider ist meine App eine Visual Studio-Erweiterung, die friedlich mit anderen Erweiterungen koexistieren muss, und ich befürchte, dass dies das Verhalten der anderen Erweiterungen versehentlich beschädigen könnte.
Omer Raviv
Diese Lösung hilft nicht, wenn es sein eigenes Programm ist, das all diese Dateien zurücklässt. Das neue Verzeichnis füllt sich nur und es ist schlecht, Dateien willkürlich aus einem Ordner zu löschen, wenn Sie keine Ahnung haben, wofür sie bestimmt sind, selbst wenn es sich um einen für Ihre Anwendung spezifischen Ordner handelt.
Ashigore
@Ashigore Ja, dies wird offensichtlich keinen Fehler beheben, den er erstellt hat. Speziell referenzieren what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName.
Ed Chapel
@OmerRaviv Diese Informationen sind hilfreich. In diesem Szenario funktioniert dies in der Tat nicht.
Ed Chapel
1
Sorry @EdChapel, SO hat meine Stimme gesperrt, ich kann sie nur entfernen, wenn die Antwort bearbeitet wurde.
Gerardo Grignoli
1

Für alle anderen, die dieses Problem haben und keinen überfüllten temporären Ordner finden können - Überprüfen Sie den Ordner "C: / Windows / Temp". Das Bereinigen dieses Ordners löste meine Probleme.

Fredrik Fahlman
quelle