您的位置:首页 > 运维架构

封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度。

2013-10-14 10:25 274 查看
本文转自:http://blog.csdn.net/career2011/article/details/6844513

封装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指针传给回调函数(此方法也是之前在网上找到的)。

好了,最后贴上代码。(由于涉及到了多线程,对于多线程的同步还没做,但是实际中貌似没发现影响。还有其它众多地方不太完善)。

[cpp] view
plaincopy

/************************************************************************** 

文件名: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);  

};  

[cpp] view
plaincopy

   

[cpp] view
plaincopy

   

[cpp] view
plaincopy

/************************************************************************** 

文件名: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()的时间不超过1000ms  

    if(iSpeedControl>1000)  

        iSpeedControl=1000;  

}  

更正:代码中的LPTSTR变量类型应改成LPCTSTR,否者不能传入CString类型参数。

更正后的下载地址

http://download.csdn.net/detail/career2011/3657624

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: