ADD: Shell folder file source (computer drive overview)

This commit is contained in:
Alexander Koblov 2023-06-03 14:42:30 +03:00
commit 701100336d
17 changed files with 2117 additions and 8 deletions

View file

@ -43,7 +43,7 @@
</Target>
<SearchPaths>
<IncludeFiles Value="$(LazarusDir)\ide;$(ProjOutDir);..\sdk;..\units"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl;filesources\shellfolder"/>
<UnitOutputDirectory Value="..\units\$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)"/>
<SrcPath Value="$(LazarusDir)\lcl;$(LazarusDir)\lcl\interfaces\$(LCLWidgetType);$(fpcsrcdir)\packages\fcl-base\src"/>
</SearchPaths>
@ -86,7 +86,7 @@
</Target>
<SearchPaths>
<IncludeFiles Value="$(LazarusDir)\ide;$(ProjOutDir);..\sdk;..\units"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl;filesources\shellfolder"/>
<UnitOutputDirectory Value="..\units\$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)"/>
<SrcPath Value="$(LazarusDir)\lcl;$(LazarusDir)\lcl\interfaces\$(LCLWidgetType);$(fpcsrcdir)\packages\fcl-base\src"/>
</SearchPaths>
@ -125,7 +125,7 @@
</Target>
<SearchPaths>
<IncludeFiles Value="$(LazarusDir)\ide;$(ProjOutDir);..\sdk;..\units"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl;filesources\shellfolder"/>
<UnitOutputDirectory Value="..\units\$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)"/>
<SrcPath Value="$(LazarusDir)\lcl;$(LazarusDir)\lcl\interfaces\$(LCLWidgetType);$(fpcsrcdir)\packages\fcl-base\src"/>
</SearchPaths>
@ -199,7 +199,7 @@ end;"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(LazarusDir)\ide;$(ProjOutDir);..\sdk;..\units"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;..\components\DDetours\Source;filesources\gio\trash;filesources\winnet\wsl"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;..\components\DDetours\Source;filesources\gio\trash;filesources\winnet\wsl;filesources\shellfolder"/>
<UnitOutputDirectory Value="..\units\$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)"/>
<SrcPath Value="$(LazarusDir)\lcl;$(LazarusDir)\lcl\interfaces\$(LCLWidgetType);$(fpcsrcdir)\packages\fcl-base\src"/>
</SearchPaths>
@ -310,7 +310,7 @@ end;"/>
<PackageName Value="Image32"/>
</Item13>
</RequiredPackages>
<Units Count="271">
<Units Count="272">
<Unit0>
<Filename Value="doublecmd.lpr"/>
<IsPartOfProject Value="True"/>
@ -1985,6 +1985,11 @@ end;"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uHighlighters"/>
</Unit270>
<Unit271>
<Filename Value="filesources\shellfolder\ushellfilesource.pas"/>
<IsPartOfProject Value="True"/>
<UnitName Value="uShellFileSource"/>
</Unit271>
</Units>
</ProjectOptions>
<CompilerOptions>
@ -1995,7 +2000,7 @@ end;"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(LazarusDir)\ide;$(ProjOutDir);..\sdk;..\units"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl"/>
<OtherUnitFiles Value="platform;platform\$(SrcOS);platform\$(SrcOS)\$(TargetOS);..\sdk;frames;fileviews;filesources;filesources\filesystem;filesources\multiarchive;filesources\multilist;filesources\searchresult;filesources\tempfilesystem;filesources\vfs;filesources\wcxarchive;filesources\wfxplugin;filesources\winnet;platform\unix\glib;platform\unix\mime;filesources\gio;rpc;rpc\sys\$(SrcOS);rpc\sys;filesources\recyclebin;filesources\gio\trash;filesources\winnet\wsl;filesources\shellfolder"/>
<UnitOutputDirectory Value="..\units\$(TargetCPU)-$(TargetOS)-$(LCLWidgetType)"/>
<SrcPath Value="$(LazarusDir)\lcl;$(LazarusDir)\lcl\interfaces\$(LCLWidgetType);$(fpcsrcdir)\packages\fcl-base\src"/>
</SearchPaths>

View file

@ -0,0 +1,156 @@
unit uShellCalcStatisticsOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
Windows, ShlObj, ComObj, ActiveX,
uFileSourceCalcStatisticsOperation,
uFileSource,
uShellFileSource,
uFile,
uGlobs, uLog;
type
TShellCalcStatisticsOperation = class(TFileSourceCalcStatisticsOperation)
private
FShellFileSource: IShellFileSource;
FStatistics: TFileSourceCalcStatisticsOperationStatistics;
procedure ProcessFile(aFile: TFile);
procedure ProcessSubDirs(AParent: IShellFolder2; AObject: PItemIDList);
procedure LogMessage(sMessage: String; logOptions: TLogOptions; logMsgType: TLogMsgType);
public
constructor Create(aTargetFileSource: IFileSource;
var theFiles: TFiles); override;
destructor Destroy; override;
procedure Initialize; override;
procedure MainExecute; override;
end;
implementation
uses
uShellFileSourceUtil, uShellFolder;
constructor TShellCalcStatisticsOperation.Create(
aTargetFileSource: IFileSource;
var theFiles: TFiles);
begin
FShellFileSource:= aTargetFileSource as IShellFileSource;
inherited Create(aTargetFileSource, theFiles);
end;
destructor TShellCalcStatisticsOperation.Destroy;
begin
inherited Destroy;
end;
procedure TShellCalcStatisticsOperation.Initialize;
begin
// Get initialized statistics; then we change only what is needed.
FStatistics := RetrieveStatistics;
end;
procedure TShellCalcStatisticsOperation.MainExecute;
var
CurrentFileIndex: Integer;
begin
for CurrentFileIndex := 0 to Files.Count - 1 do
begin
ProcessFile(Files[CurrentFileIndex]);
CheckOperationState;
end;
end;
procedure TShellCalcStatisticsOperation.ProcessFile(aFile: TFile);
var
AObject: PItemIDList;
AFolder: IShellFolder2;
begin
FStatistics.CurrentFile := aFile.FullPath;
UpdateStatistics(FStatistics);
if aFile.IsDirectory then
begin
Inc(FStatistics.Directories);
if Succeeded(FShellFileSource.FindFolder(AFile.Path, AFolder)) then
begin
if Succeeded(FShellFileSource.FindObject(AFolder, aFile.Name, AObject)) then
try
ProcessSubDirs(AFolder, AObject);
finally
CoTaskMemFree(AObject);
end;
end;
end
else begin
Inc(FStatistics.Files);
Inc(FStatistics.Size, aFile.Size);
if aFile.ModificationTime < FStatistics.OldestFile then
FStatistics.OldestFile := aFile.ModificationTime;
if aFile.ModificationTime > FStatistics.NewestFile then
FStatistics.NewestFile := aFile.ModificationTime;
end;
UpdateStatistics(FStatistics);
end;
procedure TShellCalcStatisticsOperation.ProcessSubDirs(AParent: IShellFolder2; AObject: PItemIDList);
var
ASize: Int64;
PIDL: PItemIDList;
NumIDs: LongWord = 0;
EnumIDList: IEnumIDList;
AFolder: IShellFolder2;
begin
try
OleCheck(AParent.BindToObject(AObject, nil, IID_IShellFolder2, Pointer(AFolder)));
OleCheck(AFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_STORAGE or SHCONTF_INCLUDEHIDDEN, EnumIDList));
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
try
if GetIsFolder(AParent, PIDL) then
begin
Inc(FStatistics.Directories);
ProcessSubDirs(AFolder, PIDL);
end
else begin
ASize:= GetDetails(AFolder, PIDL, SCID_FileSize);
Inc(FStatistics.Size, ASize);
Inc(FStatistics.Files);
end;
CheckOperationState;
UpdateStatistics(FStatistics);
finally
CoTaskMemFree(PIDL);
end;
except
on E: Exception do
LogMessage(E.Message, [log_errors], lmtError);
end;
end;
procedure TShellCalcStatisticsOperation.LogMessage(sMessage: String; logOptions: TLogOptions; logMsgType: TLogMsgType);
begin
case logMsgType of
lmtError:
if not (log_errors in gLogOptions) then Exit;
lmtInfo:
if not (log_info in gLogOptions) then Exit;
lmtSuccess:
if not (log_success in gLogOptions) then Exit;
end;
if logOptions <= gLogOptions then
begin
logWrite(Thread, sMessage, logMsgType);
end;
end;
end.

View file

@ -0,0 +1,202 @@
unit uShellCopyOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
Windows, ShlObj, ComObj,
uFileSourceOperation,
uFileSourceCopyOperation,
uFileSource,
uFileSourceOperationTypes,
uFileSourceOperationOptions,
uFileSourceOperationOptionsUI,
uFile,
uShellFileSource,
uShellFileOperation,
uShellFileSourceUtil;
type
{ TShellCopyOperation }
TShellCopyOperation = class(TFileSourceCopyOperation)
protected
FFileOp: IFileOperation;
FTargetFolder: IShellItem;
FSourceFilesTree: TItemList;
FShellFileSource: IShellFileSource;
FStatistics: TFileSourceCopyOperationStatistics;
procedure ShowError(const sMessage: String);
public
constructor Create(aSourceFileSource: IFileSource;
aTargetFileSource: IFileSource;
var theSourceFiles: TFiles;
aTargetPath: String); override;
destructor Destroy; override;
procedure Initialize; override;
procedure MainExecute; override;
end;
{ TShellCopyInOperation }
TShellCopyInOperation = class(TShellCopyOperation)
protected
function GetID: TFileSourceOperationType; override;
public
procedure Initialize; override;
end;
{ TShellCopyOutOperation }
TShellCopyOutOperation = class(TShellCopyOperation)
protected
function GetID: TFileSourceOperationType; override;
end;
implementation
uses
ActiveX, DCConvertEncoding, uFileSourceOperationUI, uShellFolder, uGlobs, uLog;
procedure TShellCopyOperation.ShowError(const sMessage: String);
begin
if (log_cp_mv_ln in gLogOptions) and (log_errors in gLogOptions) then
begin
logWrite(Thread, sMessage, lmtError);
end;
if AskQuestion(sMessage, '', [fsourSkip, fsourAbort],
fsourSkip, fsourAbort) = fsourAbort then
begin
RaiseAbortOperation;
end;
end;
constructor TShellCopyOperation.Create(aSourceFileSource: IFileSource;
aTargetFileSource: IFileSource;
var theSourceFiles: TFiles;
aTargetPath: String);
begin
case GetID of
fsoCopy, fsoCopyOut:
FShellFileSource:= aSourceFileSource as IShellFileSource;
fsoCopyIn:
FShellFileSource:= aTargetFileSource as IShellFileSource;
end;
FFileOp:= CreateComObject(CLSID_FileOperation) as IFileOperation;
inherited Create(aSourceFileSource, aTargetFileSource, theSourceFiles, aTargetPath);
end;
destructor TShellCopyOperation.Destroy;
begin
inherited Destroy;
FreeAndNil(FSourceFilesTree);
end;
procedure TShellCopyOperation.Initialize;
var
aFile: TFile;
Index: Integer;
AObject: PItemIDList;
AFolder: IShellFolder2;
begin
FStatistics := RetrieveStatistics;
FSourceFilesTree:= TItemList.Create;
try
for Index := 0 to SourceFiles.Count - 1 do
begin
aFile:= SourceFiles[Index];
CheckObject(FShellFileSource.FindObject(aFile.FullPath, AObject), aFile.FullPath);
FSourceFilesTree.Add(AObject);
end;
case GetID of
fsoCopy:
begin
CheckObject(FShellFileSource.FindFolder(TargetPath, AFolder), TargetPath);
OleCheck(SHGetIDListFromObject(AFolder, AObject));
try
OleCheck(SHCreateItemFromIDList(AObject, IShellItem, FTargetFolder));
finally
CoTaskMemFree(AObject);
end;
end;
fsoCopyOut:
OleCheck(SHCreateItemFromParsingName(PWideChar(CeUtf8ToUtf16(TargetPath)), nil, IShellItem, FTargetFolder));
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
procedure TShellCopyOperation.MainExecute;
var
dwCookie: DWORD;
siItemArray: IShellItemArray;
ASink: TFileOperationProgressSink;
begin
ASink:= TFileOperationProgressSink.Create(@FStatistics, @UpdateStatistics);
FFileOp.SetOperationFlags(FOF_SILENT or FOF_NOCONFIRMMKDIR);
try
FFileOp.Advise(ASink, @dwCookie);
try
OleCheck(SHCreateShellItemArrayFromIDLists(FSourceFilesTree.Count, PPItemIDList(FSourceFilesTree.List), siItemArray));
OleCheck(FFileOp.CopyItems(siItemArray, FTargetFolder));
OleCheck(FFileOp.PerformOperations);
finally
FFileOp.Unadvise(dwCookie);
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
{ TShellCopyInOperation }
function TShellCopyInOperation.GetID: TFileSourceOperationType;
begin
Result:= fsoCopyIn;
end;
procedure TShellCopyInOperation.Initialize;
var
aFile: TFile;
Index: Integer;
AObject: PItemIDList;
AFolder: IShellFolder2;
begin
FStatistics := RetrieveStatistics;
FSourceFilesTree:= TItemList.Create;
try
for Index := 0 to SourceFiles.Count - 1 do
begin
aFile := SourceFiles[Index];
AObject:= ILCreateFromPathW(PWideChar(CeUtf8ToUtf16(aFile.FullPath)));
FSourceFilesTree.Add(AObject);
end;
CheckObject(FShellFileSource.FindFolder(TargetPath, AFolder), TargetPath);
OleCheck(SHGetIDListFromObject(AFolder, AObject));
OleCheck(SHCreateItemFromIDList(AObject, IShellItem, FTargetFolder));
except
on E: Exception do ShowError(E.Message);
end;
end;
{ TShellCopyOutOperation }
function TShellCopyOutOperation.GetID: TFileSourceOperationType;
begin
Result:= fsoCopyOut;
end;
end.

View file

@ -0,0 +1,57 @@
unit uShellCreateDirectoryOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceCreateDirectoryOperation,
uShellFileSource,
uFileSource;
type
{ TShellCreateDirectoryOperation }
TShellCreateDirectoryOperation = class(TFileSourceCreateDirectoryOperation)
private
FShellFileSource: IShellFileSource;
public
constructor Create(aTargetFileSource: IFileSource;
aCurrentPath: String;
aDirectoryPath: String); override;
procedure MainExecute; override;
end;
implementation
uses
uFileSourceOperationUI, uGlobs, uLog, uLng;
{ TShellCreateDirectoryOperation }
constructor TShellCreateDirectoryOperation.Create(aTargetFileSource: IFileSource;
aCurrentPath: String; aDirectoryPath: String);
begin
FShellFileSource := aTargetFileSource as IShellFileSource;
inherited Create(aTargetFileSource, aCurrentPath, aDirectoryPath);
end;
procedure TShellCreateDirectoryOperation.MainExecute;
begin
if FShellFileSource.CreateDirectory(AbsolutePath) then
begin
if (log_dir_op in gLogOptions) and (log_success in gLogOptions) then
logWrite(Thread, Format(rsMsgLogSuccess + rsMsgLogMkDir, [AbsolutePath]), lmtSuccess);
end
else begin
if (log_dir_op in gLogOptions) and (log_errors in gLogOptions) then
logWrite(Thread, Format(rsMsgLogError + rsMsgLogMkDir, [AbsolutePath]), lmtError);
AskQuestion(Format(rsMsgErrForceDir, [AbsolutePath]), '', [fsourOk], fsourOk, fsourOk);
end;
end;
end.

View file

@ -0,0 +1,120 @@
unit uShellDeleteOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
Windows, ShlObj, ComObj,
uFileSourceDeleteOperation,
uShellFileSource,
uFileSource,
uShellFileOperation,
uShellFileSourceUtil,
uFileSourceOperationUI,
uFile,
uGlobs, uLog;
type
{ TShellDeleteOperation }
TShellDeleteOperation = class(TFileSourceDeleteOperation)
protected
FFileOp: IFileOperation;
FSourceFilesTree: TItemList;
FShellFileSource: IShellFileSource;
FStatistics: TFileSourceDeleteOperationStatistics;
procedure ShowError(const sMessage: String);
public
constructor Create(aTargetFileSource: IFileSource;
var theFilesToDelete: TFiles); override;
destructor Destroy; override;
procedure Initialize; override;
procedure MainExecute; override;
end;
implementation
uses
DCOSUtils, uLng, uShellFolder;
procedure TShellDeleteOperation.ShowError(const sMessage: String);
begin
if (log_errors in gLogOptions) and (log_delete in gLogOptions) then
begin
logWrite(Thread, sMessage, lmtError);
end;
if AskQuestion(sMessage, '', [fsourSkip, fsourAbort],
fsourSkip, fsourAbort) = fsourAbort then
begin
RaiseAbortOperation;
end;
end;
constructor TShellDeleteOperation.Create(aTargetFileSource: IFileSource;
var theFilesToDelete: TFiles);
begin
FShellFileSource:= aTargetFileSource as IShellFileSource;
FFileOp:= CreateComObject(CLSID_FileOperation) as IFileOperation;
inherited Create(aTargetFileSource, theFilesToDelete);
end;
destructor TShellDeleteOperation.Destroy;
begin
inherited Destroy;
FreeAndNil(FSourceFilesTree);
end;
procedure TShellDeleteOperation.Initialize;
var
aFile: TFile;
Index: Integer;
AObject: PItemIDList;
begin
FStatistics := RetrieveStatistics;
FSourceFilesTree:= TItemList.Create;
try
for Index := 0 to FilesToDelete.Count - 1 do
begin
aFile := FilesToDelete[Index];
CheckObject(FShellFileSource.FindObject(aFile.FullPath, AObject), aFile.FullPath);
FSourceFilesTree.Add(AObject);
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
procedure TShellDeleteOperation.MainExecute;
var
dwCookie: DWORD;
siItemArray: IShellItemArray;
ASink: TFileOperationProgressSink;
begin
ASink:= TFileOperationProgressSink.Create(@FStatistics, @UpdateStatistics);
FFileOp.SetOperationFlags(FOF_SILENT or FOF_NOCONFIRMATION);
try
FFileOp.Advise(ASink, @dwCookie);
try
OleCheck(SHCreateShellItemArrayFromIDLists(FSourceFilesTree.Count, PPItemIDList(FSourceFilesTree.List), siItemArray));
OleCheck(FFileOp.DeleteItems(siItemArray));
OleCheck(FFileOp.PerformOperations);
finally
FFileOp.Unadvise(dwCookie);
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
end.

View file

@ -0,0 +1,105 @@
unit uShellExecuteOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFile,
uFileSource,
uShellFileSource,
uFileSourceExecuteOperation;
type
{ TShellExecuteOperation }
TShellExecuteOperation = class(TFileSourceExecuteOperation)
private
FShellFileSource: IShellFileSource;
public
{en
@param(aTargetFileSource
File source where the file should be executed.)
@param(aExecutableFile
File that should be executed.)
@param(aCurrentPath
Path of the file source where the execution should take place.)
}
constructor Create(aTargetFileSource: IFileSource;
var aExecutableFile: TFile;
aCurrentPath,
aVerb: String); override;
procedure MainExecute; override;
end;
implementation
uses
Windows, ActiveX, ComObj, ShlObj, ShellAPI, DCOSUtils,
DCConvertEncoding, fMain;
constructor TShellExecuteOperation.Create(aTargetFileSource: IFileSource;
var aExecutableFile: TFile; aCurrentPath, aVerb: String);
begin
FShellFileSource := aTargetFileSource as IShellFileSource;
inherited Create(aTargetFileSource, aExecutableFile, aCurrentPath, aVerb);
end;
procedure TShellExecuteOperation.MainExecute;
var
PIDL: PItemIDList;
Menu: IContextMenu;
AFolder: IShellFolder2;
cmici: TCMInvokeCommandInfo;
AExecInfo: TShellExecuteInfoW;
begin
if Verb = 'properties' then
try
OleCheck(FShellFileSource.FindFolder(CurrentPath, AFolder));
OleCheck(FShellFileSource.FindObject(AFolder, ExecutableFile.Name, PIDL));
OleCheck(AFolder.GetUIObjectOf(frmMain.Handle, 1, PIDL, IID_IContextMenu, nil, Menu));
if Assigned(Menu) then
begin
cmici:= Default(TCMInvokeCommandInfo);
with cmici do
begin
cbSize := SizeOf(TCMInvokeCommandInfo);
hwnd := frmMain.Handle;
lpVerb := PAnsiChar(Verb);
nShow := SW_SHOWNORMAL;
end;
OleCheck(Menu.InvokeCommand(cmici));
end;
except
FExecuteOperationResult:= fseorError;
end
else if FShellFileSource.IsPathAtRoot(CurrentPath) then
begin
FResultString:= ExecutableFile.LinkProperty.LinkTo;
FExecuteOperationResult:= fseorSymLink;
end
else begin
AExecInfo:= Default(TShellExecuteInfoW);
AExecInfo.cbSize:= SizeOf(TShellExecuteInfoW);
if Failed(FShellFileSource.FindObject(AbsolutePath, AExecInfo.lpIDList)) then
FExecuteOperationResult:= fseorError
else begin
AExecInfo.fMask:= SEE_MASK_IDLIST;
if ShellExecuteExW(@AExecInfo) then
FExecuteOperationResult:= fseorSuccess
else begin
FExecuteOperationResult:= fseorError;
end;
CoTaskMemFree(AExecInfo.lpIDList);
end;
end;
end;
end.

View file

@ -0,0 +1,418 @@
unit uShellFileSource;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils, Dialogs,
Windows, ShlObj,
uFileSourceProperty,
uVirtualFileSource, uFileProperty, uFileSource,
uFileSourceOperation, uFile, uFileSourceOperationTypes;
type
{ IShellFileSource }
IShellFileSource = interface(IVirtualFileSource)
['{1E598290-5E66-423C-BB55-333E293106E8}']
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 FindObject(AParent: IShellFolder2; const AName: String; out AValue: PItemIDList): HRESULT;
end;
{ TShellFileSource }
TShellFileSource = class(TVirtualFileSource, IShellFileSource)
private
FDrives: PItemIDList;
FDesktopFolder: IShellFolder;
protected
function SetCurrentWorkingDirectory(NewDir: String): Boolean; override;
public
constructor Create; override;
destructor Destroy; override;
class function IsSupportedPath(const Path: String): Boolean; override;
class function CreateFile(const APath: String): TFile; override;
class function GetMainIcon(out Path: String): Boolean; override;
class function RootName: String;
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 FindObject(AParent: IShellFolder2; const AName: String; out AValue: PItemIDList): HRESULT;
function CreateDirectory(const Path: String): Boolean; override;
function FileSystemEntryExists(const Path: String): Boolean; override;
function GetOperationsTypes: TFileSourceOperationTypes; override;
function GetSupportedFileProperties: TFilePropertiesTypes; override;
function GetRootDir(sPath: String): String; override; overload;
function GetProperties: TFileSourceProperties; override;
function CreateListOperation(TargetPath: String): TFileSourceOperation; override;
function CreateDeleteOperation(var FilesToDelete: TFiles): TFileSourceOperation; override;
function CreateCreateDirectoryOperation(BasePath: String; DirectoryPath: String): TFileSourceOperation; override;
function CreateExecuteOperation(var ExecutableFile: TFile; BasePath, Verb: String): TFileSourceOperation; override;
function CreateMoveOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; override;
function CreateCopyOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; override;
function CreateCopyInOperation(SourceFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; override;
function CreateCopyOutOperation(TargetFileSource: IFileSource;
var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation; override;
function CreateCalcStatisticsOperation(var theFiles: TFiles): TFileSourceOperation; override;
function CreateSetFilePropertyOperation(var theTargetFiles: TFiles;
var theNewProperties: TFileProperties): TFileSourceOperation; override;
end;
implementation
uses
ActiveX, ComObj,DCConvertEncoding, uShellFolder, uShellListOperation,
uShellCopyOperation, uShellFileOperation, uShellCreateDirectoryOperation,
uShellExecuteOperation, uShellSetFilePropertyOperation, uShellFileSourceUtil,
uShellDeleteOperation, uShellMoveOperation, UShellCalcStatisticsOperation,
uLng, uShlObjAdditional;
{ TShellFileSource }
function TShellFileSource.SetCurrentWorkingDirectory(NewDir: String): Boolean;
begin
Result := True;
end;
constructor TShellFileSource.Create;
begin
inherited Create;
OleCheck(SHGetDesktopFolder(FDesktopFolder));
OleCheck(SHGetFolderLocation(0, CSIDL_DRIVES, 0, 0, {%H-}FDrives));
FOperationsClasses[fsoMove] := TShellMoveOperation.GetOperationClass;
FOperationsClasses[fsoCopy] := TShellCopyOperation.GetOperationClass;
FOperationsClasses[fsoCopyIn] := TShellCopyInOperation.GetOperationClass;
FOperationsClasses[fsoCopyOut] := TShellCopyOutOperation.GetOperationClass;
end;
destructor TShellFileSource.Destroy;
begin
inherited Destroy;
CoTaskMemFree(FDrives);
end;
class function TShellFileSource.IsSupportedPath(const Path: String): Boolean;
begin
Result:= SameText(ExcludeTrailingBackslash(Path), PathDelim + PathDelim + PathDelim + rsVfsRecycleBin);
end;
class function TShellFileSource.CreateFile(const APath: String): TFile;
begin
Result := TFile.Create(APath);
with Result do
begin
AttributesProperty := TFileAttributesProperty.CreateOSAttributes;
SizeProperty := TFileSizeProperty.Create;
ModificationTimeProperty := TFileModificationDateTimeProperty.Create;
CreationTimeProperty := TFileCreationDateTimeProperty.Create;
LinkProperty := TFileLinkProperty.Create;
CommentProperty := TFileCommentProperty.Create;
end;
end;
class function TShellFileSource.GetMainIcon(out Path: String): Boolean;
begin
Result:= True;
Path:= '%SystemRoot%\System32\shell32.dll,15';
end;
class function TShellFileSource.RootName: String;
var
DrivesPIDL: PItemIDList;
DesktopFolder: IShellFolder;
begin
OleCheckUTF8(SHGetDesktopFolder(DesktopFolder));
OleCheckUTF8(SHGetFolderLocation(0, CSIDL_DRIVES, 0, 0, {%H-}DrivesPIDL));
Result:= GetDisplayName(DesktopFolder, DrivesPIDL, SHGDN_NORMAL);
end;
function TShellFileSource.FindObject(const AObject: String; out
AValue: PItemIDList): HRESULT;
var
APath: String;
AFolder: IShellFolder2;
AItemPIDL, AFolderPIDL: PItemIDList;
begin
APath:= ExtractFileDir(AObject);
Result:= FindFolder(APath, AFolder);
if Succeeded(Result) then
begin
Result:= FindObject(AFolder, ExtractFileName(AObject), AItemPIDL);
if Succeeded(Result) then
begin
Result:= SHGetIDListFromObject(AFolder, AFolderPIDL);
if Succeeded(Result) then
begin
AValue:= ILCombine(AFolderPIDL, AItemPIDL);
CoTaskMemFree(AFolderPIDL);
end;
CoTaskMemFree(AItemPIDL);
end;
end;
end;
function TShellFileSource.FindObject(AParent: IShellFolder2;
const AName: String; out AValue: PItemIDList): HRESULT;
var
AItemName: String;
PIDL: PItemIDList;
NumIDs: LongWord = 0;
EnumIDList: IEnumIDList;
begin
Result:= AParent.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_STORAGE or SHCONTF_INCLUDEHIDDEN, EnumIDList);
if Succeeded(Result) then
begin
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
begin
AItemName:= GetDisplayName(AParent, PIDL, SHGDN_NORMAL);
if AName = AItemName then
begin
AValue:= PIDL;
Exit(S_OK);
end;
CoTaskMemFree(PIDL);
end;
end;
Result:= STG_E_FILENOTFOUND;
end;
function TShellFileSource.FindFolder(const Path: String; out
AValue: IShellFolder2): HRESULT;
function List(var AFolder: IShellFolder2; const AObject: String): HRESULT;
var
AName: String;
PIDL: PItemIDList;
NumIDs: LongWord = 0;
AValue: IShellFolder2;
EnumIDList: IEnumIDList;
begin
Result:= AFolder.EnumObjects(0, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_STORAGE or SHCONTF_INCLUDEHIDDEN, EnumIDList);
if Succeeded(Result) then
begin
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
try
AName:= GetDisplayName(AFolder, PIDL, SHGDN_NORMAL);
if AName = AObject then
begin
Result:= AFolder.BindToObject(PIDL, nil, IID_IShellFolder2, Pointer(AValue));
if Succeeded(Result) then AFolder:= AValue;
Exit;
end;
finally
CoTaskMemFree(PIDL);
end;
end;
Result:= STG_E_PATHNOTFOUND;
end;
var
Index: Integer;
APath: TStringArray;
begin
Result:= FDesktopFolder.BindToObject(FDrives, nil, IID_IShellFolder2, Pointer(AValue));
if Succeeded(Result) then
begin
APath:= Path.Split([PathDelim], TStringSplitOptions.ExcludeEmpty);
if Length(APath) > 0 then
begin
// Find subdirectory
for Index:= 0 to High(APath) do
begin
Result:= List(AValue, APath[Index]);
if Failed(Result) then Exit;
end;
end;
end;
end;
function TShellFileSource.CreateFolder(AParent: IShellFolder2;
const Name: String): HRESULT;
var
AName: WideString;
AParentItem: IShellItem;
AFileOp: IFileOperation;
AParentPIDL: PItemIDList;
begin
AName:= CeUtf8ToUtf16(Name);
Result:= SHGetIDListFromObject(AParent, AParentPIDL);
if Succeeded(Result) then
try
Result:= SHCreateItemFromIDList(AParentPIDL, IShellItem, AParentItem);
if Succeeded(Result) then
begin
AFileOp:= CreateComObject(CLSID_FileOperation) as IFileOperation;
Result:= AFileOp.NewItem(AParentItem, FILE_ATTRIBUTE_DIRECTORY, PWideChar(AName), nil, nil);
if Succeeded(Result) then
begin
Result:= AFileOp.PerformOperations();
end;
end;
finally
CoTaskMemFree(AParentPIDL);
end;
end;
function TShellFileSource.CreateDirectory(const Path: String): Boolean;
var
AName: String;
AParent: IShellFolder2;
begin
AName:= ExtractFileName(Path);
Result:= Succeeded(FindFolder(ExtractFileDir(Path), AParent));
if Result then
begin
Result:= Succeeded(CreateFolder(AParent, AName));
end;
end;
function TShellFileSource.FileSystemEntryExists(const Path: String): Boolean;
var
AObject: PItemIDList;
begin
Result:= Succeeded(FindObject(Path, AObject));
if Result then CoTaskMemFree(AObject);
end;
function TShellFileSource.GetOperationsTypes: TFileSourceOperationTypes;
begin
Result := [fsoList, fsoExecute, fsoDelete, fsoCreateDirectory,
fsoMove, fsoCopy, fsoCopyIn, fsoCopyOut, fsoSetFileProperty,
fsoCalcStatistics];
end;
function TShellFileSource.GetSupportedFileProperties: TFilePropertiesTypes;
begin
Result := inherited GetSupportedFileProperties
+ [fpSize,
fpAttributes,
fpModificationTime,
fpCreationTime,
uFileProperty.fpLink,
fpComment
];
end;
function TShellFileSource.GetRootDir(sPath: String): String;
begin
Result:= PathDelim;
end;
function TShellFileSource.GetProperties: TFileSourceProperties;
begin
Result := [fspVirtual];
end;
function TShellFileSource.CreateListOperation(TargetPath: String): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result:= TShellListOperation.Create(TargetFileSource, TargetPath);
end;
function TShellFileSource.CreateDeleteOperation(var FilesToDelete: TFiles
): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result:= TShellDeleteOperation.Create(TargetFileSource, FilesToDelete);
end;
function TShellFileSource.CreateCreateDirectoryOperation(BasePath: String;
DirectoryPath: String): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result := TShellCreateDirectoryOperation.Create(TargetFileSource, BasePath, DirectoryPath);
end;
function TShellFileSource.CreateExecuteOperation(var ExecutableFile: TFile;
BasePath, Verb: String): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result:= TShellExecuteOperation.Create(TargetFileSource, ExecutableFile, BasePath, Verb);
end;
function TShellFileSource.CreateMoveOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result := TShellMoveOperation.Create(TargetFileSource, SourceFiles, TargetPath);
end;
function TShellFileSource.CreateCopyOperation(var SourceFiles: TFiles;
TargetPath: String): TFileSourceOperation;
var
SourceFileSource: IFileSource;
begin
SourceFileSource := Self;
Result:= TShellCopyOperation.Create(SourceFileSource, SourceFileSource, SourceFiles, TargetPath);
end;
function TShellFileSource.CreateCopyInOperation(SourceFileSource: IFileSource;
var SourceFiles: TFiles; TargetPath: String): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result:= TShellCopyInOperation.Create(SourceFileSource, TargetFileSource, SourceFiles, TargetPath);
end;
function TShellFileSource.CreateCopyOutOperation(TargetFileSource: IFileSource;
var SourceFiles: TFiles; TargetPath: String): TFileSourceOperation;
var
SourceFileSource: IFileSource;
begin
SourceFileSource := Self;
Result := TShellCopyOutOperation.Create(SourceFileSource, TargetFileSource, SourceFiles, TargetPath);
end;
function TShellFileSource.CreateCalcStatisticsOperation(var theFiles: TFiles
): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result := TShellCalcStatisticsOperation.Create(TargetFileSource, theFiles);
end;
function TShellFileSource.CreateSetFilePropertyOperation(
var theTargetFiles: TFiles; var theNewProperties: TFileProperties
): TFileSourceOperation;
var
TargetFileSource: IFileSource;
begin
TargetFileSource := Self;
Result := TShellSetFilePropertyOperation.Create(TargetFileSource, theTargetFiles, theNewProperties);
end;
end.

View file

@ -0,0 +1,385 @@
unit uShellFileSourceUtil;
{$mode delphi}
interface
uses
Classes, SysUtils,
Windows, ActiveX, ShlObj, ComObj, ShlWAPI, ShellAPI,
uShellFolder, uShellFileOperation, uFileSourceCopyOperation,
uFileSourceDeleteOperation, uFileSourceSetFilePropertyOperation, uGlobs, uLog;
const
SID_SYSTEM = '{B725F130-47EF-101A-A5F1-02608C9EEBAC}';
SCID_FileSize: TSHColumnID = ( fmtid: SID_SYSTEM; pid: 12 );
SCID_DateModified: TSHColumnID = ( fmtid: SID_SYSTEM; pid: 14 );
SCID_DateCreated: TSHColumnID = ( fmtid: SID_SYSTEM; pid: 15 );
SID_COMPUTER = '{9B174B35-40FF-11D2-A27E-00C04FC30871}';
SCID_Capacity: TSHColumnID = ( fmtid: SID_COMPUTER; pid: 3 );
type
{ TItemList }
TItemList = class(TFPList)
public
destructor Destroy; override;
end;
TUpdateCopyStatisticsFunction = procedure(var NewStatistics: TFileSourceCopyOperationStatistics) of object;
TUpdateDeleteStatisticsFunction = procedure(var NewStatistics: TFileSourceDeleteOperationStatistics) of object;
TUpdateSetFilePropertyStatisticsFunction = procedure(var NewStatistics: TFileSourceSetFilePropertyOperationStatistics) of object;
{ TFileOperationProgressSink }
TFileOperationProgressSink = class(TInterfacedObject, IFileOperationProgressSink)
private
FCopyStatistics: PFileSourceCopyOperationStatistics;
FUpdateCopyStatistics: TUpdateCopyStatisticsFunction;
FDeleteStatistics: PFileSourceDeleteOperationStatistics;
FUpdateDeleteStatistics: TUpdateDeleteStatisticsFunction;
FUpdateSetFilePropertyStatistics: TUpdateSetFilePropertyStatisticsFunction;
FSetFilePropertyStatistics: PFileSourceSetFilePropertyOperationStatistics;
protected
procedure LogMessage(sMessage: String; logOptions: TLogOptions; logMsgType: TLogMsgType);
public
constructor Create(AStatistics: PFileSourceCopyOperationStatistics; AUpdateStatistics: TUpdateCopyStatisticsFunction); reintroduce; overload;
constructor Create(AStatistics: PFileSourceDeleteOperationStatistics; AUpdateStatistics: TUpdateDeleteStatisticsFunction); reintroduce; overload;
constructor Create(AStatistics: PFileSourceSetFilePropertyOperationStatistics; AUpdateStatistics: TUpdateSetFilePropertyStatisticsFunction); reintroduce; overload;
public
function StartOperations: HResult; stdcall;
function FinishOperations(hrResult: HResult): HResult; stdcall;
function PreRenameItem(dwFlags: DWORD; psiItem: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
function PostRenameItem(dwFlags: DWORD; psiItem: IShellItem; pszNewName: LPCWSTR; hrRename: HRESULT; psiNewlyCreated: IShellItem): HResult; stdcall;
function PreMoveItem(dwFlags: DWORD; psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
function PostMoveItem(dwFlags: DWORD; psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR; hrMove: HRESULT; psiNewlyCreated: IShellItem): HResult; stdcall;
function PreCopyItem(dwFlags: DWORD; psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
function PostCopyItem(dwFlags: DWORD; psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR; hrCopy: HRESULT; psiNewlyCreated: IShellItem): HResult; stdcall;
function PreDeleteItem(dwFlags: DWORD; psiItem: IShellItem): HResult; stdcall;
function PostDeleteItem(dwFlags: DWORD; psiItem: IShellItem; hrDelete: HRESULT; psiNewlyCreated: IShellItem): HResult; stdcall;
function PreNewItem(dwFlags: DWORD; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
function PostNewItem(dwFlags: DWORD; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR; pszTemplateName: LPCWSTR; dwFileAttributes: DWORD; hrNew: HRESULT; psiNewItem: IShellItem): HResult; stdcall;
function UpdateProgress(iWorkTotal: UINT; iWorkSoFar: UINT): HResult; stdcall;
function ResetTimer: HResult; stdcall;
function PauseTimer: HResult; stdcall;
function ResumeTimer: HResult; stdcall;
end;
procedure CheckObject(Result: HResult; const AName: String); inline;
var
SHCreateItemWithParent: function(pidlParent: PCIDLIST_ABSOLUTE; psfParent: IShellFolder;
pidl: PCUITEMID_CHILD; const riid: REFIID; out ppvItem): HRESULT; stdcall;
SHGetIDListFromObject: function(punk: IUnknown; out ppidl): HRESULT; stdcall;
SHCreateItemFromIDList: function(pidl: PItemIDList; const riid: REFIID; out ppv): HRESULT; stdcall;
SHCreateItemFromParsingName: function(pszPath: LPCWSTR; const pbc: IBindCtx;
const riid: TIID; out ppv): HRESULT; stdcall;
SHCreateShellItemArray: function(pidlParent: PCIDLIST_ABSOLUTE; psf: IShellFolder;
cidl: UINT; ppidl: PPItemIDList; out ppsiItemArray): HRESULT; stdcall;
SHCreateShellItemArrayFromIDLists: function(cidl: UINT; rgpidl: PPItemIDList; out ppsiItemArray): HRESULT; stdcall;
implementation
uses
DCOSUtils, DCConvertEncoding, uLng;
var
AModule: HMODULE;
procedure CheckObject(Result: HResult; const AName: String);
begin
if Failed(Result) then
begin
raise EOleError.Create(mbSysErrorMessage(Result) + LineEnding + AName);
end;
end;
{ TItemList }
destructor TItemList.Destroy;
var
AItem: PItemIDList;
begin
for AItem in Self do
begin
CoTaskMemFree(AItem);
end;
inherited Destroy;
end;
{ TFileOperationProgressSink }
procedure TFileOperationProgressSink.LogMessage(sMessage: String;
logOptions: TLogOptions; logMsgType: TLogMsgType);
begin
case logMsgType of
lmtError:
if not (log_errors in gLogOptions) then Exit;
lmtInfo:
if not (log_info in gLogOptions) then Exit;
lmtSuccess:
if not (log_success in gLogOptions) then Exit;
end;
if logOptions <= gLogOptions then
begin
logWrite(nil, sMessage, logMsgType);
end;
end;
constructor TFileOperationProgressSink.Create(
AStatistics: PFileSourceCopyOperationStatistics;
AUpdateStatistics: TUpdateCopyStatisticsFunction);
begin
FCopyStatistics:= AStatistics;
FUpdateCopyStatistics:= AUpdateStatistics;
end;
constructor TFileOperationProgressSink.Create(
AStatistics: PFileSourceDeleteOperationStatistics;
AUpdateStatistics: TUpdateDeleteStatisticsFunction);
begin
FDeleteStatistics:= AStatistics;
FUpdateDeleteStatistics:= AUpdateStatistics;
end;
constructor TFileOperationProgressSink.Create(
AStatistics: PFileSourceSetFilePropertyOperationStatistics;
AUpdateStatistics: TUpdateSetFilePropertyStatisticsFunction);
begin
FSetFilePropertyStatistics:= AStatistics;
FUpdateSetFilePropertyStatistics:= AUpdateStatistics;
end;
function TFileOperationProgressSink.StartOperations: HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.FinishOperations(hrResult: HResult
): HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.PreRenameItem(dwFlags: DWORD;
psiItem: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
var
AFileName: PWideChar;
begin
if Succeeded(psiItem.GetDisplayName(SIGDN(SIGDN_DESKTOPABSOLUTEEDITING), @AFileName)) then
begin
FSetFilePropertyStatistics^.CurrentFile:= CeUtf16ToUtf8(AFileName);
CoTaskMemFree(AFileName);
end;
Result:= S_OK;
end;
function TFileOperationProgressSink.PostRenameItem(dwFlags: DWORD;
psiItem: IShellItem; pszNewName: LPCWSTR; hrRename: HRESULT;
psiNewlyCreated: IShellItem): HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.PreMoveItem(dwFlags: DWORD;
psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR
): HResult; stdcall;
begin
Result:= PreCopyItem(dwFlags, psiItem, psiDestinationFolder, pszNewName);
end;
function TFileOperationProgressSink.PostMoveItem(dwFlags: DWORD;
psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
hrMove: HRESULT; psiNewlyCreated: IShellItem): HResult; stdcall;
begin
if (log_cp_mv_ln in gLogOptions) then
begin
with FCopyStatistics^ do
begin
if Succeeded(hrMove) then
begin
LogMessage(Format(rsMsgLogSuccess + rsMsgLogMove, [CurrentFileFrom + ' -> ' + CurrentFileTo]),
[log_cp_mv_ln], lmtSuccess);
end
else begin
LogMessage(Format(rsMsgLogError + rsMsgLogMove, [CurrentFileFrom + ' -> ' + CurrentFileTo]),
[log_cp_mv_ln], lmtError);
end;
end;
end;
Result:= S_OK;
end;
function TFileOperationProgressSink.PreCopyItem(dwFlags: DWORD;
psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR
): HResult; stdcall;
var
AFileName: PWideChar;
begin
if Succeeded(psiItem.GetDisplayName(SIGDN(SIGDN_DESKTOPABSOLUTEEDITING), @AFileName)) then
begin
FCopyStatistics^.CurrentFileFrom:= CeUtf16ToUtf8(AFileName);
CoTaskMemFree(AFileName);
end;
if Succeeded(psiDestinationFolder.GetDisplayName(SIGDN(SIGDN_DESKTOPABSOLUTEEDITING), @AFileName)) then
begin
with FCopyStatistics^ do
begin
CurrentFileTo:= CeUtf16ToUtf8(AFileName);
CoTaskMemFree(AFileName);
if Assigned(pszNewName) and (pszNewName^ <> #0) then
CurrentFileTo:= CurrentFileTo + CeUtf16ToUtf8(pszNewName)
else begin
CurrentFileTo:= CurrentFileTo + ExtractFileName(CurrentFileFrom);
end;
end;
end;
FUpdateCopyStatistics(FCopyStatistics^);
Result:= S_OK;
end;
function TFileOperationProgressSink.PostCopyItem(dwFlags: DWORD;
psiItem: IShellItem; psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
hrCopy: HRESULT; psiNewlyCreated: IShellItem): HResult; stdcall;
begin
if (log_cp_mv_ln in gLogOptions) then
begin
with FCopyStatistics^ do
begin
if Succeeded(hrCopy) then
begin
LogMessage(Format(rsMsgLogSuccess + rsMsgLogCopy, [CurrentFileFrom + ' -> ' + CurrentFileTo]),
[log_cp_mv_ln], lmtSuccess);
end
else begin
LogMessage(Format(rsMsgLogError + rsMsgLogCopy, [CurrentFileFrom + ' -> ' + CurrentFileTo]),
[log_cp_mv_ln], lmtError);
end;
end;
end;
Result:= S_OK;
end;
function TFileOperationProgressSink.PreDeleteItem(dwFlags: DWORD;
psiItem: IShellItem): HResult; stdcall;
var
AFileName: PWideChar;
begin
if Succeeded(psiItem.GetDisplayName(SIGDN(SIGDN_DESKTOPABSOLUTEEDITING), @AFileName)) then
begin
FDeleteStatistics^.CurrentFile:= CeUtf16ToUtf8(AFileName);
CoTaskMemFree(AFileName);
end;
Result:= S_OK;
end;
function TFileOperationProgressSink.PostDeleteItem(dwFlags: DWORD;
psiItem: IShellItem; hrDelete: HRESULT; psiNewlyCreated: IShellItem
): HResult; stdcall;
var
AText: String;
sfgaoAttribs: SFGAOF = 0;
begin
if log_delete in gLogOptions then
begin
psiItem.GetAttributes(SFGAO_FOLDER, @sfgaoAttribs);
if (sfgaoAttribs and SFGAO_FOLDER) = 0 then
AText:= rsMsgLogDelete
else begin
AText:= rsMsgLogRmDir;
end;
with FDeleteStatistics^ do
begin
if Succeeded(hrDelete) then
begin
LogMessage(Format(rsMsgLogSuccess + AText, [CurrentFile]),
[log_delete], lmtSuccess);
end
else begin
LogMessage(Format(rsMsgLogError + AText, [CurrentFile]),
[log_delete], lmtError);
end;
end;
end;
Result:= S_OK;
end;
function TFileOperationProgressSink.PreNewItem(dwFlags: DWORD;
psiDestinationFolder: IShellItem; pszNewName: LPCWSTR): HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.PostNewItem(dwFlags: DWORD;
psiDestinationFolder: IShellItem; pszNewName: LPCWSTR;
pszTemplateName: LPCWSTR; dwFileAttributes: DWORD; hrNew: HRESULT;
psiNewItem: IShellItem): HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.UpdateProgress(iWorkTotal: UINT;
iWorkSoFar: UINT): HResult; stdcall;
begin
if Assigned(FCopyStatistics) then
begin
FCopyStatistics^.TotalBytes:= iWorkTotal;
FCopyStatistics^.DoneBytes:= iWorkSoFar;
FUpdateCopyStatistics(FCopyStatistics^);
end
else if Assigned(FDeleteStatistics) then
begin
FDeleteStatistics^.TotalFiles:= iWorkTotal;
FDeleteStatistics^.DoneFiles:= iWorkSoFar;
FUpdateDeleteStatistics(FDeleteStatistics^);
end
else if Assigned(FSetFilePropertyStatistics) then
begin
FSetFilePropertyStatistics^.TotalFiles:= iWorkTotal;
FSetFilePropertyStatistics^.DoneFiles:= iWorkSoFar;
FUpdateSetFilePropertyStatistics(FSetFilePropertyStatistics^);
end;
Result:= S_OK;
end;
function TFileOperationProgressSink.ResetTimer: HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.PauseTimer: HResult; stdcall;
begin
Result:= S_OK;
end;
function TFileOperationProgressSink.ResumeTimer: HResult; stdcall;
begin
Result:= S_OK;
end;
initialization
if (Win32MajorVersion > 5) then
begin
AModule:= GetModuleHandleW(Shell32);
@SHGetIDListFromObject:= GetProcAddress(AModule, 'SHGetIDListFromObject');
@SHCreateItemFromIDList:= GetProcAddress(AModule, 'SHCreateItemFromIDList');
@SHCreateItemWithParent:= GetProcAddress(AModule, 'SHCreateItemWithParent');
@SHCreateShellItemArray:= GetProcAddress(AModule, 'SHCreateShellItemArray');
@SHCreateItemFromParsingName:= GetProcAddress(AModule, 'SHCreateItemFromParsingName');
@SHCreateShellItemArrayFromIDLists:= GetProcAddress(AModule, 'SHCreateShellItemArrayFromIDLists');
end;
end.

View file

@ -0,0 +1,188 @@
unit uShellListOperation;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils,
Windows, ShlObj, ComObj,
uFileSourceListOperation,
uShellFileSource,
uFileSource;
type
{ TShellListOperation }
TShellListOperation = class(TFileSourceListOperation)
private
FShellFileSource: IShellFileSource;
procedure ListFolder(AFolder: IShellFolder2; grfFlags: DWORD);
procedure ListDrives;
procedure ListDirectory;
public
constructor Create(aFileSource: IFileSource; aPath: String); override;
procedure MainExecute; override;
end;
implementation
uses
ActiveX, Variants, DCOSUtils, DCDateTimeUtils, ShellAPI,
uFile, uShellFolder, uShlObjAdditional, uShowMsg, uShellFileSourceUtil;
{ TShellListOperation }
procedure TShellListOperation.ListFolder(AFolder: IShellFolder2; grfFlags: DWORD
);
var
AFile: TFile;
PIDL: PItemIDList;
AValue: OleVariant;
rgfInOut: LongWord;
NumIDs: LongWord = 0;
EnumIDList: IEnumIDList;
begin
OleCheckUTF8(AFolder.EnumObjects(0, grfFlags, EnumIDList));
while EnumIDList.Next(1, PIDL, NumIDs) = S_OK do
try
CheckOperationState;
aFile:= TShellFileSource.CreateFile(Path);
AFile.Name:= GetDisplayName(AFolder, PIDL, SHGDN_INFOLDER);
AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_FORPARSING);
rgfInOut:= SFGAO_HIDDEN or SFGAO_FOLDER;
AFolder.GetAttributesOf(1, PIDL, rgfInOut);
if (rgfInOut and SFGAO_FOLDER <> 0) then
begin
AFile.Attributes:= FILE_ATTRIBUTE_DIRECTORY
end;
if (rgfInOut and SFGAO_HIDDEN <> 0) then
begin
AFile.Attributes:= AFile.Attributes or FILE_ATTRIBUTE_HIDDEN;
end;
AValue:= GetDetails(AFolder, PIDL, SCID_FileSize);
if AValue <> Unassigned 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_DateCreated);
if AValue <> Unassigned then
AFile.CreationTime:= AValue
else begin
AFile.CreationTimeProperty.IsValid:= False;
end;
FFiles.Add(AFile);
finally
CoTaskMemFree(PIDL);
end;
end;
procedure TShellListOperation.ListDrives;
var
AFile: TFile;
PIDL: PItemIDList;
rgfInOut: LongWord;
AValue: OleVariant;
NumIDs: LongWord = 0;
AFolder: IShellFolder2;
EnumIDList: IEnumIDList;
DrivesPIDL: PItemIDList;
DesktopFolder: IShellFolder;
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;
aFile:= TShellFileSource.CreateFile(Path);
AFile.Name:= GetDisplayName(AFolder, PIDL, SHGDN_INFOLDER);
AFile.LinkProperty.LinkTo:= GetDisplayName(AFolder, PIDL, SHGDN_FORPARSING);
AFile.ModificationTimeProperty.IsValid:= False;
AValue:= GetDetails(AFolder, PIDL, SCID_Capacity);
if not (TVarData(AValue).vtype in [varEmpty, varNull]) then
AFile.Size:= AValue
else if AFile.IsDirectory then
AFile.Size:= 0
else begin
AFile.SizeProperty.IsValid:= False;
end;
rgfInOut:= SFGAO_FILESYSTEM or SFGAO_FOLDER;
AFolder.GetAttributesOf(1, PIDL, rgfInOut);
if (SFGAO_FILESYSTEM and rgfInOut) <> 0 then
begin
AFile.Attributes:= FILE_ATTRIBUTE_VIRTUAL or FILE_ATTRIBUTE_REPARSE_POINT;
end
else if (rgfInOut and SFGAO_FOLDER <> 0) then
begin
AFile.Attributes:= FILE_ATTRIBUTE_DIRECTORY
end;
FFiles.Add(AFile);
finally
CoTaskMemFree(PIDL);
end;
end;
procedure TShellListOperation.ListDirectory;
var
AFolder: IShellFolder2;
begin
if Succeeded(FShellFileSource.FindFolder(ExcludeTrailingBackslash(Path), AFolder)) then
begin
ListFolder(AFolder, SHCONTF_FOLDERS or SHCONTF_NONFOLDERS or SHCONTF_INCLUDEHIDDEN);
end;
end;
constructor TShellListOperation.Create(aFileSource: IFileSource;
aPath: String);
begin
FFiles := TFiles.Create(aPath);
FShellFileSource:= aFileSource as IShellFileSource;
inherited Create(aFileSource, aPath);
end;
procedure TShellListOperation.MainExecute;
begin
FFiles.Clear;
try
if Path = PathDelim then
ListDrives
else begin
ListDirectory;
end;
except
on E: Exception do msgError(Thread, E.Message);
end;
end;
end.

View file

@ -0,0 +1,138 @@
unit uShellMoveOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
Windows, ShlObj, ComObj, ActiveX,
uFileSourceOperation,
uFileSourceMoveOperation,
uFileSource,
uFile,
uShellFileSource,
uShellFileOperation,
uShellFileSourceUtil;
type
{ TShellMoveOperation }
TShellMoveOperation = class(TFileSourceMoveOperation)
protected
FFileOp: IFileOperation;
FTargetFolder: IShellItem;
FSourceFilesTree: TItemList;
FShellFileSource: IShellFileSource;
FStatistics: TFileSourceMoveOperationStatistics;
procedure ShowError(const sMessage: String);
public
constructor Create(aFileSource: IFileSource;
var theSourceFiles: TFiles;
aTargetPath: String); virtual reintroduce;
destructor Destroy; override;
procedure Initialize; override;
procedure MainExecute; override;
procedure Finalize; override;
end;
implementation
uses
uFileSourceOperationOptions, uFileSourceOperationUI, uShellFolder, uGlobs,
uLog, uLng;
procedure TShellMoveOperation.ShowError(const sMessage: String);
begin
if (log_errors in gLogOptions) then
begin
logWrite(Thread, sMessage, lmtError);
end;
if AskQuestion(sMessage, '', [fsourSkip, fsourAbort],
fsourSkip, fsourAbort) = fsourAbort then
begin
RaiseAbortOperation;
end;
end;
constructor TShellMoveOperation.Create(aFileSource: IFileSource;
var theSourceFiles: TFiles; aTargetPath: String);
begin
FShellFileSource:= aFileSource as IShellFileSource;
FFileOp:= CreateComObject(CLSID_FileOperation) as IFileOperation;
inherited Create(aFileSource, theSourceFiles, aTargetPath);
end;
destructor TShellMoveOperation.Destroy;
begin
inherited Destroy;
FreeAndNil(FSourceFilesTree);
end;
procedure TShellMoveOperation.Initialize;
var
aFile: TFile;
Index: Integer;
AObject: PItemIDList;
AFolder: IShellFolder2;
begin
FStatistics := RetrieveStatistics;
FSourceFilesTree:= TItemList.Create;
try
for Index := 0 to SourceFiles.Count - 1 do
begin
aFile := SourceFiles[Index];
CheckObject(FShellFileSource.FindObject(aFile.FullPath, AObject), aFile.FullPath);
FSourceFilesTree.Add(AObject);
end;
CheckObject(FShellFileSource.FindFolder(TargetPath, AFolder), TargetPath);
OleCheck(SHGetIDListFromObject(AFolder, AObject));
try
OleCheck(SHCreateItemFromIDList(AObject, IShellItem, FTargetFolder));
finally
CoTaskMemFree(AObject);
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
procedure TShellMoveOperation.MainExecute;
var
dwCookie: DWORD;
siItemArray: IShellItemArray;
ASink: TFileOperationProgressSink;
begin
ASink:= TFileOperationProgressSink.Create(@FStatistics, @UpdateStatistics);
FFileOp.SetOperationFlags(FOF_SILENT or FOF_NOCONFIRMMKDIR);
try
FFileOp.Advise(ASink, @dwCookie);
try
OleCheck(SHCreateShellItemArrayFromIDLists(FSourceFilesTree.Count, PPItemIDList(FSourceFilesTree.List), siItemArray));
OleCheck(FFileOp.MoveItems(siItemArray, FTargetFolder));
OleCheck(FFileOp.PerformOperations);
finally
FFileOp.Unadvise(dwCookie);
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
procedure TShellMoveOperation.Finalize;
begin
end;
end.

View file

@ -0,0 +1,176 @@
unit uShellSetFilePropertyOperation;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils,
uFileSourceSetFilePropertyOperation,
uFileSource,
uFile,
uFileProperty,
uShellFileSource,
uShellFileOperation,
uShellFileSourceUtil;
type
{ TShellSetFilePropertyOperation }
TShellSetFilePropertyOperation = class(TFileSourceSetFilePropertyOperation)
private
FFileOp: IFileOperation;
FCurrentFileIndex: Integer;
FSourceFilesTree: TItemList;
FShellFileSource: IShellFileSource;
FStatistics: TFileSourceSetFilePropertyOperationStatistics;
procedure ShowError(const sMessage: String);
protected
function SetNewProperty(aFile: TFile; aTemplateProperty: TFileProperty): TSetFilePropertyResult; override;
public
constructor Create(aTargetFileSource: IFileSource;
var theTargetFiles: TFiles;
var theNewProperties: TFileProperties); override;
destructor Destroy; override;
procedure Initialize; override;
procedure MainExecute; override;
end;
implementation
uses
Windows, ActiveX, ShlObj, ComObj, DCConvertEncoding,
uFileSourceOperationUI, uShellFolder, uGlobs, uLog, uLng;
procedure TShellSetFilePropertyOperation.ShowError(const sMessage: String);
begin
if (log_errors in gLogOptions) then
begin
logWrite(Thread, sMessage, lmtError);
end;
if AskQuestion(sMessage, '', [fsourSkip, fsourAbort],
fsourSkip, fsourAbort) = fsourAbort then
begin
RaiseAbortOperation;
end;
end;
constructor TShellSetFilePropertyOperation.Create(aTargetFileSource: IFileSource;
var theTargetFiles: TFiles;
var theNewProperties: TFileProperties);
begin
FShellFileSource:= aTargetFileSource as IShellFileSource;
FFileOp:= CreateComObject(CLSID_FileOperation) as IFileOperation;
inherited Create(aTargetFileSource, theTargetFiles, theNewProperties);
// Assign after calling inherited constructor.
FSupportedProperties := [fpName];
end;
destructor TShellSetFilePropertyOperation.Destroy;
begin
inherited Destroy;
FreeAndNil(FSourceFilesTree);
end;
procedure TShellSetFilePropertyOperation.Initialize;
var
aFile: TFile;
Index: Integer;
AObject: PItemIDList;
begin
FStatistics := RetrieveStatistics;
FSourceFilesTree:= TItemList.Create;
try
for Index := 0 to TargetFiles.Count - 1 do
begin
aFile := TargetFiles[Index];
CheckObject(FShellFileSource.FindObject(aFile.FullPath, AObject), aFile.FullPath);
FSourceFilesTree.Add(AObject);
end;
except
on E: Exception do ShowError(E.Message);
end;
end;
procedure TShellSetFilePropertyOperation.MainExecute;
var
aFile: TFile;
dwCookie: DWORD;
aTemplateFile: TFile;
CurrentFileIndex: Integer;
ASink: TFileOperationProgressSink;
begin
ASink:= TFileOperationProgressSink.Create(@FStatistics, @UpdateStatistics);
FFileOp.SetOperationFlags(FOF_SILENT or FOF_NOCONFIRMMKDIR);
FFileOp.Advise(ASink, @dwCookie);
for CurrentFileIndex := 0 to FSourceFilesTree.Count - 1 do
begin
FCurrentFileIndex:= CurrentFileIndex;
AFile:= TargetFiles[FCurrentFileIndex];
if Assigned(TemplateFiles) and (FCurrentFileIndex < TemplateFiles.Count) then
aTemplateFile := TemplateFiles[FCurrentFileIndex]
else
aTemplateFile := nil;
SetProperties(FCurrentFileIndex, aFile, aTemplateFile);
with FStatistics do
begin
DoneFiles := DoneFiles + 1;
UpdateStatistics(FStatistics);
end;
CheckOperationState;
end;
FFileOp.Unadvise(dwCookie);
end;
function TShellSetFilePropertyOperation.SetNewProperty(aFile: TFile;
aTemplateProperty: TFileProperty): TSetFilePropertyResult;
var
PIDL: PItemIDList;
AItem: IShellItem;
begin
Result := sfprSuccess;
PIDL:= PItemIDList(FSourceFilesTree[FCurrentFileIndex]);
if Failed(SHCreateItemFromIDList(PIDL, IShellItem, AItem)) then
Exit(sfprError);
case aTemplateProperty.GetID of
fpName:
begin
if (aTemplateProperty as TFileNameProperty).Value <> aFile.Name then
begin
if not Succeeded(FFileOp.RenameItem(AItem, PWideChar(CeUtf8ToUtf16((aTemplateProperty as TFileNameProperty).Value)), nil)) then
Result := sfprError
else if not Succeeded(FFileOp.PerformOperations()) then
Result := sfprError
end
else
Result := sfprSkipped;
end
else
raise Exception.Create('Trying to set unsupported property');
end;
end;
end.

View file

@ -13,6 +13,7 @@ uses
type
PFileSourceDeleteOperationStatistics = ^TFileSourceDeleteOperationStatistics;
TFileSourceDeleteOperationStatistics = record
CurrentFile: String;
TotalFiles: Int64;

View file

@ -20,6 +20,7 @@ type
TSetFilePropertyResultFunction = procedure(Index: Integer; aFile: TFile;
aTemplate: TFileProperty; Result: TSetFilePropertyResult) of object;
PFileSourceSetFilePropertyOperationStatistics = ^TFileSourceSetFilePropertyOperationStatistics;
TFileSourceSetFilePropertyOperationStatistics = record
CurrentFile: String;
TotalFiles: Int64;

View file

@ -134,6 +134,7 @@ uses
, uDCReadRSVG, uFileSourceUtil, uGdiPlusJPEG, uListGetPreviewBitmap
, Dialogs, Clipbrd, uDebug, JwaDbt, uThumbnailProvider, uShellFolder
, uRecycleBinFileSource, uWslFileSource, uDCReadHEIF, uDCReadWIC
, uShellFileSource
{$IF DEFINED(DARKWIN)}
, uDarkStyle
{$ELSEIF DEFINED(LCLQT5)}
@ -586,6 +587,11 @@ begin
end;
// Register network file source
RegisterVirtualFileSource(rsVfsNetwork, TWinNetFileSource);
// Register shell folder file source
if (Win32MajorVersion > 5) then
begin
RegisterVirtualFileSource(TShellFileSource.RootName, TShellFileSource);
end;
// Register recycle bin file source
if CheckWin32Version(5, 1) then
begin

View file

@ -45,7 +45,9 @@ uses
Classes, SysUtils, Graphics, syncobjs, uFileSorting, DCStringHashListUtf8,
uFile, uIconTheme, uDrive, uDisplayFile, uGlobs, uDCReadPSD, uOSUtils,
uVectorImage
{$IF DEFINED(MSWINDOWS) and DEFINED(LCLQT5)}
{$IF DEFINED(MSWINDOWS)}
, ShlObj
{$ELSEIF DEFINED(MSWINDOWS) and DEFINED(LCLQT5)}
, fgl
{$ELSEIF DEFINED(UNIX)}
, DCFileAttributes
@ -122,6 +124,7 @@ type
FiEmblemOnline: PtrInt;
FiEmblemOffline: PtrInt;
FOneDrivePath: String;
FDesktopFolder: IShellFolder;
{$ELSEIF DEFINED(DARWIN)}
FUseSystemTheme: Boolean;
{$ELSEIF DEFINED(UNIX) AND NOT DEFINED(HAIKU)}
@ -188,6 +191,7 @@ type
{$ENDIF}
{$IF DEFINED(WINDOWS)}
function GetShellFolderIcon(AFile: TFile): PtrInt;
{en
Checks if the AIconName points to an icon resource in a library, executable, etc.
@param(AIconName
@ -357,7 +361,7 @@ uses
, uPixMapGtk, gdk2pixbuf, gdk2, glib2
{$ENDIF}
{$IFDEF MSWINDOWS}
, CommCtrl, ShellAPI, Windows, DCFileAttributes, uBitmap, uGdiPlus,
, ActiveX, CommCtrl, ShellAPI, Windows, DCFileAttributes, uBitmap, uGdiPlus,
IntfGraphics, DCConvertEncoding, uShlObjAdditional, uShellFolder
{$ELSE}
, StrUtils, Types, DCBasicTypes
@ -370,6 +374,11 @@ uses
{$ENDIF}
;
{$IF DEFINED(MSWINDOWS)}
type
TBitmap = Graphics.TBitmap;
{$ENDIF}
{$IF DEFINED(MSWINDOWS) OR DEFINED(RabbitVCS)}
const
SystemIconIndexStart: PtrInt = High(PtrInt) div 2;
@ -1330,6 +1339,46 @@ end;
{$ENDIF}
{$IFDEF WINDOWS}
function TPixMapManager.GetShellFolderIcon(AFile: TFile): PtrInt;
const
uFlags: UINT = SHGFI_SYSICONINDEX or SHGFI_PIDL;
var
PIDL: PItemIDList;
pchEaten: ULONG = 0;
AName: UnicodeString;
FileInfo: TSHFileInfoW;
dwAttributes: ULONG = 0;
begin
AName:= CeUtf8ToUtf16(AFile.LinkProperty.LinkTo);
if Succeeded(FDesktopFolder.ParseDisplayName(0, nil, PWideChar(AName),
pchEaten, PIDL, dwAttributes)) 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;
end;
finally
CoTaskMemFree(PIDL);
end;
// Could not retrieve the icon
if AFile.IsDirectory then
Result := FiDirIconID
else begin
Result := FiDefaultIconID;
end;
end;
function TPixMapManager.GetIconResourceIndex(const IconPath: String; out IconFile: String; out IconIndex: PtrInt): Boolean;
var
iPos, iIndex: Integer;
@ -1441,6 +1490,7 @@ begin
iIconSize := SHIL_EXTRALARGE;
end;
SHGetDesktopFolder(FDesktopFolder);
FSysImgList := SHGetSystemImageList(iIconSize);
{$ENDIF}
@ -1971,7 +2021,18 @@ begin
if Result < 0 then Result := FiDirIconID;
end;
Exit;
end
{$IF DEFINED(MSWINDOWS)}
else if (AFile.Attributes = (FILE_ATTRIBUTE_REPARSE_POINT or FILE_ATTRIBUTE_VIRTUAL)) and Assigned(AFile.LinkProperty) then
begin
if not LoadIcon then
Result := -1
else begin
Result:= GetShellFolderIcon(AFile);
end;
Exit;
end;
{$ENDIF}
end;
if IsDirectory or IsLinkToDirectory then

View file

@ -0,0 +1,80 @@
unit uShellFileOperation;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils,
Windows, ActiveX, ShlObj, ComObj, ShlWAPI, ShellAPI,
uShellFolder;
const
CLSID_FileOperation: TGUID = '{3ad05575-8857-4850-9277-11b85bdb8e09}';
type
IOperationsProgressDialog = IUnknown;
{ IObjectWithPropertyKey }
IObjectWithPropertyKey = interface(IUnknown)
['{fc0ca0a7-c316-4fd2-9031-3e628e6d4f23}']
function SetPropertyKey(key: REFPROPERTYKEY): HRESULT; stdcall;
function GetPropertyKey(var pkey: PROPERTYKEY): HRESULT; stdcall;
end;
{ IPropertyChange }
IPropertyChange = interface(IObjectWithPropertyKey)
['{f917bc8a-1bba-4478-a245-1bde03eb9431}']
function ApplyToPropVariant(propvarIn: REFPROPVARIANT; ppropvarOut: PPROPVARIANT): HRESULT; stdcall;
end;
{ IPropertyChangeArray }
IPropertyChangeArray = interface(IUnknown)
['{380f5cad-1b5e-42f2-805d-637fd392d31e}']
function GetCount(pcOperations: PUINT): HRESULT; stdcall;
function GetAt(iIndex: UINT; const riid: REFIID; out ppv): HRESULT; stdcall;
function InsertAt(iIndex: UINT; ppropChange: IPropertyChange): HRESULT; stdcall;
function Append(ppropChange: IPropertyChange): HRESULT; stdcall;
function AppendOrReplace(ppropChange: IPropertyChange): HRESULT; stdcall;
function RemoveAt(iIndex: UINT): HRESULT; stdcall;
function IsKeyInArray(key: REFPROPERTYKEY): HRESULT; stdcall;
end;
{ IFileOperation }
IFileOperation = interface(IUnknown)
['{947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8}']
function Advise(pfops: IFileOperationProgressSink; pdwCookie: PDWORD): HRESULT; stdcall;
function Unadvise(dwCookie: DWORD): HRESULT; stdcall;
function SetOperationFlags(dwOperationFlags: DWORD): HRESULT; stdcall;
function SetProgressMessage(pszMessage: LPCWSTR): HRESULT; stdcall;
function SetProgressDialog(popd: IOperationsProgressDialog): HRESULT; stdcall;
function SetProperties(pproparray: IPropertyChangeArray): HRESULT; stdcall;
function SetOwnerWindow(hwndOwner: HWND): HRESULT; stdcall;
function ApplyPropertiesToItem(psiItem: IShellItem): HRESULT; stdcall;
function ApplyPropertiesToItems(punkItems: IUnknown): HRESULT; stdcall;
function RenameItem(psiItem: IShellItem; pszNewName: LPCWSTR;
pfopsItem: IFileOperationProgressSink): HRESULT; stdcall;
function RenameItems(pUnkItems: IUnknown; pszNewName: LPCWSTR): HRESULT; stdcall;
function MoveItem(psiItem: IShellItem; psiDestinationFolder: IShellItem;
pszNewName: LPCWSTR; pfopsItem: IFileOperationProgressSink): HRESULT; stdcall;
function MoveItems(punkItems: IUnknown; psiDestinationFolder: IShellItem): HRESULT; stdcall;
function CopyItem(psiItem: IShellItem; psiDestinationFolder: IShellItem;
pszCopyName: LPCWSTR; pfopsItem: IFileOperationProgressSink): HRESULT; stdcall;
function CopyItems(punkItems: IUnknown; psiDestinationFolder: IShellItem): HRESULT; stdcall;
function DeleteItem(psiItem: IShellItem; pfopsItem: IFileOperationProgressSink): HRESULT; stdcall;
function DeleteItems(punkItems: IUnknown): HRESULT; stdcall;
function NewItem(psiDestinationFolder: IShellItem; dwFileAttributes: DWORD;
pszName: LPCWSTR; pszTemplateName: LPCWSTR; pfopsItem: IFileOperationProgressSink): HRESULT; stdcall;
function PerformOperations(): HRESULT; stdcall;
function GetAnyOperationsAborted(pfAnyOperationsAborted: PBOOL): HRESULT; stdcall;
end;
implementation
end.

View file

@ -98,6 +98,7 @@ function GetKnownFolderPath(const rfid: TGUID; out APath: String): Boolean;
function MultiFileProperties(pdtobj: IDataObject; dwFlags: DWORD): HRESULT;
function GetIsFolder(AParent: IShellFolder; PIDL: PItemIDList): Boolean;
function GetDisplayName(AFolder: IShellFolder; PIDL: PItemIDList; Flags: DWORD): String;
function GetDetails(AFolder: IShellFolder2; PIDL: PItemIDList; const pscid: SHCOLUMNID): OleVariant;
@ -131,6 +132,15 @@ begin
Result:= SHMultiFileProperties(pdtobj, dwFlags);
end;
function GetIsFolder(AParent: IShellFolder; PIDL: PItemIDList): Boolean;
var
Flags: LongWord;
begin
Flags:= SFGAO_FOLDER;
AParent.GetAttributesOf(1, PIDL, Flags);
Result:= (SFGAO_FOLDER and Flags) <> 0;
end;
function GetDisplayName(AFolder: IShellFolder; PIDL: PItemIDList;
Flags: DWORD): String;
var