System.IO.IOException: «Файл существует» при использовании System.IO.Path.GetTempFileName () - разрешения?


86

Один из моих клиентов получал исключение всякий раз, когда пытался использовать мой продукт. Я получил стек вызовов возникшего исключения, верхняя часть которого:

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)

Погуглив, я обнаружил множество сообщений в блогах, в которых говорится, что это исключение выдается, когда в папке% TEMP% содержится более 65535 временных файлов, и что решение состоит в том, чтобы просто удалить старые временные файлы. Я могу попросить клиента сделать это, но это может быть только временным решением - что, если он регулярно запускает какое-то другое программное обеспечение, которое часто обращается к GetTempFileName, что приведет к тому, что проблема будет повторяться снова и снова?

Я не могу просто программно очистить папку% TEMP%, так как это может каким-то образом повредить что-то еще, и я не могу избежать вызова GetTempFileName (и использования вместо этого моей собственной временной папки), поскольку это не я, а код WPF, который вызывает это.

Есть ли постоянное решение для этого?

ОБНОВЛЕНИЕ : я подтвердил, что проблема переполнения папки% TEMP% файлами журнала не вызвана моим собственным кодом, а должна быть вызвана каким-либо другим сторонним приложением на компьютере клиента. Я также изучил реализацию, Cursor.LoadFromStreamи она, конечно же, не виновата - она ​​генерирует временный файл, но затем удаляет его в finallyблоке.


3
Вы можете создать свою собственную папку «Temp», которая будет удаляться (в данных приложения), но, вероятно, было бы несерьезно изменить все ссылки, хороший вопрос
— Sayse

Вопрос не связан с WPF, удаленный тег. Кроме того, почему бы просто не исправить код, который создает так много временных файлов без удаления?
— Деннис

1
@Sayse Я не могу этого сделать, потому что WPF Cursor.LoadFromStreamгенерирует временный файл. @Dennis Это связано с Cursor.LoadFromStreamклассом WPF . Оскорбительный код, который создает так много временных файлов без удаления, может даже не быть моим собственным, и мне все равно нужно устранить исключение.
— Омер Равив

Можете ли вы узнать, какое приложение оставляет после себя все эти временные файлы? Это твое приложение? Если WPF сам создает эти временные файлы, подтвердили ли вы, что он удаляет их, когда они больше не нужны?
— Ашигоре

2
@OmerRaviv Я думаю, что ваш единственный вариант - попробовать / поймать IOE и спросить пользователя, хотят ли они, чтобы вы
— удалили

Ответы:


16

Как я упоминал в своем последнем комментарии, я думаю, что ваш единственный безопасный способ сделать это - спросить пользователя, хотят ли они, чтобы вы удалили файлы и попытались снова. Крайне важно, чтобы вы внесли в это участие пользователей, так как это на их страх и риск. В моей голове это что-то похожее на.

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

}

Необязательная проверка, чтобы убедиться, что

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

2
Я собираюсь попробовать / поймать, как вы предложили. Предложенный вами логический тест на самом деле неверен - вероятно, в папке% TEMP% есть другие файлы, помимо файлов в формате tmpXXXX.tmp, которые использует GetTempFileName (), поэтому ваш тест может вернуть истину, когда на самом деле проблем нет и false, когда есть проблема.
— Омер Равив

Тест был разработан, чтобы найти все файлы во временной папке (хотя я допускаю, что это может быть неправильная временная папка) и проверяет, равно ли это 65535, "*"найдет все файлы (независимо от того, имеют ли они расширение или нет), надеюсь, что это поможет
— Sayse

Вы можете удалить все файлы старше одного дня. Временный файл старше одного дня вряд ли будет использоваться каким-либо другим приложением.
— JT Taylor

2
Еще один пункт о необязательной проверке. Когда я столкнулся с этой проблемой на сервере сборки, в каталоге temp было более 65535 файлов. Этот счетчик работает, только если ЕДИНСТВЕННОЕ создание временных файлов осуществляется через вспомогательный класс Path.
— rshadman

37

Если это происходит с вами в производственной среде или с приложением, которое вы не можете изменить, быстрое решение - очистить папку Temp.

В зависимости от пользователя, запускающего приложение, вы должны либо

  • Пусто C:\Windows\Temp(для IIS или служб, работающих под LocalSystemучетной записью)
  • Или %temp%для локально авторизованных пользователей (что для меня так C:\Users\MyUserName\AppData\Local\Temp).

С другой стороны, если ваш собственный код выбрасывает это, и вы хотите, чтобы это никогда не повторилось:

  1. Не используйте System.IO.Path.GetTempFileName ()!

GetTempFileName()представляет собой оболочку Win32 Api двадцатилетней давности . Он генерирует имена файлов, которые очень легко конфликтуют. Он обходит эти коллиции, зацикливаясь на файловой системе, перебирая возможные имена файлов от "%temp%\tmp0000.tmp"до "tmpFFFF.tmp"и пропуская уже существующие. Это медленный и откровенно ужасный алгоритм с интенсивным вводом-выводом. Кроме того, использование только 4 шестнадцатеричных символов делает искусственное ограничение в 65536 файлов до отказа.

Альтернативой является создание имен файлов, которые не будут конфликтовать друг с другом. Например, давайте повторно используем GUID'sлогику: 32 шестнадцатеричные цифры почти никогда не конфликтуют.

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

Это расширяет ограничение с 65k до 4k миллионов файлов максимум (теоретически) ... Конечно, утечка 65k файлов уже ужасна, так что ...

  1. Не допускайте утечки временных файлов!

Дважды проверьте свое приложение на наличие всех счастливых и неудачных путей (например, неожиданных исключений). Убедитесь, что он правильно удаляет каждый FileStream и удаляет временные файлы в блоках finally.

  1. Очистите временную папку

Очистите его сейчас и научите системного администратора периодически очищать его, потому что вы не можете доверять каждому приложению в дикой природе. На своих серверах я бы автоматизировал эту задачу, используя:

  • Для глобальной 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

  • Для текущего пользователя:

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


5

Вот код, который я использовал в конце и поместил в начале пути кода инициализации моего приложения, прежде чем 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

Решения:

  1. Правый. Определите, какое приложение создает так много временных файлов и не удаляет их. Утилиты вроде Process monitorдолжны вам помочь. Потом либо исправьте приложение, либо выбросьте его. И да, это может быть ваше приложение. поэтому я бы рекомендовал вам обнаружить источник зла.
  2. Самый простой. Используйте свой собственный временный каталог. Это не поможет, если файлы создаются из вашего кода.
  3. Самый уродливый. Очистите временный каталог из вашего приложения. Вы абсолютно правы насчет последствий - вы можете сломать другое приложение.

Использование собственного временного каталога не обязательно является решением. Я использую API для создания временного файла, но записываю его в свой каталог. К сожалению, даже ЭТО не работает.
— Джордж Мауэр,

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

1

Как предложил Сэйс , вы можете попробовать установить переменную среды% TEMP% при запуске приложения.

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

Это отличная идея, но, к сожалению, мое приложение является расширением Visual Studio, которое должно мирно сосуществовать с другими расширениями, и я боюсь, что это может каким-то образом непреднамеренно повредить поведение других расширений.
— Омер Равив

Это решение не поможет, если это его собственная программа, которая оставляет после себя все эти файлы. Новый каталог просто заполнится, и было бы плохо произвольно удалять файлы из папки, если вы не знаете, для чего они нужны, даже если это папка, специфичная для вашего приложения.
— Ашигоре

@Ashigore Да, очевидно, это не исправит созданную им ошибку. Специально ссылаясь what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName.
— Эд Чапел

@OmerRaviv Эта информация полезна. Действительно, в этом сценарии это не сработает.
— Эд Чапел

1
Извините, @EdChapel, ТАК заблокировал мой голос, я не могу удалить его, пока ответ не будет отредактирован.
— Херардо Гриньоли

1

Для всех, кто столкнулся с этой проблемой и не может найти переполненную временную папку - проверьте папку «C: / Windows / Temp». Очистка этой папки решила мои проблемы.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.