📄 bsskinshellctrls.pas
字号:
SCmdVerbOpen = 'open';
SCmdVerbRename = 'rename';
SCmdVerbDelete = 'delete';
SCmdVerbPaste = 'paste';
implementation
{$R bsSkinShellCtrls}
uses ShellAPI, ComObj, TypInfo, Consts, Math, bsConst;
const
nFolder: array[TRootFolder] of Integer =
(CSIDL_DESKTOP, CSIDL_DRIVES, CSIDL_NETWORK, CSIDL_BITBUCKET, CSIDL_APPDATA,
CSIDL_COMMON_DESKTOPDIRECTORY, CSIDL_COMMON_PROGRAMS, CSIDL_COMMON_STARTMENU,
CSIDL_COMMON_STARTUP, CSIDL_CONTROLS, CSIDL_DESKTOPDIRECTORY, CSIDL_FAVORITES,
CSIDL_FONTS, CSIDL_INTERNET, CSIDL_PERSONAL, CSIDL_PRINTERS, CSIDL_PRINTHOOD,
CSIDL_PROGRAMS, CSIDL_RECENT, CSIDL_SENDTO, CSIDL_STARTMENU, CSIDL_STARTUP,
CSIDL_TEMPLATES);
SHGFI = SHGFI_SYSICONINDEX or SHGFI_SMALLICON;
var
cmvProperties: PChar = 'properties'; { Do not localize }
ICM: IContextMenu = nil;
ICM2: IContextMenu2 = nil;
DesktopFolder: TbsShellFolder = nil;
CS : TRTLCriticalSection;
{ PIDL manipulation }
procedure debug(Comp:TComponent; msg:string);
begin
ShowMessage(Comp.Name + ':' + msg);
end;
function CreatePIDL(Size: Integer): PItemIDList;
var
Malloc: IMalloc;
begin
OleCheck(SHGetMalloc(Malloc));
Result := Malloc.Alloc(Size);
if Assigned(Result) then
FillChar(Result^, Size, 0);
end;
function NextPIDL(IDList: PItemIDList): PItemIDList;
begin
Result := IDList;
Inc(PChar(Result), IDList^.mkid.cb);
end;
procedure StripLastID(IDList: PItemIDList);
var
MarkerID: PItemIDList;
begin
MarkerID := IDList;
if Assigned(IDList) then
begin
while IDList.mkid.cb <> 0 do
begin
MarkerID := IDList;
IDList := NextPIDL(IDList);
end;
MarkerID.mkid.cb := 0;
end;
end;
function GetItemCount(IDList: PItemIDList): Integer;
begin
Result := 0;
while IDList^.mkid.cb <> 0 do
begin
Inc(Result);
IDList := NextPIDL(IDList);
end;
end;
function GetPIDLSize(IDList: PItemIDList): Integer;
begin
Result := 0;
if Assigned(IDList) then
begin
Result := SizeOf(IDList^.mkid.cb);
while IDList^.mkid.cb <> 0 do
begin
Result := Result + IDList^.mkid.cb;
IDList := NextPIDL(IDList);
end;
end;
end;
function CopyPIDL(IDList: PItemIDList): PItemIDList;
var
Size: Integer;
begin
Size := GetPIDLSize(IDList);
Result := CreatePIDL(Size);
if Assigned(Result) then
CopyMemory(Result, IDList, Size);
end;
function ConcatPIDLs(IDList1, IDList2: PItemIDList): PItemIDList;
var
cb1, cb2: Integer;
begin
if Assigned(IDList1) then
cb1 := GetPIDLSize(IDList1) - SizeOf(IDList1^.mkid.cb)
else
cb1 := 0;
cb2 := GetPIDLSize(IDList2);
Result := CreatePIDL(cb1 + cb2);
if Assigned(Result) then
begin
if Assigned(IDList1) then
CopyMemory(Result, IDList1, cb1);
CopyMemory(PChar(Result) + cb1, IDList2, cb2);
end;
end;
procedure DisposePIDL(PIDL: PItemIDList);
var
MAlloc: IMAlloc;
begin
OLECheck(SHGetMAlloc(MAlloc));
MAlloc.Free(PIDL);
end;
function RelativeFromAbsolute(AbsoluteID: PItemIDList): PItemIDList;
begin
Result := AbsoluteID;
while GetItemCount(Result) > 1 do
Result := NextPIDL(Result);
Result := CopyPIDL(Result);
end;
function CreatePIDLList(ID: PItemIDList): TList;
var
TempID: PItemIDList;
begin
Result := TList.Create;
TempID := ID;
while TempID.mkid.cb <> 0 do
begin
TempID := CopyPIDL(TempID);
Result.Insert(0, TempID);
StripLastID(TempID);
end;
end;
procedure DestroyPIDLList(List: TList);
var
I: Integer;
begin
If List = nil then Exit;
for I := 0 to List.Count-1 do
DisposePIDL(List[I]);
List.Free;
end;
{ Miscellaneous }
procedure NoFolderDetails(AFolder: TbsShellFolder; HR: HResult);
begin
Raise EInvalidPath.CreateFmt(SShellNoDetails, [AFolder.DisplayName, HR]);
end;
function DesktopShellFolder: IShellFolder;
begin
OleCheck(SHGetDesktopFolder(Result));
end;
procedure CreateDesktopFolder;
var
DesktopPIDL: PItemIDList;
begin
SHGetSpecialFolderLocation(0, nFolder[rfDesktop], DesktopPIDL);
if DesktopPIDL <> nil then
DesktopFolder := TbsShellFolder.Create(nil, DesktopPIDL, DesktopShellFolder);
end;
function SamePIDL(ID1, ID2: PItemIDList): boolean;
begin
Result := DesktopShellFolder.CompareIDs(0, ID1, ID2) = 0;
end;
function DesktopPIDL: PItemIDList;
begin
OleCheck(SHGetSpecialFolderLocation(0, nFolder[rfDesktop], Result));
end;
function GetCSIDLType(const Value: string): TRootFolder;
begin
{$R+}
Result := TRootFolder(GetEnumValue(TypeInfo(TRootFolder), Value))
{$R-}
end;
function IsElement(Element, Flag: Integer): Boolean;
begin
Result := Element and Flag <> 0;
end;
function GetShellImage(PIDL: PItemIDList; Large, Open: Boolean): Integer;
var
FileInfo: TSHFileInfo;
Flags: Integer;
begin
Flags := SHGFI_PIDL or SHGFI_SYSICONINDEX;
if Open then Flags := Flags or SHGFI_OPENICON;
if Large then Flags := Flags or SHGFI_LARGEICON
else Flags := Flags or SHGFI_SMALLICON;
SHGetFileInfo(PChar(PIDL),
0,
FileInfo,
SizeOf(FileInfo),
Flags);
Result := FileInfo.iIcon;
end;
function GetCaps(ParentFolder: IShellFolder; PIDL: PItemIDList): TbsShellFolderCapabilities;
var
Flags: LongWord;
begin
Result := [];
Flags := SFGAO_CAPABILITYMASK;
ParentFolder.GetAttributesOf(1, PIDL, Flags);
if IsElement(SFGAO_CANCOPY, Flags) then Include(Result, fcCanCopy);
if IsElement(SFGAO_CANDELETE, Flags) then Include(Result, fcCanDelete);
if IsElement(SFGAO_CANLINK, Flags) then Include(Result, fcCanLink);
if IsElement(SFGAO_CANMOVE, Flags) then Include(Result, fcCanMove);
if IsElement(SFGAO_CANRENAME, Flags) then Include(Result, fcCanRename);
if IsElement(SFGAO_DROPTARGET, Flags) then Include(Result, fcDropTarget);
if IsElement(SFGAO_HASPROPSHEET, Flags) then Include(Result, fcHasPropSheet);
end;
function GetProperties(ParentFolder: IShellFolder; PIDL: PItemIDList): TbsShellFolderProperties;
var
Flags: LongWord;
begin
Result := [];
if ParentFolder = nil then Exit;
Flags := SFGAO_DISPLAYATTRMASK;
ParentFolder.GetAttributesOf(1, PIDL, Flags);
if IsElement(SFGAO_GHOSTED, Flags) then Include(Result, fpCut);
if IsElement(SFGAO_LINK, Flags) then Include(Result, fpIsLink);
if IsElement(SFGAO_READONLY, Flags) then Include(Result, fpReadOnly);
if IsElement(SFGAO_SHARE, Flags) then Include(Result, fpShared);
Flags := 0;
ParentFolder.GetAttributesOf(1, PIDL, Flags);
if IsElement(SFGAO_FILESYSTEM, Flags) then Include(Result, fpFileSystem);
if IsElement(SFGAO_FILESYSANCESTOR, Flags) then Include(Result, fpFileSystemAncestor);
if IsElement(SFGAO_REMOVABLE, Flags) then Include(Result, fpRemovable);
if IsElement(SFGAO_VALIDATE, Flags) then Include(Result, fpValidate);
end;
function GetIsFolder(Parentfolder: IShellFolder; PIDL: PItemIDList): Boolean;
var
Flags: LongWord;
begin
Flags := SFGAO_FOLDER;
ParentFolder.GetAttributesOf(1, PIDL, Flags);
Result := SFGAO_FOLDER and Flags <> 0;
end;
function GetHasSubFolders(Parentfolder: IShellFolder; PIDL: PItemIDList): Boolean;
var
Flags: LongWord;
begin
Flags := SFGAO_CONTENTSMASK;
ParentFolder.GetAttributesOf(1, PIDL, Flags);
Result := SFGAO_HASSUBFOLDER and Flags <> 0;
end;
function GetHasSubItems(ShellFolder: IShellFolder; Flags: Integer): Boolean;
var
ID: PItemIDList;
EnumList: IEnumIDList;
NumIDs: LongWord;
HR: HResult;
ErrMode: Integer;
begin
Result := False;
if ShellFolder = nil then Exit;
ErrMode := SetErrorMode(SEM_FAILCRITICALERRORS);
try
HR := ShellFolder.EnumObjects(0,
Flags,
EnumList);
if HR <> S_OK then Exit;
Result := EnumList.Next(1, ID, NumIDs) = S_OK;
finally
SetErrorMode(ErrMode);
end;
end;
function StrRetToString(PIDL: PItemIDList; StrRet: TStrRet; Flag:string=''): string;
var
P: PChar;
begin
case StrRet.uType of
STRRET_CSTR:
SetString(Result, StrRet.cStr, lStrLen(StrRet.cStr));
STRRET_OFFSET:
begin
P := @PIDL.mkid.abID[StrRet.uOffset - SizeOf(PIDL.mkid.cb)];
SetString(Result, P, PIDL.mkid.cb - StrRet.uOffset);
end;
STRRET_WSTR:
if Assigned(StrRet.pOleStr) then
Result := StrRet.pOleStr
else
Result := '';
end;
{ This is a hack bug fix to get around Windows Shell Controls returning
spurious "?"s in date/time detail fields }
if (Length(Result) > 1) and (Result[1] = '?') and (Result[2] in ['0'..'9']) then
Result := StringReplace(Result,'?','',[rfReplaceAll]);
end;
function GetDisplayName(Parentfolder: IShellFolder; PIDL: PItemIDList;
Flags: DWORD): string;
var
StrRet: TStrRet;
begin
Result := '';
if ParentFolder = nil then
begin
Result := 'parentfolder = nil'; { Do not localize }
exit;
end;
FillChar(StrRet, SizeOf(StrRet), 0);
ParentFolder.GetDisplayNameOf(PIDL, Flags, StrRet);
Result := StrRetToString(PIDL, StrRet);
{ TODO 2 -oMGD -cShell Controls : Remove this hack (on Win2k, GUIDs are returned for the
PathName of standard folders)}
if (Pos('::{', Result) = 1) then
Result := GetDisplayName(ParentFolder, PIDL, SHGDN_NORMAL);
end;
function ObjectFlags(ObjectTypes: TShellObjectTypes): Integer;
begin
Result := 0;
if otFolders in ObjectTypes then Inc(Result, SHCONTF_FOLDERS);
if otNonFolders in ObjectTypes then Inc(Result, SHCONTF_NONFOLDERS);
if otHidden in ObjectTypes then Inc(Result, SHCONTF_INCLUDEHIDDEN);
end;
procedure InvokeContextMenu(Owner: TWinControl; AFolder: TbsShellFolder; 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);
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -