本文整理自网络,侵删。
从Windows Vista(和Windows Server 2008)开始,Windows API库具有一种执行文件操作(如复制,移动,删除以及类似的文件和文件夹外壳操作)的更强大的新方法。旧的(但仍然有效)SHFileOperation函数现在已由IFileOperation接口代替。IFileOperation公开了用于复制,移动,重命名,创建和删除Shell项目的方法,以及提供进度和错误对话框的方法。
如果您曾经使用过SHFileOperation,并且被MAX_PATH问题击中(例如),则可以在IFileOperation中找到解决方案。这是在Delphi中使用IFileOperation并确保在复制操作中创建不存在的文件夹的方法。
与SHFileOperation函数相比,IFileOperation具有许多优点,例如能够在一个调用中执行不同的操作。您可以删除一些文件,复制更多文件,重命名文件夹,将属性应用于文件,所有这些操作只需一次即可。SHFileOperation一次只能执行一项操作:复制,移动,重命名或删除。
在这篇文章中,我将专注于复制文件。为简单起见,复制操作会将单个文件从其位置复制到目标文件夹,并在目标位置更改文件名。
uses ShellApi; function CopyFileSHFileOperation(const srcFile, destFile : string) : boolean;var shFOS : TShFileOpStruct;begin ZeroMemory(@shFOS, SizeOf(TShFileOpStruct)); shFOS.Wnd := Application.MainForm.Handle; shFOS.wFunc := FO_COPY; shFOS.pFrom := PChar(srcFile + #0); shFOS.pTo := PChar(destFile + #0); //Do not ask the user to confirm the creation of a //new directory if the operation requires one to be created. shFOS.fFlags := FOF_NOCONFIRMMKDIR; result := SHFileOperation(shFOS) = 0;end;SHFileOperation有一个不错的功能:如果目标文件夹不存在,该函数将创建它!FOF_NOCONFIRMMKDIR标志确保Windows不会向用户显示任何对话框,询问是否应创建目标文件夹。
IFileOperation这是使用IFileOperation接口复制文件的方法
uses ActiveX, ComObj, ShlObj;; function CopyFileIFileOperation(const srcFile, destFile : string) : boolean;//works on Windows >= Vista and 2008 servervar r : HRESULT; fileOp: IFileOperation; siSrcFile: IShellItem; siDestFolder: IShellItem; destFileFolder, destFileName : string;begin result := false; destFileFolder := ExtractFileDir(destFile); destFileName := ExtractFileName(destFile); //init com r := CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE); if Succeeded(r) then begin //create IFileOperation interface r := CoCreateInstance(CLSID_FileOperation, nil, CLSCTX_ALL, IFileOperation, fileOp); if Succeeded(r) then begin //set operations flags r := fileOp.SetOperationFlags(FOF_NOCONFIRMATION OR FOFX_NOMINIMIZEBOX); if Succeeded(r) then begin //get source shell item r := SHCreateItemFromParsingName(PChar(srcFile), nil, IShellItem, siSrcFile); if Succeeded(r) then begin //get destination folder shell item r := SHCreateItemFromParsingName(PChar(destFileFolder), nil, IShellItem, siDestFolder); //add copy operation if Succeeded(r) then r := fileOp.CopyItem(siSrcFile, siDestFolder, PChar(destFileName), nil); end; //execute if Succeeded(r) then r := fileOp.PerformOperations; result := Succeeded(r); OleCheck(r); end; end; CoUninitialize; end;end;请注意,PerformActions方法执行通过调用单个方法(如CopyItem(s),DeleteItem(s)等)添加的操作。
但是,使用IFIleOperation存在一个大问题:在复制操作中,如果目标文件夹不存在,该操作将失败!
即使使用SetOperationFlags方法设置了FOF_NOCONFIRMMKDIR,也是如此。
请注意第二次使用SHCreateItemFromParsingName的行:
//get destination folder shell itemr := SHCreateItemFromParsingName(PChar(destFileFolder), nil, IShellItem, siDestFolder);它创建并初始化目标文件夹的外壳程序项,如果该文件夹不存在,则调用将失败。
IFileOperation.CopyItem +强制目标目录该解决方案将在第二个参数中找到:const pbc:IBindCtx; 这是指向绑定上下文的指针,该绑定上下文用于将参数传递给解析函数(在本例中为SHCreateItemFromParsingName)。我们可以使用绑定上下文来强制SHCreateItemFromParsingName不查询文件系统-而是仅使用我们提供的内容。我们将提供WIN32_FIND_DATA结构(指定FILE_ATTRIBUTE_DIRECTORY)和实现IFileSystemBindData接口的对象实例的复杂混合。
这是完整的代码,以及所需接口的实现。
uses ActiveX, ComObj, ShlObj; function CopyFileIFileOperationForceDirectories(const srcFile, destFile : string) : boolean;//works on Windows >= Vista and 2008 servervar r : HRESULT; fileOp: IFileOperation; siSrcFile: IShellItem; siDestFolder: IShellItem; destFileFolder, destFileName : string; pbc : IBindCtx; w32fd : TWin32FindData; ifs : TFileSystemBindData;begin result := false; destFileFolder := ExtractFileDir(destFile); destFileName := ExtractFileName(destFile); //init com r := CoInitializeEx(nil, COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE); if Succeeded(r) then begin //create IFileOperation interface r := CoCreateInstance(CLSID_FileOperation, nil, CLSCTX_ALL, IFileOperation, fileOp); if Succeeded(r) then begin //set operations flags r := fileOp.SetOperationFlags(FOF_NOCONFIRMATION OR FOFX_NOMINIMIZEBOX); if Succeeded(r) then begin //get source shell item r := SHCreateItemFromParsingName(PChar(srcFile), nil, IShellItem, siSrcFile); if Succeeded(r) then begin //create binding context to pretend there is a folder there if NOT DirectoryExists(destFileFolder) then begin ZeroMemory(@w32fd, Sizeof(TWin32FindData)); w32fd.dwFileAttributes := FILE_ATTRIBUTE_DIRECTORY; ifs := TFileSystemBindData.Create; ifs.SetFindData(w32fd); r := CreateBindCtx(0, pbc); r := pbc.RegisterObjectParam(STR_FILE_SYS_BIND_DATA, ifs); end else pbc := nil; //get destination folder shell item r := SHCreateItemFromParsingName(PChar(destFileFolder), pbc, IShellItem, siDestFolder); //add copy operation if Succeeded(r) then r := fileOp.CopyItem(siSrcFile, siDestFolder, PChar(destFileName), nil); end; //execute if Succeeded(r) then r := fileOp.PerformOperations; result := Succeeded(r); OleCheck(r); end; end; CoUninitialize; end;end;这是TFileSystemBindData,IFileSystemBindData 接口的实现:
type TFileSystemBindData = class (TInterfacedObject, IFileSystemBindData) fw32fd: TWin32FindData; function SetFindData(var w32fd: TWin32FindData): HRESULT; stdcall; function GetFindData(var w32fd: TWin32FindData): HRESULT; stdcall; end;...function TFileSystemBindData.GetFindData(var w32fd: TWin32FindData): HRESULT;begin w32fd:= fw32fd; Result := S_OK;end; function TFileSystemBindData.SetFindData(var w32fd: TWin32FindData): HRESULT;begin fw32fd := w32fd; Result := S_OK;end;最后,用法如下:
//works even if "d:\f1\f2\f3\" does not exist!CopyFileIFileOperationForceDirectories('c:\somefile.png', 'd:\f1\f2\f3\copiedfile.png');就是这样,现在您可以使用IFIleOperation而不是SHFileOperation。当然,您需要确保您的代码至少在Windows Vista或Windows Server 2008上运行。您可以使用TOSVersion来检查代码在其上运行的操作系统。
相关阅读 >>
Delphi httpclient async异步获取网页代码
更多相关阅读请进入《Delphi》频道 >>