未引发 Delphi EInOutError 异常

时间:2021-06-22 10:33:21

标签: delphi

我正在尝试复制目录:

    procedure CopyBigDirWithSubdirs;
    {$IOCHECKS ON}
    begin
      try
        TDirectory.Copy(SrcPath, DstPath);
      except
        on E: EInOutError do something
      end;
    end;

就我而言,检查磁盘已满情况至关重要,我希望捕获 EInOutError 异常可以解决我的问题。但据我所知,TDirectory 方法根本不通知这种情况。情况更糟,因为 TDirectory.copy 可以写入部分子目录,面临磁盘已满情况并终止,因此我必须检查整个目录树以确保我的目录被正确复制。有人知道更好的解决方案吗?

2 个答案:

答案 0 :(得分:2)

{$IOCHECKS ON} 在这里不相关。这适用于传统的 Pascal I/O。同样,对于 EInOutError,您永远不会从 IOUtils 单元中的函数中得到它。

这里真正的问题是,TDirectory.CopyIOUtils 的大部分内容一样,被设计破坏了。 TDirectory.Copy 中似乎没有实现任何错误检查。值得一提的是,我工作场所的规则是,不得在我们的代码中使用 IOUtils

您要么自己编写包含一些错误检查的代码,要么找到第三方库来完成这项工作。

当然在 Windows 上,您应该使用 IFileOperation 来执行此操作。作为一个好处,您甚至可以显示标准的系统进度对话框。而且由于代码是由系统提供的,而不是由 Embarcadero 提供的,因此您可以期待它的工作。

如果您需要对其他平台的支持,那么您可能需要更加努力地找到合适的代码。

答案 1 :(得分:0)

由于使用 IFileOperation 接口看起来是我基于它编写的函数的最实用的解决方案:

function CopyItem(const Src, Dest: string ): HRESULT;
const
  FOF_SILENT = $0004;
  FOF_NOCONFIRMATION = $0010;
  FOF_NOCONFIRMMKDIR = $0200;
  FOF_NOERRORUI = $0400;
  FOF_NO_UI =(FOF_SILENT or FOF_NOCONFIRMATION or FOF_NOERRORUI or FOF_NOCONFIRMMKDIR); // don't display any UI at all
var
  lFileOperation: IFileOperation;
  psiFrom: IShellItem;
  psiTo: IShellItem;
  opAborted : longbool;
begin
  //We probably don't need to call CoInitializeEx/CoUninitialize pair as it could have been called by Delphi library
  CoInitializeEx(nil,  COINIT_APARTMENTTHREADED or COINIT_DISABLE_OLE1DDE);

  // check arguments and create the IFileOperation interface,
  if (Src='') or (Dest='') then Result := E_INVALIDARG
  else Result := CoCreateInstance(CLSID_FileOperation, nil, CLSCTX_ALL, IFileOperation, lFileOperation);

  // Set the operation flags. Turn off all UI from being shown to the user
  if Succeeded(Result) then Result := lFileOperation.SetOperationFlags(FOF_NO_UI);

  // Create IShellItem-s from the supplied source and dest paths.
  if Succeeded(Result) then Result := SHCreateItemFromParsingName(PWideChar(wideString(Src)),
                                             nil, IShellItem, psiFrom);
  if Succeeded(Result) then Result := SHCreateItemFromParsingName(PWideChar(wideString(Dest)),
                                             nil, IShellItem, psiTo);

  // This method does not copy the item, it merely declares the item to be copied
  if Succeeded(Result) then Result := lFileOperation.CopyItem(psiFrom, psiTo, nil, nil);

  // This method is called last to execute those actions that have been specified earlier
  if Succeeded(Result) then Result := lFileOperation.PerformOperations;

  // Check now if the operation was aborted by the system
  if Succeeded(Result) then
  begin
    lFileOperation.GetAnyOperationsAborted(opAborted);
    if opAborted then Result := ERROR_WRITE_FAULT;
  end;

  CoUninitialize;
end;

正如您从代码中看到的,解决方案并不完整,因为在磁盘已满错误的情况下(我对 lFileOperation 进行所有这些摆弄的原因) PerformOperations 返回 S_OK (!!!) 并且我只能通过调用 GetAnyOperationsAborted 找到错误它没有准确指定错误条件,而只是设置 opAborted 标志。那我就得猜一下堕胎的真实案例了。