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

如何通过对话框取消CopyFileEx复制进程?无全局变量实现

实现CopyFileEx的无全局变量取消逻辑

我来帮你搞定这个问题,核心思路是用自定义结构体封装共享状态替代全局变量,让对话框、复制函数和进度回调都能安全访问取消标志。下面是修改后的完整代码,之后我会逐一拆解关键改动:

// 定义共享状态结构体,封装所有跨函数需要传递的数据
struct CopyContext {
    std::vector<FILECONSOLIDATEPARAMS>& fileList;
    HWND hDialog;
    BOOL isCancelled;

    // 构造函数初始化引用和取消标志
    CopyContext(std::vector<FILECONSOLIDATEPARAMS>& vec) : fileList(vec), hDialog(nullptr), isCancelled(FALSE) {}
};

// 复制函数
BOOL copy(HWND &hWnd, std::vector<FILECONSOLIDATEPARAMS> &vec) {
    // 创建共享上下文,彻底避免全局变量
    CopyContext context(vec);

    // 将上下文指针传递给对话框过程
    HWND hCopy = CreateDialogParam(GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), hwndmain, dlgboxcopyproc, reinterpret_cast<LPARAM>(&context));
    if (!hCopy) {
        ShowErrMsg(GetLastError());
        return FALSE;
    }
    context.hDialog = hCopy; // 把对话框句柄存入上下文

    HWND hIDC_STATIC = GetDlgItem(hCopy, IDC_STATIC);
    HWND hIDC_STATIC4 = GetDlgItem(hCopy, IDC_STATIC4);

    for (size_t s = 0; s < vec.size() && !context.isCancelled; ++s) {
        if (context.isCancelled) break; // 提前检查取消标志,避免启动新的复制任务

        SendMessage(hIDC_STATIC, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(vec[s].filename));
        SendMessage(hIDC_STATIC4, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(vec[s].destination));

        // 把取消标志的地址传给CopyFileEx,让它能实时检测状态
        BOOL b = CopyFileEx(vec[s].filename, vec[s].destination, CopyProgressRoutine, &context, &context.isCancelled, NULL);
        
        if (!b) {
            DWORD dw = GetLastError();
            // 如果是用户主动取消,不用弹出错误提示
            if (dw != ERROR_REQUEST_ABORTED) {
                ShowErrMsg(dw);
            }
            // 不管是错误还是取消,都跳出循环
            break;
        }
    }

    PostMessage(hCopy, WM_CLOSE, 0, 0); // 用WM_CLOSE优雅关闭对话框
    return TRUE;
}

// 对话框过程
INT_PTR CALLBACK dlgboxcopyproc(HWND hWndDlg, UINT Msg, WPARAM wParam, LPARAM lParam) {
    static CopyContext* pContext = nullptr; // 保存上下文指针,不用全局变量

    switch (Msg) {
        case WM_INITDIALOG: {
            // 保存传递过来的上下文指针
            pContext = reinterpret_cast<CopyContext*>(lParam);
            pContext->hDialog = hWndDlg; // 更新对话框句柄

            // 初始化进度条控件
            INITCOMMONCONTROLSEX _icex;
            _icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
            _icex.dwICC = ICC_PROGRESS_CLASS;
            InitCommonControlsEx(&_icex);

            HWND hIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
            SendMessage(hIDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
            return (INT_PTR)TRUE;
        }
        case WM_COMMAND: {
            switch (LOWORD(wParam)) {
                case IDCANCEL:
                    if (pContext) {
                        pContext->isCancelled = TRUE; // 设置取消标志,通知复制进程终止
                    }
                    // 不立即关闭对话框,等CopyFileEx结束后再处理
                    return (INT_PTR)TRUE;
            }
            break;
        }
        case WM_CLOSE: {
            EndDialog(hWndDlg, 0);
            return (INT_PTR)TRUE;
        }
    }
    return FALSE;
}

// 复制进度回调函数
DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred,
                                   LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred,
                                   DWORD dwStreamNumber, DWORD dwCallbackReason,
                                   HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData) {
    CopyContext* pContext = reinterpret_cast<CopyContext*>(lpData);
    if (!pContext) return PROGRESS_CONTINUE;

    // 检查取消标志,返回PROGRESS_CANCEL终止当前复制
    if (pContext->isCancelled) {
        return PROGRESS_CANCEL;
    }

    HWND hIDC_PROGRESS1 = GetDlgItem(pContext->hDialog, IDC_PROGRESS1);
    if (hIDC_PROGRESS1 && TotalFileSize.QuadPart > 0) {
        DOUBLE Percentage = ((DOUBLE)TotalBytesTransferred.QuadPart / (DOUBLE)TotalFileSize.QuadPart) * 100;
        switch (dwCallbackReason) {
            case CALLBACK_CHUNK_FINISHED:
                SendMessage(hIDC_PROGRESS1, PBM_SETPOS, static_cast<WPARAM>(Percentage), 0);
                break;
            case CALLBACK_STREAM_SWITCH:
                SendMessage(hIDC_PROGRESS1, PBM_SETPOS, 0, 0); // 切换文件时重置进度条
                break;
        }
    }

    // 处理消息循环,避免对话框在复制过程中假死
    MSG msg;
    while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return PROGRESS_CONTINUE;
}

关键改动详解:

  1. 自定义CopyContext结构体:

    • 把文件列表引用、对话框句柄、取消标志isCancelled打包在一起,所有跨函数的状态都通过这个结构体指针传递,彻底告别全局变量
    • 用构造函数初始化引用和默认状态,保证数据安全
  2. 复制函数的优化:

    • 创建CopyContext实例后,把它的指针传给对话框和CopyFileEx
    • 每次循环前检查取消标志,避免启动新的复制任务
    • 处理CopyFileEx返回值时,区分"用户取消"(ERROR_REQUEST_ABORTED)和其他复制错误,只对非取消错误弹出提示
  3. 对话框过程的调整:

    • 在WM_INITDIALOG中保存上下文指针,后续操作都通过这个指针修改取消标志
    • 点击取消按钮时,仅设置isCancelled = TRUE,不立即关闭对话框,等待复制任务自然终止后再通过WM_CLOSE关闭
  4. 进度回调的取消逻辑:

    • 每次回调先检查isCancelled标志,一旦为TRUE就返回PROGRESS_CANCEL,直接通知CopyFileEx终止当前复制
    • 添加消息循环处理,解决复制过程中对话框假死的问题(因为CopyFileEx是阻塞调用,需要手动处理消息队列)
  5. 取消标志的正确传递:

    • CopyFileEx的pbCancel参数直接传&context.isCancelled,这样当标志变为TRUE时,CopyFileEx会实时检测到并终止操作

关于PROGRESS_CANCEL的处理:

当回调返回PROGRESS_CANCEL时,CopyFileEx会立即终止复制,返回FALSE,此时GetLastError()会返回ERROR_REQUEST_ABORTED,我们可以通过这个错误码判断是用户主动取消,而非其他复制错误。

内容的提问来源于stack exchange,提问作者jude

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:20:51