基于FindFirstFile/FindNextFile实现病毒扫描进度显示方案问询
病毒扫描应用进度条实时显示问题
我正在开发一款带UI的病毒扫描应用,希望实现0-100的扫描进度实时显示在进度条中。目前使用栈结合FindFirstFileA、FindNextFileA实现了指定目录的文件遍历扫描,已定义scannedFiles和progress变量尝试更新进度,但效果未达预期。代码中progress = scannedFiles * 0.25仅为测试代码,最初计划先统计目录总文件数,再实时更新已扫描文件数来计算进度。
ScanThread类头文件
#ifndef SCANTHREAD_H #define SCANTHREAD_H #include <QThread> #include <QObject> #include <QDebug> #include "mainwindow.h" #include "stack.h" class ScanThread : public QThread { Q_OBJECT public: ScanThread(MainWindow *mainWindow = NULL, std::string sFolderScan = "", QObject *parent = Q_NULLPTR); ~ScanThread(); private: void run(void); void scanFolder(std::string sFolder); public slots: signals: void scanDone(QString data); void scanningFile(QString sFile); private: MainWindow *m_mainWindow; std::string m_sFolderScan; Stack *m_stack; public: int scannedFiles = 0; double progress; }; #endif
scanthread.cpp文件
#include "scanthread.h" ScanThread::ScanThread(MainWindow *mainWindow, std::string sFolderScan, QObject *parent): m_mainWindow(mainWindow), m_sFolderScan(sFolderScan) { //m_mainWindow = mainWindow; //m_sFolderScan = sFolderScan; m_stack = new Stack(); } ScanThread::~ScanThread() { delete m_stack; } void ScanThread::scanFolder(std::string sFolder) { std::string sFolderFind; WIN32_FIND_DATAA FindFileData; HANDLE hFind = INVALID_HANDLE_VALUE; QString sFolderScan; char* szPath = new char[MAX_PATH + 1]; ZeroMemory(szPath, MAX_PATH + 1); strcpy_s(szPath, MAX_PATH,sFolder.c_str()); m_stack->Push(szPath); while (!m_stack->isStackEmpty()) { szPath = m_stack->Pop(); sFolderScan = szPath; sFolderFind = std::string(szPath) + "\\*.*"; hFind = FindFirstFileA(sFolderFind.c_str(), &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { delete[] szPath; szPath = NULL; continue; } do { if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { std::string sFol = FindFileData.cFileName; if (sFol == "." || sFol == "..") { continue; } char* szPath2 = new char[MAX_PATH + 1]; ZeroMemory(szPath2, MAX_PATH + 1); strcpy_s(szPath2, MAX_PATH, QString::fromStdString(sFolderScan.toStdString() + "\\" + FindFileData.cFileName).toStdString().c_str()); m_stack->Push(szPath2); } else { emit scanningFile(QString::fromStdString(sFolderScan.toStdString() + "\\" + FindFileData.cFileName)); scannedFiles++; progress = scannedFiles * 0.25; } }while (FindNextFileA(hFind, &FindFileData)); FindClose(hFind); delete[] szPath; szPath = NULL; } if (!m_stack->isStackEmpty()) m_stack->FreeStack(); } void ScanThread::run(void) { connect(this, SIGNAL(scanDone(QString)), m_mainWindow, SLOT(ScanDone(QString))); connect(this, SIGNAL(scanningFile(QString)), m_mainWindow, SLOT(scanningFile(QString))); scanFolder(m_sFolderScan); this->exec(); }
解决方法
1. 先统计目标目录总文件数
在正式扫描前,先遍历一遍目标目录及其子目录统计总文件数,复用现有栈遍历逻辑即可:
int countTotalFiles(std::string sFolder) { int total = 0; Stack* countStack = new Stack(); char* szPath = new char[MAX_PATH + 1]; ZeroMemory(szPath, MAX_PATH + 1); strcpy_s(szPath, MAX_PATH, sFolder.c_str()); countStack->Push(szPath); while (!countStack->isStackEmpty()) { szPath = countStack->Pop(); std::string sFolderFind = std::string(szPath) + "\\*.*"; WIN32_FIND_DATAA FindFileData; HANDLE hFind = FindFirstFileA(sFolderFind.c_str(), &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { delete[] szPath; continue; } do { if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { std::string sFol = FindFileData.cFileName; if (sFol == "." || sFol == "..") continue; char* szPath2 = new char[MAX_PATH + 1]; ZeroMemory(szPath2, MAX_PATH + 1); strcpy_s(szPath2, MAX_PATH, (std::string(szPath) + "\\" + FindFileData.cFileName).c_str()); countStack->Push(szPath2); } else { total++; } } while (FindNextFileA(hFind, &FindFileData)); FindClose(hFind); delete[] szPath; } delete countStack; return total; }
2. 添加进度更新信号
在ScanThread类的signals块中新增信号,用于向主窗口传递进度值:
signals: void scanDone(QString data); void scanningFile(QString sFile); void progressUpdated(int progressValue); // 新增进度信号
3. 实时计算并发送进度
在ScanThread类中添加私有成员存储总文件数,扫描时实时计算进度并发送信号:
// 类的private块中新增 private: int m_totalFiles = 0;
修改scanFolder函数,先统计总文件数,再在扫描每个文件后计算进度:
void ScanThread::scanFolder(std::string sFolder) { m_totalFiles = countTotalFiles(sFolder); // 先统计总文件数 // ... 原有遍历逻辑 ... else { emit scanningFile(QString::fromStdString(sFolderScan.toStdString() + "\\" + FindFileData.cFileName)); scannedFiles++; if (m_totalFiles > 0) { int currentProgress = static_cast<int>((static_cast<double>(scannedFiles) / m_totalFiles) * 100); emit progressUpdated(currentProgress); // 发送进度信号 } } // ... 原有逻辑结束 ... }
4. 主窗口接收信号更新UI
在MainWindow类中添加槽函数,接收进度信号并更新进度条:
// MainWindow类中新增槽函数 void updateProgressBar(int progress) { ui->progressBar->setValue(progress); }
修改ScanThread的run函数,连接进度信号并结束线程时退出而非阻塞:
void ScanThread::run(void) { connect(this, SIGNAL(scanDone(QString)), m_mainWindow, SLOT(ScanDone(QString))); connect(this, SIGNAL(scanningFile(QString)), m_mainWindow, SLOT(scanningFile(QString))); connect(this, SIGNAL(progressUpdated(int)), m_mainWindow, SLOT(updateProgressBar(int))); // 连接进度信号 scanFolder(m_sFolderScan); emit scanDone("扫描完成"); this->quit(); // 替换exec(),避免线程阻塞 }
5. 注意事项
- 统计总文件数会额外消耗时间,若目标目录文件极多,可改为动态估算进度(比如每扫描100个文件更新一次估算值),平衡精度与启动速度。
- Qt跨线程信号槽会自动将UI操作调度到主线程,无需额外加锁。
- 避免直接访问公共变量传递进度,信号槽是更安全的跨线程数据传递方式。
内容的提问来源于stack exchange,提问作者Thanh Duy Cao
相关产品推荐
相关产品推荐

