UPD: Use PIDL in the shell operations

This commit is contained in:
Alexander Koblov 2023-06-19 18:54:21 +03:00
commit d39cdc867c
9 changed files with 154 additions and 130 deletions

View file

@ -102,7 +102,6 @@ end;
procedure TShellCopyOperation.Initialize;
var
AName: String;
Index: Integer;
AObject: PItemIDList;
AFolder: IShellFolder2;
@ -113,8 +112,7 @@ begin
try
for Index := 0 to SourceFiles.Count - 1 do
begin
AName:= SourceFiles[Index].LinkProperty.LinkTo;
OleCheck(FShellFileSource.ParseDisplayName(AName, AObject));
AObject:= ILClone(TFileShellProperty(SourceFiles[Index].LinkProperty).Item);
FSourceFilesTree.Add(AObject);
end;
case GetID of

View file

@ -73,7 +73,6 @@ end;
procedure TShellDeleteOperation.Initialize;
var
AName: String;
Index: Integer;
AObject: PItemIDList;
begin
@ -84,8 +83,7 @@ begin
try
for Index := 0 to FilesToDelete.Count - 1 do
begin
AName := FilesToDelete[Index].LinkProperty.LinkTo;
OleCheck(FShellFileSource.ParseDisplayName(AName, AObject));
AObject:= ILClone(TFileShellProperty(FilesToDelete[Index].LinkProperty).Item);
FSourceFilesTree.Add(AObject);
end;
except

View file

@ -38,8 +38,8 @@ type
implementation
uses
Windows, ActiveX, ComObj, ShlObj, ShellAPI, DCOSUtils,
DCConvertEncoding, fMain;
Windows, ComObj, ShlObj, ShellAPI, DCOSUtils,
DCConvertEncoding, uShellFileSourceUtil, fMain;
constructor TShellExecuteOperation.Create(aTargetFileSource: IFileSource;
var aExecutableFile: TFile; aCurrentPath, aVerb: String);
@ -58,8 +58,8 @@ var
begin
if Verb = 'properties' then
try
OleCheck(FShellFileSource.FindFolder(CurrentPath, AFolder));
OleCheck(FShellFileSource.FindObject(AFolder, ExecutableFile.Name, PIDL));
PIDL:= TFileShellProperty(ExecutableFile.LinkProperty).Item;
OleCheck(SHBindToParent(PIDL, IID_IShellFolder2, AFolder, PIDL));
OleCheck(AFolder.GetUIObjectOf(frmMain.Handle, 1, PIDL, IID_IContextMenu, nil, Menu));
if Assigned(Menu) then
begin
@ -84,19 +84,13 @@ begin
else begin
AExecInfo:= Default(TShellExecuteInfoW);
AExecInfo.cbSize:= SizeOf(TShellExecuteInfoW);
AExecInfo.lpIDList:= TFileShellProperty(ExecutableFile.LinkProperty).Item;
AExecInfo.fMask:= SEE_MASK_IDLIST;
if Failed(FShellFileSource.FindObject(AbsolutePath, AExecInfo.lpIDList)) then
FExecuteOperationResult:= fseorError
if ShellExecuteExW(@AExecInfo) then
FExecuteOperationResult:= fseorSuccess
else begin
AExecInfo.fMask:= SEE_MASK_IDLIST;
if ShellExecuteExW(@AExecInfo) then
FExecuteOperationResult:= fseorSuccess
else begin
FExecuteOperationResult:= fseorError;
end;
CoTaskMemFree(AExecInfo.lpIDList);
FExecuteOperationResult:= fseorError;
end;
end;
end;

View file

@ -20,7 +20,6 @@ type
function CreateFolder(AParent: IShellFolder2; const Name: String): HRESULT;
function FindFolder(const Path: String; out AValue: IShellFolder2): HRESULT;
function FindObject(const AObject: String; out AValue: PItemIDList): HRESULT;
function ParseDisplayName(const AName: String; out PIDL: PItemIDList): HRESULT;
function FindObject(AParent: IShellFolder2; const AName: String; out AValue: PItemIDList): HRESULT;
end;
@ -47,7 +46,6 @@ type
function CreateFolder(AParent: IShellFolder2; const Name: String): HRESULT;
function FindFolder(const Path: String; out AValue: IShellFolder2): HRESULT;
function FindObject(const AObject: String; out AValue: PItemIDList): HRESULT;
function ParseDisplayName(const AName: String; out PIDL: PItemIDList): HRESULT;
function FindObject(AParent: IShellFolder2; const AName: String; out AValue: PItemIDList): HRESULT;
function CreateDirectory(const Path: String): Boolean; override;
@ -126,7 +124,7 @@ begin
SizeProperty := TFileSizeProperty.Create;
ModificationTimeProperty := TFileModificationDateTimeProperty.Create;
CreationTimeProperty := TFileCreationDateTimeProperty.Create;
LinkProperty := TFileLinkProperty.Create;
LinkProperty := TFileShellProperty.Create;
CommentProperty := TFileCommentProperty.Create;
end;
end;
@ -174,12 +172,6 @@ begin
end;
end;
function TShellFileSource.ParseDisplayName(const AName: String; out
PIDL: PItemIDList): HRESULT;
begin
Result:= uShellFolder.ParseDisplayName(FDesktopFolder, AName, PIDL);
end;
function TShellFileSource.FindObject(AParent: IShellFolder2;
const AName: String; out AValue: PItemIDList): HRESULT;
var

View file

@ -7,7 +7,7 @@ interface
uses
Classes, SysUtils,
Windows, ActiveX, ShlObj, ComObj, ShlWAPI, ShellAPI,
uShellFolder, uShellFileOperation, uFileSourceCopyOperation,
uShellFolder, uShellFileOperation, uFileSourceCopyOperation, uFileProperty,
uFileSourceDeleteOperation, uFileSourceSetFilePropertyOperation, uGlobs, uLog;
type
@ -19,6 +19,18 @@ type
destructor Destroy; override;
end;
{ TFileShellProperty }
TFileShellProperty = class(TFileLinkProperty)
private
FItem: PItemIDList;
public
destructor Destroy; override;
function Clone: TFileLinkProperty; override;
procedure CloneTo(FileProperty: TFileProperty); override;
property Item: PItemIDList read FItem write FItem;
end;
TCheckOperationState = function(): Boolean of object;
TUpdateCopyStatisticsFunction = procedure(var NewStatistics: TFileSourceCopyOperationStatistics) of object;
TUpdateDeleteStatisticsFunction = procedure(var NewStatistics: TFileSourceDeleteOperationStatistics) of object;
@ -60,6 +72,8 @@ type
function ResumeTimer: HResult; stdcall;
end;
function SHBindToParent(pidl: LPCITEMIDLIST; constref riid: TREFIID; out ppv; var ppidlLast: LPCITEMIDLIST): HRESULT; stdcall; external Shell32;
var
SHCreateItemWithParent: function(pidlParent: PCIDLIST_ABSOLUTE; psfParent: IShellFolder;
pidl: PCUITEMID_CHILD; const riid: REFIID; out ppvItem): HRESULT; stdcall;
@ -97,6 +111,33 @@ begin
inherited Destroy;
end;
{ TFileShellProperty }
destructor TFileShellProperty.Destroy;
begin
inherited Destroy;
if Assigned(FItem) then CoTaskMemFree(FItem);
end;
function TFileShellProperty.Clone: TFileLinkProperty;
begin
Result := TFileShellProperty.Create;
CloneTo(Result);
end;
procedure TFileShellProperty.CloneTo(FileProperty: TFileProperty);
begin
if Assigned(FileProperty) then
begin
inherited CloneTo(FileProperty);
if FileProperty is TFileShellProperty then
begin
TFileShellProperty(FileProperty).FItem := ILClone(Self.FItem);
end;
end;
end;
{ TFileOperationProgressSink }
procedure TFileOperationProgressSink.LogMessage(sMessage: String;

View file

@ -42,64 +42,71 @@ var
PIDL: PItemIDList;
AValue: OleVariant;
rgfInOut: LongWord;
AParent: PItemIDList;
NumIDs: LongWord = 0;
EnumIDList: IEnumIDList;
begin
OleCheckUTF8(AFolder.EnumObjects(0, grfFlags, EnumIDList));
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
OleCheckUTF8(SHGetIDListFromObject(AFolder, AParent));
try
CheckOperationState;
OleCheckUTF8(AFolder.EnumObjects(0, grfFlags, EnumIDList));
aFile:= TShellFileSource.CreateFile(Path);
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
try
CheckOperationState;
AFile.Name:= GetDisplayNameEx(AFolder, PIDL, SHGDN_INFOLDER);
AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_FORPARSING);
aFile:= TShellFileSource.CreateFile(Path);
rgfInOut:= SFGAOF_DEFAULT;
AFile.Name:= GetDisplayNameEx(AFolder, PIDL, SHGDN_INFOLDER);
TFileShellProperty(AFile.LinkProperty).Item:= ILCombine(AParent, PIDL);
AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_INFOLDER or SHGDN_FORPARSING);
if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) then
begin
if (rgfInOut and SFGAO_STORAGE <> 0) then
rgfInOut:= SFGAOF_DEFAULT;
if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) then
begin
AFile.Attributes:= FILE_ATTRIBUTE_DEVICE or FILE_ATTRIBUTE_VIRTUAL;
if (rgfInOut and SFGAO_STORAGE <> 0) then
begin
AFile.Attributes:= FILE_ATTRIBUTE_DEVICE or FILE_ATTRIBUTE_VIRTUAL;
end;
if (rgfInOut and SFGAO_FOLDER <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_DIRECTORY;
end;
if (rgfInOut and SFGAO_HIDDEN <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_HIDDEN;
end;
end;
if (rgfInOut and SFGAO_FOLDER <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_DIRECTORY;
AValue:= GetDetails(AFolder, PIDL, SCID_FileSize);
if VarIsOrdinal(AValue) then
AFile.Size:= AValue
else if AFile.IsDirectory then
AFile.Size:= 0
else begin
AFile.SizeProperty.IsValid:= False;
end;
if (rgfInOut and SFGAO_HIDDEN <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_HIDDEN;
AValue:= GetDetails(AFolder, PIDL, SCID_DateModified);
if AValue <> Unassigned then
AFile.ModificationTime:= AValue
else begin
AFile.ModificationTimeProperty.IsValid:= False;
end;
end;
AValue:= GetDetails(AFolder, PIDL, SCID_FileSize);
if VarIsOrdinal(AValue) then
AFile.Size:= AValue
else if AFile.IsDirectory then
AFile.Size:= 0
else begin
AFile.SizeProperty.IsValid:= False;
end;
AValue:= GetDetails(AFolder, PIDL, SCID_DateCreated);
if AValue <> Unassigned then
AFile.CreationTime:= AValue
else begin
AFile.CreationTimeProperty.IsValid:= False;
end;
AValue:= GetDetails(AFolder, PIDL, SCID_DateModified);
if AValue <> Unassigned then
AFile.ModificationTime:= AValue
else begin
AFile.ModificationTimeProperty.IsValid:= False;
FFiles.Add(AFile);
finally
CoTaskMemFree(PIDL);
end;
AValue:= GetDetails(AFolder, PIDL, SCID_DateCreated);
if AValue <> Unassigned then
AFile.CreationTime:= AValue
else begin
AFile.CreationTimeProperty.IsValid:= False;
end;
FFiles.Add(AFile);
finally
CoTaskMemFree(PIDL);
CoTaskMemFree(AParent);
end;
end;
@ -119,48 +126,53 @@ var
begin
OleCheckUTF8(SHGetDesktopFolder(DesktopFolder));
OleCheckUTF8(SHGetFolderLocation(0, CSIDL_DRIVES, 0, 0, {%H-}DrivesPIDL));
OleCheckUTF8(DesktopFolder.BindToObject(DrivesPIDL, nil, IID_IShellFolder2, Pointer(AFolder)));
OleCheckUTF8(AFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_STORAGE, EnumIDList));
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
try
CheckOperationState;
OleCheckUTF8(DesktopFolder.BindToObject(DrivesPIDL, nil, IID_IShellFolder2, Pointer(AFolder)));
aFile:= TShellFileSource.CreateFile(Path);
OleCheckUTF8(AFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_STORAGE, EnumIDList));
AFile.Name:= GetDisplayNameEx(AFolder, PIDL, SHGDN_INFOLDER);
AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_FORPARSING);
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
try
CheckOperationState;
rgfInOut:= SFGAOF_DEFAULT;
AFile.Attributes:= FILE_ATTRIBUTE_DEVICE or FILE_ATTRIBUTE_VIRTUAL;
aFile:= TShellFileSource.CreateFile(Path);
if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) then
begin
if (SFGAO_FILESYSTEM and rgfInOut) <> 0 then
AFile.Name:= GetDisplayNameEx(AFolder, PIDL, SHGDN_INFOLDER);
TFileShellProperty(AFile.LinkProperty).Item:= ILCombine(DrivesPIDL, PIDL);
AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_INFOLDER or SHGDN_FORPARSING);
rgfInOut:= SFGAOF_DEFAULT;
AFile.Attributes:= FILE_ATTRIBUTE_DEVICE or FILE_ATTRIBUTE_VIRTUAL;
if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_NORMAL;
end
else if (rgfInOut and SFGAO_FOLDER <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_DIRECTORY;
if (SFGAO_FILESYSTEM and rgfInOut) <> 0 then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_NORMAL;
end
else if (rgfInOut and SFGAO_FOLDER <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_DIRECTORY;
end;
end;
AFile.ModificationTimeProperty.IsValid:= False;
AValue:= GetDetails(AFolder, PIDL, SCID_Capacity);
if VarIsOrdinal(AValue) then
AFile.Size:= AValue
else if AFile.IsDirectory then
AFile.Size:= 0
else begin
AFile.SizeProperty.IsValid:= False;
end;
FFiles.Add(AFile);
finally
CoTaskMemFree(PIDL);
end;
AFile.ModificationTimeProperty.IsValid:= False;
AValue:= GetDetails(AFolder, PIDL, SCID_Capacity);
if VarIsOrdinal(AValue) then
AFile.Size:= AValue
else if AFile.IsDirectory then
AFile.Size:= 0
else begin
AFile.SizeProperty.IsValid:= False;
end;
FFiles.Add(AFile);
finally
CoTaskMemFree(PIDL);
CoTaskMemFree(DrivesPIDL);
end;
end;

View file

@ -78,7 +78,6 @@ end;
procedure TShellMoveOperation.Initialize;
var
AName: String;
Index: Integer;
AObject: PItemIDList;
AFolder: IShellFolder2;
@ -89,8 +88,7 @@ begin
try
for Index := 0 to SourceFiles.Count - 1 do
begin
AName:= SourceFiles[Index].LinkProperty.LinkTo;
OleCheck(FShellFileSource.ParseDisplayName(AName, AObject));
AObject:= ILClone(TFileShellProperty(SourceFiles[Index].LinkProperty).Item);
FSourceFilesTree.Add(AObject);
end;
OleCheck(FShellFileSource.FindFolder(TargetPath, AFolder));

View file

@ -84,7 +84,6 @@ end;
procedure TShellSetFilePropertyOperation.Initialize;
var
AName: String;
Index: Integer;
AObject: PItemIDList;
begin
@ -94,8 +93,7 @@ begin
try
for Index := 0 to TargetFiles.Count - 1 do
begin
AName:= TargetFiles[Index].LinkProperty.LinkTo;
OleCheck(FShellFileSource.ParseDisplayName(AName, AObject));
AObject:= ILClone(TFileShellProperty(TargetFiles[Index].LinkProperty).Item);
FSourceFilesTree.Add(AObject);
end;
except

View file

@ -124,7 +124,6 @@ type
FiEmblemOnline: PtrInt;
FiEmblemOffline: PtrInt;
FOneDrivePath: String;
FDesktopFolder: IShellFolder;
{$ELSEIF DEFINED(DARWIN)}
FUseSystemTheme: Boolean;
{$ELSEIF DEFINED(UNIX) AND NOT DEFINED(HAIKU)}
@ -362,7 +361,8 @@ uses
{$ENDIF}
{$IFDEF MSWINDOWS}
, ActiveX, CommCtrl, ShellAPI, Windows, DCFileAttributes, uBitmap, uGdiPlus,
IntfGraphics, DCConvertEncoding, uShlObjAdditional, uShellFolder
IntfGraphics, DCConvertEncoding, uShlObjAdditional, uShellFolder,
uShellFileSourceUtil
{$ELSE}
, StrUtils, Types, DCBasicTypes
{$ENDIF}
@ -1343,27 +1343,21 @@ function TPixMapManager.GetShellFolderIcon(AFile: TFile): PtrInt;
const
uFlags: UINT = SHGFI_SYSICONINDEX or SHGFI_PIDL;
var
PIDL: PItemIDList;
FileInfo: TSHFileInfoW;
begin
if Succeeded(ParseDisplayName(FDesktopFolder, AFile.LinkProperty.LinkTo, PIDL)) then
try
if (SHGetFileInfoW(PWideChar(PIDL), 0, {%H-}FileInfo,
SizeOf(FileInfo), uFlags) <> 0) then
begin
Result := FileInfo.iIcon + SystemIconIndexStart;
{$IF DEFINED(LCLQT5)}
FPixmapsLock.Acquire;
try
Result := CheckAddSystemIcon(Result);
finally
FPixmapsLock.Release;
end;
{$ENDIF}
Exit;
if (SHGetFileInfoW(PWideChar(TFileShellProperty(AFile.LinkProperty).Item),
0, {%H-}FileInfo, SizeOf(FileInfo), uFlags) <> 0) then
begin
Result := FileInfo.iIcon + SystemIconIndexStart;
{$IF DEFINED(LCLQT5)}
FPixmapsLock.Acquire;
try
Result := CheckAddSystemIcon(Result);
finally
FPixmapsLock.Release;
end;
finally
CoTaskMemFree(PIDL);
{$ENDIF}
Exit;
end;
// Could not retrieve the icon
if AFile.IsDirectory then
@ -1484,7 +1478,6 @@ begin
iIconSize := SHIL_EXTRALARGE;
end;
SHGetDesktopFolder(FDesktopFolder);
FSysImgList := SHGetSystemImageList(iIconSize);
{$ENDIF}