// FileCopy.cpp: implementation of the CFileCopy class. // ////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "FileCopyDemo.h" #include "FileCopy.h" #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #define new DEBUG_NEW #endif #include #include #include ////////////////////////////////////////////////////////////////////// // Construction/Destruction ////////////////////////////////////////////////////////////////////// class CAutoLock { private: LPCRITICAL_SECTION m_pcsLcok; public: CAutoLock(LPCRITICAL_SECTION pcsLcok) { m_pcsLcok = pcsLcok; if (m_pcsLcok) { EnterCriticalSection(m_pcsLcok); } } ~CAutoLock() { if (m_pcsLcok) { LeaveCriticalSection(m_pcsLcok); m_pcsLcok = NULL; } } }; class CStopwatch { public: CStopwatch() { QueryPerformanceFrequency(&m_liPerfFreq); Start(); } void Start() { QueryPerformanceCounter(&m_liPerfStart); } __int64 Now() const { LARGE_INTEGER liPerfNow; QueryPerformanceCounter(&liPerfNow); return (liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000 / m_liPerfFreq.QuadPart; } __int64 NowInMicro() const { LARGE_INTEGER liPerfNow; QueryPerformanceCounter(&liPerfNow); return (liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000000 / m_liPerfFreq.QuadPart; } private: LARGE_INTEGER m_liPerfFreq; //counter per second LARGE_INTEGER m_liPerfStart; //starting count }; CFileCopy::CFileCopy() { m_bCancel = FALSE; m_dw64TotalFileSize = 0; m_dw64TotalBytesTransferred = 0; m_bIsCoping = FALSE; m_hPauseEvent = NULL; m_hFeedbackExitEvent = NULL; m_hAsyncCopyThreadHandle = NULL; m_hFeedbackProgressThreadHandle = NULL; m_pUserData = NULL; m_pAsyncCopyResultCB = NULL; m_pCopingProgressCB = NULL; m_dwProgressFeedbackTime = 0; InitializeCriticalSection(&m_csLock); } CFileCopy::~CFileCopy() { Cancel(); DeleteCriticalSection(&m_csLock); } BOOL CFileCopy::CopyFile(IN LPCTSTR lpSrcFileName, IN LPCTSTR lpDesFileName, IN const BOOL bSynchronousCopy, IN const DWORD dwProgressFeedbackTime, OUT DOUBLE &dbSpeed, OUT tstring &strErrorMsg) { { CAutoLock autolock(&m_csLock); if (m_bIsCoping) { strErrorMsg = _T("In coping."); return FALSE; } m_bIsCoping = TRUE; } // 同步复制 if (bSynchronousCopy) { m_dwProgressFeedbackTime = dwProgressFeedbackTime; BOOL bRet = SynchronousCopyFile( lpSrcFileName, lpDesFileName, dwProgressFeedbackTime, dbSpeed, strErrorMsg ); m_bIsCoping = FALSE; return bRet; } // 异步复制 m_strSrcFile = lpSrcFileName; m_strDesFile = lpDesFileName; m_dwProgressFeedbackTime = dwProgressFeedbackTime; unsigned threadID = 0; m_hAsyncCopyThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &AsyncCopyThreadFunc, this, 0, &threadID); if (NULL == m_hAsyncCopyThreadHandle) { strErrorMsg = GetLastErrorMsg(_T("_beginthreadex"), GetLastError()); return FALSE; } return TRUE; } BOOL CFileCopy::SynchronousCopyFile(LPCTSTR lpSrcFileName, LPCTSTR lpDesFileName, const DWORD dwProgressFeedbackTime, DOUBLE &dbSpeed, tstring &strErrorMsg) { m_bCancel = FALSE; m_dw64TotalFileSize = 0; m_dw64TotalBytesTransferred = 0; if (NULL == (m_hPauseEvent = CreateEvent(NULL, TRUE, TRUE, NULL))) { strErrorMsg = GetLastErrorMsg(_T("CreateEvent"), GetLastError()); return FALSE; } if (0 != dwProgressFeedbackTime) { if (NULL == (m_hFeedbackExitEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) { strErrorMsg = GetLastErrorMsg(_T("CreateEvent"), GetLastError()); CloseAllHandles(); return FALSE; } unsigned threadID = 0; m_hFeedbackProgressThreadHandle = (HANDLE)_beginthreadex(NULL, 0, &FeedbackProgressThreadFunc, this, 0, &threadID); if (NULL == m_hFeedbackProgressThreadHandle) { strErrorMsg = GetLastErrorMsg(_T("_beginthreadex"), GetLastError()); CloseAllHandles(); return FALSE; } } CStopwatch stopwatch; BOOL bRet = CopyFileEx(lpSrcFileName, lpDesFileName, CopyProgressRoutine, this, &m_bCancel, COPY_FILE_ALLOW_DECRYPTED_DESTINATION | COPY_FILE_FAIL_IF_EXISTS); if (!bRet) { strErrorMsg = GetLastErrorMsg(_T("CopyFileEx"), GetLastError()); } else { // 成功了才需计算速度 __int64 n64ElapseTimeInMs = stopwatch.Now(); dbSpeed = (DOUBLE)(__int64)m_dw64TotalFileSize / n64ElapseTimeInMs; } if (0 != dwProgressFeedbackTime) { SetEvent(m_hFeedbackExitEvent); WaitForSingleObject(m_hFeedbackProgressThreadHandle, INFINITE); } CloseAllHandles(); return bRet; } DWORD CALLBACK CFileCopy::CopyProgressRoutine( LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER StreamSize, LARGE_INTEGER StreamBytesTransferred, DWORD dwStreamNumber, DWORD dwCallbackReason, HANDLE hSourceFile, HANDLE hDestinationFile, LPVOID lpData ) { CFileCopy *pFileCopy = (CFileCopy *)lpData; if (!pFileCopy) { return PROGRESS_CANCEL; } pFileCopy->m_dw64TotalFileSize = TotalFileSize.QuadPart; pFileCopy->m_dw64TotalBytesTransferred = TotalBytesTransferred.QuadPart; // 用于控制暂停 WaitForSingleObject(pFileCopy->m_hPauseEvent, INFINITE); return PROGRESS_CONTINUE; } tstring CFileCopy::GetLastErrorMsg(LPCTSTR lpszFunction, const DWORD dwLastError) { LPVOID lpMsgBuf = NULL; LPVOID lpDisplayBuf = NULL; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dwLastError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); StringCchPrintf((LPTSTR)lpDisplayBuf, LocalSize(lpDisplayBuf) / sizeof(TCHAR), TEXT("%s failed with error %d: %s"), lpszFunction, dwLastError, lpMsgBuf); tstring strLastError = (LPTSTR)lpDisplayBuf; LocalFree(lpMsgBuf); LocalFree(lpDisplayBuf); return strLastError; } void CFileCopy::CloseAllHandles() { if (m_hPauseEvent) { CloseHandle(m_hPauseEvent); m_hPauseEvent = NULL; } if (m_hFeedbackExitEvent) { CloseHandle(m_hFeedbackExitEvent); m_hFeedbackExitEvent = NULL; } if (m_hAsyncCopyThreadHandle) { CloseHandle(m_hAsyncCopyThreadHandle); m_hAsyncCopyThreadHandle = NULL; } if (m_hFeedbackProgressThreadHandle) { CloseHandle(m_hFeedbackProgressThreadHandle); m_hFeedbackProgressThreadHandle = NULL; } } void CFileCopy::Pause() { if (m_hPauseEvent) { ResetEvent(m_hPauseEvent); } } void CFileCopy::Resume() { if (m_hPauseEvent) { SetEvent(m_hPauseEvent); } } void CFileCopy::Cancel() { m_bCancel = TRUE; Resume(); } unsigned CFileCopy::AsyncCopy() { unsigned usRet = 1; DOUBLE dbSpeed = 0.0; tstring strErrorMsg; BOOL bCopySuccess = SynchronousCopyFile( m_strSrcFile.c_str(), m_strDesFile.c_str(), m_dwProgressFeedbackTime, dbSpeed, strErrorMsg ); if (m_pAsyncCopyResultCB) { m_pAsyncCopyResultCB(m_pUserData, bCopySuccess, dbSpeed, strErrorMsg); } m_bIsCoping = FALSE; return usRet; } unsigned CFileCopy::AsyncCopyThreadFunc(void* pArguments) { CFileCopy *pThis = (CFileCopy *)pArguments; if (NULL == pThis) { _endthreadex (1); return 1; } unsigned usRet = pThis->AsyncCopy(); _endthreadex (usRet); return usRet; } unsigned CFileCopy::FeedbackProgress() { unsigned usRet = 1; DWORD64 dw64TotalBytesTransferred = 0; DWORD64 dw64TotalBytesTransferredBak = 0; DOUBLE dbSpeed = 0.0; DWORD dwPercentage = 0; __int64 n64ElapseTimeInMs = 0; __int64 n64UsedTimeInMs = 0; __int64 n64NowInMs = 0; CStopwatch stopwatch; while (WAIT_TIMEOUT == WaitForSingleObject(m_hFeedbackExitEvent, m_dwProgressFeedbackTime)) { n64NowInMs = stopwatch.Now(); dw64TotalBytesTransferred = m_dw64TotalBytesTransferred; n64ElapseTimeInMs = n64NowInMs - n64UsedTimeInMs; n64UsedTimeInMs = n64NowInMs; if (0 == n64ElapseTimeInMs) { dbSpeed = 0.0; } else { dbSpeed = (DOUBLE)(__int64)(dw64TotalBytesTransferred - dw64TotalBytesTransferredBak) / n64ElapseTimeInMs; } dw64TotalBytesTransferredBak = dw64TotalBytesTransferred; if (0 == m_dw64TotalFileSize) { dwPercentage = 0; } else { dwPercentage = m_dw64TotalBytesTransferred * 100 / m_dw64TotalFileSize; } if (m_pCopingProgressCB) { m_pCopingProgressCB(m_pUserData, dbSpeed, dwPercentage); } } return usRet; } unsigned CFileCopy::FeedbackProgressThreadFunc(void* pArguments) { CFileCopy *pThis = (CFileCopy *)pArguments; if (NULL == pThis) { _endthreadex (1); return 1; } unsigned usRet = pThis->FeedbackProgress(); _endthreadex (usRet); return usRet; }