93

我的一位客户在尝试使用我的产品时遇到了异常。我得到了已经发生的异常的调用栈,最上面的是:

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% 文件夹溢出日志文件的问题不是由我自己的代码引起的,而一定是由客户机器上的其他一些 3rd 方应用程序引起的。我还研究了它的实现,Cursor.LoadFromStream它肯定没有错——它会生成一个临时文件,然后将其删除finally

4

8 回答 8

46

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

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

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

另一方面,如果你自己的代码抛出了这个,并且你想防止这种情况再次发生:

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

GetTempFileName()是已有二十年历史的 Win32 Api的包装器。它生成的文件名很容易发生冲突。"%temp%\tmp0000.tmp"它通过在文件系统上大量循环、从to迭代可能的文件名"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 并删除 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

于 2018-05-18T14:05:58.717 回答
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;
于 2013-08-21T07:20:23.463 回答
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);
        }
    }
于 2013-08-21T11:49:06.753 回答
2

解决方案:

  1. 正确的那一个。检测哪个应用程序产生了这么多临时文件而不是删除它们。像这样的实用程序Process monitor应该可以帮助你。然后要么修复应用程序,要么将其丢弃。是的,这可能是您的应用程序。这就是为什么我建议你检测邪恶的根源。
  2. 最简单的一个。使用您自己的临时目录。如果文件是从您的代码创建的,这将无济于事。
  3. 最丑的一个。从您的应用程序中清除临时目录。你对后果是绝对正确的——你可能会破坏另一个应用程序。
于 2013-08-21T07:11:22.503 回答
2
// one more implementation
string GetTempFileName()
{
    return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
于 2020-02-03T21:26:06.087 回答
2

对于遇到此问题并且找不到任何溢出的临时文件夹的其他人 - 检查“C:/Windows/Temp”文件夹。清理这个文件夹解决了我的问题。

于 2017-08-07T13:29:56.640 回答
1

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

Environment.SetEnvironmentVariable("TEMP", "<dir>");
于 2013-08-21T07:06:54.520 回答
0

将文件属性显式设置为临时,并确保在使用后删除文件。

 File.SetAttributes(tempFileName, FileAttributes.Temporary);
于 2021-03-22T18:37:01.893 回答