我正在尝试创建一个使用 IFileOperation 删除文件中的简单示例 给定目录,包含在另一个 q 的答案中,以便与其他方法进行比较。
下面是我的MRE的代码。它
成功在 C:\Temp 的子目录中创建 1000 个文件,然后尝试删除
它们位于 DeleteFiles
方法中。这个看似“简单”的任务失败了,但我不确定
正是它脱轨的地方。代码中的注释显示了我的期望
和实际结果。有一次,我没有看到异常,而是弹出了一个窗口
要求确认删除名称奇怪的项目,该项目显然是一个数组
引用 shell 项目的数字,但我尝试使用 Ctrl-C 捕获它失败了;
我相当确定我错过了一两个步骤,滥用了所涉及的接口(interface) 或两者。我的问题是,任何人都可以显示对代码的必要更正,以使 IFileOperation.DeleteItems() 删除有问题的文件,因为我完全不了解这些东西?我对使用 shell 接口(interface)或其他方式删除这些文件的替代方法不感兴趣。
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;
最佳答案
您正在泄漏 ILCreateFromPath()
返回的内存,当您使用完返回的 PItemIDList
后,您需要调用 ILFree()
.
此外,您不应该取消引用 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;
话虽如此,即使工作正常,您也不会为各个文件创建包含 1000 个 IShellItem
的 IShellItemArray
。您正在为 C:\Temp
子目录本身创建一个 IShellItemArray
,其中包含 1 个 IShellItem
。
如果您的目标是删除整个文件夹,那么这很好。但在这种情况下,我建议改用 SHCreateItemFromIDList()
或 SHCreateItemFromParsingName()
,然后将该 IShellItem
传递给 IFileOperation。删除项目()
。
但是,如果您的目标是删除单个文件而不删除子目录,那么您必须:
获取子目录的
IShellFolder
接口(interface),然后使用IShellFolder.EnumObjects()
枚举其文件的相对 PIDL,然后将 PIDL 传递到数组到SHCreateShellItemArray()
。获取子目录的
IShellFolder
接口(interface),然后使用IShellFolder.GetUIObjectOf()
查询其IDataObject
接口(interface),以及然后使用SHCreateShellItemArrayFromDataObject()
,或者直接将IDataObject
提供给IFileOperation.DeleteItems()
。获取子目录的
IShellItem
接口(interface),然后使用IShellItem.BindToHandler()
查询其IEnumShellItems
接口(interface),然后通过直接到IFileOperation.DeleteItems()
。
关于delphi - 如何在Delphi中正确使用IFileOperation删除文件夹中的文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58597454/