System.IO.IOException:使用System.IO.Path.GetTempFileName()时,“文件存在”-分辨率?


84

每当他尝试使用我的产品时,我的一位客户就遇到了例外。我获得了发生的异常的调用堆栈,其顶部是:

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
您可以创建自己的“临时”文件夹,该文件夹将被删除(在应用程序数据中),但可能会更改所有引用,这是一个麻烦,很好的问题
Sayse 2013年

问题与WPF无关,已删除标签。另外,为什么不修复该代码,该代码会生成许多不删除的临时文件?
丹尼斯

1
@Sayse我不能这样做,因为是WPFCursor.LoadFromStream生成了临时文件。@Dennis与WPF的Cursor.LoadFromStream课程有关。产生如此多的临时文件而没有删除的令人讨厌的代码甚至可能不是我自己的,我仍然需要解决该异常。
Omer Raviv

您能找出哪个应用程序留下了所有这些临时文件吗?是您的应用程序吗?如果WPF本身正在创建这些临时文件,您是否已确认在不再需要它们时将其删除?
Ashigore

2
@OmerRaviv我认为您唯一的选择是尝试/捕获IOE,并询问用户是否要删除临时文件
并重

Answers:


15

正如我在上一条评论中提到的那样,我认为唯一安全的方法是询问用户是否要删除文件并重试。这是必要的,你得到了用户输入到这个,这样一来它是在他们自己的危险。在我的脑海中它类似于。

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%文件夹中可能还有其他文件,除了GetTempFileName()使用的“ tmpXXXX.tmp”格式的文件外,因此,当实际上没有问题时,您的测试可能会返回true,并且有问题时为false。
Omer Raviv

该测试旨在查找临时文件夹中的所有文件(尽管我承认它可能不是正确的临时文件夹),并查看该值是否等于65535,"*"将查找所有文件(无论它们是否具有扩展名)希望对您有所帮助
2013年

您可以删除所有早于一天的文件。超过一天的临时文件不太可能被任何其他应用程序使用。
JT泰勒

2
可选检查的另一点。当我在构建服务器上遇到此问题时,temp目录包含65535个以上的文件。仅当通过Path helper类创建临时文件时,此计数才有效。
rshadman

36

如果在生产环境或无法更改的应用程序上发生这种情况,快速的解决方法是清空Temp文件夹。

根据运行应用程序的用户,您应该

  • C:\Windows\Temp(用于IIS或在LocalSystem帐户下运行的服务)
  • %temp%对于本地登录的用户(对我来说是C:\Users\MyUserName\AppData\Local\Temp)。

另一方面,如果您自己的代码正在抛出此错误,并且您想防止此错误再次发生:

  1. 不要使用System.IO.Path.GetTempFileName()!

GetTempFileName()是已有20年历史的Win32 Api的包装。它生成的文件名很容易冲突。它通过在文件系统上大量循环,将可能的文件名从迭代"%temp%\tmp0000.tmp""tmpFFFF.tmp"并跳过现有文件名来规避这些归类。这是一个I / O密集型,缓慢且坦率的可怕算法。同样,由于仅使用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并删除Final块中的临时文件。

  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​​生成临时文件名,但将其写入自己的目录。不幸的是,即使失败了。
2013年

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

1

正如Sayse所建议的那样,您可以在应用启动时尝试设置%TEMP%环境变量。

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

那是个好主意,但不幸的是,我的应用程序是Visual Studio扩展,必须与其他扩展和平共存,而且我担心这可能会无意中破坏其他扩展的行为。
Omer Raviv

如果是他自己的程序遗留下了所有这些文件,那么该解决方案将无济于事。新目录将被填满,即使您不知道文件的用途,也无法从文件夹中任意删除文件,这很不好。
Ashigore

@Ashigore是的,很明显,这不能解决他所创建的错误。具体参考what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName
Ed Chapel 2013年

@OmerRaviv该信息很有帮助。实际上,在这种情况下这是行不通的。
Ed Chapel 2013年

1
抱歉,@ EdChapel,所以已锁定我的投票,除非对答案进行了编辑,否则我将无法删除它。
Gerardo Grignoli

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.