实际需求介绍:
由于本系统将采用光盘随书分发,故写了个光盘运行菜单程序,使用户可以选择安装和快捷浏览部分文件。本来这个菜单程序功能也没什么,很容易实现。谁知最后客户突然要求加个菜单项来拷贝音频文件,这就麻烦了,音频文件有600MB,使用CopyFile函数肯定不行,这么大肯定需要一些时间,这么长时间总不能不给点提示让用户干等吧!而我又是一个很懒的人,不想自己去实现进度显示功能,很想调用Windows的右键菜单复制功能来实现。 【注1】
没法子,谁让“客户”是上帝呢!只有“加班”查找资料了!功夫不负有心人,让我看到了外壳操作函数SHFileOperation可以实现上述功能,于是赶紧学习了下其使用方法。
MSDN上SHFileOperation函数原型如下:
int SHFileOperation( __in LPSHFILEOPSTRUCT lpFileOp );
(1) 该函数的功能是 复制、移动、重命名、删除 一个文件系统对象。【注2】
(2) 参数lpFileOp是一个指向SHFILEOPSTRUCT结构体的指针,该参数不能为空,SHFILEOPSTRUCT结构体包含了该函数执行指定操作所需的信息。
(3) 返回值:成功返回0,否则返回非0。【注3】
MSDN上SHFILEOPSTRUCT结构体定义如下:
typedef struct _SHFILEOPSTRUCT {
HWND hwnd;
UINT wFunc;
LPCTSTR pFrom;
LPCTSTR pTo;
FILEOP_FLAGS fFlags;
BOOL fAnyOperationsAborted;
LPVOID hNameMappings;
LPCTSTR lpszProgressTitle;
} SHFILEOPSTRUCT, *LPSHFILEOPSTRUCT;
各成员含义如下:
(1) hwnd 显示信息的对话框的窗口句柄
(2) wFunc 执行的操作,可取以下值。
可取的值 |
将执行的操作 |
FO_COPY |
将pFrom指定的文件复制到pTo |
FO_DELETE |
删除pFrom指定的文件 |
FO_MOVE |
将pFrom指定的文件移动到pTo |
FO_RENAME |
重命名pFrom指定的单个文件 |
(3) pFrom 指定要操作的文件。该成员是一个路径,最好使用完整路径,在路径的最后文件名部分可以使用通配符,如“*”等,进行指定多个文件。虽然该成员申明的类型是LPCTSTR,一个以单个NULL字符(即\0)终结的字符串,但实际上是一个可以保存多个以NULL字符终结的文件名的缓冲,该缓冲在最后再加一个NULL字符表示结尾。
(4) pTo 指示目的文件夹(目录)或目的文件。该成员如果不用(如当wFunc=FO_DELETE时)必须设为NULL。和pFrom一样,该成员也是一个以两个NULL字符结尾的字符串,但该成员不能使用通配符。对于复制与移动操作,如果pTo指定的目录不存在,将显示一个询问是否创建新目录的对话框,要禁止显示该询问对话框而直接创建,请设置FOF_NOCONFIRMMKDIR到fFlags成员。如果fFlags中含有FOF_MULTIDESTFILES,pTo缓冲中可含多个文件名。
(5) fFlag 控制文件操作标志。可取下列值通过“|”结合。
值 |
说明 |
FOF_ALLOWUNDO |
允许撤消,允许放回回收站。如果pFrom不包含完全限定路径和文件名,该标志将被忽视 |
FOF_CONFIRMMOUSE |
(未使用) |
FOF_FILESONLY |
pFrom的文件名部分含有通配符时只操作文件 |
FOF_MULTIDESTFILES |
pTo指定多个目的文件而不是一个目的文件夹(目录) |
FOF_NOCONFIRMATION |
以“全部是”自动响应所有显示对话框 |
FOF_NOCONFIRMMKDIR |
不询问是否创建新目录(当操作需要创建新目录时自动创建) |
FOF_NO_CONNECTED_ELEMENTS |
不移动关联文件(5.0版本) |
FOF_NOCOPYSECURITYATTRIBS |
不复制属性,目的文件从新文件夹获取安全属性(4.71版本) |
FOF_NOERRORUI |
当出错时不显示对话框 |
FOF_NORECURSEREPARSE |
(未使用) |
FOF_NORECURSION |
执行操作只针对当前目录,不递归针对子文件夹 |
FOF_NO_UI |
后台执行操作,不显示界面和所有对话框,相当于FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NOCONFIRMMKDIR。(6.0.6060版本——Windows Vista) |
FOF_RENAMEONCOLLISION |
当文件名冲突时执行重命名操作 |
FOF_SILENT |
不显示进度对话框界面 |
FOF_SIMPLEPROGRESS |
示进度对话框界面,但不显示当前正在操作的文件(简单模式) |
FOF_WANTMAPPINGHANDLE |
当已指定FOF_RENAMEONCOLLISION和所以文件已被重命名,分配一个包含新旧文件名的名称映射对象给hNameMappings成员,当不再使用该对象时,需用SHFreeNameMappings释放该对象 |
FOF_WANTNUKEWARNING |
当文件被永久删除而非删除到回收站时,给出一个警告(5.0版本) |
(6) fAnyOperationsAborted 如果执行的操作在其完成前,人为取消退出或系统默认选择取消退出,则该成员被设置为True,否则为Flase。
(7) hNameMappings 一个名称映射对象句柄,该对象包含重命名文件的新旧文件名称。只有在fFlags成员含有FOF_WANTMAPPINGHANDLE标志时,该成员才可用。
(8) lpszProgressTitle 进度对话框界面的标题,只有在fFlags成员含有FOF_SIMPLEPROGRESS标志时,该成员才有效。
在使用SHFILEOPSTRUCT结构体给SHFileOperation传参之前,有必要了解MSDN上关于该结构体解释说明的Remarks部分http://msdn.microsoft.com/en-us/library/bb759795(v=VS.85).aspx。
通过以上的介绍,利用SHFileOperation函数很容易实现复制文件时显示进度对话框的功能。Delphi源代码如下:
{******************************************************************} { FileOperationDialog unit } { (文件操作对话框单元) } { } { Author:tht QQ:245806497 } { Blog:http://blog.csdn.net/tht2009 } { Copyright(c) 2011 THT } { 2011年9月 } {******************************************************************} {(1)dlgCopyFiles(FromPath,ToPath:string) 复制同目录下的文件 } { } {******************************************************************} unit FileOPDlg; interface uses ShellAPI; function dlgCopyFiles(FromPath,ToPath:string):Integer; implementation //******************************************************************** //功能:将FromPath路径指定的文件拷贝到ToPath指定路径下 //参数:(1)FromPath,一个完全限定的文件路径,指定源操作文件位置 // (2)ToPath,目的文件位置,如果ToPath指定的目录不存在将自动创建它 //返回值: 1,复制操作成功完成 // 0,复制操作被取消 // -1,复制操作执行失败 // // 说明:ToPath中不能含有通配符 // 若FromPath指定一个文件,则ToPath可以指定一个目录或一个将创建的 // 新文件;若FromPath指定一个目录或含有通配符的路径,即使ToPath指 // 定一个新文件,ToPath也将被解析为一个目录(目录名即为新文件名)。 //********************************************************************* function dlgCopyFiles(FromPath,ToPath:string):Integer; var lpFileOP:SHFileOpStruct; begin lpFileOP.Wnd:=0; lpFileOP.wFunc:=FO_COPY; lpFileOP.pFrom:=PWideChar(FromPath+#0); lpFileOP.pTo:=PWideChar(ToPath+#0); //自动创建目录 lpFileOP.fFlags:=FOF_NOCONFIRMMKDIR; Result:=SHFileOperation(lpFileOP); if(lpFileOP.fAnyOperationsAborted)then Result:=0 else begin if(Result=0)then Result:=1 else Result:=-1; end; end; end.
[1]:虽然大小只有600MB,但文件数量有两万多个,而且文件分布无规律,如果要达到系统复制的界面效果,一个个去枚举文件,我估计也不是件简单的事。
[2]:该函数在Windows Vista 上已被IFileOperation接口替代,使用后者能获得更强大的功能。有关IFileOperation 接口的详细资料请参见MSDN的介绍http://msdn.microsoft.com/en-us/library/bb775771(v=VS.85).aspx 和http://blog.csdn.net/tht2009/article/details/6787271。
[3]:更多关于SHFileOperation函数返回值的说明请参见http://msdn.microsoft.com/en-us/library/bb762164(v=VS.85).aspx。