You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 08:55:36