Skip to content

Instantly share code, notes, and snippets.

@exceedsystem
Created August 19, 2023 22:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save exceedsystem/8cc67d6d63c3495bf47d6d52d3ea8c6b to your computer and use it in GitHub Desktop.
Save exceedsystem/8cc67d6d63c3495bf47d6d52d3ea8c6b to your computer and use it in GitHub Desktop.
Sample tool for cleaning up the temporary directory under the Windows user directory in .NET 7(C#)
amespace UserTempCleaner
{
internal class Program
{
// Threshold of elapsed time since the last use of the file/folder
private const int ELAPSED_HOURS = 168;
private static readonly string LOG_FILE = $"UserTempCleaner_{DateTime.Now:yyyyMMddHHmmss}.log";
private static void Main(string[] args)
{
WriteLogFile("Begin:");
var userTempDirPath = Environment.GetEnvironmentVariable("TEMP");
if (userTempDirPath == null)
{
WriteLogFile($"Environment variable 'TEMP' not defined.");
return;
}
var fileTree = GetFileTree(userTempDirPath);
Cleanup(userTempDirPath, fileTree);
WriteLogFile("End:");
}
private static FileTree GetFileTree(string path, FileTree? root = null)
{
FileTree parent = root ?? new FileTree(null, path, true);
var atrb = File.GetAttributes(path);
if ((atrb & FileAttributes.Directory) == FileAttributes.Directory)
{
if (Directory.Exists(path))
{
foreach (var dir in Directory.GetDirectories(path))
{
var fileTree = new FileTree(parent, dir, true);
var subDirInfo = GetFileTree(dir, fileTree);
parent.AddChild(subDirInfo);
}
}
}
foreach (var file in Directory.GetFiles(path))
{
var fileTree = new FileTree(parent, file);
parent.AddChild(fileTree);
}
return parent;
}
private static DateTime GetLastUsedTime(string path)
{
var cTime = File.GetCreationTime(path);
var wTime = File.GetLastWriteTime(path);
var aTime = File.GetLastAccessTime(path);
return new[] { cTime, wTime, aTime }.Max();
}
private static bool Cleanup(string basePath, FileTree fileTree)
{
var files = fileTree.Children!.Where(o => !o.IsDirectory);
if (fileTree.Parent == null)
{
// When the root directory is the target
foreach (var file in files)
{
if (File.Exists(file.Path))
{
if (IsTarget(GetLastUsedTime(file.Path)) && !IsFileLocked(file.Path))
{
MoveToRecycleBin(basePath, file.Path, TargetType.File);
}
}
}
}
else
{
// When the subdirectory is the target
if (!files.All(o => IsTarget(GetLastUsedTime(o.Path)) && !IsFileLocked(o.Path)))
{
// Exclude the directory from deletion if there's even one file that cannot be deleted
return false;
}
}
var directories = fileTree.Children!.Where(o => o.IsDirectory);
foreach (var directory in directories)
{
if (Cleanup(basePath, directory))
{
if (fileTree.Parent == null)
{
if (Directory.Exists(directory.Path))
{
if (IsTarget(GetLastUsedTime(directory.Path)))
{
MoveToRecycleBin(basePath, directory.Path, TargetType.Directory);
}
}
}
}
else
{
if (fileTree.Parent != null)
{
return false;
}
}
}
return true;
}
private static bool IsTarget(DateTime dtm)
{
var diff = DateTime.Now.Subtract(dtm);
return (diff.TotalHours >= ELAPSED_HOURS);
}
private static void MoveToRecycleBin(string basePath, string path, TargetType targetType)
{
if (!path.StartsWith(basePath, StringComparison.OrdinalIgnoreCase))
{
// Path mismatch (potential for directory traversal)
WriteLogFile($"Ignored({targetType.ToString().ToLower()}):'{path}'");
return;
}
try
{
switch (targetType)
{
case TargetType.Directory:
Microsoft.VisualBasic.FileIO.FileSystem.DeleteDirectory(
path,
Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
break;
case TargetType.File:
Microsoft.VisualBasic.FileIO.FileSystem.DeleteFile(
path,
Microsoft.VisualBasic.FileIO.UIOption.OnlyErrorDialogs,
Microsoft.VisualBasic.FileIO.RecycleOption.SendToRecycleBin);
break;
}
WriteLogFile($"Deleted({targetType.ToString().ToLower()}):'{path}'");
}
catch (Exception e)
{
WriteLogFile($"Error:Could not delete {targetType.ToString().ToLower()} '{path}'. ({e.Message})");
}
}
private static bool IsFileLocked(string filePath)
{
try
{
new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None).Dispose();
}
catch (IOException)
{
return true;
}
return false;
}
private static void WriteLogFile(string msg)
{
var logMsg = $"{DateTime.Now:yyyyMMddHHmmss}:{msg}";
Console.WriteLine(logMsg);
File.AppendAllLines(LOG_FILE, new[] { logMsg });
}
private sealed class FileTree
{
public List<FileTree>? _children;
public FileTree(FileTree? parent, string path, bool isDirectory = false)
{
Parent = parent;
Path = path;
if (isDirectory)
{
_children = new List<FileTree>();
}
}
public FileTree? Parent { get; init; }
public string Path { get; init; }
public bool IsDirectory => _children != null;
public IReadOnlyList<FileTree>? Children => _children;
public void AddChild(FileTree fileTree) => _children?.Add(fileTree);
public override string ToString() => $"Path={Path}, Children={Children?.Count}";
}
private enum TargetType
{
Directory,
File
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment