Originally meant to be launched as a thread at one time, but I ditched that:
Code:
void DoEvents()
{
DoEventsThread(NULL);
}
UINT __cdecl DoEventsThread(void* pParam)
{
MSG msg;
while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
I also found that there is a difference between copying a file over the network and to another place on another HD. The network transfer is slow using the above implemtation as well (GetMessage), but not when copying to another HD.
Here is the thread that does the copying:
Code:
UINT __cdecl CopyThread(void* pParam)
{
ProgressArguments* pArgs = (ProgressArguments*)pParam;
CString strDir, strFileName, strExt, strCopyFrom, strCopyTo, strTemp;
UINT64 nNumOfFiles = 0, unAdd = 0;
bool bError = false;
pArgs->punAdd = &unAdd;
pArgs->pbError = &bError;
//for (size_t i = 0; i < pArgs->pCopyClass->strFilesToCopy->size(); i++)
//{
// _wsplitpath_s(pArgs->pCopyClass->strFilesToCopy->at(i), NULL, 0, strDir.GetBuffer(5000), 5000, NULL, 0, NULL, 0);
// strDir.ReleaseBuffer();
// strCopyTo = pArgs->pCopyClass->strFolderCopyTo + strDir + strFileName;
// strCopyTo.Replace(pArgs->pCopyClass->strFolderCopyFrom, L"");
// SHCreateDirectoryEx(NULL, strCopyTo, NULL);
//}
for (size_t i = 0; i < pArgs->pCopyClass->strFilesToCopy->size(); i++)
{
nPrevious = 0;
//pArgs->pProgressDlg->nPreviousByteCountAll = 0;
strCopyFrom = pArgs->pCopyClass->strFilesToCopy->at(i);
_wsplitpath_s(strCopyFrom, NULL, 0, strDir.GetBuffer(5000), 5000, strFileName.GetBuffer(500), 500, strExt.GetBuffer(500), 500);
strDir.ReleaseBuffer();
strFileName.ReleaseBuffer();
strExt.ReleaseBuffer();
strFileName += strExt;
strCopyTo = pArgs->pCopyClass->strFolderCopyTo + strDir;
strCopyTo.Replace(pArgs->pCopyClass->strFolderCopyFrom, L"");
SHCreateDirectoryEx(NULL, strCopyTo, NULL);
strCopyTo += strFileName;
pArgs->pProgressDlg->m_NowCopying = L"Now copying file " + strFileName + L"...";
strTemp.Format(L"Copying file %I64u ", ++nNumOfFiles);
if (bThreadReady)
strTemp.AppendFormat(L"out of %I64u file(s)...", nTotalFiles);
else
strTemp.AppendFormat(L"...");
pArgs->pProgressDlg->m_CopyFileOfFiles = strTemp;
if (hEstimateTime) SetEvent(hEstimateTime);
BOOL bSuccess = CopyFileEx(strCopyFrom, strCopyTo, &CopyProgressRoutine, pArgs, NULL, COPY_FILE_FAIL_IF_EXISTS);
if (pArgs->pProgressDlg->bCancel) break;
if (!bSuccess)
{
if (GetLastError() != ERROR_FILE_EXISTS)
pArgs->pErrorDlg->m_Errors.AppendFormat(L"Unable to copy file %s (%s)...\r\n", strFileName, LastError( GetLastError() ));
// Add the files size to the copied amount
FILE* f;
_wfopen_s(&f, pArgs->pCopyClass->strFilesToCopy->at(i), L"rb");
if (f == NULL) continue;
_fseeki64(f, 0, SEEK_END);
unAdd += _ftelli64(f);
fclose(f);
// Recalculate the estimate time. The reason for calling the thread again is to rule out the case when the copy operations fails and it skips files. When it does,
// it will calculate faulty data. Therefore, we restart this thread again.
bError = true;
//bAbortThread = true; // This will hinder any estimation threads from setting the dwBytesPerSecond variable.
dwBytesPerSecond = 0; // If some / any threads has already set this variable, then disable it so that faulty estimation time won't appear.
TerminateThread(hEstTimeThread, 0); // Kill the estimation time thread
hEstTimeThread = NULL;
pArgs->pProgressDlg->m_EstimatedTimeAll = "Calculating...";
}
else;
//{
// WaitForSingleObject(hEvent, INFINITE);
// ResetEvent(hEvent);
//}
}
FILE* f;
_wfopen_s(&f, pArgs->pCopyClass->strFilesToCopy->at( pArgs->pCopyClass->strFilesToCopy->size() - 1 ), L"rb");
if (f != NULL)
{
_fseeki64(f, 0, SEEK_END);
unAdd = _ftelli64(f);
fclose(f);
}
if (!pArgs->pProgressDlg->bCancel)
{
pArgs->pProgressDlg->GetDlgItem(IDC_CANCEL)->SetWindowText(L"OK");
pArgs->pProgressDlg->m_ProgressAll.SetPos(100);
pArgs->pProgressDlg->m_ProgressCurrent.SetPos(100);
pArgs->pProgressDlg->m_CopyFileOfFiles.Format(L"Copying file %I64u ", nNumOfFiles++);
if (bThreadReady)
pArgs->pProgressDlg->m_CopyFileOfFiles.AppendFormat(L"out of %I64u file(s)...", nTotalFiles);
else
pArgs->pProgressDlg->m_CopyFileOfFiles.AppendFormat(L"...");
pArgs->pProgressDlg->m_EstimatedTimeAll = L"Estimated time reamining: 00:00:00";
pArgs->pProgressDlg->m_EstimatedTimeCurrent = L"Estimated time reamining: 00:00:00";
CString strUnit = GetSize(nTotalSize);
pArgs->pProgressDlg->m_DataCopiedAll.Format(L"Copied %I64u %s ", nTotalSize, strUnit);
if (bThreadReady)
pArgs->pProgressDlg->m_DataCopiedAll.AppendFormat(L"of %I64u %s (%i%%)...", nTotalSize, strUnit, 100);
else
pArgs->pProgressDlg->m_DataCopiedAll += L"so far...";
strUnit = GetSize(unAdd);
if (unAdd != 0)
{
pArgs->pProgressDlg->m_DataCopiedCurrent.Format(L"Copied %I64u %s ", unAdd, strUnit);
if (bThreadReady)
pArgs->pProgressDlg->m_DataCopiedCurrent.AppendFormat(L"of %I64u %s (%i%%)...", unAdd, strUnit, 100);
else
pArgs->pProgressDlg->m_DataCopiedCurrent += L"so far...";
}
else
pArgs->pProgressDlg->m_DataCopiedCurrent = L"Copied an unknown amount of data...";
}
return S_OK;
}
...And here is the callback routine for the copy:
Code:
DWORD CALLBACK CopyProgressRoutine(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber,
DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, void* lpData)
{
ProgressArguments* pArgs = (ProgressArguments*)lpData;
if (pArgs->pProgressDlg->bCancel)
{
SetEvent(hEvent);
return PROGRESS_CANCEL;
}
if (TotalBytesTransferred.QuadPart == 0 && (*pArgs->pnDataCopied + TotalFileSize.QuadPart) > pArgs->nFreeSpace)
{
pArgs->pProgressDlg->bCancel = true;
MessageBox(NULL, L"Uh-oh! It seems that the target drive has insufficient space to continue the copy operation.\nFile copy operation has been cancelled.", L"Insufficient free space", MB_ICONERROR);
return PROGRESS_CANCEL;
}
if ( (pArgs->pProgressDlg->nTimeLeftCurrent <= -3) || // Did the operation take 3 or more seconds longer than expected?
(TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart && pArgs->pProgressDlg->nTimeLeftCurrent >= 3) ) // Did the operation go 3 or more seconds faster than expected?
{
// Estimated time is off by at least 3 seconds, so fix it!
*pArgs->pbError = true; // This will force the next if to recalc the estimated time.
// Oh, and let's set the counters to calculating again.
pArgs->pProgressDlg->m_EstimatedTimeAll = L"Calculating...";
pArgs->pProgressDlg->m_EstimatedTimeCurrent = L"Calculating...";
bCalcEstTimeAll = true;
bCalcEstTimeCurrent = true;
dwBytesPerSecond = 0;
pArgs->pProgressDlg->nTimeLeftCurrent = 0;
}
if (*pArgs->pbError)
{
// WHOA! An error (or more) occoured and this is the first file that there does not seem to be an error with!
SetEvent(hEstimateTime); // Since we started copying, it must be set so the estimation thread will continue.
ResetEvent(hEstimateTimeB); // Allow estimation thread to gather number of bytes copied so far...
bAbortThread = false;
// I assume it's safe to say that if an error occoured and we skipped files, it couldn't have copied them, so set the new previous count to the current anount of copied byte so it won't
// take into account the data we skipped.
pArgs->pProgressDlg->nPreviousByteCountAll = *pArgs->pnDataCopied + *pArgs->punAdd;
*pArgs->pnDataCopied += *pArgs->punAdd;
*pArgs->punAdd = 0;
*pArgs->pbError = false;
TerminateThread(hEstTimeThread, 0); // Kill any running thread
hEstTimeThread = AfxBeginThread(&EstimatedTimeThread, pArgs, THREAD_PRIORITY_TIME_CRITICAL)->m_hThread;
WaitForSingleObject(hEstimateTimeB, INFINITE);
}
// Update progress for total files
INT64 nTransferred = TotalBytesTransferred.QuadPart - nPrevious;
*pArgs->pnDataCopied += nTransferred;
UINT64 nCopiedData = *pArgs->pnDataCopied;
UINT64 nTotalData = nTotalSize;
nPrevious = TotalBytesTransferred.QuadPart;
CString strUnit = GetSize(nCopiedData);
CString strTemp;
strTemp.Format(L"Copied %I64u %s ", nCopiedData, strUnit);
if (bThreadReady)
{
ASSERT(nTotalSize != 0);
pArgs->pProgressDlg->m_ProgressAll.SetPos( (int)( (float)*pArgs->pnDataCopied / nTotalSize * 100 ) );
strUnit = GetSize(nTotalData);
strTemp.AppendFormat(L"of %I64u %s (%i%%)...", nTotalData, strUnit, pArgs->pProgressDlg->m_ProgressAll.GetPos());
}
else
strTemp += L"so far...";
pArgs->pProgressDlg->m_DataCopiedAll = strTemp;
if (dwBytesPerSecond != 0 && bCalcEstTimeAll)
{
// Start estimation time for all files
UINT64 nBytes = nTotalSize - *pArgs->pnDataCopied;
UINT64 nSeconds = nBytes / dwBytesPerSecond;
pArgs->pProgressDlg->nTimeLeftAll = nSeconds;
pArgs->pProgressDlg->SetTimer(2, 1000, NULL);
bCalcEstTimeAll = false;
}
if (dwBytesPerSecond != 0 && bCalcEstTimeCurrent)
{
// Start estimation time for current file
UINT64 nBytes = TotalFileSize.QuadPart - TotalBytesTransferred.QuadPart;
UINT64 nSeconds = nBytes / dwBytesPerSecond;
pArgs->pProgressDlg->nTimeLeftCurrent = nSeconds;
pArgs->pProgressDlg->GetDlgItem(IDC_ESTIMATEDTIME_CURRENT)->ShowWindow(SW_SHOW);
bCalcEstTimeCurrent = false;
}
if (TotalBytesTransferred.QuadPart == TotalFileSize.QuadPart)
{
// File copy of this file is complete,
//pArgs->pProgressDlg->GetDlgItem(IDC_ESTIMATEDTIME_CURRENT)->ShowWindow(SW_HIDE);
bCalcEstTimeCurrent = true;
//SetEvent(hEvent);
}
// Update progress for current file
ASSERT(TotalFileSize.QuadPart != 0);
pArgs->pProgressDlg->m_ProgressCurrent.SetPos( (int)( (float)TotalBytesTransferred.QuadPart / TotalFileSize.QuadPart * 100 ) );
strUnit = GetSize((UINT64&)TotalBytesTransferred.QuadPart);
CString strUnit2 = GetSize((UINT64&)TotalFileSize.QuadPart);
pArgs->pProgressDlg->m_DataCopiedCurrent.Format(L"Copied %I64u %s of %I64u %s (%i%%)...", TotalBytesTransferred.QuadPart, strUnit, TotalFileSize.QuadPart, strUnit2,
pArgs->pProgressDlg->m_ProgressCurrent.GetPos());
return PROGRESS_CONTINUE;
}
I don't know if it helps anything.