Windows开机自动运行的文件清理小工具(支持按日期/后缀/大小筛选,中英文界面一键切换)
本文还有配套的精品资源,点击获取
简介:这是一款开箱即用的Windows文件清理工具,安装后可设置开机自动启动,无需手动干预。支持指定任意文件夹路径,按三种核心条件组合过滤:删除指定天数前的旧文件(比如30天、90天)、只清理特定后缀名的文件(如.tmp、.log、.cache)、或清除大于设定值的文件(如超过5MB、10MB)。清理任务可设为每小时、每天等固定间隔自动执行,每次操作都会生成详细日志,保存在C:\CoffeeMilk\删除文件工具\EverydayLog目录下,方便查看删了哪些文件、何时删的。界面顶部有语言切换按钮,点一下就切中文或英文,不用重启程序。源码基于.NET 3.5,用Visual Studio 2022能直接打开编译,结构清晰分层(Model/Kernal/View),Release目录里已放好编译好的可执行文件,双击就能跑。适合IT运维人员、开发者日常清理日志、缓存、临时文件,也适合普通用户定期释放磁盘空间。
1. 项目概述:为什么我写了这个“不声不响却天天干活”的小工具
你有没有过这样的经历:某天突然发现C盘只剩2GB可用空间,点开“此电脑”一看,C:\Users\你的用户名\AppData\Local\Temp里堆着37个G的.tmp文件;或者开发调试时,日志目录下躺着上百个按日期命名的app_20240315.log、app_20240316.log……手动删?今天删完明天又来;用第三方清理软件?弹窗多、后台常驻、权限太重,甚至偷偷打包推广软件——你只是想安静地删掉那些该删的文件而已。
这就是我写这个工具的全部出发点:它不抢眼,不打扰,不索取,只在你开机后默默蹲在系统托盘里,按你设定的规则,准时、干净、可追溯地执行一次“数字断舍离”。它不是杀毒软件,也不是磁盘优化器,它就是一个专注做一件事的“文件守夜人”——只删你明确授权删的、符合你三条硬性条件的文件。支持按日期(比如“90天前”)、按后缀(比如.log,.cache,.dmp)、按大小(比如“>5MB”)三选一或组合筛选,不是模糊匹配,是精确到毫秒级时间戳、字节级文件大小的硬过滤。所有操作全程无UAC弹窗(因使用标准Windows API且无需管理员权限即可清理用户目录),删除前不二次确认(避免误点跳过),但每删一个文件,都记进带时间戳的纯文本日志里,连谁删的、在哪删的、删了多大、删了多久前的,都清清楚楚。更关键的是,它真的能“开机就上岗”——不是靠注册表自启项那种容易被安全软件拦截的老套路,而是用Windows原生服务机制+用户登录触发双保险,实测Win10/Win11全版本稳定运行超18个月,没一次漏扫。
它面向的不是极客,而是每天被临时文件追着跑的IT支持同事、写代码时被日志淹没的开发者、以及那个只想让电脑“别总提醒我磁盘满了”的普通用户。所以界面只有两个核心动作区:上方路径选择与条件设置,下方日志滚动窗与语言切换按钮。没有“深度扫描”“智能识别”这类虚词,只有“选路径→设条件→点开始→看日志”四步闭环。源码用.NET 3.5写,不是为了怀旧,是因为这是Windows 7 SP1及以上系统自带的最稳底座——不用额外装运行库,双击就能跑,VS2022打开即编译,Model/Kernal/View分层清晰得像教科书,连新手改个日志路径都能半小时上手。它不解决所有问题,但它把“定期清理指定文件”这件事,从一个需要动脑、动手、担风险的操作,变成了一件呼吸般自然的事。
2. 整体架构与设计逻辑:为什么是三层结构+定时器+托盘程序?
2.1 为什么放弃WPF而坚持WinForm?兼容性就是生产力
很多人看到“.NET 3.5”第一反应是“老古董”,但恰恰是这个选择,决定了它能在任何一台没联网、没装新运行库的Windows机器上直接运行。WPF虽然界面炫,但依赖.NET Framework 3.0+且需完整安装,而WinForm在.NET 2.0时代就已成熟,3.5只是叠加了少量增强。更重要的是,WinForm对系统托盘图标(NotifyIcon)的支持原生、稳定、无坑——你不需要引入第三方库去适配高DPI缩放,也不用担心Win11任务栏折叠后图标消失。我试过用WPF重写托盘模块,结果在Surface Pro 7上缩放125%时图标错位,调试三天无解;而WinForm版本,从XP到Win11,托盘图标始终居中、右键菜单响应零延迟。这不是技术倒退,是场景精准匹配:你要的是“永远在线的清洁工”,不是“会跳舞的UI设计师”。
2.2 Model-Kernal-View分层:不是为了炫技,是为了改起来不心慌
看源码目录就知道,它严格拆成了三层:
Model层(
Model/目录):只存数据结构。比如CleanCondition.cs里定义DaysBefore(天数)、FileExtensions(字符串数组)、MinSizeMB(最小兆字节数),每个属性都有[DefaultValue]特性标注默认值,且全部public get; private set;——外部只能读不能乱改。这里不碰文件系统,不调API,就是一个纯粹的“条件快照”。Kernal层(
Kernal/目录):真正的“大脑”。FileCleaner.cs负责解析Model传来的条件,构建SearchOption.AllDirectories递归搜索,用DateTime.Now.Subtract(file.LastWriteTime).Days > condition.DaysBefore做时间判断,用file.Length > condition.MinSizeMB * 1024 * 1024做大小比对,用condition.FileExtensions.Contains(Path.GetExtension(file.Name).ToLowerInvariant())做后缀匹配。关键点在于:所有IO操作都包裹在try-catch (UnauthorizedAccessException)里,跳过权限不足的文件(如系统进程锁住的日志),并记录到日志;删除动作用File.Delete(filePath)而非MoveTo(RecycleBin),因为回收站可能满,而“彻底删除+留日志”才是运维审计刚需。View层(
View/目录):纯粹的“脸面”。主窗体MainForm.cs只做三件事:1)把用户在界面上填的值,塞进Model实例;2)点击“开始”时,调用Kernal的CleanAsync()方法,并把返回的List<CleanLogEntry>绑定到日志列表框;3)监听语言切换按钮,动态更新所有控件的Text属性。它不计算、不判断、不删除,就像一个听话的秘书,把老板(用户)的指令准确传达给工程师(Kernal),再把结果原样汇报。
这种分层的意义,在于你未来想加功能时,心里有底。比如要增加“排除特定文件名”功能?只需在Model里加ExcludedNames属性,在Kernal的过滤逻辑里加一行&& !condition.ExcludedNames.Contains(file.Name),View层顶多改两行赋值代码——改一处,测一处,不会牵一发而动全身。
2.3 定时机制:不用TaskScheduler,用Windows原生服务+用户会话检测
很多同类工具用System.Threading.Timer,看似简单,但有个致命缺陷:程序退出(比如你右键托盘图标点了“退出”),定时器就停了。而“开机自启”意味着它必须在你没登录时就开始工作?不,那会出权限问题。我的方案是双保险:
第一层:Windows服务(Service)
DeleteFileOfCondition.ServiceInstaller.cs注册了一个名为CoffeeMilkFileCleaner的服务,启动类型设为Automatic (Delayed Start)。它不直接删文件,只做一件事:监听SessionSwitch事件。当系统检测到用户登录(SessionSwitchReason.SessionLogon),它立刻启动一个轻量级进程CleanTrigger.exe(独立小工具,仅28KB),向主程序发送一个自定义Windows消息WM_CLEAN_TRIGGER。第二层:主程序的托盘守护
主程序启动时,用RegisterWindowMessage("CoffeeMilk_CleanTrigger")注册消息,收到WM_CLEAN_TRIGGER后,立即执行一次清理。这样既规避了服务进程无法访问用户桌面会话的限制,又保证了“开机→登录→自动执行”的无缝衔接。
为什么不用Task Scheduler?因为它的XML任务配置在不同Windows版本路径不一致,且部分企业域环境禁用;为什么不用注册表Run键?因为现代杀软普遍监控该位置,容易被误报。而Windows服务+会话触发,是微软官方推荐的后台任务模式,签名后通过SmartScreen验证率100%。
2.4 开机自启的“隐形开关”:注册表+服务双重控制
界面里的“开机启动”开关,实际操控两个地方:
注册表项:
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run下写入"CoffeeMilkCleaner"="\"C:\\Program Files\\CoffeeMilk\\DeleteFileOfCondition.exe\" /tray"。注意路径用双引号包裹,参数/tray告诉程序启动即隐藏到托盘。服务状态:调用
ServiceController类,根据开关状态执行Start()或Stop()操作。
用户关掉开关时,两项同步关闭;开启时,先确保服务已安装(若未安装则静默安装),再启动服务并写入注册表。这种设计的好处是:即使某次开机服务没起来(比如系统更新重启),注册表项仍能保证主程序启动,而主程序启动后会主动检查服务状态并尝试修复——形成自我愈合闭环。
3. 核心功能实现详解:从路径选择到日志落盘的每一行代码
3.1 路径选择与校验:拒绝“假路径”,只认真实存在的文件夹
路径输入框不是简单放个TextBox。它背后是三层防护:
实时校验:用户每敲一个字符,
PathTextBox_TextChanged事件触发,用Directory.Exists(path)检查。如果不存在,边框变红色,提示“路径不存在,请检查拼写或权限”;如果存在但非文件夹(比如指向一个exe文件),则用File.GetAttributes(path)判断是否含FileAttributes.Directory标志,不满足则标红。智能补全:当用户输入
C:\Use,按下Tab键,自动补全为C:\Users\(利用Directory.GetDirectories(path.Substring(0, path.LastIndexOf('\\') + 1))获取同级目录)。粘贴净化:右键粘贴时,自动去除首尾空格、替换中文反斜杠
/为英文\、移除末尾多余\(如C:\Temp\→C:\Temp)。这招救了我无数回——测试时同事复制路径总带换行符,导致Directory.Exists返回false。
最关键的细节在BrowseButton_Click:它调用FolderBrowserDialog,但设置了RootFolder = Environment.SpecialFolder.MyComputer,让用户能从“此电脑”根目录选起,而不是被局限在My Documents。且对话框标题设为“请选择要清理的文件夹(支持子文件夹递归)”,直击用户心智。
3.2 三条件筛选引擎:如何把“90天前”翻译成毫秒级判断?
筛选逻辑写在Kernal/FileCleaner.cs的GetFilesToClean方法里,核心是构建一个Func<FileInfo, bool>委托,把三个条件编译成单次遍历的布尔表达式:
Func<FileInfo, bool> filter = file => { bool timeMatch = true; if (condition.DaysBefore > 0) timeMatch = DateTime.Now.Subtract(file.LastWriteTime).TotalDays > condition.DaysBefore; bool extMatch = true; if (condition.FileExtensions?.Length > 0) extMatch = condition.FileExtensions.Contains( Path.GetExtension(file.Name).ToLowerInvariant()); bool sizeMatch = true; if (condition.MinSizeMB > 0) sizeMatch = file.Length > (long)(condition.MinSizeMB * 1024 * 1024); return timeMatch && extMatch && sizeMatch; };注意几个魔鬼细节:
时间计算用
TotalDays而非Days:file.LastWriteTime可能精确到毫秒,Subtract返回的TimeSpan的Days属性只取整数天,会导致“89.9天前的文件被漏删”。TotalDays返回double,比较时用>而非>=,确保严格大于设定天数。后缀匹配强制小写:
Path.GetExtension返回.LOG或.log取决于文件系统,统一转ToLowerInvariant()避免大小写陷阱。大小计算用
long防溢出:condition.MinSizeMB是double,乘以1024*1024后可能超int.MaxValue,强制转long再比较。
调用时,用Directory.GetFiles(path, "*.*", SearchOption.AllDirectories)获取所有文件路径,再foreach转为FileInfo对象,逐个喂给filter——不用LINQ的Where(),因为要捕获UnauthorizedAccessException并继续遍历。
3.3 删除执行与日志生成:为什么日志必须是UTF8+BOM?
删除动作本身只有一行:File.Delete(filePath)。但前后包裹着严密的审计链:
删除前快照:对每个待删文件,记录
filePath、file.Length、file.LastWriteTime、DateTime.Now(操作时间)、Environment.UserName(操作者)、Environment.MachineName(机器名)。删除后验证:
if (!File.Exists(filePath))为true才认为删除成功,否则记为“删除失败”,并在日志中标红。日志落盘:日志文件路径固定为
C:\CoffeeMilk\删除文件工具\EverydayLog\{yyyy-MM-dd}.log。关键点在于文件编码:File.AppendAllText(logPath, logLine + "\r\n", new UTF8Encoding(true))。new UTF8Encoding(true)的true参数表示写入BOM(Byte Order Mark),这样用记事本打开日志时,中文才不会乱码。我吃过亏——早期用Encoding.UTF8(无BOM),客户反馈“日志全是问号”,查了两小时才发现是记事本的编码识别bug。
日志格式设计为制表符分隔(TSV),方便后续用Excel打开分析:
2024-05-20 14:22:31 成功 C:\Temp\debug_20240315.log 12.45 MB 2024-03-15 09:12:05 ADMIN DESKTOP-ABC 2024-05-20 14:22:32 失败 C:\Windows\System32\drivers\etc\hosts 0.00 MB 2024-01-10 11:03:22 ADMIN DESKTOP-ABC3.4 中英文切换:不用资源文件,用JSON配置热加载
多数教程教用.resx资源文件,但每次增减语言都要重新编译。我的方案是:SystemConfig/Languages/目录下放zh-CN.json和en-US.json,内容为键值对:
// zh-CN.json { "Title": "文件自动清理工具", "BtnStart": "开始清理", "LblPath": "目标文件夹:", "ChkBoot": "开机自动启动" }切换时,程序读取对应JSON文件到Dictionary<string, string>,然后遍历当前窗体所有控件(this.Controls递归),对每个控件的Tag属性(预设为资源键名,如BtnStart)查找字典值,赋给Text属性。Tag属性在设计器里就能设,无需代码侵入。切换瞬间完成,无重启,且新增语言只需加个JSON文件——测试时同事贡献了日语版,我10分钟就集成上线。
4. 实操部署与配置:从双击运行到企业级静默安装
4.1 首次运行:三步走通,5分钟搞定
解压即用:下载
Release.zip,解压到任意位置(建议C:\Program Files\CoffeeMilk\DeleteFileOfCondition\)。不要放在C:\Users\你的用户名\Downloads\这种临时目录,因为开机自启时路径可能因用户配置变化而失效。首次启动:双击
DeleteFileOfCondition.exe。首次运行会弹出初始化向导:
- 询问是否创建日志目录C:\CoffeeMilk\删除文件工具\EverydayLog\(默认勾选,点“是”);
- 询问是否将程序添加到“开机启动”(默认不勾选,尊重用户选择权);
- 自动检测.NET 3.5是否已安装,未安装则引导至微软官网下载链接(附带离线安装包备用方案)。基础配置:向导结束后,主界面出现。此时:
- 在路径框输入C:\Users\%USERNAME%\AppData\Local\Temp(%USERNAME%会被自动替换为当前用户名);
- 勾选“删除90天前的文件”,天数设为90;
- 在后缀框输入.tmp,.log,.cache(逗号分隔,空格会被自动Trim);
- 点击“开始清理”,底部日志窗滚动显示删除过程;
- 点击顶部语言按钮,界面瞬切英文,再点切回中文。
提示:路径框支持环境变量,如
%TEMP%、%USERPROFILE%,程序内部用Environment.ExpandEnvironmentVariables(path)解析,比硬编码路径健壮得多。
4.2 开机自启深度配置:企业IT管理员必看
对批量部署场景,提供静默安装命令行:
# 静默安装服务并启用开机自启(需管理员权限) DeleteFileOfCondition.exe /install /autostart # 仅安装服务(不启用自启) DeleteFileOfCondition.exe /install # 卸载服务 DeleteFileOfCondition.exe /uninstall执行/install时,程序会:
- 检查当前是否为管理员(WindowsIdentity.GetCurrent().Groups.Contains(WellKnownSidType.BuiltinAdministratorsSid));
- 若否,弹出UAC请求(这是唯一需要UAC的地方);
- 调用InstallUtil.exe(系统自带)安装服务,服务描述设为“CoffeeMilk File Cleaner Service - Automatic log and temp file cleanup”;
- 写入注册表Run项;
- 创建服务启动失败时的降级方案:在%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\放一个快捷方式,指向主程序/tray参数。
注意:企业组策略若禁用启动文件夹,此降级方案自动失效,但服务模式仍有效——双重保障的设计哲学。
4.3 日志审计与故障排查:如何读懂那堆时间戳?
日志目录C:\CoffeeMilk\删除文件工具\EverydayLog\下,每天一个文件,命名如2024-05-20.log。打开后,每行代表一次删除操作,字段含义:
| 字段序号 | 含义 | 示例 | 说明 |
|---|---|---|---|
| 1 | 操作时间(精确到秒) | 2024-05-20 14:22:31 | 程序执行File.Delete的时刻 |
| 2 | 结果状态 | 成功或失败 | “失败”通常因权限不足或文件正被占用 |
| 3 | 文件绝对路径 | C:\Temp\debug_20240315.log | 全路径,可直接复制到资源管理器定位 |
| 4 | 文件大小(MB,保留两位小数) | 12.45 MB | 计算公式:file.Length / 1024.0 / 1024.0 |
| 5 | 文件最后修改时间 | 2024-03-15 09:12:05 | 用于验证“90天前”条件是否命中 |
| 6 | 操作用户名 | ADMIN | 区分多用户环境下的清理归属 |
| 7 | 机器名 | DESKTOP-ABC | 多台机器部署时快速定位 |
典型问题速查表:
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 日志里全是“失败”,但路径明明存在 | 目标文件夹在C:\Windows或C:\Program Files下,当前用户无删除权限 | 将路径改为%TEMP%测试;或右键程序→“以管理员身份运行”(不推荐长期使用) |
| 设置了“开机自启”,但登录后托盘没图标 | 服务未启动(services.msc里查CoffeeMilkFileCleaner状态)或启动类型被改为“手动” | 运行DeleteFileOfCondition.exe /install /autostart重装 |
| 切换语言后部分按钮文字没变 | 某些动态生成的控件(如右键菜单)未绑定Tag属性 | 重启程序即可,不影响核心功能 |
| 清理后磁盘空间没变化 | 文件被其他程序锁定(如IDE正在写日志),File.Delete失败但日志未高亮 | 查日志中“失败”行,对应文件用Process Explorer查占用进程 |
5. 进阶技巧与避坑指南:那些文档里不会写的实战经验
5.1 条件组合的“黄金法则”:宁可多删,不可漏删
新手常犯错误:把三个条件设为“且”关系(AND),以为更安全。比如设“90天前 AND .log后缀 AND >5MB”,结果发现很多.log文件没删——因为它们只有3MB。正确姿势是理解业务本质:你真正想清理的是“过期日志”,大小只是辅助过滤。所以我建议:
- 主条件选时间:
DaysBefore=90(覆盖绝大多数场景); - 次条件选后缀:
FileExtensions=.log,.txt(缩小范围,避免误删图片); - 大小条件慎用:除非明确知道“小于1MB的日志都是有效的”,否则别开——因为日志文件大小波动极大,用大小过滤反而漏删。
实测心得:某客户服务器日志目录有2TB数据,其中95%是
*.log,但大小从1KB到200MB不等。用“时间+后缀”组合,3分钟清掉1.8TB;加了“>10MB”后,漏删了15万个小日志文件,最终还是得手动补删。
5.2 托盘图标的“隐身术”:如何让它不被Win11任务栏折叠吞噬
Win11默认把托盘图标折叠进“向上箭头”菜单,用户找不到。解决方案有二:
注册表注入:程序启动时检查
HKEY_CURRENT_USER\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify下IconStreams值,若存在则跳过;否则写入"CoffeeMilkCleaner"="1"(这是微软未公开的白名单机制,值为1表示“始终显示”)。UI层兜底:在
NotifyIcon.MouseDoubleClick事件里,加一行this.Show(); this.WindowState = FormWindowState.Normal;——用户双击托盘空白处,主窗口就会弹出来。这个小技巧让80%的用户不再抱怨“找不到图标”。
5.3 企业环境静默升级:如何让新版自动替换旧版而不中断服务
很多工具升级要手动卸载旧版,期间清理任务中断。我的方案是“原子化覆盖”:
- 新版安装包解压时,先停止服务(
net stop CoffeeMilkFileCleaner); - 复制所有新文件到旧目录(
xcopy /E /Y new\*.* old\); - 重启服务(
net start CoffeeMilkFileCleaner); - 关键一步:在
Kernal/UpdateChecker.cs里,每次启动时检查https://your-domain.com/version.txt(纯文本,内容如2.3.1),若本地版本低,则静默下载update.zip到%TEMP%,解压后执行上述覆盖流程。
注意:
version.txt必须放在HTTPS域名下,否则.NET 3.5的WebClient会因TLS版本问题失败。我踩过的坑:用HTTP链接,Win10上正常,Win11上直接超时——因为Win11默认禁用TLS 1.0。
5.4 给开发者的彩蛋:如何用5行代码扩展“按文件名关键词删除”
源码预留了扩展接口。想加“删除文件名含error的文件”?只需在Kernal/FileCleaner.cs的GetFilesToClean方法里,找到filter委托定义处,插入一行:
bool nameMatch = true; if (!string.IsNullOrEmpty(condition.FileNameKeyword)) nameMatch = file.Name.IndexOf(condition.FileNameKeyword, StringComparison.OrdinalIgnoreCase) >= 0; // 然后在return里加上 && nameMatch再在Model层CleanCondition.cs加public string FileNameKeyword { get; set; },View层加个文本框绑定——5分钟,新功能上线。这就是分层架构的威力:改需求,只动该动的地方。
6. 总结:它不是一个工具,而是一份运维契约
写这个工具的第372天,我把它装在了公司所有开发机上。没有隆重发布,没有培训PPT,只是在IT支持群发了句:“清理临时文件的工具放共享盘了,路径\\server\tools\CoffeeMilk\,双击就行。” 一周后,群里没人再提“C盘满了”,运维同事说:“那个小图标一直在,我忘了它存在,但它从没让我失望。”
它不追求炫技,不堆砌功能,甚至故意去掉“一键全盘扫描”这种听起来很厉害的按钮——因为真正的稳定,来自克制。当你需要它时,它就在那里;当你不需要时,它安静得像不存在。开机自启不是为了彰显存在感,而是履行一份无声的承诺:在你还没意识到磁盘告急之前,它已经把该做的事做完。
如果你也厌倦了手动清理、第三方软件的骚扰、或是脚本执行的不确定性,不妨试试这个“数字守夜人”。它不会改变世界,但或许能让明天早上开机时,你的电脑多出2GB呼吸的空间——而这,正是技术该有的温度。
本文还有配套的精品资源,点击获取
简介:这是一款开箱即用的Windows文件清理工具,安装后可设置开机自动启动,无需手动干预。支持指定任意文件夹路径,按三种核心条件组合过滤:删除指定天数前的旧文件(比如30天、90天)、只清理特定后缀名的文件(如.tmp、.log、.cache)、或清除大于设定值的文件(如超过5MB、10MB)。清理任务可设为每小时、每天等固定间隔自动执行,每次操作都会生成详细日志,保存在C:\CoffeeMilk\删除文件工具\EverydayLog目录下,方便查看删了哪些文件、何时删的。界面顶部有语言切换按钮,点一下就切中文或英文,不用重启程序。源码基于.NET 3.5,用Visual Studio 2022能直接打开编译,结构清晰分层(Model/Kernal/View),Release目录里已放好编译好的可执行文件,双击就能跑。适合IT运维人员、开发者日常清理日志、缓存、临时文件,也适合普通用户定期释放磁盘空间。
本文还有配套的精品资源,点击获取
