用Delphi实现Windows文件夹管理树

二、实现原理

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

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

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

  3)调用IShellFolder的BindToObject成员函数获得子文件夹的IShellFolder接口。

  4)重复步骤2)、3)列举出某文件夹下的所有子文件夹,只至所获得的IShellFolder接口为nil为止。

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

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

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

  2)function IShellFolder.EnumObjects(hwndOwner: HWND; grfFlags: DWORD;

out EnumIDList: IEnumIDList): HResult;

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

  3)function IShellFolder.BindToObject(pidl: PItemIDList; pbcReserved: Pointer;

const riid: TIID; out ppvOut: Pointer): HResult;

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

  其它函数可以查阅Delphi提供的《Win32 Programmer's Reference》。

三、程序清单

下面的源代码在Windows98中实现,并在Windows2000测试版中测试无误(程序运行结果如图1所示),有兴趣的读者可以将其改写成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;

图1 程序运行结果

TForm1 = class(TForm)

TreeView1: TTreeView;

procedure FormDestroy(Sender: TObject);

procedure FormCreate(Sender: TObject);

procedure TreeView1Expanding(Sender: TObject; Node: TTreeNode;

var AllowExpansion: Boolean);

private

FItemList: TList;

procedure SetTreeViewImageList;

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

end;

var

Form1: TForm1;

implementation

{$R *.DFM}

uses

ActiveX, ComObj, ShellAPI, CommCtrl;

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

procedure DisposePIDL(ID: PItemIDList);

var

Malloc: IMalloc;

begin

if ID = nil then Exit;

OLECheck(SHGetMalloc(Malloc));

Malloc.Free(ID);

end;

function CopyItemID(ID: PItemIDList): PItemIDList;

var

Malloc: IMalloc;

begin

Result := nil;

OLECheck(SHGetMalloc(Malloc));

if Assigned(ID) then

begin

Result := Malloc.Alloc(ID^.mkid.cb + sizeof(ID^.mkid.cb));

CopyMemory(Result, ID, ID^.mkid.cb + sizeof(ID^.mkid.cb));

end;

end;

function NextPIDL(ID: PItemIDList): PItemIDList;

begin

Result := ID;

Inc(PChar(Result), ID^.mkid.cb);

end;

function GetPIDLSize(ID: PItemIDList): Integer;

begin

Result := 0;

if Assigned(ID) then

begin

Result := sizeof(ID^.mkid.cb);

while ID^.mkid.cb <> 0 do

begin

Inc(Result, ID^.mkid.cb);

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 := Malloc.Alloc(Size);

if Assigned(Result) then

FillChar(Result^, Size, 0);

finally

end;

end;

function ConcatPIDLs(ID1, ID2: PItemIDList): PItemIDList;

var

cb1, cb2: Integer;

begin

if Assigned(ID1) then

cb1 := GetPIDLSize(ID1) - sizeof(ID1^.mkid.cb)

else

cb1 := 0;

cb2 := GetPIDLSize(ID2);

Result := CreatePIDL(cb1 + cb2);

if Assigned(Result) then

begin

if Assigned(ID1) then

CopyMemory(Result, ID1, cb1);

CopyMemory(PChar(Result) + cb1, ID2, cb2);

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;

Folder.GetDisplayNameOf(PIDL, Flags, StrRet);

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:

Result := StrRet.pOleStr;

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), 0, FileInfo, sizeof(TSHFileInfo), Flags);

Result := FileInfo.iIcon;

end;

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

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

begin

with TreeNode do

begin

ImageIndex := GetIcon(FullPIDL, False);

SelectedIndex := GetIcon(FullPIDL, True);

end;

end;

// 获得系统的图标列表

procedure TForm1.SetTreeViewImageList;

var

ImageList: THandle;

FileInfo: TSHFileInfo;

begin

ImageList := SHGetFileInfo(PChar('C:/'), 0, FileInfo,

sizeof(TSHFileInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);

if ImageList <> 0 then

TreeView_SetImageList(TreeView1.Handle, ImageList, 0);

end;

// 生成文件夹管理树

procedure TForm1.FillTreeView(Folder: IShellFolder;

FullPIDL: PItemIDList; ParentNode: TTreeNode);

var

TreeViewItem: PTreeViewItem;

EnumIDList: IEnumIDList;

PIDLs, FullItemPIDL: PItemIDList;

NumID: LongWord;

ChildNode: TTreeNode;

Attr: Cardinal;

begin

try

OLECheck(Folder.EnumObjects(Handle, SHCONTF_FOLDERS, EnumIDList));

while EnumIDList.Next(1, PIDLs, NumID) = S_OK do

begin

FullItemPIDL := ConcatPIDLs(FullPIDL, PIDLs);

TreeViewItem := New(PTreeViewItem);

TreeViewItem.ParentFolder := Folder;

TreeViewItem.Pidl := CopyItemID(PIDLs);

TreeViewItem.FullPidl := FullItemPIDL;

TreeViewItem.HasExpanded := False;

FItemList.Add(TreeViewItem);

ChildNode := TreeView1.Items.AddChildObject(ParentNode,

GetDisplayName(Folder, PIDLs, False), TreeViewItem);

GetItemIcons(FullItemPIDL, ChildNode);

Attr := SFGAO_HASSUBFOLDER or SFGAO_FOLDER;

Folder.GetAttributesOf(1, 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

ChildNode.HasChildren := True;

end;

except

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

end;

end;

procedure TForm1.FormDestroy(Sender: TObject);

var

I: Integer;

begin

try

for I := 0 to FItemList.Count-1 do

begin

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

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

end;

FItemList.Clear;

FItemList.Free;

except

end;

end;

procedure TForm1.FormCreate(Sender: TObject);

var

Folder: IShellFolder;

begin

SetTreeViewImageList;

OLECheck(SHGetDesktopFolder(Folder));

FItemList := TList.Create;

FillTreeView(Folder, nil, nil);

end;

procedure TForm1.TreeView1Expanding(Sender: TObject; Node: TTreeNode;

var AllowExpansion: Boolean);

var

TVItem: PTreeViewItem;

SHFolder: IShellFolder;

begin

TVItem := PTreeViewItem(Node.Data);

if TVItem.HasExpanded then Exit;

OLECheck(TVItem.ParentFolder.BindToObject(TVItem^.Pidl,

nil, IID_IShellFolder, Pointer(SHFolder)));

FillTreeView(SHFolder, TVItem^.FullPidl, Node);

Node.AlphaSort;

TVItem^.HasExpanded := True;

end;

end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第1章 窗体与界面设计 1 1.1 菜单应用 2 实例001 在系统菜单中添加菜单项 2 实例002 带历史信息的菜单 3 实例003 菜单动态合并 4 实例004 像“开始”菜单一样漂亮的菜单 5 实例005 多彩的菜单 6 实例006 可以拉伸的菜单界面 8 1.2 工具栏设计 9 实例007 带背景的工具栏 9 实例008 浮动工具栏 10 1.3 状态栏设计 11 实例009 在状态栏中显示检查框 11 实例010 带进度条的状态栏 12 实例011 状态栏中加入图标 13 1.4 导航菜单界面 14 实例012 OutLook界面 14 实例013 带导航菜单的主界面 15 实例014 图形化的导航界面 16 1.5 特色程序界面 18 实例015 类QQ的程序界面 18 实例016 类似Windows XP的程序界面 20 实例017 以图形按钮显示的界面 21 实例018 以型显示的程序界面 23 实例019 以XPManifest组件显示界面 24 实例020 动态按钮的窗体界面 25 1.6 特殊形状的窗体 26 实例021 非矩形窗体 26 实例022 建立字体形状窗体 28 1.7 多媒体光盘 29 实例023 自动启动的多媒体光盘程序 29 实例024 为触摸屏程序添加虚拟键盘 30 实例025 触摸屏系统 31 1.8 窗体效果 33 实例026 半透明渐显窗体 33 实例027 窗口颜色的渐变 34 实例028 带背景的窗体 35 实例029 为窗体增加边框 36 1.9 窗体动画 37 实例030 窗体中的滚动字幕 37 实例031 动画显示窗体 38 实例032 制作闪烁的窗体 39 实例033 直接在窗体上绘图 40 实例034 动画形式的程序界面 41 实例035 以时钟显示界面窗体 42 1.10 标题栏窗体 44 实例036 标题栏上的按钮 45 实例037 使窗体标题栏文字右对齐 47 实例038 没有标题栏但可以改变大小的窗口 48 1.11 设置窗体位置 49 实例039 不可移动的窗体 49 实例040 设置窗体在屏幕中的位置 50 实例041 始终在最上面的窗体 51 1.12 设置窗体大小 52 实例042 限制窗体大小 52 实例043 获取桌面大小 53 实例044 组件大小随窗体的改变而改变 53 实例045 在窗口间移动按钮 54 实例046 如何实现Office助手 55 1.13 窗体控制技术 56 实例047 在关闭窗口前加入确认对话框 56 实例048 程序在循环中响应界面操作 57 实例049 使用任意组件拖动窗体 58 实例050 动态创建窗体和释放窗体 59 实例051 修改提示字体及颜色 60 1.14 其他技术 61 实例052 窗口融合技术 61 实例053 给MDI窗体加背景 62 实例054 如何关闭MDI类型窗体中的子窗体 63 实例055 向提示框中添加图标 64 第2章 控件应用 67 2.1 TEdit控件应用典型实例 68 实例056 从右至左输入数据 68 实例057 为TEdit控件添加列表选择框 69 实例058 只允许输入数字的TEdit组件 70 2.2 TSpeedButton控件应用典型实例 71 实例059 在TSpeedButton按钮中显示图标 71 实例060 折行显示按钮标题 72 2.3 TMemo控件应用典型实例 73 实例061 设置TMemo组件的边界 73 实例062 TMemo组件的光标定位 74 实例063 得到TMemo中的可见行数 75 2.4 TListBox控件应用典型实例 76 实例064 在TListBox组件间交换数据 76 实例065 为TListBox添加水平滚动条 77 实例066 将数据库数据添加到组合框中 78 实例067 借助绑定控件实现数据选择录入 79 2.5 TListView控件应用典型实例 80 实例068 TListView列表拒绝添加重复信息 80 实例069 将数据库数据添加到TListView控件 82 实例070 用TListView控件制作导航界面 83 实例071 在TListView控件中对数据排序或统计 84 实例072 在TListView组件中绘制底纹 86 实例073 在列表视图中拖动视图项 87 2.6 TTreeView控件应用典型实例 88 实例074 将数据库数据显示到视图中 88 实例075 用TTreeView控件制作导航界面 90 实例076 TTreeView组件遍历磁盘目录 91 实例077 TTreeView组件在数据库中的应用 92 2.7 TStringGrid控件应用典型实例 94 实例078 程序运行时对

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值