C#中使用SHFileOperation调用Windows的复制文件对话框

C#中,复制文件的操作十分简单,System.IO.File.Copy()。但是用过的同学都知道,这个方法在复制大文件的时候非常不好用,因为它会阻塞当期线程直到文件复制完毕,要终止也麻烦(把复制操作放到线程中,通过终止线程来终止操作)。如果能使用Explorer中复制文件时的对话框,就能直观的显示复制进度,并且能随时取消复制操作。

要实现Explorer中复制粘贴时的对话框,可以自己编写相关代码,使用异步读写文件字节流的方式来复制文件,这种方式我们今天就不讨论了;

另外一种方法,是使用Windows API SHFileOperation来达到目的;

定义API: 

        /// <summary>

        /// 映射API方法

        /// </summary>

        /// <param name="lpFileOp"></param>

        /// <returns></returns>

        [DllImport("shell32.dll", SetLastError = true, CharSet = CharSet.Unicode)]

        private static extern int SHFileOperation(SHFILEOPSTRUCT lpFileOp);

 

        /// <summary>

        /// 多个文件路径的分隔符

        /// </summary>

        private const string FILE_SPLITER = "\0";

 

        /// <summary>

        /// Shell文件操作数据类型

        /// </summary>

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]

        private class SHFILEOPSTRUCT

        {

            public IntPtr hwnd;

            /// <summary>

            /// 设置操作方式

            /// </summary>

            public wFunc wFunc;

            /// <summary>

            /// 源文件路径

            /// </summary>

            public string pFrom;

            /// <summary>

            /// 目标文件路径

            /// </summary>

            public string pTo;

            /// <summary>

            /// 允许恢复

            /// </summary>

            public FILEOP_FLAGS fFlags;

            /// <summary>

            /// 监测有无中止

            /// </summary>

            public bool fAnyOperationsAborted;

            public IntPtr hNameMappings;

            /// <summary>

            /// 设置标题

            /// </summary>

            public string lpszProgressTitle;

        }

 

        /// <summary>

        /// 文件操作方式

        /// </summary>

        private enum wFunc

        {

            /// <summary>

            /// 移动

            /// </summary>

            FO_MOVE = 0x0001,

            /// <summary>

            /// 复制

            /// </summary>

            FO_COPY = 0x0002,

            /// <summary>

            /// 删除

            /// </summary>

            FO_DELETE = 0x0003,

            /// <summary>

            /// 重命名

            /// </summary>

            FO_RENAME = 0x0004

        }

 

        /// <summary>

        /// fFlags枚举值,

        /// 参见:http://msdn.microsoft.com/zh-cn/library/bb759795(v=vs.85).aspx

        /// </summary>

        private enum FILEOP_FLAGS

        {

 

            ///<summary>

            ///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.

            ///</summary>

            FOF_MULTIDESTFILES = 0x1,

            ///<summary>

            ///不再使用

            ///Not currently used.

            ///</summary>

            FOF_CONFIRMMOUSE = 0x2,

            ///<summary>

            ///不显示一个进度对话框

            ///Do not display a progress dialog box.

            ///</summary>

            FOF_SILENT = 0x4,

            ///<summary>

            ///碰到有抵触的名字时,自动分配前缀

            ///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.

            ///</summary>

            FOF_RENAMEONCOLLISION = 0x8,

            ///<summary>

            ///不对用户显示提示

            ///Respond with "Yes to All" for any dialog box that is displayed.

            ///</summary>

            FOF_NOCONFIRMATION = 0x10,

            ///<summary>

            ///填充 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.

            ///</summary>

            FOF_WANTMAPPINGHANDLE = 0x20,

            ///<summary>

            ///允许撤销

            ///Preserve Undo information, if possible. If pFrom does not contain fully qualified path and file names, this flag is ignored.

            ///</summary>

            FOF_ALLOWUNDO = 0x40,

            ///<summary>

            ///使用 *.* 时, 只对文件操作

            ///Perform the operation on files only if a wildcard file name (*.*) is specified.

            ///</summary>

            FOF_FILESONLY = 0x80,

            ///<summary>

            ///简单进度条,意味着不显示文件名。

            ///Display a progress dialog box but do not show the file names.

            ///</summary>

            FOF_SIMPLEPROGRESS = 0x100,

            ///<summary>

            ///建新目录时不需要用户确定

            ///Do not confirm the creation of a new directory if the operation requires one to be created.

            ///</summary>

            FOF_NOCONFIRMMKDIR = 0x200,

            ///<summary>

            ///不显示出错用户界面

            ///Do not display a user interface if an error occurs.

            ///</summary>

            FOF_NOERRORUI = 0x400,

            ///<summary>

            /// 不复制 NT 文件的安全属性

            ///Do not copy the security attributes of the file.

            ///</summary>

            FOF_NOCOPYSECURITYATTRIBS = 0x800,

            ///<summary>

            /// 不递归目录

            ///Only operate in the local directory. Don't operate recursively into subdirectories.

            ///</summary>

            FOF_NORECURSION = 0x1000,

            ///<summary>

            ///Do not move connected files as a group. Only move the specified files.

            ///</summary>

            FOF_NO_CONNECTED_ELEMENTS = 0x2000,

            ///<summary>

            ///Send a warning if a file is being destroyed during a delete operation rather than recycled. This flag partially overrides FOF_NOCONFIRMATION.

            ///</summary>

            FOF_WANTNUKEWARNING = 0x4000,

            ///<summary>

            ///Treat reparse points as objects, not containers.

            ///</summary>

            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<T>.Count(),这个需要using System.Linq;当然这个与主功能没关系,如果你使用Framework版本低于3.5,可以将IEnumerable<T>.Count()换成别的实现。感谢二楼“蜗牛往前走”的提示!

posted @ 2013-05-13 17:35  季风哥  阅读(2299)  评论(3编辑  收藏  举报