明輝手游網(wǎng)中心:是一個免費提供流行視頻軟件教程、在線學(xué)習(xí)分享的學(xué)習(xí)平臺!

用Delphi完成Windows文件夾管理樹

[摘要]摘要:本文利用Windows名空間所提供的IShellFolder接口,用Delphi實現(xiàn)了文件夾管理樹的生成。 關(guān)鍵字:文件夾 接口 Delphi一、概述Windows95/98視覺感觀上區(qū)別Windows3.1的一個重要方面就是大量采用了樹形視圖控件,資源管理器左側(cè)的文件夾管理樹便是如此,它將...
摘要:本文利用Windows名空間所提供的IShellFolder接口,用Delphi實現(xiàn)了文件夾管理樹的生成。

關(guān)鍵字:文件夾 接口 Delphi


一、概述

Windows95/98視覺感觀上區(qū)別Windows3.1的一個重要方面就是大量采用了樹形視圖控件,資源管理器左側(cè)的文件夾管理樹便是如此,它將本地和網(wǎng)絡(luò)上的文件夾和文件等資源以層次樹的方式羅列出來,為用戶集中管理計算機提供了極大便利,同時在外貌上也煥然一新。Delphi為我們提供了大量Windows標(biāo)準(zhǔn)控件,但遺憾的是在目錄瀏覽方面卻只提供了一個Windows3.1樣式的DirectoryListBox(Delphi5的測試版也是如此),因此,在Delphi中實現(xiàn)Windows文件夾管理樹對開發(fā)更“地道”的Windows程序有著重大意義。

二、實現(xiàn)原理

Windows文件夾管理樹的實現(xiàn)實質(zhì)上是對Windows名空間(Namespace)的遍歷。名空間中每個文件夾都提供了一個IShellFolder接口,遍歷名空間的方法是:

1)調(diào)用SHGetDesktopFolder函數(shù)獲得桌面文件夾的IShellFolder接口,桌面文件夾是文件夾管理樹的根節(jié)點。

2)再調(diào)用所獲得的IShellFolder接口的EnumObjects成員函數(shù)列舉出子文件夾。

3)調(diào)用IShellFolder的BindToObject成員函數(shù)獲得子文件夾的IShellFolder接口。 4)重復(fù)步驟2)、3)列舉出某文件夾下的所有子文件夾,只至所獲得的IShellFolder接口為nil為止。

下面解釋將要用到的幾個主要函數(shù),它們在ShlObj單元中定義:

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

該函數(shù)通過ppshf獲得桌面文件夾的IShellFolder接口。

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

out EnumIDList: IEnumIDList): HResult;

該函數(shù)獲得一個IEnumIDList接口,通過調(diào)用該接口的Next等函數(shù)可以列舉出IShellFolder接口所對應(yīng)的文件夾的內(nèi)容,內(nèi)容的類型由grfFlags來指定。我們需要列舉出子文件夾來,因此grfFlags的值指定為SHCONTF_FOLDERS。HwndOwner是屬主窗口的句柄。

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

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

該函數(shù)獲得某個子文件夾的IShellFolder接口,該接口由ppvOut返回。pidl是一個指向元素標(biāo)識符列表的指針,Windows95/98中用元素標(biāo)識符和元素標(biāo)識符列表來標(biāo)識名空間中的對象,它們分別類似于文件名和路徑。需要特別指出的是:pidl作為參數(shù)傳遞給Shell API函數(shù)時,必須是相對于桌面文件夾的絕對路徑,而傳遞給IShellFolder接口的成員函數(shù)時,則應(yīng)是相對于該接口所對應(yīng)文件夾的相對路徑。pbcReserved應(yīng)指定為nil,riid則應(yīng)指定為IID_IShellFolder。

其它函數(shù)可以查閱Delphi提供的《Win32 Programmer's Reference》。

三、程序清單

下面的源代碼在Windows98中實現(xiàn),并在Windows2000測試版中測試無誤(程序運行結(jié)果如圖1所示),有興趣的讀者可以將其改寫成Delphi組件,以備常用。

unit BrowseTreeView;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

ShlObj, ComCtrls;

type

PTreeViewItem = ^TTreeViewItem;

TTreeViewItem = record

ParentFolder: IShellFolder; // 接點對應(yīng)的文件夾的父文件夾的IShellFolder接口

Pidl, FullPidl: PItemIDList; // 接點對應(yīng)的文件夾的相對和絕對項目標(biāo)識符列表

HasExpanded: Boolean; // 接點是否展開

end;

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;

// 以下是幾個對項目標(biāo)識符進行操作的函數(shù)

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;

// 將二進制表示的項目標(biāo)識符列表轉(zhuǎn)換成有可識的項目名

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;

// 獲得每個文件夾在系統(tǒng)中的圖標(biāo)

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

begin

with TreeNode do

begin

ImageIndex := GetIcon(FullPIDL, False);

SelectedIndex := GetIcon(FullPIDL, True);

end;

end;

// 獲得系統(tǒng)的圖標(biāo)列表

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.