// AutoFileCleanupToolDlg.cpp: 实现文件 // #include "pch.h" #include "framework.h" #include "AutoFileCleanupTool.h" #include "AutoFileCleanupToolDlg.h" #include "afxdialogex.h" #include "Config.h" #ifdef _DEBUG #define new DEBUG_NEW #endif // 日志颜色宏定义 #define LOG_COLOR_NORMAL RGB(0, 0, 0) // 普通:黑色 #define LOG_COLOR_SUCCESS RGB(0, 128, 0) // 成功:绿色 #define LOG_COLOR_ERROR RGB(255, 0, 0) // 错误:红色 #define LOG_COLOR_WARNING RGB(255, 165, 0) // 警告:橙色 #define LOG_COLOR_TIME RGB(0, 0, 255) // 时间戳:蓝色 // 配置文件路径 #define AUTO_FILE_CLEANUP_CONFIG_PATH _T("C:\\EdgeInspector_App\\Config\\AutoFileCleanup.cfg") // 定时器 ID #define ID_TIMER_AUTO_CLEANUP 1001 // 自动清理定时器 ID // 托盘图标 ID 与消息宏 #define ID_TRAY_RESTORE 2001 // 恢复窗口 #define ID_TRAY_EXIT 2002 // 退出程序 #define WM_TRAY_ICON_NOTIFY (WM_USER + 1000) // 托盘图标回调消息 ID // 托盘提示文本宏 #define TRAY_ICON_TOOLTIP_TEXT _T("Auto File Cleanup Tool") // 用于应用程序“关于”菜单项的 CAboutDlg 对话框 class CAboutDlg : public CDialogEx { public: CAboutDlg(); // 对话框数据 #ifdef AFX_DESIGN_TIME enum { IDD = IDD_ABOUTBOX }; #endif protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持 // 实现 protected: DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX) { } void CAboutDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); } BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx) END_MESSAGE_MAP() // CAutoFileCleanupToolDlg 对话框 CAutoFileCleanupToolDlg::CAutoFileCleanupToolDlg(CWnd* pParent /*=nullptr*/) : CDialogEx(IDD_AUTOFILECLEANUPTOOL_DIALOG, pParent) , m_nCleanupTimerId(0) // 定时器 ID 初始化为 0(未启动) , m_bCleanupRunning(FALSE) // 清理状态初始化为未运行 , m_nTrayIconID(0) // 托盘图标 ID 初始化为 0 , m_bTrayIconCreated(FALSE) // 托盘图标未创建 , m_bExitingFromTray(FALSE) // 是否从托盘退出标志 , m_strSourceFolder(_T("")) // 源路径默认空 , m_strTargetFolder(_T("")) // 目标路径默认空 , m_nScanInterval(10) // 默认扫描间隔 10 分钟 , m_bEnableAutoStart(FALSE) // 默认不自动启动 , m_bSafeMode(FALSE) // 默认开启安全模式(仅模拟,不删除) , m_bDeleteEmptyFolders(FALSE) // 默认不删除空文件夹 { m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); } void CAutoFileCleanupToolDlg::SaveSettings() { UpdateData(TRUE); CString strTarget = AUTO_FILE_CLEANUP_CONFIG_PATH; // 如果文件不存在,先创建一个空文件 CFileFind findFile; if (!findFile.FindFile(strTarget)) { HANDLE hFile = CreateFile(strTarget, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle(hFile); } else { AppendLogLineRichStyled(_T("Unable to create configuration file. Please verify permissions."), LOG_COLOR_ERROR); return; } } CConfig Config; if (!Config.SetRegiConfig(NULL, NULL, (TCHAR*)(LPCTSTR)strTarget, FileMap_Mode)) { return; } Config.SetItemValue(_T("SOURCE_FOLDER_PATH"), m_strSourceFolder); Config.SetItemValue(_T("TARGET_FOLDER_PATH"), m_strTargetFolder); Config.SetItemValue(_T("SCAN_INTERVAL_MINUTES"), m_nScanInterval); Config.SetItemValue(_T("ENABLE_AUTO_START_CLEANUP"), m_bEnableAutoStart); Config.SetItemValue(_T("ENABLE_SAFE_MODE"), m_bSafeMode); Config.SetItemValue(_T("ENABLE_DELETE_EMPTY_FOLDERS"), m_bDeleteEmptyFolders); Config.WriteToFile(); } void CAutoFileCleanupToolDlg::LoadSettings() { CString strTarget = AUTO_FILE_CLEANUP_CONFIG_PATH; CFileFind findFile; if (!findFile.FindFile(strTarget)) { SaveSettings(); return; } CConfig Config; if (!Config.SetRegiConfig(NULL, NULL, (TCHAR*)(LPCTSTR)strTarget, FileMap_Mode)) { return; } // 先读取配置到变量 Config.GetItemValue(_T("SOURCE_FOLDER_PATH"), m_strSourceFolder, _T("")); Config.GetItemValue(_T("TARGET_FOLDER_PATH"), m_strTargetFolder, _T("")); Config.GetItemValue(_T("SCAN_INTERVAL_MINUTES"), m_nScanInterval, 10); Config.GetItemValue(_T("ENABLE_AUTO_START_CLEANUP"), m_bEnableAutoStart, FALSE); Config.GetItemValue(_T("ENABLE_SAFE_MODE"), m_bSafeMode, TRUE); Config.GetItemValue(_T("ENABLE_DELETE_EMPTY_FOLDERS"), m_bDeleteEmptyFolders, FALSE); // 更新到界面 UpdateData(FALSE); } void CAutoFileCleanupToolDlg::StartCleanup() { UpdateData(TRUE); if (m_bCleanupRunning) { AppendLogLineRichStyled(_T("Cleanup already running."), LOG_COLOR_WARNING); return; } int nIntervalMs = max(m_nScanInterval, 1) * 60 * 1000; m_nCleanupTimerId = SetTimer(ID_TIMER_AUTO_CLEANUP, nIntervalMs, NULL); m_bCleanupRunning = TRUE; AppendLogLineRichStyled(_T("Auto cleanup started."), LOG_COLOR_SUCCESS); UpdateControlStates(); } void CAutoFileCleanupToolDlg::StopCleanup() { if (!m_bCleanupRunning) { AppendLogLineRichStyled(_T("Cleanup is not running."), LOG_COLOR_WARNING); return; } KillTimer(ID_TIMER_AUTO_CLEANUP); m_nCleanupTimerId = 0; m_bCleanupRunning = FALSE; AppendLogLineRichStyled(_T("Auto cleanup stopped."), LOG_COLOR_WARNING); UpdateControlStates(); } void CAutoFileCleanupToolDlg::UpdateControlStates() { BOOL bRunning = m_bCleanupRunning; // 路径输入框 GetDlgItem(IDC_EDIT_SOURCE_FOLDER)->EnableWindow(!bRunning); GetDlgItem(IDC_EDIT_TARGET_FOLDER)->EnableWindow(!bRunning); GetDlgItem(IDC_EDIT_SCAN_INTERVAL)->EnableWindow(!bRunning); // 复选框 GetDlgItem(IDC_CHECK_ENABLE_AUTO_START)->EnableWindow(!bRunning); GetDlgItem(IDC_CHECK_SAFE_MODE)->EnableWindow(!bRunning); GetDlgItem(IDC_CHECK_DELETE_EMPTY_FOLDERS)->EnableWindow(!bRunning); // 按钮 GetDlgItem(IDC_BUTTON_BROWSE_SOURCE)->EnableWindow(!bRunning); GetDlgItem(IDC_BUTTON_BROWSE_TARGET)->EnableWindow(!bRunning); GetDlgItem(IDC_BUTTON_START_CLEANUP)->EnableWindow(!bRunning); GetDlgItem(IDC_BUTTON_STOP_CLEANUP)->EnableWindow(bRunning); } void CAutoFileCleanupToolDlg::AppendLogLineBatchBegin() { m_ctrlLog.SetRedraw(FALSE); m_ctrlLog.SetEventMask(0); // 防止触发不必要的通知 } void CAutoFileCleanupToolDlg::AppendLogLineBatchEnd() { m_ctrlLog.SetRedraw(TRUE); m_ctrlLog.Invalidate(); // 强制重绘 m_ctrlLog.SetEventMask(ENM_CHANGE | ENM_SELCHANGE); } void CAutoFileCleanupToolDlg::TrimRichEditLineLimit(int maxLines) { int lineCount = m_ctrlLog.GetLineCount(); if (lineCount <= maxLines) { return; } // 获取多余行的字符数范围 int charIndex = m_ctrlLog.LineIndex(maxLines); m_ctrlLog.SetSel(0, charIndex); // 选中多余内容 m_ctrlLog.ReplaceSel(_T("")); // 删除 } void CAutoFileCleanupToolDlg::AppendLogLineRichStyled(const CString& content, COLORREF color /*= RGB(0, 0, 0)*/) { // 检查窗口和日志控件是否有效 if (!::IsWindow(GetSafeHwnd()) || !::IsWindow(m_ctrlLog.GetSafeHwnd())) { return; } // 时间戳 CString timestamp; CTime now = CTime::GetCurrentTime(); timestamp.Format(_T("[%02d:%02d:%02d] "), now.GetHour(), now.GetMinute(), now.GetSecond()); // 插入点移到最后(也可以设为 0 表示顶部) m_ctrlLog.SetSel(-1, -1); // 插入时间(蓝色) CHARFORMAT2 cfTime = {}; cfTime.cbSize = sizeof(cfTime); cfTime.dwMask = CFM_COLOR; cfTime.crTextColor = LOG_COLOR_TIME; m_ctrlLog.SetSelectionCharFormat(cfTime); m_ctrlLog.ReplaceSel(timestamp); // 插入日志正文(传入颜色) CHARFORMAT2 cfMsg = {}; cfMsg.cbSize = sizeof(cfMsg); cfMsg.dwMask = CFM_COLOR; cfMsg.crTextColor = color; m_ctrlLog.SetSelectionCharFormat(cfMsg); m_ctrlLog.ReplaceSel(content + _T("\r\n")); // 限制最大行数 TrimRichEditLineLimit(100); } void CAutoFileCleanupToolDlg::HighlightAllMatches(const CString& strSearch, COLORREF clrHighlight) { if (strSearch.IsEmpty()) { return; } long nStart = 0; long nEnd = m_ctrlLog.GetTextLength(); FINDTEXTEX ft = { 0 }; ft.chrg.cpMin = 0; ft.chrg.cpMax = nEnd; ft.lpstrText = strSearch.GetString(); // 高亮前不清除全文颜色,避免历史多色混淆 while (m_ctrlLog.FindText(FR_DOWN, &ft) != -1) { m_ctrlLog.SetSel(ft.chrgText.cpMin, ft.chrgText.cpMax); CHARFORMAT2 cf = {}; cf.cbSize = sizeof(cf); cf.dwMask = CFM_COLOR; cf.crTextColor = clrHighlight; m_ctrlLog.SetSelectionCharFormat(cf); // 下次搜索从后面开始 ft.chrg.cpMin = ft.chrgText.cpMax; } m_ctrlLog.SetSel(-1, 0); } void CAutoFileCleanupToolDlg::DoDataExchange(CDataExchange* pDX) { CDialogEx::DoDataExchange(pDX); DDX_Text(pDX, IDC_EDIT_SOURCE_FOLDER, m_strSourceFolder); DDX_Text(pDX, IDC_EDIT_TARGET_FOLDER, m_strTargetFolder); DDX_Text(pDX, IDC_EDIT_SCAN_INTERVAL, m_nScanInterval); DDX_Check(pDX, IDC_CHECK_ENABLE_AUTO_START, m_bEnableAutoStart); DDX_Check(pDX, IDC_CHECK_SAFE_MODE, m_bSafeMode); DDX_Check(pDX, IDC_CHECK_DELETE_EMPTY_FOLDERS, m_bDeleteEmptyFolders); DDX_Control(pDX, IDC_RICHEDIT2_LOG, m_ctrlLog); } BEGIN_MESSAGE_MAP(CAutoFileCleanupToolDlg, CDialogEx) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_CLOSE() ON_WM_TIMER() ON_COMMAND(ID_TRAY_RESTORE, &CAutoFileCleanupToolDlg::OnTrayRestore) ON_COMMAND(ID_TRAY_EXIT, &CAutoFileCleanupToolDlg::OnTrayExit) ON_MESSAGE(WM_TRAY_ICON_NOTIFY, &CAutoFileCleanupToolDlg::OnTrayIconClick) ON_BN_CLICKED(IDC_BUTTON_BROWSE_SOURCE, &CAutoFileCleanupToolDlg::OnBnClickedButtonBrowseSource) ON_BN_CLICKED(IDC_BUTTON_BROWSE_TARGET, &CAutoFileCleanupToolDlg::OnBnClickedButtonBrowseTarget) ON_BN_CLICKED(IDC_BUTTON_START_CLEANUP, &CAutoFileCleanupToolDlg::OnBnClickedButtonStartCleanup) ON_BN_CLICKED(IDC_BUTTON_STOP_CLEANUP, &CAutoFileCleanupToolDlg::OnBnClickedButtonStopCleanup) ON_WM_QUERYDRAGICON() END_MESSAGE_MAP() // CAutoFileCleanupToolDlg 消息处理程序 BOOL CAutoFileCleanupToolDlg::OnInitDialog() { CDialogEx::OnInitDialog(); // 将“关于...”菜单项添加到系统菜单中。 // IDM_ABOUTBOX 必须在系统命令范围内。 ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX); ASSERT(IDM_ABOUTBOX < 0xF000); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != nullptr) { BOOL bNameValid; CString strAboutMenu; bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX); ASSERT(bNameValid); if (!strAboutMenu.IsEmpty()) { pSysMenu->AppendMenu(MF_SEPARATOR); pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu); } } // 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动 // 执行此操作 SetIcon(m_hIcon, TRUE); // 设置大图标 SetIcon(m_hIcon, FALSE); // 设置小图标 // TODO: 在此添加额外的初始化代码 // 初始化日志框 LoadSettings(); AppendLogLineRichStyled(_T("准备就绪..."), LOG_COLOR_SUCCESS); // 如果启用了自动启动清理,则立即开始 if (m_bEnableAutoStart) { StartCleanup(); } // 托盘图标初始化 m_trayIconData.cbSize = sizeof(NOTIFYICONDATA); // 设置托盘图标数据结构的大小 m_trayIconData.hWnd = m_hWnd; // 设置窗口句柄 m_trayIconData.uID = m_nTrayIconID; // 设置托盘图标 ID m_trayIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; // 设置托盘图标的标志(图标、消息、提示文本) m_trayIconData.uCallbackMessage = WM_TRAY_ICON_NOTIFY; // 设置回调消息 WM_TRAY_ICON_NOTIFY m_trayIconData.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); // 加载托盘图标 lstrcpy(m_trayIconData.szTip, TRAY_ICON_TOOLTIP_TEXT); // 设置托盘提示文本 // 添加托盘图标 Shell_NotifyIcon(NIM_ADD, &m_trayIconData); m_bTrayIconCreated = TRUE; return TRUE; // 除非将焦点设置到控件,否则返回 TRUE } void CAutoFileCleanupToolDlg::OnSysCommand(UINT nID, LPARAM lParam) { if ((nID & 0xFFF0) == IDM_ABOUTBOX) { CAboutDlg dlgAbout; dlgAbout.DoModal(); } else { CDialogEx::OnSysCommand(nID, lParam); } } // 如果向对话框添加最小化按钮,则需要下面的代码 // 来绘制该图标。 对于使用文档/视图模型的 MFC 应用程序, // 这将由框架自动完成。 void CAutoFileCleanupToolDlg::OnPaint() { if (IsIconic()) { CPaintDC dc(this); // 用于绘制的设备上下文 SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0); // 使图标在工作区矩形中居中 int cxIcon = GetSystemMetrics(SM_CXICON); int cyIcon = GetSystemMetrics(SM_CYICON); CRect rect; GetClientRect(&rect); int x = (rect.Width() - cxIcon + 1) / 2; int y = (rect.Height() - cyIcon + 1) / 2; // 绘制图标 dc.DrawIcon(x, y, m_hIcon); } else { CDialogEx::OnPaint(); } } void CAutoFileCleanupToolDlg::OnClose() { // TODO: 在此添加消息处理程序代码和/或调用默认值 auto CleanupAndExit = [&]() { if (m_bCleanupRunning) { StopCleanup(); } if (m_bTrayIconCreated) { Shell_NotifyIcon(NIM_DELETE, &m_trayIconData); m_bTrayIconCreated = FALSE; } SaveSettings(); CDialogEx::OnClose(); }; if (m_bExitingFromTray) { // 从托盘退出,直接退出,不弹提示 CleanupAndExit(); return; } // 正常关闭流程 int nResult = AfxMessageBox(_T("Do you want to minimize the application to the system tray?"), MB_YESNO | MB_ICONQUESTION); if (nResult == IDYES) { AppendLogLineRichStyled(_T("Application minimized to tray."), LOG_COLOR_WARNING); ShowWindow(SW_HIDE); } else { CleanupAndExit(); } } void CAutoFileCleanupToolDlg::OnTimer(UINT_PTR nIDEvent) { // TODO: 在此添加消息处理程序代码和/或调用默认值 if (nIDEvent == ID_TIMER_AUTO_CLEANUP) { // 清理逻辑放这里 AppendLogLineRichStyled(_T("Performing auto cleanup..."), LOG_COLOR_NORMAL); if (m_bSafeMode) { AppendLogLineRichStyled(_T("Safe mode: No files will be deleted."), LOG_COLOR_NORMAL); } else { AppendLogLineRichStyled(_T("Scanning and cleaning files..."), LOG_COLOR_NORMAL); } AppendLogLineRichStyled(_T("Cleanup cycle completed."), LOG_COLOR_SUCCESS); } CDialogEx::OnTimer(nIDEvent); } void CAutoFileCleanupToolDlg::OnTrayRestore() { ShowWindow(SW_SHOW); // 恢复窗口 SetForegroundWindow(); // 将窗口置于前端 } void CAutoFileCleanupToolDlg::OnTrayExit() { m_bExitingFromTray = TRUE; PostMessage(WM_CLOSE); } LRESULT CAutoFileCleanupToolDlg::OnTrayIconClick(WPARAM wParam, LPARAM lParam) { if (wParam == m_nTrayIconID) { if (LOWORD(lParam) == WM_LBUTTONUP) { // 左键点击恢复窗口 ShowWindow(SW_SHOW); SetForegroundWindow(); } else if (LOWORD(lParam) == WM_RBUTTONUP) { // 右键点击弹出菜单 CMenu menu; menu.CreatePopupMenu(); menu.AppendMenu(MF_STRING, ID_TRAY_RESTORE, _T("Restore")); menu.AppendMenu(MF_STRING, ID_TRAY_EXIT, _T("Exit")); // 获取鼠标当前位置,并显示菜单 POINT pt; GetCursorPos(&pt); menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this); } } return 0; } void CAutoFileCleanupToolDlg::OnBnClickedButtonBrowseSource() { // TODO: 在此添加控件通知处理程序代码 CFolderPickerDialog dlg(NULL, OFN_FILEMUSTEXIST, this); if (dlg.DoModal() == IDOK) { m_strSourceFolder = dlg.GetPathName(); UpdateData(FALSE); // 更新到界面 } } void CAutoFileCleanupToolDlg::OnBnClickedButtonBrowseTarget() { // TODO: 在此添加控件通知处理程序代码 CFolderPickerDialog dlg(NULL, OFN_FILEMUSTEXIST, this); if (dlg.DoModal() == IDOK) { m_strTargetFolder = dlg.GetPathName(); UpdateData(FALSE); // 更新到界面 } } void CAutoFileCleanupToolDlg::OnBnClickedButtonStartCleanup() { // TODO: 在此添加控件通知处理程序代码 StartCleanup(); } void CAutoFileCleanupToolDlg::OnBnClickedButtonStopCleanup() { // TODO: 在此添加控件通知处理程序代码 StopCleanup(); } //当用户拖动最小化窗口时系统调用此函数取得光标 //显示。 HCURSOR CAutoFileCleanupToolDlg::OnQueryDragIcon() { return static_cast(m_hIcon); }