其他语言

位置:IT落伍者 >> 其他语言 >> 浏览文章

用Delphi实现Windows文件夹管理树


发布日期:2020年06月18日
 
用Delphi实现Windows文件夹管理树

摘要本文利用Windows名空间所提供的IShellFolder接口用Delphi实现了文件夹管理树的生成

关键字文件夹 接口 Delphi

概述

Windows/视觉感观上区别Windows的一个重要方面就是大量采用了树形视图控件资源管理器左侧的文件夹管理树便是如此它将本地和网络上的文件夹和文件等资源以层次树的方式罗列出来为用户集中管理计算机提供了极大便利同时在外貌上也焕然一新Delphi为我们提供了大量Windows标准控件但遗憾的是在目录浏览方面却只提供了一个Windows样式的DirectoryListBox(Delphi的测试版也是如此)因此在Delphi中实现Windows文件夹管理树对开发更地道的Windows程序有着重大意义

实现原理

Windows文件夹管理树的实现实质上是对Windows名空间(Namespace)的遍历名空间中每个文件夹都提供了一个IShellFolder接口遍历名空间的方法是

)调用SHGetDesktopFolder函数获得桌面文件夹的IShellFolder接口桌面文件夹是文件夹管理树的根节点

)再调用所获得的IShellFolder接口的EnumObjects成员函数列举出子文件夹

)调用IShellFolder的BindToObject成员函数获得子文件夹的IShellFolder接口 )重复步骤)列举出某文件夹下的所有子文件夹只至所获得的IShellFolder接口为nil为止

下面解释将要用到的几个主要函数它们在ShlObj单元中定义

)function SHGetDesktopFolder(var ppshf: IShellFolder): HResult;

该函数通过ppshf获得桌面文件夹的IShellFolder接口

)function IShellFolderEnumObjects(hwndOwner: HWND; grfFlags: DWORD;out EnumIDList: IEnumIDList): HResult;

该函数获得一个IEnumIDList接口通过调用该接口的Next等函数可以列举出IShellFolder接口所对应的文件夹的内容内容的类型由grfFlags来指定我们需要列举出子文件夹来因此grfFlags的值指定为SHCONTF_FOLDERSHwndOwner是属主窗口的句柄

)function IShellFolderBindToObject(pidl: PItemIDList; pbcReserved: Pointer;const riid: TIID; out ppvOut: Pointer): HResult;

该函数获得某个子文件夹的IShellFolder接口该接口由ppvOut返回pidl是一个指向元素标识符列表的指针Windows/中用元素标识符和元素标识符列表来标识名空间中的对象它们分别类似于文件名和路径需要特别指出的是pidl作为参数传递给Shell API函数时必须是相对于桌面文件夹的绝对路径而传递给IShellFolder接口的成员函数时则应是相对于该接口所对应文件夹的相对路径pbcReserved应指定为nilriid则应指定为IID_IShellFolder

其它函数可以查阅Delphi提供的《Win Programmers Reference》

程序清单

下面的源代码在Windows中实现并在Windows测试版中测试无误(程序运行结果如图所示)有兴趣的读者可以将其改写成Delphi组件以备常用

unit BrowseTreeView;

interface

uses

Windows Messages SysUtils Classes Graphics Controls Forms Dialogs

ShlObj ComCtrls;

type

PTreeViewItem = ^TTreeViewItem;

TTreeViewItem = record

ParentFolder: IShellFolder; // 接点对应的文件夹的父文件夹的IShellFolder接口

Pidl FullPidl: PItemIDList; // 接点对应的文件夹的相对和绝对项目标识符列表

HasExpanded: Boolean; // 接点是否展开

end;

TForm = class(TForm)

TreeView: TTreeView;

procedure FormDestroy(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure TreeViewExpanding(Sender: TObject; Node: TTreeNode;

var AllowExpansion: Boolean);

private

FItemList: TList;

procedure SetTreeViewImageList;

procedure FillTreeView(Folder: IShellFolder; FullPIDL: PItemIDList; ParentNode: TTreeNode);

end;

var

Form: TForm;

implementation

{$R *DFM}

uses

ActiveX ComObj ShellAPI CommCtrl;

// 以下是几个对项目标识符进行操作的函数

procedure DisposePIDL(ID: PItemIDList);

var

Malloc: IMalloc;

begin

if ID = nil then Exit;

OLECheck(SHGetMalloc(Malloc));

MallocFree(ID);

end;

function CopyItemID(ID: PItemIDList): PItemIDList;

var

Malloc: IMalloc;

begin

Result := nil;

OLECheck(SHGetMalloc(Malloc));

if Assigned(ID) then

begin

Result := MallocAlloc(ID^mkidcb + sizeof(ID^mkidcb));

CopyMemory(Result ID ID^mkidcb + sizeof(ID^mkidcb));

end;

end;

function NextPIDL(ID: PItemIDList): PItemIDList;

begin

Result := ID;

Inc(PChar(Result) ID^mkidcb);

end;

function GetPIDLSize(ID: PItemIDList): Integer;

begin

Result := ;

if Assigned(ID) then

begin

Result := sizeof(ID^mkidcb);

while ID^mkidcb <> do

begin

Inc(Result ID^mkidcb);

ID := NextPIDL(ID);

end;

end;

end;

function CreatePIDL(Size: Integer): PItemIDList;

var

Malloc: IMalloc;

HR: HResult;

begin

Result := nil;

HR := SHGetMalloc(Malloc);

if Failed(HR) then Exit;

try

Result := MallocAlloc(Size);

if Assigned(Result) then

FillChar(Result^ Size );

finally

end;

end;

function ConcatPIDLs(ID ID: PItemIDList): PItemIDList;

var

cb cb: Integer;

begin

if Assigned(ID) then

cb := GetPIDLSize(ID) sizeof(ID^mkidcb)

else

cb := ;

cb := GetPIDLSize(ID);

Result := CreatePIDL(cb + cb);

if Assigned(Result) then

begin

if Assigned(ID) then

CopyMemory(Result ID cb);

CopyMemory(PChar(Result) + cb ID cb);

end;

end;

// 将二进制表示的项目标识符列表转换成有可识的项目名

function GetDisplayName(Folder: IShellFolder; PIDL: PItemIDList;

ForParsing: Boolean): String;

var

StrRet: TStrRet;

P: PChar;

Flags: Integer;

begin

Result := ;

if ForParsing then

Flags := SHGDN_FORPARSING

else

Flags := SHGDN_NORMAL;

FolderGetDisplayNameOf(PIDL Flags StrRet);

case StrRetuType of

STRRET_CSTR:

SetString(Result StrRetcStr lStrLen(StrRetcStr));

STRRET_OFFSET:

begin

P := @PIDLmkidabID[StrRetuOffset sizeof(PIDLmkidcb)];

SetString(Result P PIDLmkidcb StrRetuOffset);

end;

STRRET_WSTR:

Result := StrRetpOleStr;

end;

end;

function GetIcon(PIDL: PItemIDList; Open: Boolean): Integer;

const

IconFlag = SHGFI_PIDL or SHGFI_SYSICONINDEX or SHGFI_SMALLICON;

var

FileInfo: TSHFileInfo;

Flags: Integer;

begin

if Open then

Flags := IconFlag or SHGFI_OPENICON

else

Flags := IconFlag;

SHGetFileInfo(PChar(PIDL) FileInfo sizeof(TSHFileInfo) Flags);

Result := FileInfoiIcon;

end;

// 获得每个文件夹在系统中的图标

procedure GetItemIcons(FullPIDL: PItemIDList; TreeNode: TTreeNode);

begin

with TreeNode do

begin

ImageIndex := GetIcon(FullPIDL False);

SelectedIndex := GetIcon(FullPIDL True);

end;

end;

// 获得系统的图标列表

procedure TFormSetTreeViewImageList;

var

ImageList: THandle;

FileInfo: TSHFileInfo;

begin

ImageList := SHGetFileInfo(PChar(C:\) FileInfo

sizeof(TSHFileInfo) SHGFI_SYSICONINDEX or SHGFI_SMALLICON);

if ImageList <> then

TreeView_SetImageList(TreeViewHandle ImageList );

end;

// 生成文件夹管理树

procedure TFormFillTreeView(Folder: IShellFolder;

FullPIDL: PItemIDList; ParentNode: TTreeNode);

var

TreeViewItem: PTreeViewItem;

EnumIDList: IEnumIDList;

PIDLs FullItemPIDL: PItemIDList;

NumID: LongWord;

ChildNode: TTreeNode;

Attr: Cardinal;

begin

try

OLECheck(FolderEnumObjects(Handle SHCONTF_FOLDERS EnumIDList));

while EnumIDListNext( PIDLs NumID) = S_OK do

begin

FullItemPIDL := ConcatPIDLs(FullPIDL PIDLs);

TreeViewItem := New(PTreeViewItem);

TreeViewItemParentFolder := Folder;

TreeViewItemPidl := CopyItemID(PIDLs);

TreeViewItemFullPidl := FullItemPIDL;

TreeViewItemHasExpanded := False;

FItemListAdd(TreeViewItem);

ChildNode := TreeViewItemsAddChildObject(ParentNode

GetDisplayName(Folder PIDLs False) TreeViewItem);

GetItemIcons(FullItemPIDL ChildNode);

Attr := SFGAO_HASSUBFOLDER or SFGAO_FOLDER;

FolderGetAttributesOf( PIDLs Attr);

if Bool(Attr and (SFGAO_HASSUBFOLDER or SFGAO_FOLDER)) then

if Bool(Attr and SFGAO_FOLDER) then

if Bool(Attr and SFGAO_HASSUBFOLDER) then

ChildNodeHasChildren := True;

end;

except

// 你可在此处对异常进行处理

end;

end;

procedure TFormFormDestroy(Sender: TObject);

var

I: Integer;

begin

try

for I := to FItemListCount do

begin

DisposePIDL(PTreeViewItem(FItemList[i])PIDL);

DisposePIDL(PTreeViewItem(FItemList[i])FullPIDL);

end;

FItemListClear;

FItemListFree;

except

end;

end;

procedure TFormFormCreate(Sender: TObject);

var

Folder: IShellFolder;

begin

SetTreeViewImageList;

OLECheck(SHGetDesktopFolder(Folder));

FItemList := TListCreate;

FillTreeView(Folder nil nil);

end;

procedure TFormTreeViewExpanding(Sender: TObject; Node: TTreeNode;

var AllowExpansion: Boolean);

var

TVItem: PTreeViewItem;

SHFolder: IShellFolder;

begin

TVItem := PTreeViewItem(NodeData);

if TVItemHasExpanded then Exit;

OLECheck(TVItemParentFolderBindToObject(TVItem^Pidl

nil IID_IShellFolder Pointer(SHFolder)));

FillTreeView(SHFolder TVItem^FullPidl Node);

NodeAlphaSort;

TVItem^HasExpanded := True;

end;

end

               

上一篇:DELPHI基础教程:开发Delphi对象式数据管理功能(五)[1]

下一篇:Delphi趣味编程实例三则