如何通过对话框取消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; }
关键改动详解:
自定义
CopyContext结构体:- 把文件列表引用、对话框句柄、取消标志
isCancelled打包在一起,所有跨函数的状态都通过这个结构体指针传递,彻底告别全局变量 - 用构造函数初始化引用和默认状态,保证数据安全
- 把文件列表引用、对话框句柄、取消标志
复制函数的优化:
- 创建
CopyContext实例后,把它的指针传给对话框和CopyFileEx - 每次循环前检查取消标志,避免启动新的复制任务
- 处理
CopyFileEx返回值时,区分"用户取消"(ERROR_REQUEST_ABORTED)和其他复制错误,只对非取消错误弹出提示
- 创建
对话框过程的调整:
- 在
WM_INITDIALOG中保存上下文指针,后续操作都通过这个指针修改取消标志 - 点击取消按钮时,仅设置
isCancelled = TRUE,不立即关闭对话框,等待复制任务自然终止后再通过WM_CLOSE关闭
- 在
进度回调的取消逻辑:
- 每次回调先检查
isCancelled标志,一旦为TRUE就返回PROGRESS_CANCEL,直接通知CopyFileEx终止当前复制 - 添加消息循环处理,解决复制过程中对话框假死的问题(因为
CopyFileEx是阻塞调用,需要手动处理消息队列)
- 每次回调先检查
取消标志的正确传递:
CopyFileEx的pbCancel参数直接传&context.isCancelled,这样当标志变为TRUE时,CopyFileEx会实时检测到并终止操作
关于PROGRESS_CANCEL的处理:
当回调返回PROGRESS_CANCEL时,CopyFileEx会立即终止复制,返回FALSE,此时GetLastError()会返回ERROR_REQUEST_ABORTED,我们可以通过这个错误码判断是用户主动取消,而非其他复制错误。
内容的提问来源于stack exchange,提问作者jude
相关产品推荐
相关产品推荐

