Einer meiner Kunden bekam eine Ausnahme, wenn er versuchte, mein Produkt zu verwenden. Ich habe den Callstack der aufgetretenen Ausnahme erhalten, deren oberste lautet:
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)
Beim Googeln habe ich viele Blog-Beiträge gefunden, die besagen, dass diese Ausnahme ausgelöst wird, wenn sich mehr als 65535 temporäre Dateien im Ordner% TEMP% befinden und die Lösung darin besteht, die alten temporären Dateien einfach zu löschen. Ich kann den Kunden bitten, dies zu tun, aber dies ist möglicherweise nur eine vorübergehende Lösung. Was ist, wenn er regelmäßig eine andere Software ausführt, die häufig GetTempFileName aufruft, wodurch das Problem immer wieder auftritt?
Ich kann den Ordner% TEMP% nicht einfach programmgesteuert löschen, da dies etwas anderes beschädigen könnte, und ich kann es nicht vermeiden, GetTempFileName aufzurufen (und stattdessen meinen eigenen temporären Ordner zu verwenden), da nicht ich, sondern WPF-Code ihn aufruft.
Gibt es dafür eine dauerhafte Lösung?
UPDATE : Ich habe bestätigt, dass das Problem, bei dem der Ordner% TEMP% mit Protokolldateien überfüllt ist, nicht durch meinen eigenen Code verursacht wird und von einer anderen Drittanbieteranwendung auf dem Computer des Kunden verursacht werden muss. Ich habe mich auch mit der Implementierung von befasst Cursor.LoadFromStream
und es ist sicherlich kein Fehler - es generiert eine temporäre Datei, löscht sie dann aber im finally
Block.
Cursor.LoadFromStream
, die die temporäre Datei generieren. @ Tennis Es hängt mit derCursor.LoadFromStream
Klasse von WPF zusammen . Der fehlerhafte Code, der so viele temporäre Dateien ohne Löschung erzeugt, ist möglicherweise nicht einmal mein eigener, und ich müsste die Ausnahme noch beheben.Antworten:
Wie ich in meinem letzten Kommentar erwähnt habe, besteht Ihre einzige sichere Möglichkeit darin, den Benutzer zu fragen, ob er möchte, dass Sie Dateien löschen und es erneut versuchen. Es ist unbedingt erforderlich, dass Sie die Benutzer dazu auffordern, auf diese Weise auf eigene Gefahr. In meinem Kopf ist es etwas Ähnliches.
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; } }
Eine optionale Überprüfung, um sicherzustellen, könnte sein,
Directory.EnumerateFiles(Path.GetTempPath(), "*", SearchOption.TopLevelOnly) .Count() == ushort.MaxValue;
quelle
"*"
werden alle Dateien gefunden (unabhängig davon, ob sie eine Erweiterung haben oder nicht). Ich hoffe, es hilftWenn Ihnen dies in einer Produktionsumgebung oder mit einer App passiert, die Sie nicht ändern können, besteht die schnelle Lösung darin, den Ordner Temp zu leeren.
Abhängig vom Benutzer, auf dem die Anwendung ausgeführt wird, sollten Sie dies auch tun
C:\Windows\Temp
(für IIS oder Dienste, die unterLocalSystem
Konto ausgeführt werden)%temp%
für lokal angemeldete Benutzer (was für mich istC:\Users\MyUserName\AppData\Local\Temp
).Auf der anderen Seite, wenn Ihr eigener Code dies auslöst und Sie verhindern möchten, dass dies jemals wieder passiert:
GetTempFileName()
ist ein Wrapper der zwei Jahrzehnte alten Win32-API . Es werden Dateinamen generiert, die sehr leicht kollidieren. Es umgeht diese Kollisionen, indem es das Dateisystem stark durchläuft, mögliche Dateinamen von"%temp%\tmp0000.tmp"
bis wiederholt"tmpFFFF.tmp"
und bereits vorhandene überspringt. Dies ist ein E / A-intensiver, langsamer und ehrlich gesagt schrecklicher Algorithmus. Durch die Verwendung von nur 4 Hex-Zeichen wird das künstliche Limit von 65536 Dateien festgelegt, bevor ein Fehler auftritt.Die Alternative besteht darin, Dateinamen zu generieren, die nicht kollidieren.
GUID's
Lassen Sie uns zum Beispiel die Logik wiederverwenden : 32 hexadezimale Ziffern kollidieren fast nie.private string GetTempFileName() { return Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); } // Sample: c:\Windows\Temp\2e38fe87-f6bb-4b0d-90b3-2d07016324c1
Dies erweitert das Limit von 65.000 auf maximal 4.000 Millionen Dateien (theoretisch) ... Natürlich ist es schon schrecklich, 65.000 Dateien durchgesickert zu haben, also ...
Überprüfen Sie Ihre App auf alle glücklichen und unglücklichen Pfade (wie unerwartete Ausnahmen). Stellen Sie sicher, dass jeder FileStream korrekt entsorgt und die temporären Dateien in finally-Blöcken gelöscht werden.
Bereinigen Sie es jetzt und weisen Sie den Systemadministrator an, es regelmäßig zu bereinigen, da Sie nicht jeder App in freier Wildbahn vertrauen können. Auf meinen eigenen Servern würde ich diese Aufgabe automatisieren, indem ich:
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
quelle
Hier ist der Code, den ich am Ende verwendet und früh in den Initialisierungscodepfad meiner App eingefügt habe, bevor Aufrufe
Cursor.LoadFromStream
auftreten können: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); } }
quelle
Lösungen:
Process monitor
sollten Ihnen helfen. Korrigieren Sie dann entweder die Anwendung oder werfen Sie sie weg. Und ja, dies könnte Ihre Anwendung sein. Deshalb würde ich Ihnen empfehlen, die Quelle des Bösen zu entdecken.quelle
// one more implementation string GetTempFileName() { return Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); }
quelle
Wie von Sayse vorgeschlagen, können Sie versuchen, die Umgebungsvariable% TEMP% beim Start Ihrer App festzulegen.
Environment.SetEnvironmentVariable("TEMP", "<dir>");
quelle
what if they are regularly running some other piece of software that makes frequent calls to GetTempFileName
.Für alle anderen, die dieses Problem haben und keinen überfüllten temporären Ordner finden können - Überprüfen Sie den Ordner "C: / Windows / Temp". Das Bereinigen dieses Ordners löste meine Probleme.
quelle