我的一位客户在尝试使用我的产品时遇到异常。我获得了已经发生的异常的callstack,其顶部是:
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)
谷歌搜索这个,我发现有很多blog posts说明当%TEMP%文件夹中有超过65535个临时文件时抛出此异常,并且该解决方案只是清除旧的临时文件。我可以要求客户这样做,但这可能只是一个临时的解决方案 - 如果他们经常运行其他一些经常调用GetTempFileName的软件会怎样,这会让问题一遍又一遍地重复出现?
我不能只是以编程方式清除%TEMP%文件夹,因为这可能会以某种方式损坏其他内容,我无法避免调用GetTempFileName(并使用我自己的临时文件夹),因为它不是我,而是WPF代码叫它。
对此有没有永久的解决方案?
UPDATE :我已经确认%TEMP%文件夹溢出日志文件的问题不是由我自己的代码引起的,而且必须是由其他一些第三方应用程序引起的。客户的机器。我还研究了Cursor.LoadFromStream
的实现,它确实没有错 - 它生成一个临时文件,但随后在finally
块中删除它。
答案 0 :(得分:18)
如果您在生产环境上或您无法更改的应用中发生这种情况,则快速解决方法是清空Temp文件夹。
根据运行应用程序的用户,您应该
C:\Windows\Temp
(对于IIS或在LocalSystem
帐户下运行的服务)%temp%
本地登录用户(对我来说是C:\Users\MyUserName\AppData\Local\Temp
)。另一方面,如果你自己的代码抛出这个,你想要再次发生这种情况:
GetTempFileName()
是two decades old 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文件已经很糟糕了,所以......
仔细检查您的应用是否有所有快乐和不快乐的路径(例如意外异常)。确保它正确处理每个FileStream并删除Finally块中的临时文件。
立即清理它,并教育系统管理员定期清理它,因为你无法信任野外的每个应用程序。 在我自己的服务器上,我将使用以下方法自动执行此任务:
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
答案 1 :(得分:14)
正如我在上一篇评论中提到的,我认为您唯一安全的方法是询问用户是否要删除文件并重试。你得到了用户输入的命令式,这样就有了自己的危险。在我的头脑里它的东西类似于。
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 :(得分: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);
}
}
答案 3 :(得分:2)
解决方案:
Process monitor
这样的工具应该可以帮到你。然后修复应用程序或将其丢弃。是的,这可能是您的应用程序。这就是为什么我建议你去发现邪恶的来源。答案 4 :(得分:2)
// one more implementation
string GetTempFileName()
{
return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
}
答案 5 :(得分:1)
建议Sayse,您可以尝试在应用启动时设置%TEMP%环境变量。
Environment.SetEnvironmentVariable("TEMP", "<dir>");
答案 6 :(得分:1)
对于遇到此问题并且找不到任何溢出的临时文件夹的任何其他人 - 检查“C:/ Windows / Temp”文件夹。清理此文件夹解决了我的问题。
答案 7 :(得分:0)
将文件属性显式设置为临时,并确保在使用后删除该文件。
File.SetAttributes(tempFileName, FileAttributes.Temporary);