Delphi IFileOperation替换SHFileOperation


本文整理自网络,侵删。

 
从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 server
var
  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 item
r := 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 server
var
  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 idhttp控件断点下载

Delphi xe10 安卓设备信息

Delphi 获取系统注册的文件图标

Delphi spcomm的一些用法注意

Delphi 图像翻转

Delphi idhttp 断开连接/超时读取

Delphi 得到一个cuid用户唯一标识

Delphi 用dbexpress处理jpg图片

Delphi httpclient async异步获取网页代码

Delphi窗口显示和关闭的时候出现动画效果

更多相关阅读请进入《Delphi》频道 >>



打赏

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,您说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

分享从这里开始,精彩与您同在

评论

管理员已关闭评论功能...