📄 shellctrls.pas
字号:
if otHidden in ObjectTypes then Inc(Result, SHCONTF_INCLUDEHIDDEN);
end;
procedure InvokeContextMenu(Owner: TWinControl; AFolder: TShellFolder; X, Y: Integer);
var
PIDL: PItemIDList;
CM: IContextMenu;
Menu: HMenu;
ICI: TCMInvokeCommandInfo;
P: TPoint;
Command: LongBool;
ICmd: integer;
ZVerb: array[0..255] of char;
Verb: string;
Handled: boolean;
SCV: IShellCommandVerb;
HR: HResult;
begin
if AFolder = nil then Exit;
PIDL := AFolder.RelativeID;
AFolder.ParentShellFolder.GetUIObjectOf(Owner.Handle, 1, PIDL, IID_IContextMenu, nil, CM);
if CM = nil then Exit;
P.X := X;
P.Y := Y;
Windows.ClientToScreen(Owner.Handle, P);
Menu := CreatePopupMenu;
try
CM.QueryContextMenu(Menu, 0, 1, $7FFF, CMF_EXPLORE or CMF_CANRENAME);
CM.QueryInterface(IID_IContextMenu2, ICM2); //To handle submenus.
try
Command := TrackPopupMenu(Menu, TPM_LEFTALIGN or TPM_LEFTBUTTON or TPM_RIGHTBUTTON or
TPM_RETURNCMD, P.X, P.Y, 0, Owner.Handle, nil);
finally
ICM2 := nil;
end;
if Command then
begin
ICmd := LongInt(Command) - 1;
HR := CM.GetCommandString(ICmd, GCS_VERBA, nil, ZVerb, SizeOf(ZVerb));
Verb := StrPas(ZVerb);
Handled := False;
if Supports(Owner, IShellCommandVerb, SCV) then
begin
HR := 0;
SCV.ExecuteCommand(Verb, Handled);
end;
if not Handled then
begin
FillChar(ICI, SizeOf(ICI), #0);
with ICI do
begin
cbSize := SizeOf(ICI);
hWND := Owner.Handle;
lpVerb := MakeIntResource(ICmd);
nShow := SW_SHOWNORMAL;
end;
HR := CM.InvokeCommand(ICI);
end;
if Assigned(SCV) then
SCV.CommandCompleted(Verb, HR = S_OK);
end;
finally
DestroyMenu(Menu);
end;
end;
procedure DoContextMenuVerb(AFolder: TShellFolder; Verb: PChar);
var
ICI: TCMInvokeCommandInfo;
CM: IContextMenu;
PIDL: PItemIDList;
begin
if AFolder = nil then Exit;
FillChar(ICI, SizeOf(ICI), #0);
with ICI do
begin
cbSize := SizeOf(ICI);
fMask := CMIC_MASK_ASYNCOK;
hWND := 0;
lpVerb := Verb;
nShow := SW_SHOWNORMAL;
end;
PIDL := AFolder.RelativeID;
AFolder.ParentShellFolder.GetUIObjectOf(0, 1, PIDL, IID_IContextMenu, nil, CM);
CM.InvokeCommand(ICI);
end;
function GetIShellFolder(IFolder: IShellFolder; PIDL: PItemIDList;
Handle: THandle = 0): IShellFolder;
var
HR : HResult;
begin
if Assigned(IFolder) then
begin
HR := IFolder.BindToObject(PIDL, nil, IID_IShellFolder, Pointer(Result));
if HR <> S_OK then
IFolder.GetUIObjectOf(Handle, 1, PIDL, IID_IShellFolder, nil, Pointer(Result));
if HR <> S_OK then
IFolder.CreateViewObject(Handle, IID_IShellFolder, Pointer(Result));
end;
if not Assigned(Result) then
DesktopShellFolder.BindToObject(PIDL, nil, IID_IShellFolder, Pointer(Result));
end;
function GetIShellDetails(IFolder: IShellFolder; PIDL: PItemIDList;
Handle: THandle = 0): IShellDetails;
var
HR : HResult;
begin
if Assigned(IFolder) then
begin
HR := IFolder.BindToObject(PIDL, nil, IID_IShellDetails, Pointer(Result));
if HR <> S_OK then
IFolder.GetUIObjectOf(Handle, 1, PIDL, IID_IShellDetails, nil, Pointer(Result));
if HR <> S_OK then
IFolder.CreateViewObject(Handle, IID_IShellDetails, Pointer(Result));
end;
if not Assigned(Result) then
DesktopShellFolder.BindToObject(PIDL, nil, IID_IShellDetails, Pointer(Result));
end;
function GetIShellFolder2(IFolder: IShellFolder; PIDL: PItemIDList;
Handle: THandle = 0): IShellFolder2;
var
HR : HResult;
begin
if (Win32MajorVersion >= 5) then
begin
HR := DesktopShellFolder.BindToObject(PIDL, nil, IID_IShellFolder2, Pointer(Result));
if HR <> S_OK then
IFolder.GetUIObjectOf(Handle, 1, PIDL, IID_IShellFolder2, nil, Pointer(Result));
if (HR <> S_OK) and (IFolder <> nil) then
IFolder.BindToObject(PIDL, nil, IID_IShellFolder2, Pointer(Result));
end
else
Result := nil;
end;
function CreateRootFromPIDL(Value: PItemIDList): TShellFolder;
var
SF: IShellFolder;
begin
SF := GetIShellFolder(DesktopShellFolder, Value);
if SF = NIL then SF := DesktopShellFolder;
//special case - Desktop folder can't bind to itself.
Result := TShellFolder.Create(DesktopFolder, Value, SF);
end;
function CreateRootFolder(RootFolder: TShellFolder; OldRoot : TRoot;
var NewRoot: TRoot): TShellFolder;
var
P: PWideChar;
NewPIDL: PItemIDList;
NumChars,
Flags,
HR: LongWord;
ErrorMsg : string;
begin
HR := S_FALSE;
if GetEnumValue(TypeInfo(TRootFolder), NewRoot) >= 0 then
begin
HR := SHGetSpecialFolderLocation(
0,
nFolder[GetCSIDLType(NewRoot)],
NewPIDL);
end
else if Length(NewRoot) > 0 then
begin
if NewRoot[Length(NewRoot)] = ':' then NewRoot := NewRoot + '\';
NumChars := Length(NewRoot);
Flags := 0;
P := StringToOleStr(NewRoot);
HR := DesktopShellFolder.ParseDisplayName(0, nil, P, NumChars, NewPIDL, Flags);
end;
if HR <> S_OK then
begin
{ TODO : Remove the next line? }
// Result := RootFolder;
ErrorMsg := Format( SErrorSettingPath, [ NewRoot ] );
NewRoot := OldRoot;
raise Exception.Create( ErrorMsg );
end;
Result := CreateRootFromPIDL(NewPIDL);
if Assigned(RootFolder) then RootFolder.Free;
end;
{ TShellFolder }
constructor TShellFolder.Create(AParent: TShellFolder; ID: PItemIDList;
SF: IShellFolder);
var
DesktopID: PItemIDList;
begin
inherited Create;
FLevel := 0;
FDetails := TStringList.Create;
FIShellFolder := SF;
FIShellFolder2 := nil;
FIShellDetails := nil;
FParent := AParent;
FPIDL := CopyPIDL(ID);
if FParent <> nil then
FFullPIDL := ConcatPIDLs(AParent.FFullPIDL, ID)
else
begin
DesktopID := DesktopPIDL;
try
FFullPIDL := ConcatPIDLs(DesktopID, ID);
finally
DisposePIDL(DesktopID);
end;
end;
if FParent = nil then
FParent := DesktopFolder;
while AParent <> nil do
begin
AParent := AParent.Parent;
if AParent <> nil then Inc(FLevel);
end;
end;
destructor TShellFolder.Destroy;
begin
if Assigned(FDetails) then
FDetails.Free;
FDetails := nil;
if Assigned(FPIDL) then
DisposePIDL(FPIDL);
if Assigned(FFullPIDL) then
DisposePIDL(FFullPIDL);
inherited Destroy;
end;
function TShellFolder.GetDetailInterface: IInterface;
begin
if (not Assigned(FDetailInterface)) and Assigned(FIShellFolder) then
begin
FIShellDetails := GetIShellDetails(FIShellFolder, FFullPIDL, FViewHandle);
if (not Assigned(FIShellDetails)) and (Win32MajorVersion >= 5) then
begin
FIShellFolder2 := GetIShellFolder2(FIShellFolder, FFullPIDL, FViewHandle);
if not Assigned(FIShellFolder2) then // Hack!
{ Note: Although QueryInterface will not work in this instance,
IShellFolder2 is indeed supported for this Folder if IShellDetails
is not. In all tested cases, hard-casting the interface to
IShellFolder2 has worked. Hopefully, Microsoft will fix this bug in
a future release of ShellControls }
FIShellFolder2 := IShellFolder2(FIShellFolder);
end;
if Assigned(FIShellFolder2) then
Result := FIShellFolder2
else
Result := FIShellDetails;
FDetailInterface := Result;
end
else
Result := FDetailInterface;
end;
function TShellFolder.GetShellDetails: IShellDetails;
begin
if not Assigned(FDetailInterface) then
GetDetailInterface;
Result := FIShellDetails;
end;
function TShellFolder.GetShellFolder2: IShellFolder2;
begin
if not Assigned(FDetailInterface) then
GetDetailInterface;
Result := FIShellFolder2;
end;
procedure TShellFolder.LoadColumnDetails(RootFolder: TShellFolder;
Handle: THandle; ColumnCount: integer);
procedure GetDetailsOf(AFolder: TShellFolder; var Details: TWin32FindData);
var
szPath: array[ 0 .. MAX_PATH] of char;
Path: string;
Handle: THandle;
begin
FillChar(Details, SizeOf(Details), 0);
FillChar(szPath,MAX_PATH,0);
Path := AFolder.PathName;
Handle := Windows.FindFirstFile(PChar(Path), Details);
try
if Handle = INVALID_HANDLE_VALUE then
NoFolderDetails(AFolder, Windows.GetLastError);
finally
Windows.FindClose(Handle);
end;
end;
function CalcFileSize(FindData: TWin32FindData): int64;
begin
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
Result := FindData.nFileSizeHigh * MAXDWORD + FindData.nFileSizeLow
else
Result := -1;
end;
function CalcModifiedDate(FindData: TWin32FindData): TDateTime;
var
LocalFileTime: TFileTime;
Age : integer;
begin
if (FindData.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) = 0 then
begin
FileTimeToLocalFileTime(FindData.ftLastWriteTime, LocalFileTime);
if FileTimeToDosDateTime(LocalFileTime, LongRec(Age).Hi,
LongRec(Age).Lo) then
begin
Result := FileDateToDateTime(Age);
Exit;
end;
end;
Result := -1;
end;
function DefaultDetailColumn(FindData: TWin32FindData; Col: integer): string;
begin
case Col of
//1 : Result := FindData.cFileName; // Name
1 : Result := IntToStr(CalcFileSize(FindData)); // Size
2 : Result := ExtractFileExt(FindData.cFileName); // Type
3 : Result := DateTimeToStr(CalcModifiedDate(FindData)); // Modified
4 : Result := IntToStr(FindData.dwFileAttributes);
end;
end;
procedure AddDetail(HR: HResult; PIDL: PItemIDList; SD: TShellDetails);
begin
if HR = S_OK then
FDetails.Add(StrRetToString(PIDL, SD.str))
else
FDetails.Add('');
end;
var
SF2: IShellFolder2;
ISD: IShellDetails;
J: Integer;
SD: TShellDetails;
HR: HResult;
//AFolder: TShellFolder;
FindData: TWin32FindData;
begin
if not Assigned(FDetails) or (FDetails.Count >= ColumnCount) then Exit; // Details are loaded
FDetails.Clear;
FViewHandle := Handle;
SF2 := RootFolder.ShellFolder2;
{//!
if fpFileSystem in Properties then
ColumnCount := 4;
}
if Assigned(SF2) then
begin
// Already have name and icon, so see if we can provide details
for J := 1 to ColumnCount do
begin
HR := SF2.GetDetailsOf(FPIDL, J, SD);
AddDetail(HR, FPIDL, SD);
end;
end
else
begin
ISD := RootFolder.ShellDetails;
if Assigned(ISD) then
begin
for J := 1 to ColumnCount do
begin
HR := ISD.GetDetailsOf(FPIDL, J, SD);
AddDetail(HR, FPIDL, SD);
end;
end
else if (fpFileSystem in RootFolder.Properties) then
begin
GetDetailsOf(Self, FindData);
for J := 1 to ColumnCount do
FDetails.Add(DefaultDetailColumn(FindData, J));
end;
end;
end;
function TShellFolder.GetDetails(Index: integer): string;
begin
if FDetails.Count > 0 then
Result := FDetails[Index-1] // Index is 1-based
else
Raise Exception.CreateFmt(SCallLoadDetails, [ Self.DisplayName ] );
end;
procedure TShellFolder.SetDetails(Index: integer; const Value: string);
begin
if Index < FDetails.Count then
FDetails[Index - 1] := Value // Index is 1-based
else
FDetails.Insert(Index - 1, Value); // Index is 1-based
end;
function TShellFolder.ParentShellFolder: IShellFolder;
begin
if FParent <> nil then
Result := FParent.ShellFolder
else
OLECheck(SHGetDesktopFolder(Result));
end;
function TShellFolder.Properties: TShellFolderProperties;
begin
Result := GetProperties(ParentShellFolder, FPIDL);
end;
function TShellFolder.Capabilities: TShellFolderCapabilities;
begin
Result := GetCaps(ParentShellFolder, FPIDL);
end;
function TShellFolder.SubFolders: Boolean;
begin
Result := GetHasSubFolders(ParentShellFolder, FPIDL);
end;
function TShellFolder.IsFolder: Boolean;
begin
Result := GetIsFolder(ParentShellFolder, FPIDL);
end;
function TShellFolder.PathName: string;
begin
Result := GetDisplayName(DesktopShellFolder, FFullPIDL, SHGDN_FORPARSING);
end;
function TShellFolder.DisplayName: string;
var
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -