C#中,复制文件的操作十分简单,System.IO.File.Copy()。但是用过的同学都知道,这个方法在复制大文件的时候非常不好用,因为它会阻塞当期线程直到文件复制完毕,要终止也麻烦(把复制操作放到线程中,通过终止线程来终止操作)。如果能使用Explorer中复制文件时的对话框,就能直观的显示复制进度,并且能随时取消复制操作。
要实现Explorer中复制粘贴时的对话框,可以自己编写相关代码,使用异步读写文件字节流的方式来复制文件,这种方式我们今天就不讨论了;
另外一种方法,是使用Windows API SHFileOperation来达到目的;
定义API:
////// 映射API方法 /// /// /// [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)] private static extern int SHFileOperation(SHFILEOPSTRUCT lpFileOp); /// /// 多个文件路径的分隔符 /// private const string FILE_SPLITER = "\0"; /// /// Shell文件操作数据类型 /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private class SHFILEOPSTRUCT { public IntPtr hwnd; /// /// 设置操作方式 /// public wFunc wFunc; /// /// 源文件路径 /// public string pFrom; /// /// 目标文件路径 /// public string pTo; /// /// 允许恢复 /// public FILEOP_FLAGS fFlags; /// /// 监测有无中止 /// public bool fAnyOperationsAborted; public IntPtr hNameMappings; /// /// 设置标题 /// public string lpszProgressTitle; } /// /// 文件操作方式 /// private enum wFunc { /// /// 移动 /// FO_MOVE = 0x0001, /// /// 复制 /// FO_COPY = 0x0002, /// /// 删除 /// FO_DELETE = 0x0003, /// /// 重命名 /// FO_RENAME = 0x0004 } /// /// fFlags枚举值, /// 参见:http://msdn.microsoft.com/zh-cn/library/bb759795(v=vs.85).aspx /// private enum FILEOP_FLAGS { /// ///pTo 指定了多个目标文件,而不是单个目录 ///The pTo member specifies multiple destination files (one for each source file) rather than one directory where all source files are to be deposited. /// FOF_MULTIDESTFILES = 0x1, /// ///不再使用 ///Not currently used. /// FOF_CONFIRMMOUSE = 0x2, /// ///不显示一个进度对话框 ///Do not display a progress dialog box. /// FOF_SILENT = 0x4, /// ///碰到有抵触的名字时,自动分配前缀 ///Give the file being operated on a new name in a move, copy, or rename operation if a file with the target name already exists. /// FOF_RENAMEONCOLLISION = 0x8, /// ///不对用户显示提示 ///Respond with "Yes to All" for any dialog box that is displayed. /// FOF_NOCONFIRMATION = 0x10, /// ///填充 hNameMappings 字段,必须使用 SHFreeNameMappings 释放 ///If FOF_RENAMEONCOLLISION is specified and any files were renamed, assign a name mapping object containing their old and new names to the hNameMappings member. /// FOF_WANTMAPPINGHANDLE = 0x20, /// ///允许撤销 ///Preserve Undo information, if possible. If pFrom does not contain fully qualified path and file names, this flag is ignored. /// FOF_ALLOWUNDO = 0x40, /// ///使用 *.* 时, 只对文件操作 ///Perform the operation on files only if a wildcard file name (*.*) is specified. /// FOF_FILESONLY = 0x80, /// ///简单进度条,意味着不显示文件名。 ///Display a progress dialog box but do not show the file names. /// FOF_SIMPLEPROGRESS = 0x100, /// ///建新目录时不需要用户确定 ///Do not confirm the creation of a new directory if the operation requires one to be created. /// FOF_NOCONFIRMMKDIR = 0x200, /// ///不显示出错用户界面 ///Do not display a user interface if an error occurs. /// FOF_NOERRORUI = 0x400, /// /// 不复制 NT 文件的安全属性 ///Do not copy the security attributes of the file. /// FOF_NOCOPYSECURITYATTRIBS = 0x800, /// /// 不递归目录 ///Only operate in the local directory. Don't operate recursively into subdirectories. /// FOF_NORECURSION = 0x1000, /// ///Do not move connected files as a group. Only move the specified files. /// FOF_NO_CONNECTED_ELEMENTS = 0x2000, /// ///Send a warning if a file is being destroyed during a delete operation rather than recycled. This flag partially overrides FOF_NOCONFIRMATION. /// FOF_WANTNUKEWARNING = 0x4000, /// ///Treat reparse points as objects, not containers. /// FOF_NORECURSEREPARSE = 0x8000, }
使用方法:
public static int Copy(string sourceFiles, string targetFiles) { SHFILEOPSTRUCT pm = new SHFILEOPSTRUCT(); pm.wFunc = wFunc.FO_COPY; //设置对话框标题,在win7中无效 pm.lpszProgressTitle = "复制文件"; pm.pFrom = sourceFiles; pm.pTo = targetFiles; pm.fFlags = FILEOP_FLAGS.FOF_NOCONFIRMATION | FILEOP_FLAGS.FOF_MULTIDESTFILES | FILEOP_FLAGS.FOF_ALLOWUNDO; return SHFileOperation(pm); }
返回值如果为0表示复制成功,非0表示出错,返回值代表意义可参看http://msdn.microsoft.com/zh-cn/library/bb762164(v=vs.85).aspx
这是复制一个文件的,那么如果需要复制多个文件呢?注意上面的pm.pFrom和pm.pTo这两个string属性,传入的字符串可以为多个文件,需要用’\0’作为两个文件路径的分隔符,最后要以’\0\0’(两个)作为结尾,然后还要注意pFrom和pTo文件个数应该一致;(关于使用’\0’分割,一开始我真是想到,试过用分号,逗号,回车符,发现都不行,后来还是看了MSDN才发现应该用’\0’的。)
如果你想要源为文件列表,目标为一个目录,那么,pTo就改成目录,同时要注意pm.fFlags就不要加上FILEOP_FLAGS.FOF_MULTIDESTFILES;
当然,SHFileOperation并不仅仅可以用于复制文件,从上面的wFunc枚举我们可以发现除了复制还有移动、删除、重命名的操作;使用方法也是大同小异。我将其封装于一个类中,并提供多种重载。
原来上传的附件有问题: ShellFileOperation.rar
请下载这个:ShellFileOperation(修正).rar 我封装的类里面使用到了IEnumerable