Browse Source

UPD: Use PIDL in the shell operations

Alexander Koblov 2 years ago
parent
commit
d39cdc867c

+ 1 - 3
src/filesources/shellfolder/ushellcopyoperation.pas

@@ -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

+ 1 - 3
src/filesources/shellfolder/ushelldeleteoperation.pas

@@ -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

+ 9 - 15
src/filesources/shellfolder/ushellexecuteoperation.pas

@@ -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;

+ 1 - 9
src/filesources/shellfolder/ushellfilesource.pas

@@ -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

+ 42 - 1
src/filesources/shellfolder/ushellfilesourceutil.pas

@@ -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;

+ 84 - 72
src/filesources/shellfolder/ushelllistoperation.pas

@@ -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
-      begin
-        AFile.Attributes:= FILE_ATTRIBUTE_DEVICE or FILE_ATTRIBUTE_VIRTUAL;
-      end;
-      if (rgfInOut and SFGAO_FOLDER <> 0) then
+      rgfInOut:= SFGAOF_DEFAULT;
+
+      if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) then
       begin
-        AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_DIRECTORY;
+        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_HIDDEN <> 0) then
-      begin
-        AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_HIDDEN;
+
+      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;
-    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_DateModified);
+      if AValue <> Unassigned then
+        AFile.ModificationTime:= AValue
+      else begin
+        AFile.ModificationTimeProperty.IsValid:= False;
+      end;
 
-    AValue:= GetDetails(AFolder, PIDL, SCID_DateModified);
-    if AValue <> Unassigned then
-      AFile.ModificationTime:= AValue
-    else begin
-      AFile.ModificationTimeProperty.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_DateCreated);
-    if AValue <> Unassigned then
-      AFile.CreationTime:= AValue
-    else begin
-      AFile.CreationTimeProperty.IsValid:= False;
+      FFiles.Add(AFile);
+    finally
+      CoTaskMemFree(PIDL);
     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)));
+  try
+    OleCheckUTF8(DesktopFolder.BindToObject(DrivesPIDL, nil, IID_IShellFolder2, Pointer(AFolder)));
 
-  OleCheckUTF8(AFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_STORAGE, EnumIDList));
+    OleCheckUTF8(AFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_STORAGE, EnumIDList));
 
-  while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
-  try
-    CheckOperationState;
+    while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
+    try
+      CheckOperationState;
 
-    aFile:= TShellFileSource.CreateFile(Path);
+      aFile:= TShellFileSource.CreateFile(Path);
 
-    AFile.Name:= GetDisplayNameEx(AFolder, PIDL, SHGDN_INFOLDER);
-    AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_FORPARSING);
+      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;
+      rgfInOut:= SFGAOF_DEFAULT;
+      AFile.Attributes:= FILE_ATTRIBUTE_DEVICE or FILE_ATTRIBUTE_VIRTUAL;
 
-    if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) then
-    begin
-      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
+      if Succeeded(AFolder.GetAttributesOf(1, PIDL, rgfInOut)) 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;
-    end;
 
-    AFile.ModificationTimeProperty.IsValid:= False;
+      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;
+      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);
+      FFiles.Add(AFile);
+    finally
+      CoTaskMemFree(PIDL);
+    end;
   finally
-    CoTaskMemFree(PIDL);
+    CoTaskMemFree(DrivesPIDL);
   end;
 end;
 

+ 1 - 3
src/filesources/shellfolder/ushellmoveoperation.pas

@@ -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));

+ 1 - 3
src/filesources/shellfolder/ushellsetfilepropertyoperation.pas

@@ -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

+ 14 - 21
src/platform/upixmapmanager.pas

@@ -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}