专门清除cef缓存控制台程序

发布时间:2026/9/3 7:42:38
专门清除cef缓存控制台程序 一、思路vb.net编程中使用cefsharp内核关闭程序时无法有效清除cefsharp创建的【缓存】文件长期积累或者一次开启多个使用cefsharp内核浏览器的实例会造成缓存大量占用磁盘空间。但是cefsharp没有适当的清理缓存函数方法。即便强行清理也会残留容量不小的缓存文件。甚至导致程序出错。单独创建一个【专门清除cef缓存控制台程序】在关闭程序后启动这个【控制台】程序在后台静悄悄的删除全部缓存文件。经过测试只要正常关闭使用cefsharp的程序缓存清除成功率达到了99%。二、给使用cefsharp的程序创建配套的独立【控制台】缓存清除代码很简单使用很容易修改解决方案64位目标框架.NET 10三、代码Imports System Imports System.Diagnostics Imports System.IO Imports System.Linq Imports System.Threading Module Program 配置常量 Const MAX_WAIT_SECONDS As Integer 15 Const POLL_INTERVAL_MS As Integer 400 Const POST_WAIT_MS As Integer 600 Const DELETE_RETRY_COUNT As Integer 5 Const RETRY_DELAY_MS As Integer 700 Sub Main(args As String()) If args.Length 1 Then Console.WriteLine(缺少缓存目录参数) Return End If Dim CacheDir As String args(0).Trim() If Not Directory.Exists(CacheDir) Then Console.WriteLine(目录不存在 CacheDir) Return End If Console.WriteLine(等待 CefSharp.BrowserSubprocess 进程退出...) WaitAllBrowserSubprocessExit() Console.WriteLine(子进程已退出等待释放文件句柄...) Thread.Sleep(POST_WAIT_MS) Console.WriteLine(开始删除缓存文件夹...) Dim ok As Boolean SafeDeleteDirectory(CacheDir) If ok Then Console.WriteLine(✅ 删除成功) Else Console.WriteLine(⚠️ 直接删除失败执行重命名待删除兜底) Dim parent As String Path.GetDirectoryName(CacheDir) Dim newName As String Path.Combine(parent, Path.GetFileName(CacheDir) _PENDING_DELETE_ Guid.NewGuid().ToString(N)) Try Directory.Move(CacheDir, newName) Console.WriteLine(已标记待删除 newName) Catch ex As Exception Console.WriteLine(兜底重命名失败 ex.Message) End Try End If End Sub summary循环等待所有 CefSharp.BrowserSubprocess 退出/summary Private Sub WaitAllBrowserSubprocessExit() Dim totalWait As Integer 0 Dim maxWaitMs As Integer MAX_WAIT_SECONDS * 1000 Do Dim subs() As Process Process.GetProcessesByName(CefSharp.BrowserSubprocess) If subs.Length 0 Then Exit Do End If Thread.Sleep(POLL_INTERVAL_MS) totalWait POLL_INTERVAL_MS If totalWait maxWaitMs Then Exit Do End If Loop End Sub summary带重试递归删除文件夹/summary Private Function SafeDeleteDirectory(folderPath As String) As Boolean If Not Directory.Exists(folderPath) Then Return True For retry As Integer 1 To DELETE_RETRY_COUNT Try 删除所有文件 For Each f In Directory.GetFiles(folderPath, *.*, SearchOption.AllDirectories) File.Delete(f) Next 由深至浅删除子文件夹 Dim subDirs Directory.GetDirectories(folderPath, *, SearchOption.AllDirectories).OrderByDescending(Function(d) d.Length) For Each d In subDirs Directory.Delete(d, False) Next 删除根目录 Directory.Delete(folderPath, False) Return True Catch ex As IOException Console.WriteLine($删除失败重试 {retry} ... {ex.Message}) Thread.Sleep(RETRY_DELAY_MS) Catch Return False End Try Next Return False End Function End Module四、编译生成及使用发布后的文件放入到自定义文件夹例如删除将【删除】文件夹拷贝到要使用cef缓存清除的项目中编辑对应的【项目】文件双击【项目】打开或者直接编辑*.vbproj在最后添加ItemGroup Content Include删除\**\* CopyToOutputDirectoryPreserveNewest/CopyToOutputDirectory /Content /ItemGroup在主窗体关闭代码中添加Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing e.Cancel True 拦截原生窗体关闭接管退出流程 全部定时器立刻停止 TM登录.Enabled False TM退出登录.Enabled False TM静音.Enabled False TM刷新分数.Enabled False TM定时检测播放状态.Enabled False TM自动调整视频尺寸.Enabled False Dim accountName TB身份证号.Text.Trim If Not String.IsNullOrWhiteSpace(accountName) Then Dim cacheKey LCefSharp全局管理器.GetCacheKey(accountName) Dim cacheFolder As String Path.Combine(AppContext.BaseDirectory, CefCache, cacheKey) Dim cleanerExe As String Path.Combine(AppContext.BaseDirectory, 删除, CefCacheCleaner.exe) If File.Exists(cleanerExe) Then 只杀死本程序目录下的CefSharp子进程不影响电脑其他软件 KillLocalBrowserSubprocess() Dim psi As New ProcessStartInfo() psi.FileName cleanerExe psi.Arguments ${cacheFolder} psi.CreateNoWindow True 调试打开窗口发布改回True True psi.UseShellExecute False Process.Start(psi) Thread.Sleep(700) 给外部清理程序启动窗口时间 End If End If 主进程直接退出释放全部文件锁Cleaner.exe继续后台执行删除任务 Environment.Exit(0) End Sub Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed ❗❗❗这里什么代码都不要写全部清理逻辑移到FormClosing End Sub可以相应精简不需要的语句主要是指定正确的【缓存】路径和【清除缓存】程序路径及名称我这里是在cefsharp初始化时指定了在运行目录下的CefCache中的特殊的哈希值缓存路径2026年9月2日一步一坑一坑一悟。每个坑当时觉得烦踩过之后就成了别人没有的经验壁垒。之前的代码只能清理单个程序实例的cefsharp缓存。如果程序实例多开会影响其他程序实例中的cefsharp经过改进现在能够多开实例互不影响只清理自己进程的的cefsharp缓存Imports System Imports System.Diagnostics Imports System.IO Imports System.Linq Imports System.Runtime.InteropServices Imports System.Threading Module Program 配置常量 Const MAX_WAIT_SECONDS As Integer 15 Const POLL_INTERVAL_MS As Integer 400 Const POST_WAIT_MS As Integer 600 Const DELETE_RETRY_COUNT As Integer 5 Const RETRY_DELAY_MS As Integer 700 Sub Main(args As String()) If args.Length 2 Then Console.WriteLine(用法: CefCacheCleaner.exe 缓存目录 父进程PID) Return End If Dim CacheDir As String args(0).Trim() Dim parentPid As Integer If Not Integer.TryParse(args(1), parentPid) Then Console.WriteLine(无效的父进程PID args(1)) Return End If If Not Directory.Exists(CacheDir) Then Console.WriteLine(目录不存在 CacheDir) Return End If ✅ 改动1传入parentPid只等自己的子进程 Console.WriteLine($等待 PID{parentPid} 的 CefSharp 子进程退出...) WaitOwnBrowserSubprocessExit(parentPid) Console.WriteLine(子进程已退出等待释放文件句柄...) Thread.Sleep(POST_WAIT_MS) Console.WriteLine(开始删除缓存文件夹...) Dim ok As Boolean SafeDeleteDirectory(CacheDir) If ok Then Console.WriteLine(✅ 删除成功) Else Console.WriteLine(⚠️ 直接删除失败执行重命名待删除兜底) Dim parent As String Path.GetDirectoryName(CacheDir) Dim newName As String Path.Combine(parent, Path.GetFileName(CacheDir) _PENDING_DELETE_ Guid.NewGuid().ToString(N)) Try Directory.Move(CacheDir, newName) Console.WriteLine(已标记待删除 newName) Catch ex As Exception Console.WriteLine(兜底重命名失败 ex.Message) End Try End If End Sub summary ✅ 改动2只等待属于指定父进程的 CefSharp 子进程退出 不再用 GetProcessesByName 无差别等待 /summary Private Sub WaitOwnBrowserSubprocessExit(parentPid As Integer) Dim totalWait As Integer 0 Dim maxWaitMs As Integer MAX_WAIT_SECONDS * 1000 Do Dim hasOwnChild As Boolean False For Each p In Process.GetProcessesByName(CefSharp.BrowserSubprocess) Try If GetParentProcessId(p.Id) parentPid Then hasOwnChild True Exit For End If Catch 进程可能已退出忽略 Finally Try : p.Dispose() : Catch : End Try End Try Next If Not hasOwnChild Then Exit Do Thread.Sleep(POLL_INTERVAL_MS) totalWait POLL_INTERVAL_MS If totalWait maxWaitMs Then Console.WriteLine(⚠️ 等待超时继续执行删除...) Exit Do End If Loop End Sub summary通过 NtQueryInformationProcess 获取父进程ID/summary Private Function GetParentProcessId(childPid As Integer) As Integer Const PROCESS_QUERY_INFORMATION As UInteger H400 Const ProcessBasicInformation As Integer 0 Dim handle NativeMethods.OpenProcess(PROCESS_QUERY_INFORMATION, False, childPid) If handle IntPtr.Zero Then Return -1 Try Dim pbi As New NativeMethods.PROCESS_BASIC_INFORMATION() Dim returnLength As UInteger 0 Dim status NativeMethods.NtQueryInformationProcess( handle, ProcessBasicInformation, pbi, Marshal.SizeOf(pbi), returnLength) If status 0 Then ✅ 推荐兼容 32/64 位 Return CInt(pbi.InheritedFromUniqueProcessId.ToInt64() And HFFFFFFFFL) End If Finally NativeMethods.CloseHandle(handle) End Try Return -1 End Function summary带重试递归删除文件夹保持不变/summary Private Function SafeDeleteDirectory(folderPath As String) As Boolean If Not Directory.Exists(folderPath) Then Return True For retry As Integer 1 To DELETE_RETRY_COUNT Try For Each f In Directory.GetFiles(folderPath, *.*, SearchOption.AllDirectories) File.Delete(f) Next Dim subDirs Directory.GetDirectories(folderPath, *, SearchOption.AllDirectories) _ .OrderByDescending(Function(d) d.Length) For Each d In subDirs Directory.Delete(d, False) Next Directory.Delete(folderPath, False) Return True Catch ex As IOException Console.WriteLine($删除失败重试 {retry}/{DELETE_RETRY_COUNT} ... {ex.Message}) Thread.Sleep(RETRY_DELAY_MS) Catch Return False End Try Next Return False End Function End Module P/Invoke 声明 Friend NotInheritable Class NativeMethods StructLayout(LayoutKind.Sequential) Public Structure PROCESS_BASIC_INFORMATION Public Reserved1 As IntPtr Public PebBaseAddress As IntPtr Public Reserved2 As IntPtr Public Reserved3 As IntPtr Public UniqueProcessId As IntPtr Public InheritedFromUniqueProcessId As IntPtr End Structure DllImport(ntdll.dll) Public Shared Function NtQueryInformationProcess( processHandle As IntPtr, processInformationClass As Integer, ByRef processInformation As PROCESS_BASIC_INFORMATION, processInformationLength As Integer, ByRef returnLength As UInteger) As Integer End Function DllImport(kernel32.dll) Public Shared Function OpenProcess( desiredAccess As UInteger, inheritHandle As Boolean, processId As Integer) As IntPtr End Function DllImport(kernel32.dll, SetLastError:True) Public Shared Function CloseHandle(hObject As IntPtr) As Boolean End Function End Class主要改进和调用控制台清理程序已经升级为父PID方案主窗体的子进程终止也应该统一使用相同策略避免两套逻辑并存带来维护负担和潜在的不一致行为。Private Sub Form1_FormClosing(sender As Object, e As FormClosingEventArgs) Handles Me.FormClosing If e.CloseReason CloseReason.UserClosing Then e.Cancel True Return End If OCR模型卸载 Try : UnloadOcrModel() : Catch : End Try ✅ 缓存清理 —— 传入缓存目录 当前进程PID Dim accountName TB身份证号.Text.Trim If Not String.IsNullOrWhiteSpace(accountName) Then Dim cacheKey LCefSharp全局管理器.GetCacheKey(accountName) Dim cacheFolder Path.Combine(AppContext.BaseDirectory, CefCache, cacheKey) Dim cleanerExe Path.Combine(AppContext.BaseDirectory, 删除, CefCacheCleaner.exe) If File.Exists(cleanerExe) AndAlso Directory.Exists(cacheFolder) Then Dim myPid As Integer Process.GetCurrentProcess().Id Dim psi As New ProcessStartInfo( cleanerExe, ${cacheFolder} {myPid} ✅ 第二个参数父进程PID ) psi.CreateNoWindow True psi.UseShellExecute False Process.Start(psi) End If End If ✅ 只杀自己的子进程如果仍需要的话 KillOwnBrowserSubprocesses() ✅ 优雅退出 Application.Exit() End Sub summary 只终止属于当前进程的 CefSharp 子进程 替代原来按路径匹配的 KillLocalBrowserSubprocess /summary Private Sub KillOwnBrowserSubprocesses() Dim myPid As Integer Process.GetCurrentProcess().Id For Each p In Process.GetProcessesByName(CefSharp.BrowserSubprocess) Try If GetParentProcessId(p.Id) myPid Then p.Kill() Try : p.WaitForExit(1500) : Catch : End Try End If Catch 进程已退出或无权访问安全跳过 Finally Try : p.Dispose() : Catch : End Try End Try Next End Sub ✅ 改为 Friend NotInheritable Class WinApiHelper StructLayout(LayoutKind.Sequential) Public Structure PROCESS_BASIC_INFORMATION Public Reserved1 As IntPtr Public PebBaseAddress As IntPtr Public Reserved2 As IntPtr Public Reserved3 As IntPtr Public UniqueProcessId As IntPtr Public InheritedFromUniqueProcessId As IntPtr End Structure DllImport(ntdll.dll) Public Shared Function NtQueryInformationProcess( processHandle As IntPtr, processInformationClass As Integer, ByRef processInformation As PROCESS_BASIC_INFORMATION, processInformationLength As Integer, ByRef returnLength As UInteger) As Integer End Function DllImport(kernel32.dll) Public Shared Function OpenProcess( desiredAccess As UInteger, inheritHandle As Boolean, processId As Integer) As IntPtr End Function DllImport(kernel32.dll, SetLastError:True) Public Shared Function CloseHandle(hObject As IntPtr) As Boolean End Function End Class summary通过 NtQueryInformationProcess 获取父进程ID/summary Private Function GetParentProcessId(childPid As Integer) As Integer Const PROCESS_QUERY_INFORMATION As UInteger H400 Const ProcessBasicInformation As Integer 0 ✅ 全部改为 WinApiHelper Dim handle WinApiHelper.OpenProcess(PROCESS_QUERY_INFORMATION, False, childPid) If handle IntPtr.Zero Then Return -1 Try Dim pbi As New WinApiHelper.PROCESS_BASIC_INFORMATION() Dim returnLength As UInteger 0 Dim status WinApiHelper.NtQueryInformationProcess( handle, ProcessBasicInformation, pbi, Marshal.SizeOf(pbi), returnLength) If status 0 Then Return Convert.ToInt32(pbi.InheritedFromUniqueProcessId) End If Finally WinApiHelper.CloseHandle(handle) End Try Return -1 End Function Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed ❗❗❗这里什么代码都不要写全部清理逻辑移到FormClosing End Sub