[英]How to correctly use IFileOperation in Delphi to delete the files in a folder
我正在尝试创建一个使用 IFileOperation 删除给定目录中的文件的简单示例,以包含在另一个 q 的答案中,以便与其他方法进行比较。
下面是我的MRE的代码。 它成功地在 C:\Temp 的子目录中创建了 1000 个文件,然后尝试在DeleteFiles
方法中删除它们。 这个所谓的“简单”任务失败了,但我不确定它到底是从哪里脱轨的。 代码中的注释显示了我的期望和实际结果。 有一次,我没有注意到异常,而是弹出一个要求确认删除具有奇怪名称的项目的弹出窗口,这显然是一个引用 shell 项目的数字数组,但我尝试使用 Ctrl-C 捕获它失败的;
我很确定我要么缺少一两个步骤,要么滥用所涉及的接口,要么两者兼而有之。 我的问题是,任何人都可以对代码进行必要的更正以让 IFileOperation.DeleteItems() 删除有问题的文件,因为我对这些东西完全不了解吗? 我对使用 shell 接口或其他方式删除这些文件的替代方法不感兴趣。
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()
返回的 memory ,您需要在使用返回的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 个IShellItemArray
的IShellItem
。 您正在为IShellItem
C:\Temp
子目录本身创建一个包含 1 个IShellItemArray
的 IShellItemArray。
如果您的目标是删除整个文件夹,这很好。 但在这种情况下,我建议改用SHCreateItemFromIDList()
或SHCreateItemFromParsingName()
,然后将该IShellItem
传递给IFileOperation.DeleteItem()
。
但是,如果您的目标是删除单个文件而不删除子目录,那么您将不得不:
获取子目录的IShellFolder
接口,然后使用IShellFolder.EnumObjects()
枚举其文件的相关 PIDL,然后将数组中的 PIDL 传递给SHCreateShellItemArray()
。
获取子目录的IShellFolder
接口,然后使用IShellFolder.GetUIObjectOf()
查询IDataObject
接口,然后使用SHCreateShellItemArrayFromDataObject()
,或者直接将IDataObject
提供给IFileOperation.DeleteItems()
。
获取子目录的IShellItem
接口,然后使用IShellItem.BindToHandler()
查询其IEnumShellItems
接口,然后将其直接传递给IFileOperation.DeleteItems()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.