Uno de mis clientes tenía una excepción cada vez que intentaba utilizar mi producto. Obtuve la pila de llamadas de la excepción que se había producido, la parte superior de la cual es:
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)
Al buscar en Google, encontré muchas publicaciones de blog que indican que esta excepción se produce cuando hay más de 65535 archivos temporales en la carpeta% TEMP%, y que la solución es simplemente borrar los archivos temporales antiguos. Puedo pedirle al cliente que haga eso, pero esto podría ser solo una solución temporal: ¿qué pasa si ejecutan regularmente algún otro software que realiza llamadas frecuentes a GetTempFileName, lo que hará que el problema vuelva a ocurrir una y otra vez?
No puedo simplemente borrar programáticamente la carpeta% TEMP%, ya que eso de alguna manera podría dañar algo más, y no puedo evitar llamar a GetTempFileName (y usar mi propia carpeta temporal en su lugar) ya que no soy yo, sino el código WPF el que lo llama.
¿Existe alguna solución permanente para esto?
ACTUALIZACIÓN : He confirmado que el problema en el que la carpeta% TEMP% se desborda con archivos de registro no es causado por mi propio código, y debe ser causado por alguna otra aplicación de terceros en la máquina del cliente. También examiné la implementación de Cursor.LoadFromStream
y seguramente no tiene la culpa: genera un archivo temporal, pero luego lo elimina en el finally
bloque.
Cursor.LoadFromStream
que genera el archivo temporal. @Dennis Está relacionado con laCursor.LoadFromStream
clase de WPF . Es posible que el código ofensivo que produce tantos archivos temporales sin eliminarlos ni siquiera sea el mío, y aún necesitaría abordar la excepción.Respuestas:
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. Es imperativo que obtenga la participació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 podría ser,
Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly) .Count() == ushort.MaxValue;
fuente
"*"
encontrará todos los archivos (independientemente de si tienen una extensión o no) espero que ayudeSi esto le sucede 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
C:\Windows\Temp
(para IIS o servicios que se ejecutan en laLocalSystem
cuenta)%temp%
para los usuarios que han iniciado sesión localmente (que para mí esC:\Users\MyUserName\AppData\Local\Temp
).Por otro lado, si su propio código arroja esto y desea evitar que esto vuelva a suceder nunca más:
GetTempFileName()
es un contenedor de la API Win32 de hace dos décadas . Genera nombres de archivos que colisionarán muy fácilmente. Elude esos collitions por bucle en gran medida en el sistema de archivos, la iteración de posibles nombres de los archivos de"%temp%\tmp0000.tmp"
a"tmpFFFF.tmp"
y saltar las 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 archivos que no colisionen. Por ejemplo, reutilicemos la
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:\Windows\Temp\2e38fe87-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 ...
Revise 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.
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:
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
fuente
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
Cursor.LoadFromStream
que ocurra cualquier llamada a :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); } }
fuente
Soluciones:
Process monitor
deberían ayudarte. Luego, arregle la aplicación o deséchela. Y sí, esta podría ser tu aplicación. por eso te recomiendo que detectes la fuente del mal.fuente
// one more implementation string GetTempFileName() { return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); }
fuente
Como sugirió Sayse , puede intentar configurar la variable de entorno% TEMP% cuando se inicie su aplicación.
Environment.SetEnvironmentVariable("TEMP", "<dir>");
fuente
what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName
.Para cualquier otra persona que haya experimentado este problema y no pueda encontrar ninguna carpeta temporal desbordada, consulte la carpeta "C: / Windows / Temp". Limpiar esta carpeta resolvió mis problemas.
fuente