#nullable disable using System; using System.Linq; using System.Collections.Generic; using System.IO; using System.Threading; namespace BizHawk.Common { /// /// Starts a thread which cleans any filenames in %temp% beginning with bizhawk.bizdelete. /// Files shouldn't be named that unless they're safe to delete, but notably, they may still be in use. That won't hurt this component. /// When they're no longer in use, this component will then be able to delete them. /// public static class TempFileManager { // TODO - manage paths other than %temp%, make not static, or allow adding multiple paths to static instance public static string GetTempFilename(string friendlyName, string extension = null, bool delete = true) { string guidPart = Guid.NewGuid().ToString(); var fname = $"biz-{System.Diagnostics.Process.GetCurrentProcess().Id}-{friendlyName}-{guidPart}{extension ?? ""}"; if (delete) { fname = RenameTempFilenameForDelete(fname); } return Path.Combine(Path.GetTempPath(), fname); } /// filename in is not one generated by public static string RenameTempFilenameForDelete(string path) { string filename = Path.GetFileName(path); string dir = Path.GetDirectoryName(path); if (!filename.StartsWith("biz-")) { throw new InvalidOperationException(); } filename = $"bizdelete-{filename.Remove(0, 4)}"; return Path.Combine(dir, filename); } public static void Start() { lock (typeof(TempFileManager)) { if (thread != null) { return; } thread = new Thread(ThreadProc) { IsBackground = true, Priority = ThreadPriority.Lowest }; thread.Start(); } } static void ThreadProc() { // squirrely logic, trying not to create garbage var knownTempDirs = new HashSet(); var dis = new List(); for (;;) { lock (typeof(TempFileManager)) { knownTempDirs.Add(Path.GetTempPath()); if (dis.Count != knownTempDirs.Count) dis = knownTempDirs.Select(x => new DirectoryInfo(x)).ToList(); } foreach(var di in dis) { FileInfo[] fis = null; try { fis = di.GetFiles("bizdelete-*"); } catch { } if (fis != null) { foreach (var fi in fis) { try { if (OSTailoredCode.IsUnixHost) { fi.Delete(); // naive deletion, Mono doesn't care } else { Win32Imports.DeleteFileW(fi.FullName); // SHUT. UP. THE. EXCEPTIONS. } } catch { } // try not to do more than one thing per frame Thread.Sleep(100); } } } // try not to slam the filesystem too hard, we don't want this to cause any hiccups Thread.Sleep(5000); } } public static void Stop() { } static Thread thread; public static void HelperSetTempPath(string path) { //yes... this is how we're doing it, for now, until it's proven to be troublesome Directory.CreateDirectory(path); Environment.SetEnvironmentVariable("TMP", path); Environment.SetEnvironmentVariable("TEMP", path); } } }