封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度。
封裝CopyFileEx函數,實現文件復制中的暫停,控速,獲取進度等。
我的第一篇博客
前段時間無意間想到如何控制文件復制過程的復制速度,并且能實時獲得復制進度。對于一個幾兆甚至更小的文件,調用API函數CopyFile(Ex)來復制,很快就能完成的。然而對于一個幾百兆的大文件來說,如果仍然采用調用同步CopyFileEx,那么函數將阻塞相當長的時間。并且對于大文件我更希望能知道復制的進度。為此,百度谷歌了很長時間(也曾在csdn發過帖子http://topic.csdn.net/u/20110730/16/8ebd7515-83f6-4868-8d7c-2d7a783cda8b.html在此感謝各位壇友的幫助),得知CopyFileEx這個函數能實現我的要求。因此用自己那點淺薄的C++知識對CopyFileEx函數進行了簡單的封裝,算是實現了自己的需求。今天把之前的代碼整理了下,寫在自己的csdn博客上,一來算是為自己整理下思路,鍛煉下自己的文字描述能力,二來,也想獲得各位前輩的指點,若能給那些和我當初一樣迷茫的童鞋一個參考,實乃萬幸。
想想這也是我自己的第一篇博客,菜鳥一個,不怕拍磚!吼吼!^_^?
CopyFileEx函數原型
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
);
前兩個參數很容易明白,既然是復制文件的函數肯定要有源文件和目標文件了。第三個參數是一個回調函數的地址,在復制過程中,每當復制完成一塊數據之后便調用一次,回調函數返回后再繼續復制過程。如果再回調函數中讓線程Sleep()一定的時間,便能減緩整個復制過程的速度,在回調函數中讓線程暫定也就能暫停整個復制過程了。第四個數lpData是傳給回調函數的參數,可以將在回調函數中需要修改的數據通過指針的方式傳入。lpCancel:函數在執行過程中會不斷的檢測它指向的數值,一旦為TRUE便會取消復制過程。因此,可以用它來實現復制的停止。最后一個參數指示函數執行過程中的一些其它行為,不是非常關心,這里不在贅述。
對于回調函數
WORD 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? LPVOID lpData
);
系統給我們傳入很多有用的數據。可以看到有文件長度,已經拷貝的字節數,每個數據塊的長度,回調原因,甚至包括源文件和目標文件的句柄(這里我對第3,4,5這個三個參數并不是十分理解,不過影響不大~)等等。lpData就是之前我們傳入的指針。很顯然,通過TotalBytesTransferred與TotalFileSize的比值我們就能知道復制進度。另外這個函數返回不同的值也有不同的作用。基本原理就是這樣。
為了能讓CopyFileEx不阻塞當前線程,我在類中創建新的線程來調用它,當CopyFileEx返回時通過PostMessage發送窗口消息來通知應用程序文件復制的結果。
要封裝成類,但是這里用到了兩個回調函數(線程回調函數和CopyFileEx的回調函數),而回調函數只能是全局函數,因此我將兩個回調函數都寫成類的靜態函數,為了能方便訪問類中的成員變量又將this指針傳給回調函數(此方法也是之前在網上找到的)。
好了,最后貼上代碼。(由于涉及到了多線程,對于多線程的同步還沒做,但是實際中貌似沒發現影響。還有其它眾多地方不太完善)。
/************************************************************************** 文件名:CopyFile.h 文件說明:類FileCopy頭文件 簡要說明:封裝CopyFileEx函數,實現文件復制過程的暫停,控速,異步與同步。創建新的線程,并在其中調用CopyFileEx函數,利用CopyFileEx的回調函數實現暫停,控制速度,獲取進度等功能。 完成日期:21:14 2011/10/4 備注:代碼不夠完善,沒能做好線程同步工作,有時間再去改進! **************************************************************************/#pragma once#define WM_COPYFILE_NOTIFY WM_USER+118+2 //自定義的windows消息,用來通知窗口class FileCopy { private:LPTSTR lpExistingPathName; //源文件LPTSTR lpNewPathName; //目標文件int iSpeedControl; //速度控制的變量BOOL bCancel; //取消標志,用來傳入CopyFileEx的回調函數HANDLE hEvent_Pause; //“復制暫停”事件float fCopyProgress; //復制進度HWND hNotifyWnd; //接受通知消息的窗口HANDLE hEvent_End; //“復制結束”事件HANDLE hThread_Copy; //線程句柄LARGE_INTEGER totalFileSize; //總的文件長度 LARGE_INTEGER totalBytesTransferred; //已經復制的字節數int ret_PGR; //作為CopyProgressRoutine的返回值,此參數未用void Initialize(); //初始化內部數據:各種句柄和變量;//線程函數,在線程中調用CopyFileEx實現文件復制static DWORD WINAPI ThreadProc_Copy(LPVOID lpParam);//CopyFileEx的回調函數,在此函數中實現文件復制過程的控制。static 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);public:FileCopy(void);//可以在構造函數中初始化數據FileCopy(LPTSTR lpExistingPathName,LPTSTR lpNewPathName,HWND hNotifyWnd=NULL);~FileCopy(void);//初始化必要的參數,源文件和目標文件BOOL Init(LPTSTR lpExistingPathName,LPTSTR lpNewPathName,HWND hNotifyWnd=NULL);//開始拷貝過程BOOL Begin();//暫停復制void Pause();//恢復復制void Resume();//取消復制void Cancel();//停止復制//void Stop(); //Stop();結束復制過程,但保存已經復制的內容,Cancel();會刪除已復制的內容。//等待復制結束,用來實現“同步”效果,調用此函數后線程會阻塞,直到復制完成或取消。void WaitForEnd();//獲取復制進度float GetProgress();//獲取文件總大小,函數返回方式模仿 API GetFileSize(); 一般情況下超過4GB的文件不多//,lpFileSizeHigh直接忽視就行了DWORD GetTotalFileSize(DWORD* lpFileSizeHigh=NULL);//獲取已經復制的字節數;DWORD GetBytesTransferred(DWORD* lpTransferredHigh=NULL);//設置復制速度void SetSpeed(int iSpeed); };??/************************************************************************** 文件名:CopyFile.cpp 文件說明:類FileCopy實現文件,詳細信息見FileCopy.h文件 完成日期:21:14 2011/10/4 **************************************************************************/#include "StdAfx.h" #include "FileCopy.h"FileCopy::FileCopy(void) {Initialize(); }FileCopy::FileCopy(LPTSTR lpExistingPathName,LPTSTR lpNewPathName,HWND hNotifyWnd) {Init(lpExistingPathName,lpNewPathName,hNotifyWnd); }FileCopy::~FileCopy(void) { //這里貌似做的不夠好。。。。。-_-CloseHandle(hEvent_End);CloseHandle(hEvent_Pause);CloseHandle(hThread_Copy); }void FileCopy::Initialize() {bCancel=FALSE;hNotifyWnd=NULL;fCopyProgress=0;hEvent_Pause=NULL;iSpeedControl=-1;totalFileSize.HighPart=0;totalFileSize.LowPart=0;totalBytesTransferred.HighPart=0;totalBytesTransferred.LowPart=0;hThread_Copy=NULL;ret_PGR=PROGRESS_CONTINUE;//初始化 “復制結束”事件 手動重置 無信號if(hEvent_End!=NULL)CloseHandle(hEvent_End);hEvent_End=CreateEvent(NULL,TRUE,FALSE,NULL);//初始化 “復制暫停”事件, 手動重置 有信號狀態if(hEvent_Pause!=NULL)CloseHandle(hEvent_Pause);hEvent_Pause=CreateEvent(NULL,TRUE,TRUE,NULL); }BOOL FileCopy::Init(LPTSTR lpExistingPathName,LPTSTR lpNewPathName,HWND hNotifyWnd/* =NULL */) {Initialize();this->lpExistingPathName=lpExistingPathName;this->lpNewPathName=lpNewPathName;this->hNotifyWnd=hNotifyWnd;HANDLE hFile=CreateFile(lpExistingPathName,GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);if(INVALID_HANDLE_VALUE==hFile)return FALSE;if(!GetFileSizeEx(hFile,&totalFileSize))return FALSE;return TRUE; }BOOL FileCopy::Begin() {//在線程中調用CopyFileEx函數,為了保持類的封裝性,//線程函數被寫成類的靜態成員函數,此處傳入this指針為了訪問成員變量//CopyFileEx的回調函數也是類似于這樣實現的。hThread_Copy=CreateThread(NULL,0,ThreadProc_Copy,this,0,NULL);if(NULL==hThread_Copy){return FALSE;}return TRUE; }DWORD WINAPI FileCopy::ThreadProc_Copy(LPVOID lpParam) {//獲得當前類的實例中的相關數據HWND hNotifyWnd=((FileCopy*)lpParam)->hNotifyWnd;LPTSTR lpExistingPathName=((FileCopy*)lpParam)->lpExistingPathName;LPTSTR lpNewPathName=((FileCopy*)lpParam)->lpNewPathName;//調用核心API函數CopyFileEx來復制文件BOOL bSucceed=CopyFileEx(lpExistingPathName,lpNewPathName,CopyProgressRoutine,lpParam,&(((FileCopy*)lpParam)->bCancel),COPY_FILE_ALLOW_DECRYPTED_DESTINATION|COPY_FILE_COPY_SYMLINK|COPY_FILE_FAIL_IF_EXISTS);//拷貝結束,向窗口發送通知消息;if(hNotifyWnd!=NULL){if(bSucceed){PostMessage(hNotifyWnd,WM_COPYFILE_NOTIFY,1,(LPARAM)lpExistingPathName);}else{PostMessage(hNotifyWnd,WM_COPYFILE_NOTIFY,0,(LPARAM)lpExistingPathName);}}//將“拷貝結束”事件設置成信號狀態SetEvent(((FileCopy*)lpParam)->hEvent_End);return 0; }DWORD CALLBACK FileCopy::CopyProgressRoutine(LARGE_INTEGER TotalFileSize,LARGE_INTEGER TotalBytesTransferred,LARGE_INTEGER StreamSize,LARGE_INTEGER StreamBytesTransferred,DWORD dwStreamNumber,DWORD dwCallbackReason,HANDLE hSourceFile,HANDLE hDestinationFile,LPVOID lpData) { //保存文件長度和已經復制的數據量((FileCopy*)lpData)->totalFileSize=TotalFileSize;((FileCopy*)lpData)->totalBytesTransferred=TotalBytesTransferred;//計算復制進度((FileCopy*)lpData)->fCopyProgress=TotalBytesTransferred.QuadPart*1.0/TotalFileSize.QuadPart;//通過事件對象實現暫停;WaitForSingleObject(((FileCopy*)lpData)->hEvent_Pause,INFINITE);//通過Sleep()來控制復制速度int iSpeed=((FileCopy*)lpData)->iSpeedControl;if(iSpeed>=0)Sleep(iSpeed); //返回0,繼續復制,以通過bCancel控制復制結束,此返回值暫時未用return PROGRESS_CONTINUE; }void FileCopy::Pause() {ResetEvent(hEvent_Pause); }void FileCopy::Resume() {SetEvent(hEvent_Pause); }void FileCopy::Cancel() {bCancel=TRUE;Resume(); //恢復暫停狀態,讓線程自然結束! }void FileCopy::WaitForEnd() {WaitForSingleObject(hEvent_End,INFINITE); }float FileCopy::GetProgress() {return fCopyProgress; }DWORD FileCopy::GetTotalFileSize(DWORD* lpFileSizeHigh) {if(lpFileSizeHigh)*lpFileSizeHigh=totalFileSize.HighPart;return totalFileSize.LowPart; }DWORD FileCopy::GetBytesTransferred(DWORD* lpTransferredHigh) {if(lpTransferredHigh)*lpTransferredHigh=totalBytesTransferred.HighPart;return totalBytesTransferred.LowPart; }void FileCopy::SetSpeed(int iSpeed) {iSpeedControl=iSpeed; //每次線程Sleep()的時間不超過1000msif(iSpeedControl>1000)iSpeedControl=1000; }
更正:代碼中的LPTSTR變量類型應改成LPCTSTR,否者不能傳入CString類型參數。
更正后的下載地址
http://download.csdn.net/detail/career2011/3657624
?
?
?
轉載于:https://www.cnblogs.com/canghai118/archive/2011/10/04/3034251.html
總結
以上是生活随笔為你收集整理的封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度。的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: TP-Link TL-WR2041N V
- 下一篇: 腾讯web前端招聘条件汇总