Обновлением Progress Bar Ctrl при использование CopyFileEx

sami_znaete_kto

Доброго времени суток. Visual c++ не очень хорошо знаю, потому прошу строго не судить за нубские вопросы.Необходимо организовать копирование списка файлов с отображением хода процесса. Погуглил, нашел такую функцию как CopyFileEx, которая в процессе своего выполнения вызывает CopyProgressRoutine, через нее то и можно завязать обновление ProgressCtrl. Мне не совсем понятно как организовать работу CopyProgressRoutine. Можно навоять какую-нибудь функцию, скажем CopyFilesList, в ней организовать цикл, который будет вызывать CopyFileEx. Меня интересует как изменять прогрессбар из CopyProgressRoutine? Ведь это внешняя функция и она не получит доступ к членам класса диалога. Подскажите пожалуйста.Как передать в CopyProgressRoutine ссылку на прогрессбар?Разобрался как передать указатель на прогрессбар в CopyProgressRoutine, но возник новый вопрос . Чуточку кода для наглядности:
DWORD CALLBACK MyCopyProgressRoutine(
    LARGE_INTEGER TotalFileSize,
    LARGE_INTEGER TotalBytesTransferred,
    LARGE_INTEGER StreamSize,
    LARGE_INTEGER StreamBytesTransferred,
    DWORD dwStreamNumber,
    DWORD dwCallbackReason,
    HANDLE hSourceFile,
    HANDLE hDestinationFile,
    LPVOID lpData){
        switch(dwCallbackReason)
        {
        case CALLBACK_CHUNK_FINISHED:
            CProgressCtrl * progressCtrl = (CProgressCtrl *)lpData;
            progressCtrl->SetPos(TotalBytesTransferred.QuadPart / TotalFileSize.QuadPart *100);
            break;
        }
    return PROGRESS_CONTINUE;
  };
/*...............................................................*/
m_progress.SetRange(0, 100);
m_progress.SetStep(1);
if(CopyFileExW(sFrom, sTo, MyCopyProgressRoutine, &m_progress, &bCancel, COPY_FILE_FAIL_IF_EXISTS)){
        MessageBox(L"File copy done!", L"CopyList", MB_OK);
    }
    else{
        MessageBox(L"File copy error!", L"CopyList", MB_OK);
    }
Копирование происходит, но вот прогрессбар не обновляется, а по окончанию копирования сразу запоняется. Почему, кто подскажет? Как я понимаю контрол не обновляется потому как выполняется функция в которой происходит вызов CopyFileExW. В таком случае как обновлять?Как бы разобрался с CopyFileEx, все копируется, но не обновляется прогрессбар и приложение замирает до окончания копирования. Погуглил, нашел подобную ситуацию, там вопрос решился переносом вызова CopyFileEx в отдельный поток с которого CopyProgressRoutine отправляет сообщения прогрессбару на обновление. Сделал так, теперь приложение не замирает, но прогрессбар все еще не обновляется, только когда файл скопирован прогресс заполняется одним скачком. Вот куски кода которые это делают.
DWORD CALLBACK MyCopyProgressRoutine(
    LARGE_INTEGER TotalFileSize,
    LARGE_INTEGER TotalBytesTransferred,
    LARGE_INTEGER StreamSize,
    LARGE_INTEGER StreamBytesTransferred,
    DWORD dwStreamNumber,
    DWORD dwCallbackReason,
    HANDLE hSourceFile,
    HANDLE hDestinationFile,
    LPVOID lpData){
        switch(dwCallbackReason)
        {
        case CALLBACK_CHUNK_FINISHED:
            CString s;
            s.Format(_T("%d\n%d"), (TotalBytesTransferred.QuadPart / TotalFileSize.QuadPart *100), TotalBytesTransferred.QuadPart);
            CProgressCtrl * progressCtrl = (CProgressCtrl *)lpData;
            MessageBox(progressCtrl->GetSafeHwnd(), s, L"File copy status", MB_OK);
            ::PostMessage(progressCtrl->m_hWnd ,PBM_SETPOS, (WPARAM)(TotalBytesTransferred.QuadPart / TotalFileSize.QuadPart *100), 0);
            break;
        }
    return PROGRESS_CONTINUE;
  }
// потоковая процедура
UINT CopyFileInThread(LPVOID pParam)
{
    BOOL bCancel = false;
    
    CString sFrom = L"D:\\movie.avi", sTo = L"C:\\movie.avi";
 
    CProgressCtrl * progress = (CProgressCtrl *)pParam;
 
    progress->SetRange(0, 100);
 
    CopyFileExW(sFrom, sTo, MyCopyProgressRoutine, progress, &bCancel, COPY_FILE_FAIL_IF_EXISTS);
 
    return 0;
}
/////////////////////////////////////////////////
AfxBeginThread(CopyFileInThread, &m_progress);
;
При этом
MessageBox(progressCtrl->GetSafeHwnd(), s, L"File copy status", MB_OK);
Постоянно 0 выводит. Что это такое?! Подскажите пожалуйста. Потому как я уже начинаю терять веру
9 ответов

sami_znaete_kto

Постоянно 0 выводит. Что это такое?! Подскажите пожалуйста. Потому как я уже начинаю терять веру
>SetPos(TotalBytesTransferred.QuadPart / TotalFileSize.QuadPart *100);
- делишь целое на целое и умножаешь на целое и ещё хочешь чтобы что то шло TotalBytesTransferred.QuadPart всегда меньше TotalFileSize.QuadPart таким образом у тебя всегда 0 при делении. вот и всё что нужно для успеха
progressCtrl->SetPos(TotalBytesTransferred.QuadPart*100.0/ TotalFileSize.QuadPart);


sami_znaete_kto

Спасибо за ответ, все так просто оказалось, когда понимать, что делаешь))).Назрел попутный вопрос, есть структура: ...
struct ToThreadInfo
{
LPBOOL bCancelJob;
CProgressCtrl *ProgressBar;
FlsLst *Files;
LPCTSTR sCopyPath;
HWND hWnd;
};
... public переменные в классе диалога: ...
ToThreadInfo to_thread_info;
BOOL volatile m_bCopyCanceled;
... структура заполняется и передается в потоковую функцию как param: ...
to_thread_info.bCancelJob = (LPBOOL)&m_bCopyCanceled;
... в потоке: ...
ToThreadInfo *info = (ToThreadInfo *)pParam;
... как мне получить доступ bCancelJob из info? Идея заключается в том чтобы отловить из потока когда произойдет изменение этой самой bCancelJob, а она будет меняться в обработчике кнопки. Мне нужно знать что bCancelJob изменился, в потоке эта переменная используется как параметр условия выхода из CopyFileEx. Скорее всего я что-то намудрил(((. Очень нужно разрулить ситуацию, подскажите пожалуйста. Или просто объясните как организовать выход из CopyFileEx по желанию, она работает в отдельном потоке. Спасибо.


sami_znaete_kto

а если убить поток в котором осуществляется копирование,скажем при нажатии на кнопку отмена?


sami_znaete_kto

sami_znaete_kto, смотришь MSDN и видишь:
BOOL WINAPI CopyFileEx(
  _In_      LPCTSTR lpExistingFileName,
  _In_      LPCTSTR lpNewFileName,
  _In_opt_  LPPROGRESS_ROUTINE lpProgressRoutine,
  _In_opt_  LPVOID lpData,
  _In_opt_  LPBOOL pbCancel,
  _In_      DWORD dwCopyFlags
);
pbCancel [in, optional] If this flag is set to TRUE during the copy operation, the operation is canceled. Otherwise, the copy operation will continue to completion.Т.е. если во время копирования переменная pbCancel изменится на TRUE, то копирование прекратится.


sami_znaete_kto

Не переменная pbCancel, а переменная, на которую указывает pbCancel. А поскольку указатель передается как член ToThreadInfo, то достаточно будет просто изменить m_bCopyCanceled


sami_znaete_kto

Благодарен за помощь, но у меня такой вариант тоже не прокатывает. В рабочем потоке есть цикл, где вызывается CopyFileEx, есть у нее параметр pbCancel, он отвечает за досрочное завершение работы функции, но не аварийное, недокопированный файл удаляется.
// Прототипчик для наглядности:
BOOL WINAPI CopyFileEx(
  _In_      LPCTSTR lpExistingFileName,
  _In_      LPCTSTR lpNewFileName,
  _In_opt_  LPPROGRESS_ROUTINE lpProgressRoutine,
  _In_opt_  LPVOID lpData,
  _In_opt_  LPBOOL pbCancel,
  _In_      DWORD dwCopyFlags
);
// Структура в которой передаю все что необходимо рабочему потоку:
struct ToThreadInfo
{
    LPBOOL bCancelJob;
    CProgressCtrl *ProgressBar;
    FlsLst *Files;
    LPCTSTR sCopyPath;
    HWND hWnd;
};
В качестве этого параметра я пытаюсь передать bCancelJob из ToThreadInfo по ссылке, делаю так:
to_thread_info.bCancelJob = (LPBOOL)&m_bCopyCanceled;
Далее уже в рабочем потоке:
BOOL bCancelJob = *(info->bCancelJob);
Но когда вызываю CopyFileEx его приходится преобразовать к
(LPBOOL)*(info->bCancelJob)
, дальше происходит копирование файла, в это время обработчик кнопки из родительского потока (самого диалога, это как бы диалоговое приложение) меняет значение переменной из класса диалога
BOOL volatile m_bCopyCanceled;
на TRUE, что должно заставить CopyFileEx прекратить свою работу, но этого не происходит, она спокойно себе копирует файл до конца. После нее проверяю значение
(LPBOOL)*(info->bCancelJob)
оно уже изменено, как бы все правильно. Но почему на него не реагирует CopyFileEx? Проясните ситуацию, пожалуйста.


sami_znaete_kto

как мне получить доступ bCancelJob из info?
Как после обработки потока запустить его с новым методом?На пальцах - есть класс делаем в нём структуру
STRUCT_TYPE pStruct;
, которую хотим впихнуть в поток, делаем метод класс
static void ThreadCall(PVOID pParam)
{
     COurClass * pThis = (COurClass *)pParam;
     pThis->Trhead(&pThis->pStruct);
}
в программе
 
COurClass pClass;
_beginthread(pClass.ThreadCall, 0, this);


sami_znaete_kto

Далее уже в рабочем потоке: BOOL bCancelJob = *(info->bCancelJob);
Погоди, а кто мешает:
CopyFileEx(..., info->bCancelJob, ...);
?


sami_znaete_kto

Ребята, всем спасибо за оказанную помощь! Все получилось! Тему можно закрывать.