快捷搜索:

.NET 相关问题 : Windows Vista 中的 IFileOperation

本文示例源代码或素材下载

问:我有许多文件操作盼望作为批处置惩罚履行,并且盼望这些操作能应用标准的 Windows® 进度 UI。我知道可以应用 System.IO 命名空间中的类一一履行所有的文件操作,然则随后还必要创建自己的进度 UI,这就意味着我要完成的事情比想象中要繁重得多。我留意到 Windows Vista® 中包孕一个新的 IFileOperations 接口,然则没有看到任何示例先容若何从托管代码应用该接口。它是若何实现的?

问:我有许多文件操作盼望作为批处置惩罚履行,并且盼望这些操作能应用标准的 Windows® 进度 UI。我知道可以应用 System.IO 命名空间中的类一一履行所有的文件操作,然则随后还必要创建自己的进度 UI,这就意味着我要完成的事情比想象中要繁重得多。我留意到 Windows Vista® 中包孕一个新的 IFileOperations 接口,然则没有看到任何示例先容若何从托管代码应用该接口。它是若何实现的?

答:Windows Vista 实际上包括一个新的复制引擎,完全支持您想完成的事情。不过,可能之前已有的功能可以满意您的必要。例如,假如想复制、移动、重命名或删除单个文件或目录,可以使用 SHFileOperation(由 shell32.dll 供给),它已经过 Visual Basic® 运行库封装。假如应用的是 Visual Basic 2005,那么只需应用 My 命名空间中的功能即可,例如:

答:Windows Vista 实际上包括一个新的复制引擎,完全支持您想完成的事情。不过,可能之前已有的功能可以满意您的必要。例如,假如想复制、移动、重命名或删除单个文件或目录,可以使用 SHFileOperation(由 shell32.dll 供给),它已经过 Visual Basic® 运行库封装。假如应用的是 Visual Basic 2005,那么只需应用 My 命名空间中的功能即可,例如:

My.Computer.FileSystem.CopyDirectory(

sourcePath, destinationPath, UIOption.AllDialogs)

在 C# 中完成相同的义务则还必要多做一点事情,即添加对 Microsoft.VisualBasic.dll 的引用(从 Microsoft® .NET Framework 安装目录),并应用如下代码:

using Microsoft.VisualBasic.FileIO;

...

FileSystem.CopyDirectory(

sourcePath, destinationPath, UIOption.AllDialogs);

运行后会孕育发生进度 UI,该 UI 与从 Windows 资本治理器履行相同的文件操作时所看到的相同。事实上,在 Windows Vista 上运行时,会自动得到新的 Window Vista 进度 UI,如图 1 所示。

图 1Windows Vista 进度对话框

然则,SHFileOperation 和 .NET 所供给的包装只能对一个文件系统工具(可所以目录)进行操作,每次调用也只能进行一项操作(复制、移动、重命名或删除),而不能在同一操作内混杂搭配或操作不合的文件。这恰是 IFileOperation 发挥感化的地方。IFileOperation 是 SHFileOperation 的承袭者,供给了一系列的新功能,此中包括批处置惩罚大年夜量操作的能力,即可以混杂履行复制、重命名、移动、删除,以致是创建新项目等操作,并且它还可以对任何文件集履行操作,而不仅仅局限于同一目录。这些只是 IFileOperation 供给的很酷的功能中的一小部分,要想更深入懂得,请参阅 shellrevealed.com/blogs/shellblog/archive/2007/04/16/IFileOperation-_1320_-Part-1_3A00_-Introduction.aspx。

Figure3IFileOperationProgressSink

[ComImport]

[Guid("04b0f1a7-9490-44bc-96e1-4296a31252e2")]

[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]

public interface IFileOperationProgressSink

{

void StartOperations();

void FinishOperations(uint hrResult);

void PreRenameItem(uint dwFlags, IShellItem psiItem,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName);

void PostRenameItem(uint dwFlags, IShellItem psiItem,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName,

uint hrRename, IShellItem psiNewlyCreated);

void PreMoveItem(uint dwFlags, IShellItem psiItem, IShellItem

psiDestinationFolder,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName);

void PostMoveItem(uint dwFlags, IShellItem psiItem,

IShellItem psiDestinationFolder,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName,

uint hrMove, IShellItem psiNewlyCreated);

void PreCopyItem(uint dwFlags, IShellItem psiItem,

IShellItem psiDestinationFolder,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName);

void PostCopyItem(uint dwFlags, IShellItem psiItem,

IShellItem psiDestinationFolder,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName,

uint hrCopy, IShellItem psiNewlyCreated);

void PreDeleteItem(uint dwFlags, IShellItem psiItem);

void PostDeleteItem(uint dwFlags, IShellItem psiItem, uint hrDelete,

IShellItem psiNewlyCreated);

void PreNewItem(uint dwFlags, IShellItem psiDestinationFolder,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName);

void PostNewItem(uint dwFlags, IShellItem psiDestinationFolder,

[MarshalAs(UnmanagedType.LPWStr)] string pszNewName,

[MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName,

uint dwFileAttributes, uint hrNew, IShellItem psiNewItem);

void UpdateProgress(uint iWorkTotal, uint iWorkSoFar);

void ResetTimer();

void PauseTimer();

void ResumeTimer();

}

在深入评论争论若何直接从托管代码应用这些接口之前,我们先看一下在它们周围实现的包装。我的 FileOperation 类的概要如图 4 所示。作为应用此类的示例,让我们假设有一个 C:test 目录,此中包孕两个文件和一个目录:test1.txt、test2.txt 和 SampleDir。我想将第一个文件复制到 copy.text,删除第二个文件,然后将此目录移至 NewDir;我可以应用以下代码实现这个目标:

Figure4托管 FileOperation 包装

class FileOperation : IDisposable

{

public FileOperation() : this(null);

public FileOperation(FileOperationProgressSink callbackSink) :

this(callbackSink, null);

public FileOperation(

FileOperationProgressSink callbackSink, IWin32Window owner);

public void CopyItem(

string source, string destination, string newName);

public void MoveItem(

string source, string destination, string newName);

public void RenameItem(string source, string newName);

public void DeleteItem(string source);

public void NewItem(

string folderName, string name, FileAttributes attrs);

public void PerformOperations();

public void Dispose();

}

using(FileOperation fileOp = new FileOperation())

{

fileOp.CopyItem(@"C:testtest1.txt", @"C:test", @"copy.txt");

fileOp.DeleteItem(@"C:testtest2.txt");

fileOp.MoveItem(@"C:testSampleDir", @"C:test", @"NewDir");

fileOp.PerformOperations();

}

public void CopyItem(string source, string destination, string newName)

{

ThrowIfDisposed();

using (ComReleaser sourceItem =

CreateShellItem(source))

using (ComReleaser destinationItem =

CreateShellItem(destination))

{

_fileOperation.CopyItem(

sourceItem.Item, destinationItem.Item, newName, null);

}

}

先暂时轻忽 ThrowIfDisposed 和 ComReleaser,CopyItem 首先会为源路径创建 IShellItem 实例,为目标文件夹创建 IShellItem 实例,然后将它们(连同项目的新名称)通报到 IFileOperation 的 CopyItem 措施。FileOperation 上的所有操作措施都是经由过程上述措施实现,PerformOperations 只是委派给底层 IFileOperation 上的相同措施:

public void PerformOperations()

{

ThrowIfDisposed();

_fileOperation.PerformOperations();

}

CopyItem 实现中显示的 ComReleaser 类是一个小赞助器类,它可以让我们更轻松地应用 Marshal.FinalReleaseComObject 来处置惩罚 COM 工具(这些工具终极都邑被垃圾网络器清理,然则鉴于它们的创建频率,我选择的法子是确保不再应用它们时即将其处置惩罚掉落)。类型的构建函数吸收 COM 工具,并将其缓存到私有成员变量中;随后其 Dispose 措施会开释该工具。它还会公开 Item 属性,该属性会返回工具,这使得可以在上述类似的情形中轻松应用它。ComReleaser 如图 5 所示。

Figure5用于开释 COM 工具的赞助器类

现在所有剩下要做的便是 FileOperationProgressSink 实现。FileOperationProgressSink 类实现图 3 中显示的 IFileOperationProgressSink 接口:

public class FileOperationProgressSink : IFileOperationProgressSink

{

...

}

它的实现措施是为接口的每一措施供给虚拟措施实现。这样,假如要供给自定义行径以相应事故,只需从 FileOperationProgressSink 派生即可。例如,PreRenameItem 是在重命名项目之前调用的措施:

public virtual void PreRenameItem(

uint dwFlags, IShellItem psiItem, string pszNewName)

{

#if DEBUG

string message = string.Format("Renaming {0} to {1}",

item.GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY), pszNewName);

Debug.WriteLine(message);

#endif

}

然则,也可以在自己的进度接管器中以任何选择的要领覆盖它:

public class MyProgressSink : FileOperationProgressSink

{

public virtual void PreRenameItem(

uint dwFlags, IShellItem psiItem, string pszNewName)

{

Console.WriteLine("Goodbye, {0}", item.GetDisplayName(0));

}

}

然后,当实例化 FileOperation 类时,只需供给接管器实例:

using(FileOperation fileOp = new FileOperation(new MyProgressSink()))

{

...

}

总而言之,Windows Vista 团队在 IFileOperation API 的设计方面做得相称出色,使得从托管代码应用它时异常简便。然则请留意,我在此处未先容到的功能还有许许多多。例如,Windows Vista 中的 IFileOperation 不仅支持每操作进度接管器(我未先容),而且还支持经由过程多次调用 IFileOperation::Advise(每个要注册的接管器调用一次),为所有操作供给多个接管器。此外,IFileOperation 还支持许多操作标记,用于节制目录递归、显示哪些对话框以及若何显示、若何处置惩罚冲突等,这一点我也未说起。它还公开了我没有为其供给包装的其他一些操作,例如与改动项目属性相关的操作(经由过程 SetProperties、ApplyPropertiesToItem 和 ApplyPropertiesToItems 措施)。此外,IFileOperation 还供给与所履行的每一操作相关的具体差错信息。假如您必要得到任何这些功能,最简单的法子便是从《MSDN 杂志》网站下载我所供给的代码,并根据实际必要进行改动。

您可能还会对下面的文章感兴趣: