System.IO.IOException: "Il file esiste" quando si utilizza System.IO.Path.GetTempFileName () - risoluzioni?


86

Uno dei miei clienti ha ottenuto un'eccezione ogni volta che ha provato a utilizzare il mio prodotto. Ho ottenuto il callstack dell'eccezione che si era verificata, il cui inizio è:

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)

Cercando su Google, ho trovato molti post sul blog che affermano che questa eccezione viene generata quando ci sono più di 65535 file temporanei nella cartella% TEMP% e che la soluzione è semplicemente cancellare i vecchi file temporanei. Posso chiedere al cliente di farlo, ma questa potrebbe essere solo una soluzione temporanea: cosa succede se eseguono regolarmente qualche altro software che effettua chiamate frequenti a GetTempFileName, il che farà sì che il problema si ripresenti più e più volte?

Non posso semplicemente cancellare a livello di programmazione la cartella% TEMP%, poiché ciò potrebbe in qualche modo danneggiare qualcos'altro, e non posso evitare di chiamare GetTempFileName (e utilizzare invece la mia cartella temporanea) poiché non sono io ma il codice WPF che lo chiama.

Esiste una soluzione permanente per questo?

AGGIORNAMENTO : Ho confermato che il problema in cui la cartella% TEMP% trabocca di file di registro non è causato dal mio codice e deve essere causato da qualche altra applicazione di terze parti sul computer del cliente. Ho anche esaminato l'implementazione di Cursor.LoadFromStreame sicuramente non è colpa: genera un file temporaneo, ma poi lo elimina in finallyblocco.


3
Potresti creare la tua cartella "Temp" che viene eliminata (nei dati dell'applicazione ") ma probabilmente sarebbe un problema cambiare tutti i riferimenti, buona domanda
Sayse

La domanda non è correlata a WPF, tag rimosso. Inoltre, perché semplicemente non correggere il codice, che produce così tanti file temporanei senza eliminazione?
Dennis

1
@Dice che non posso farlo perché è WPF Cursor.LoadFromStreamche sta generando il file temporaneo. @Dennis È correlato alla Cursor.LoadFromStreamclasse di WPF . Il codice offensivo che produce così tanti file temporanei senza eliminazione potrebbe non essere nemmeno il mio e dovrei comunque affrontare l'eccezione.
Omer Raviv

Riesci a scoprire quale applicazione sta lasciando tutti questi file temporanei? È la tua applicazione? Se WPF sta creando questi file temporanei, hai confermato che li sta eliminando quando non sono più necessari?
Ashigore

2
@OmerRaviv Penso che la tua unica opzione sia provare / catturare l'IOE e chiedere all'utente se vuole che elimini i file temporanei e riprova
Sayse

Risposte:


16

Come ho detto nel mio ultimo commento, penso che il tuo unico modo sicuro per farlo sia chiedere all'utente se desidera che elimini i file e riprova. È imperativo che tu ottenga l'input degli utenti, in questo modo è a loro rischio e pericolo. Nella mia testa è qualcosa di simile a.

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

}

Un controllo facoltativo per assicurarsi che potrebbe essere,

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

2
Vado con una prova / cattura come hai suggerito. Il test booleano che hai suggerito è effettivamente sbagliato: è probabile che ci siano altri file nella cartella% TEMP%, oltre a quelli nel formato "tmpXXXX.tmp" utilizzato da GetTempFileName (), quindi il tuo test potrebbe restituire vero quando in realtà non c'è problema false quando c'è un problema.
Omer Raviv

Il test è stato progettato per trovare tutti i file in una cartella temporanea (anche se ammetto che potrebbe non essere la cartella temporanea giusta) e vede se è uguale a 65535, "*"troverà tutti i file (indipendentemente dal fatto che abbiano un'estensione o meno) spero che aiuti
Sayse

Potresti eliminare tutti i file più vecchi, diciamo, di un giorno. È improbabile che un file temporaneo più vecchio di un giorno venga utilizzato da altre applicazioni.
JT Taylor,

2
Solo un altro punto sul controllo opzionale. Quando ho riscontrato questo problema su un server di compilazione, la directory temporanea conteneva più di 65535 file. Quel conteggio funziona solo se l'UNICA cosa che crea file temporanei è tramite la classe helper Path.
rshadman

37

Se ti succede in un ambiente di produzione o con un'app che non puoi modificare, la soluzione rapida è svuotare la cartella Temp.

A seconda dell'utente che esegue l'applicazione, dovresti farlo

  • Vuoto C:\Windows\Temp(per IIS o servizi in esecuzione con LocalSystemaccount)
  • O %temp%per gli utenti connessi localmente (che per me lo è C:\Users\MyUserName\AppData\Local\Temp).

D'altra parte, se il tuo codice lo lancia e vuoi evitare che ciò accada mai più:

  1. Non utilizzare System.IO.Path.GetTempFileName ()!

GetTempFileName()è un wrapper dell'API Win32 vecchia di due decenni . Genera nomi di file che si scontreranno molto facilmente. Elude queste collizioni ripetendo pesantemente il file system, iterando possibili nomi di file da "%temp%\tmp0000.tmp"a "tmpFFFF.tmp"e saltando quelli già esistenti. Questo è un algoritmo I / O intensivo, lento e francamente terribile. Inoltre, l'utilizzo di soli 4 caratteri esadecimali è ciò che rende il limite artificiale di 65536 file prima di fallire.

L'alternativa è generare nomi di file che non entrino in collisione. Ad esempio, riutilizziamo la GUID'slogica: 32 cifre esadecimali non entreranno quasi mai in collisione.

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

Questo espande il limite da 65k a 4k milioni di file al massimo (teoricamente) ... Ovviamente, aver trapelato 65k file è già terribile, quindi ...

  1. Non perdere file temporanei!

Controlla la tua app per tutti i percorsi felici e infelici (come eccezioni impreviste). Assicurati che elimini correttamente ogni FileStream ed elimini i file temporanei nei blocchi Finalmente.

  1. Pulisci la cartella temp

Puliscilo ora e istruisci l'amministratore di sistema a pulirlo periodicamente, perché non puoi fidarti di tutte le app in circolazione. Sui miei server automatizzerei questa attività utilizzando:

  • Per Windows \ Temp globale

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

  • Per l'utente corrente:

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


5

Ecco il codice che ho usato alla fine e inserito all'inizio del percorso del codice di inizializzazione della mia app, prima che si verifichi qualsiasi chiamata a Cursor.LoadFromStream:

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

2

Soluzioni:

  1. Quello giusto. Rileva quale applicazione produce così tanti file temporanei e non li elimina. Utilità come Process monitordovrebbero aiutarti. Quindi aggiusta l'applicazione o gettala via. E sì, questa potrebbe essere la tua applicazione. ecco perché ti consiglio di individuare la fonte del male.
  2. Il più semplice. Usa la tua directory temporanea. Questo non aiuterà se i file vengono creati dal tuo codice.
  3. Il più brutto. Cancella la directory temporanea dall'applicazione. Hai assolutamente ragione sulle conseguenze: potresti interrompere un'altra applicazione.

L'utilizzo della propria directory temporanea non è necessariamente una soluzione. Uso l'API per generare un nomefile temp ma lo scrivo nella mia directory. Purtroppo anche QUELLO fallisce.
George Mauer

2
// one more implementation
string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}

1

Come suggerito da Sayse , potresti provare a impostare la variabile d'ambiente% TEMP% all'avvio della tua app.

Environment.SetEnvironmentVariable("TEMP", "<dir>");

È un'ottima idea, ma sfortunatamente la mia app è un'estensione di Visual Studio che deve coesistere pacificamente con altre estensioni e temo che ciò possa in qualche modo danneggiare inavvertitamente il comportamento delle altre estensioni.
Omer Raviv

Questa soluzione non aiuterà se è il suo programma a lasciare tutti questi file. La nuova directory si riempirà ed è un male eliminare arbitrariamente i file da una cartella quando non hai idea di cosa siano, anche se si tratta di una cartella specifica per la tua applicazione.
Ashigore

@Ashigore Sì, ovviamente questo non risolverà un bug che ha creato. Riferimento specifico what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName.
Ed Chapel

@OmerRaviv Queste informazioni sono utili. In effetti questo non funzionerà in questo scenario.
Ed Chapel

1
Mi dispiace @EdChapel, SO ha bloccato il mio voto, non posso rimuoverlo a meno che la risposta non venga modificata.
Gerardo Grignoli

1

Per chiunque altro abbia riscontrato questo problema e non riesca a trovare alcuna cartella temporanea traboccante - Controllare la cartella "C: / Windows / Temp". La pulizia di questa cartella ha risolto i miei problemi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.