Как правильно использовать IFileOperation в Delphi для удаления файлов в папке - PullRequest
3 голосов
/ 28 октября 2019

Я пытаюсь создать простой пример использования IFileOperation для удаления файлов в данном каталоге, чтобы включить в ответ другой q для сравнения с другими методами.

Ниже приведен код моего MRE . Он успешно создает 1000 файлов в подкаталоге C: \ Temp, а затем пытается удалить их методом DeleteFiles. Эта якобы «легкая» задача не удалась, но я точно не знаю, где она сходит с рельсов. Комментарии в коде показывают, что я ожидаю, и фактические результаты. В одном случае, вместо отмеченного исключения, я получил всплывающее окно с просьбой подтвердить удаление элемента с нечетным именем, которое, очевидно, представляло собой массив чисел, относящихся к элементу оболочки, но моя попытка захватить его с помощью Ctrl-Cне удалось;

Я вполне уверен, что пропустил один или два шага, неправильно использовал задействованные интерфейсы или оба. Мой вопрос: может ли кто-нибудь показать необходимые исправления в коде, чтобы получить IFileOperation.DeleteItems () для удаления файлов, о которых идет речь, так как я полностью разбираюсь в этом? Меня не интересуют альтернативные методы удаления этих файлов, использование интерфейсов оболочки или иное.

procedure TForm2.DeleteFiles;
var
  iFileOp: IFileOperation;
  iIDList : ItemIDList;
  iItemArray : IShellItemArray;
  iArray : Array[0..1] of ItemIDList;
  Count : DWord;
begin
  iFileOp := CreateComObject(CLSID_FileOperation) as IFileOperation;

  iIDList := ILCreateFromPath(sPath)^;

  //  IFileOperation.DeleteItems seems to require am IShellItemArray, so the following attempts
  //  to create one
  //  The definition of SHCreateShellItemArrayFromIDLists
  //  seems to require a a zero-terminated array of ItemIDLists so the next steps
  //  attempt to create one

  ZeroMemory(@iArray, SizeOf(iArray));
  iArray[0] := iIDList;
  OleCheck(SHCreateShellItemArrayFromIDLists(1, @iArray, iItemArray));

  //  Next test the number of items in iItemArray, which I'm expecting to be 1000
  //  seeing as the CreateFiles routine creats that many

  OleCheck(iItemArray.GetCount(Count));
  Caption := IntToStr(Count);  //  Duh, this shows Count to be 1, not the expected 1000

  OleCheck(iFileOp.DeleteItems(iItemArray));
  OleCheck( iFileOp.PerformOperations );
  // Returns Exception 'No object for moniker'

end;

procedure TForm2.Button1Click(Sender: TObject);
begin
  DeleteFiles;
end;

procedure CreateFiles;
var
  i : Integer;
  SL : TStringList;
  FileName,
  FileContent : String;
begin

  SL := TStringList.Create;
  try
    if not (DirectoryExists(sPath)) then
      MkDir(sPath);

    SL.BeginUpdate;
    for i := 0 to 999 do begin
      FileName := Format('File%d.Txt', [i]);
      FileContent := Format('content of file %s', [FileName]);
      SL.Text := FileContent;
      SL.SaveToFile(sPath + '\' + FileName);
    end;
    SL.EndUpdate;
  finally
    SL.Free;
  end;
end;

procedure TForm2.FormCreate(Sender: TObject);
begin
  CreateFiles;
end;

1 Ответ

6 голосов
/ 29 октября 2019

Вы теряете память, возвращаемую ILCreateFromPath(), вам нужно позвонить ILFree(), когда вы закончите, используя возвращенный PItemIDList.

Кроме того, вы не должны разыменовывать PItemIDList. SHCreateShellItemArrayFromIDLists() ожидает массив PItemIDList указателей, но вы даете ему массив ItemIDList экземпляров.

Попробуйте вместо этого:

procedure TForm2.DeleteFiles;
var
  iFileOp: IFileOperation;
  iIDList : PItemIDList;
  iItemArray : IShellItemArray;
  Count : DWord;
begin
  iFileOp := CreateComObject(CLSID_FileOperation) as IFileOperation;

  iIDList := ILCreateFromPath(sPath);
  try
    OleCheck(SHCreateShellItemArrayFromIDLists(1, @iIDList, iItemArray));
  finally
    ILFree(iIDList);
  end;

  //  Next test the number of items in iItemArray, which I'm expecting to be 1000
  //  seeing as the CreateFiles routine creates that many

  OleCheck(iItemArray.GetCount(Count));
  Caption := IntToStr(Count);  //  Duh, this shows Count to be 1, not the expected 1000

  OleCheck(iFileOp.DeleteItems(iItemArray));
  OleCheck( iFileOp.PerformOperations );
  // Returns Exception 'No object for moniker'
end;

Как говорится, даже еслиэто работало правильно, вы не создаете IShellItemArray, содержащий 1000 IShellItem с для отдельных файлов. Вы создаете IShellItemArray, содержащий 1 IShellItem, для самого подкаталога C:\Temp.

Это хорошо, если ваша цель - удалить всю папку. Но в этом случае я бы предложил вместо этого использовать SHCreateItemFromIDList() или SHCreateItemFromParsingName(), а затем передать это IShellItem в IFileOperation.DeleteItem().

Но, если ваша цель - удалить отдельные файлы без удаленияподкаталог, то вам нужно будет либо:

  • получить интерфейс IShellFolder для подкаталога, затем перечислить относительные PIDL его файлов, используя IShellFolder.EnumObjects(), а затем передатьPIDL в массиве для SHCreateShellItemArray().

  • получают интерфейс IShellFolder подкаталога, затем запрашивают его для интерфейса IDataObject, используя IShellFolder.GetUIObjectOf(), а затем используют SHCreateShellItemArrayFromDataObject() или просто передайте IDataObject непосредственно IFileOperation.DeleteItems().

  • , получите интерфейс IShellItem для подкаталога, затем запросите его интерфейс IEnumShellItems, используя IShellItem.BindToHandler(), изатем передайте это непосредственно IFileOperation.DeleteItems().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...