Solución:
Si esto te está sucediendo en un entorno de producción o con una aplicación que no puede cambiar, la solución rápida es vaciar la carpeta Temp.
Dependiendo del usuario que esté ejecutando la aplicación, debería
- Vacío
C:WindowsTemp
(para IIS o servicios que se ejecutan bajoLocalSystem
cuenta) - O
%temp%
para los usuarios que han iniciado sesión localmente (que para mí esC:UsersMyUserNameAppDataLocalTemp
).
Por otro lado, si su propio código arroja esto y desea evitar que esto vuelva a suceder nunca más:
- ¡No use System.IO.Path.GetTempFileName ()!
GetTempFileName()
es un contenedor de Win32 Api de hace dos décadas. Genera nombres de archivos que colisionarán muy fácilmente. Evita esas colisiones realizando un gran bucle en el sistema de archivos, iterando posibles nombres de archivo de "%temp%tmp0000.tmp"
para "tmpFFFF.tmp"
y omitiendo los ya existentes. Este es un algoritmo de E / S intensivo, lento y francamente terrible. Además, el uso de solo 4 caracteres hexadecimales es lo que hace que el límite artificial de 65536 archivos antes de fallar.
La alternativa es generar nombres de archivo que no colisionen. Por ejemplo, reutilicemos GUID's
lógica: 32 dígitos hexadecimales casi nunca colisionarán.
private string GetTempFileName()
{
return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
}
// Sample: c:WindowsTemp2e38fe87-f6bb-4b0d-90b3-2d07016324c1
Esto expande el límite de 65k a 4k millones de archivos como máximo (teóricamente) … Por supuesto, haber filtrado 65k archivos ya es terrible, así que …
- ¡No pierda archivos temporales!
Verifique su aplicación para ver todos los caminos felices e infelices (como excepciones inesperadas). Asegúrese de que esté desechando correctamente cada FileStream y eliminando los archivos temporales en los bloques Finalmente.
- Limpiar la carpeta temporal
Límpielo ahora y eduque al administrador del sistema para que lo limpie periódicamente, porque no puede confiar en todas las aplicaciones en la naturaleza. En mis propios servidores, automatizaría esta tarea usando:
- Para Windows Temp global
schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete Global Temp Files" /sc WEEKLY /ST 12:00 /ru system
- Para el usuario actual:
schtasks /Create /TR "cmd /c call DEL /F /S /Q %^TEMP%" /TN "Delete %username% Temp Files" /sc WEEKLY /ST 12:00
Como mencioné en mi último comentario, creo que la única forma segura de hacerlo es preguntarle al usuario si quiere que elimine archivos y vuelva a intentarlo. Está imperativo que obtenga la opinión de los usuarios en esto, de esta manera es bajo su propio riesgo. En mi cabeza es algo similar a.
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;
}
}
Una verificación opcional para asegurarse de que podría ser,
Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly)
.Count() == ushort.MaxValue;
Aquí está el código que usé al final, y puse temprano en la ruta del código de inicialización de mi aplicación, antes de cualquier llamada a Cursor.LoadFromStream
puede ocurrir:
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);
}
}