I'm trying to create a simple example of using IFileOperation to delete the files in a given directory, to include in the answer to another q for comparison with other methods.
Below is the code of my MRE. It
successfully creates 1000 files in a subdirectory off C:\Temp and then attempts to delete
them in the DeleteFiles method. This supposedly "easy" task fails but I'm not sure
exactly where it comes off the rails. The comments in the code show what I'm expecting
and the actual results. On one occasion, instead of the exception noted, I got a pop-up
asking for confirmation to delete an item with an odd name which was evidently an array of
numbers referring to a shell item, but my attempt to capture it using Ctrl-C failed;
我相当确定我会错失一两个步骤,或者滥用所涉及的接口,或者两者兼而有之。我的问是,有人可以对代码进行必要的更正,以获取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;
Run Code Online (Sandbox Code Playgroud)
您正在泄漏返回的内存,使用返回的内存时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;
Run Code Online (Sandbox Code Playgroud)
话虽如此,即使这正常工作,您也不会为单个文件创建一个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()。
| 归档时间: |
|
| 查看次数: |
168 次 |
| 最近记录: |