System.IO.IOException: „Plik istnieje” podczas korzystania z System.IO.Path.GetTempFileName () - rozwiązania?


84

Jeden z moich klientów miał wyjątek, gdy próbował użyć mojego produktu. Otrzymałem stos wywołań wyjątku, który wystąpił, którego górna część to:

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)

Szukając tego w Google, znalazłem wiele postów na blogu, w których stwierdzono, że ten wyjątek jest generowany, gdy w folderze% TEMP% jest więcej niż 65535 plików tymczasowych, a rozwiązaniem jest po prostu wyczyszczenie starych plików tymczasowych. Mogę poprosić klienta o zrobienie tego, ale może to być tylko tymczasowe rozwiązanie - co zrobić, jeśli regularnie korzysta z innego oprogramowania, które często wywołuje GetTempFileName, co spowoduje, że problem będzie się powtarzał?

Nie mogę po prostu programowo wyczyścić folderu% TEMP%, ponieważ może to w jakiś sposób uszkodzić coś innego i nie mogę uniknąć wywołania GetTempFileName (i zamiast tego używania własnego folderu tymczasowego), ponieważ to nie ja, ale kod WPF, który go wywołuje.

Czy jest na to trwałe rozwiązanie?

AKTUALIZACJA : Potwierdziłem, że problem polegający na przepełnieniu folderu% TEMP% plikami dziennika nie jest spowodowany moim własnym kodem, a musi być spowodowany przez inną aplikację innej firmy na komputerze klienta. Przyjrzałem się też implementacji Cursor.LoadFromStreami na pewno nie jest to wina - generuje plik tymczasowy, ale potem usuwa go w finallybloku.


3
Możesz stworzyć własny folder „Temp”, który zostanie usunięty (w danych aplikacji), ale prawdopodobnie będzie to kłopot ze zmianą wszystkich odniesień, dobre pytanie
Powiedz,

Pytanie nie jest związane z WPF, usunięto tag. Poza tym, dlaczego po prostu nie naprawić kodu, który tworzy tak wiele plików tymczasowych bez usuwania?
Dennis

1
@ Powiedz, nie mogę tego zrobić, ponieważ to WPF Cursor.LoadFromStreamgeneruje plik tymczasowy. @Dennis Jest to związane z Cursor.LoadFromStreamklasą WPF . Szkodliwy kod, który tworzy tak wiele plików tymczasowych bez usunięcia, może nawet nie być moim własnym, a mimo to musiałbym zająć się wyjątkiem.
Omer Raviv

Czy możesz dowiedzieć się, która aplikacja pozostawia wszystkie te pliki tymczasowe? Czy to Twoja aplikacja? Jeśli WPF sam tworzy te pliki tymczasowe, czy potwierdziłeś, że usuwa je, gdy nie są już potrzebne?
Ashigore,

2
@OmerRaviv Myślę, że jedyną opcją jest wtedy próba / złapanie IOE i
spytanie

Odpowiedzi:


15

Jak wspomniałem w moim ostatnim komentarzu, myślę, że jedynym bezpiecznym sposobem na to jest zapytanie użytkownika, czy chce, abyś usunął pliki, i spróbuj ponownie. Konieczne jest , aby użytkownicy mieli w tym udział, w ten sposób jest to na własne ryzyko. W mojej głowie to coś podobnego do.

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

}

Opcjonalne sprawdzenie, czy może być,

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

2
Zamierzam spróbować złapać, jak sugerowałeś. Sugerowany przez Ciebie test logiczny jest błędny - prawdopodobnie w folderze% TEMP% znajdują się inne pliki oprócz plików w formacie „tmpXXXX.tmp”, których używa GetTempFileName (), więc Twój test może zwrócić prawdę, gdy w rzeczywistości nie ma problemu i fałsz, gdy JEST problem.
Omer Raviv

Test został zaprojektowany tak, aby znaleźć wszystkie pliki w folderze tymczasowym (chociaż przyznaję, że może to nie być właściwy folder tymczasowy) i zobaczy, czy to równa się 65535, "*"znajdzie wszystkie pliki (niezależnie od tego, czy mają rozszerzenie, czy nie), mam nadzieję, że to pomoże
Sayse

Możesz usunąć wszystkie pliki starsze niż, powiedzmy, jeden dzień. Plik tymczasowy starszy niż jeden dzień prawdopodobnie nie będzie używany przez żadną inną aplikację.
JT Taylor,

2
Jeszcze jeden punkt na opcjonalnym czeku. Kiedy napotkałem ten problem na serwerze kompilacji, katalog tymczasowy zawierał ponad 65535 plików. Ta liczba działa tylko wtedy, gdy JEDYNA rzecz tworząca pliki tymczasowe odbywa się za pośrednictwem klasy pomocniczej Path.
rshadman

36

Jeśli dzieje się tak w środowisku produkcyjnym lub w aplikacji, której nie możesz zmienić, szybkim rozwiązaniem jest opróżnienie folderu Temp.

W zależności od użytkownika, który uruchamia aplikację, należy również

  • Puste C:\Windows\Temp(dla usług IIS lub usług działających na LocalSystemkoncie)
  • Lub %temp%dla użytkowników zalogowanych lokalnie (co dla mnie jest C:\Users\MyUserName\AppData\Local\Temp).

Z drugiej strony, jeśli wyrzuca to Twój własny kod i chcesz zapobiec temu, aby to się powtórzyło:

  1. Nie używaj System.IO.Path.GetTempFileName ()!

GetTempFileName()jest opakowaniem dwóch dekad starego Win32 Api . Generuje nazwy plików, które bardzo łatwo się kolidują. Omija te kolizje, intensywnie zapętlając system plików, iterując możliwe nazwy plików od "%temp%\tmp0000.tmp"do "tmpFFFF.tmp"i pomijając już istniejące. To jest intensywny we / wy, powolny i szczerze mówiąc okropny algorytm. Również użycie tylko 4 znaków szesnastkowych jest tym, co tworzy sztuczny limit 65536 plików przed niepowodzeniem.

Alternatywą jest generowanie nazw plików, które nie będą kolidować. Na przykład użyjmy GUID'slogiki ponownie : 32 cyfry szesnastkowe prawie nigdy się nie kolidują.

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

To zwiększa limit z 65 000 do 4 000 milionów plików (teoretycznie) ... Oczywiście, wyciek 65 000 plików jest już straszny, więc ...

  1. Nie wyciekaj plików tymczasowych!

Dokładnie sprawdź swoją aplikację pod kątem wszystkich szczęśliwych i nieszczęśliwych ścieżek (takich jak nieoczekiwane wyjątki). Upewnij się, że poprawnie usuwa każdy FileStream i usuwa pliki tymczasowe w blokach Final.

  1. Wyczyść folder tymczasowy

Wyczyść go teraz i naucz administratora systemu, aby czyścił go okresowo, ponieważ nie możesz ufać każdej aplikacji w środowisku naturalnym. Na własnych serwerach zautomatyzowałbym to zadanie używając:

  • W przypadku globalnego systemu 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

  • Dla aktualnego użytkownika:

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


5

Oto kod, którego użyłem na końcu i umieściłem wcześnie w ścieżce kodu inicjalizacji mojej aplikacji, zanim Cursor.LoadFromStreammogą wystąpić jakiekolwiek wywołania :

    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

Rozwiązania:

  1. Poprawny. Wykryj, która aplikacja tworzy tak wiele plików tymczasowych i nie usuwaj ich. Narzędzia takie jak Process monitorpowinny ci pomóc. Następnie napraw aplikację lub wyrzuć ją. I tak, może to być Twoja aplikacja. dlatego radziłbym ci wykryć źródło zła.
  2. Najłatwiejszy. Użyj własnego katalogu tymczasowego. To nie pomoże, jeśli pliki są tworzone z twojego kodu.
  3. Najbrzydszy. Wyczyść katalog tymczasowy z aplikacji. Masz całkowitą rację co do konsekwencji - możesz zepsuć inną aplikację.

Używanie własnego katalogu tymczasowego niekoniecznie jest rozwiązaniem. Używam interfejsu API do generowania nazwy pliku tymczasowego, ale zapisuję go w moim własnym katalogu. Niestety nawet TO zawodzi.
George Mauer

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

1

Jak zasugerował Sayse , możesz spróbować ustawić zmienną środowiskową% TEMP% podczas uruchamiania aplikacji.

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

To świetny pomysł, ale niestety moja aplikacja jest rozszerzeniem Visual Studio, które musi spokojnie współistnieć z innymi rozszerzeniami i obawiam się, że może to w jakiś sposób nieumyślnie uszkodzić zachowanie innych rozszerzeń.
Omer Raviv

To rozwiązanie nie pomoże, jeśli jest to jego własny program, który pozostawia wszystkie te pliki. Nowy katalog po prostu się zapełni i źle jest arbitralnie usuwać pliki z folderu, gdy nie masz pojęcia, do czego one służą, nawet jeśli jest to folder specyficzny dla twojej aplikacji.
Ashigore

@Ashigore Tak, oczywiście to nie naprawi błędu, który stworzył. W szczególności odniesienia what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName.
Ed Chapel

@OmerRaviv Ta informacja jest pomocna. Rzeczywiście, to nie zadziała w tym scenariuszu.
Ed Chapel

1
Przepraszamy, @EdChapel, SO zablokował mój głos, nie mogę go usunąć, chyba że odpowiedź zostanie zmieniona.
Gerardo Grignoli

1

Dla każdego, kto napotkał ten problem i nie może znaleźć przepełnionego folderu tymczasowego - sprawdź folder „C: / Windows / Temp”. Czyszczenie tego folderu rozwiązało moje problemy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.