打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
用Delphi实现Windows文件夹管理树
2005-03-10 15:55 1007人阅读 评论(0) 收藏 举报

二、实现原理

  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.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
IShellFolder基础概念介绍
文件夹怎么加密
DELPHI 获取特殊系统目录
文件夹浏览(SHBrowseForFolder)
如何显示隐藏文件?
如何获取资源管理器窗口的信息
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服