我创建了一个带有进度条和取消按钮的对话框,使用CreateDialogParam显示复制多个文件时的状态(使用CopyFileEx)。
如何正确使用CopyFileEx取消进程,从按对话框中的取消按钮开始?无论如何我可以不使用全局变量吗?我如何正确处理返回的PROGRESS_CANCEL?我在下面的代码中提供了一些问题,以便更清楚地了解我需要哪些帮助。
//copy function
BOOL copy(HWND &hWnd, std::vector <FILECONSOLIDATEPARAMS> &vec)
{
//pass vector as lparam to dialogbox proc
LPARAM lp = reinterpret_cast<LPARAM>(&vec);
HWND hCopy = CreateDialogParam(GetModuleHandle(NULL),
MAKEINTRESOURCE(IDD_DIALOG1),
hwndmain, (DLGPROC)dlgboxcopyproc, lp);
static HWND hIDC_STATIC, hIDC_STATIC4;
hIDC_STATIC = GetDlgItem(hCopy, IDC_STATIC);
hIDC_STATIC4 = GetDlgItem(hCopy, IDC_STATIC4);
LPBOOL pbCancel = FALSE;
size_t s;
for (s = 0; s != vec.size(); s++)
{
SendMessage(hIDC_STATIC, WM_SETTEXT, 0, (LPARAM)vec[s].filename);
SendMessage(hIDC_STATIC4, WM_SETTEXT, 0,(LPARAM)vec[s].destination);
BOOL b = CopyFileEx(vec[s].filename, vec[s].destination,
&CopyProgressRoutine,(LPVOID)hCopy,pbCancel, NULL);
//how to catch and process PROGRESS_CANCEL?
if (!b)
{
DWORD dw = GetLastError();
ShowErrMsg(dw);
}
}
PostMessage(hCopy, WM_DESTROY, 0, 0);
return TRUE;
}
//dialogbox procedure
INT_PTR CALLBACK dlgboxcopyproc(HWND hWndDlg,UINT Msg,WPARAM wParam,LPARAM
lParam)
{
//translate passed lparam back to vector
std::vector<FILECONSOLIDATEPARAMS>& vect =
*reinterpret_cast<std::vector<FILECONSOLIDATEPARAMS>*>(lParam);
INITCOMMONCONTROLSEX _icex;
_icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
_icex.dwICC = ICC_PROGRESS_CLASS;
InitCommonControlsEx(&_icex);
static HWND hParent;
static HWND hIDCancel;
static HWND hIDC_PROGRESS1;
static HWND hIDC_STATIC;
hParent = GetParent(hWndDlg);
hIDCancel = GetDlgItem(hWndDlg, IDCANCEL);
hIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
switch (Msg)
{
case WM_INITDIALOG:
{
SendMessage(hIDC_PROGRESS1, PBM_SETRANGE, 0, MAKELPARAM(0, 100));
}
return (INT_PTR)TRUE;
case WM_COMMAND:
{
switch (LOWORD(wParam))
{
case IDCANCEL:
EndDialog(hWndDlg, FALSE); //how to make pbCancel = TRUE?
return (INT_PTR)TRUE;
}
}
break;
case WM_DESTROY:
{
DestroyWindow(hWndDlg);
}
}
return FALSE;
}
//copyprogressroutine callback function
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)
{
HWND hWndDlg = (HWND)lpData;
static HWND hwndIDC_PROGRESS1;
hwndIDC_PROGRESS1 = GetDlgItem(hWndDlg, IDC_PROGRESS1);
DOUBLE Percentage = ((DOUBLE)TotalBytesTransferred.QuadPart /
(DOUBLE)TotalFileSize.QuadPart) * 100;
switch (dwCallbackReason)
{
case CALLBACK_CHUNK_FINISHED:
SendMessage(hwndIDC_PROGRESS1, PBM_SETPOS, (WPARAM) Percentage, 0);
break;
case CALLBACK_STREAM_SWITCH:
Percentage = 0;
break;
}
return PROGRESS_CONTINUE; //how to make conditional return PROGRESS_CANCEL?
}
答案 0 :(得分:0)
代码
LPBOOL pbCancel = FALSE;
CopyFileEx(, pbCancel, )
无意义。实际上我们只是简单地传递0 pbCancel 并且没有能力取消操作。
我们需要 alocate 一些BOOL类型的变量(让它命名为 bCancel )并将此变量的指针传递给CopyFileEx
像这样的东西:
BOOL bCancel = FALSE;
CopyFileEx(, &bCancel, )
CopyFileEx
将通过传递指针定期查询此变量的值,如果它变为true - 中断操作并返回ERROR_REQUEST_ABORTED
错误。
下一个 - CopyFileEx
直到操作完成才返回 - 这是同步api - 因此无法从GUI线程调用它(或者它只是阻止GUI)。需要从单独的线程中调用它。
这么基本的解决方案 - 分配一些数据结构,这里放置BOOL bCancel
,以及我们将在复制期间使用的其他数据。我们将从2个线程 - gui线程(来自对话框程序)和单独的线程(将调用CopyFileEx
)访问此数据结构。为了正确实现这一点 - 需要在结构上使用引用计数。 lpProgressRoutine 需要向gui线程发送消息以通知它有关进度的信息(gui线程可以在此通知上移动进度条)。对话框可以包含例如Cancel
按钮。当用户点击它时 - GUI线程只需设置bCancel = TRUE
,结果CopyFileEx
中止下一个块的操作。开始复制 - 需要启动新线程,调用CopyFileEx
。基于所需的逻辑 - 可以从WM_INITDIALOG
执行此操作,或者说当用户按下对话框中的某个按钮时
基本代码:
struct CopyDlg
{
enum {
WM_COPYRESULT = WM_APP, WM_PROGRESS
};
struct ProgressData {
LARGE_INTEGER TotalFileSize;
LARGE_INTEGER TotalBytesTransferred;
LARGE_INTEGER StreamSize;
LARGE_INTEGER StreamBytesTransferred;
DWORD dwStreamNumber;
};
PCWSTR m_lpExistingFileName, m_lpNewFileName;
HWND m_hwnd, m_hwndFile, m_hwndStream;
ULONG m_shift;
LONG m_dwRefCount;
LONG m_hwndLock;
BOOL m_bCancel;
CopyDlg()
{
m_dwRefCount = 1;
}
void AddRef()
{
InterlockedIncrement(&m_dwRefCount);
}
void Release()
{
if (!InterlockedDecrement(&m_dwRefCount))
{
delete this;
}
}
void LockWnd()
{
InterlockedIncrement(&m_hwndLock);
}
void UnlockWnd()
{
if (!InterlockedDecrement(&m_hwndLock))
{
// want m_hwnd be valid at PostMessage(p->m_hwnd,) time
EndDialog(m_hwnd, 0);
}
}
void OnProgress(DWORD dwCallbackReason, ProgressData* p)
{
if (dwCallbackReason == CALLBACK_STREAM_SWITCH)
{
if (p->dwStreamNumber == 1)
{
m_shift = 0;
LONGLONG QuadPart = p->TotalFileSize.QuadPart;
while (QuadPart > MAXLONG)
{
m_shift++;
QuadPart >>= 1;
}
SendMessage(m_hwndFile, PBM_SETRANGE32, 0, (LPARAM)QuadPart);
SendMessage(m_hwndFile, PBM_SETPOS, 0, 0);
}
SendMessage(m_hwndStream, PBM_SETRANGE32, 0, (LPARAM)(p->StreamSize.QuadPart >> m_shift));
SendMessage(m_hwndStream, PBM_SETPOS, 0, 0);
}
else
{
SendMessage(m_hwndStream, PBM_SETPOS, (LPARAM)(p->StreamBytesTransferred.QuadPart >> m_shift), 0);
SendMessage(m_hwndFile, PBM_SETPOS, (LPARAM)(p->TotalBytesTransferred.QuadPart >> m_shift), 0);
}
}
ULONG StartCopy(PCWSTR lpExistingFileName, PCWSTR lpNewFileName)
{
m_bCancel = FALSE;
m_lpExistingFileName = lpExistingFileName;
m_lpNewFileName = lpNewFileName;
LockWnd();
AddRef();
if (HANDLE hThread = CreateThread(0, 0, reinterpret_cast<PTHREAD_START_ROUTINE>(CopyThread), this, 0, 0))
{
CloseHandle(hThread);
return NOERROR;
}
Release();
UnlockWnd();
return GetLastError();
}
static INT_PTR CALLBACK _DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_INITDIALOG)
{
SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam);
}
if (CopyDlg* p = reinterpret_cast<CopyDlg*>(GetWindowLongPtrW(hwndDlg, DWLP_USER)))
{
p->AddRef();
INT_PTR r = p->DialogProc(hwndDlg, uMsg, wParam, lParam);
p->Release();
return r;
}
return 0;
}
INT_PTR DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_NCDESTROY:
Release();
break;
case WM_DESTROY:
m_bCancel = TRUE;
break;
case WM_COMMAND:
switch (wParam)
{
case MAKEWPARAM(IDCANCEL, BN_CLICKED):
m_bCancel = TRUE;
break;
}
break;
case WM_CLOSE:
m_bCancel = TRUE;
UnlockWnd();
ShowWindow(hwndDlg, SW_HIDE);//for not get wm_close twice
break;
case WM_COPYRESULT:
UnlockWnd();
// lParam == error code from CopyFileExW
DbgPrint("CopyFileExW=%u\n", (ULONG)lParam);
break;
case WM_PROGRESS:
OnProgress((DWORD)wParam, reinterpret_cast<ProgressData*>(lParam));
delete (void*)lParam;
break;
case WM_INITDIALOG:
AddRef();
m_hwnd = hwndDlg;
m_hwndStream = GetDlgItem(hwndDlg, IDC_PROGRESS1);
m_hwndFile = GetDlgItem(hwndDlg, IDC_PROGRESS2);
m_hwndLock = 1;
StartCopy(L"**", L"**");
break;
}
return 0;
}
static ULONG CALLBACK CopyThread(CopyDlg* p)
{
PostMessage(p->m_hwnd, WM_COPYRESULT, 0, CopyFileExW(
p->m_lpExistingFileName, p->m_lpNewFileName,
reinterpret_cast<LPPROGRESS_ROUTINE>(CopyProgressRoutine),
p, &p->m_bCancel, 0) ? NOERROR : GetLastError());
p->Release();
return 0;
}
static DWORD CALLBACK CopyProgressRoutine(
__in LARGE_INTEGER TotalFileSize,
__in LARGE_INTEGER TotalBytesTransferred,
__in LARGE_INTEGER StreamSize,
__in LARGE_INTEGER StreamBytesTransferred,
__in DWORD dwStreamNumber,
__in DWORD dwCallbackReason,
__in HANDLE /*hSourceFile*/,
__in HANDLE /*hDestinationFile*/,
__in_opt CopyDlg* p
)
{
switch(dwCallbackReason)
{
case CALLBACK_CHUNK_FINISHED:
case CALLBACK_STREAM_SWITCH:
if (ProgressData* data = new ProgressData)
{
data->TotalFileSize = TotalFileSize;
data->TotalBytesTransferred = TotalBytesTransferred;
data->StreamSize = StreamSize;
data->StreamBytesTransferred = StreamBytesTransferred;
data->dwStreamNumber = dwStreamNumber;
if (!PostMessage(p->m_hwnd, WM_PROGRESS, dwCallbackReason, (LPARAM)data))
{
delete data;
return PROGRESS_CANCEL;
}
}
break;
}
// for debugging
//Sleep(3000);
//MessageBoxW(0,0,0,0);
return PROGRESS_CONTINUE;
}
};
if (CopyDlg* p = new CopyDlg)
{
DialogBoxParamW((HINSTANCE)&__ImageBase, MAKEINTRESOURCE(IDD_DIALOG1), HWND_DESKTOP, CopyDlg::_DialogProc, (LPARAM)p);
p->Release();
}