BOOL CopyFile(LPCTSTR lpExistingFileName,LPCTSTR lpNewFileName,BOOL bFailIfExists );
说明
Long,非零表示成功,零表示失败。
参数类型及说明 :
lpExistingFileName String:输入参数,已经存在的所需复制文件的源路径。
lpNewFileName String:输入参数,新文件路径,复制文件的目的路径。
bFailIfExists Long:输入参数,指明如果在目的路径存在文件时是否覆盖。如果设为TRUE(非零),将不覆盖已经存在的文件;如果存在,则返回失败。
返回值:
返回BOOL值,表示文件复制是否成功。
function CopyFileEx( lpExistingFileName: PWideChar; { 源文件 } lpNewFileName: PWideChar; { 新的目标文件 } lpProgressRoutine: TFNProgressRoutine; { 回调函数; 每复制 64K 调用一次 } lpData: Pointer; { 给回调函数的参数 } pbCancel: PBool; { 是个布尔值指针; True 是取消复制 } dwCopyFlags: DWORD { 复制选项; 下面有补充... } ): BOOL; stdcall; { 返回成功或失败 } //dwCopyFlags(复制选项): COPY_FILE_FAIL_IF_EXISTS = $00000001; { 如果目标存在则失败返回 } COPY_FILE_RESTARTABLE = $00000002; { 若失败则重新开始 } //CopyFileEx 使用的回调函数: function ProgressRoutine( TotalFileSize: LARGE_INTEGER; { 文件总字节数 } TotalBytesTransferred: LARGE_INTEGER; { 已复制的字节数 } StreamSize: LARGE_INTEGER; { 当前流的字节数 } StreamBytesTransferred: LARGE_INTEGER; { 当前流已拷贝的字节数 } dwStreamNumber: DWORD; { 当前流序号 } dwCallbackReason: DWORD; { 回调函数的状态; 下面有补充... } hSourceFile: THANDLE; { 源文件句柄 } hDestinationFile: THANDLE; { 目标文件句柄 } lpData: Pointer { CopyFileEx 传递的参数指针 } ): DWORD; stdcall; { 返回值; 下面有补充... } //dwCallbackReason(回调函数的状态): CALLBACK_CHUNK_FINISHED = $00000000; { 复制进行中 } CALLBACK_STREAM_SWITCH = $00000001; { 准备开始} //回调函数可以使用的返回值: PROGRESS_CONTINUE = 0; { 继续 } PROGRESS_CANCEL = 1; { 取消 } PROGRESS_STOP = 2; { 暂停 } PROGRESS_QUIET = 3; { 终止回调, 但不停止复制 } 一个实现复制进度的测试, 测试前在窗体上放个 TProgressBar: //回调函数; 为了运算我把其中的 LARGE_INTEGER 类型改成 Int64 了 function ProgressRoutine(TotalFileSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred: Int64; dwStreamNumber, dwCallbackReason: DWORD; hSourceFile, hDestinationFile: THANDLE; lpData: Pointer): DWORD; stdcall; begin Form1.ProgressBar1.Position := Trunc(TotalBytesTransferred / TotalFileSize * 100); Application.ProcessMessages; Result := PROGRESS_CONTINUE; end; //复制 procedure TForm1.Button1Click(Sender: TObject); const s = 'C:\Temp\Test.rar'; d = 'C:\Temp\NewDir\Test.rar'; begin Assert(FileExists(s), '源文件不存在'); Assert(DirectoryExists(ExtractFilePath(d)), '目标路径不存在'); CopyFileEx(PChar(s), PChar(d), @ProgressRoutine, nil, nil, COPY_FILE_RESTARTABLE); end; |
封装CopyFileEx函数,实现文件复制中的暂停,控速,获取进度等。
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指针传给回调函数(此方法也是之前在网上找到的)。
好了,最后贴上代码。(由于涉及到了多线程,对于多线程的同步还没做,但是实际中貌似没发现影响。还有其它众多地方不太完善)。
view plain
/**************************************************************************
文件名: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);
};
view plain
view plain
view plain
/**************************************************************************
文件名: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;
}