熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Delphi編程 >> 正文

Delphi深度探索之PItemIDList的基本概念

2022-06-13   來源: Delphi編程 

PIDL的秘密

  從Windows 開始微軟公司為操作系統引入了新的外殼界面新的外殼從根本上改變了應用程序同操作系統的結合方式遺憾的是微軟公司對於發布同外殼相關的編程信息方面顯得很吝啬可以得到的資料非常少而且質量也不高對於Delphi開發者來說情況就更為嚴重了因為幾乎所有的Windows API 文檔都是針對C/C++程序員的但是Nothing is impossible在本文中我們將開始外殼編程的歷險就讓我們從PIDL開始吧

外殼命名空間

  新外殼系統中的一個核心概念就是命名空間(namespace)對於DOS來說命名空間可以理解為就是整個文件系統它有著樹一樣的繼承關系它的樹根被稱為根目錄

  對於Windows x和NT來說命名空間仍然是樹狀繼承關系的但它不再一一對應於文件系統了文件系統變成了一個大的命名空間的一部分新的命名空間發展了原有的文件夾和文件概念新的文件夾仍然類似於舊的DOS目錄包含其他的命名空間元素比如文件夾和外殼對象而新的外殼對象同舊的DOS文件不同之處在於所有的系統目錄都是文件夾但並不是所有的文件夾都是目錄所有的文件都是外殼對象但不是所有的外殼對象都是文件

  新的命名空間的樹根就是桌面文件夾這從資源管理器左邊的樹視圖中就能看到桌面下包括我的電腦文件夾其中包括了舊的DOS命名空間磁盤驅動器桌面和我的電腦明顯不是文件系統的一部分同樣的特殊的文件夾比如控制面板打印機回收站和網絡鄰居等等都不是原來意義上的文件系統了

  但不管外殼的概念如何變化它必須是可唯一標識的每個外殼中的文件夾和對象必須有一個唯一的名字名字有兩種類型相對和絕對的名字相對名字是指相對一個給定的父對象它是唯一的比如我叫張三我哥哥叫張大和張二那麼對於我的父親來說我的名字就可以唯一地確定我的身份了但如何從全國所有名叫張三的同胞中找出我來呢這就需要絕對的名字了這時就應該用中國北京某胡同的張大胡子的兒子張三來唯一地確定我了對於外殼對象來說相對於根節點的路徑就可以用來唯一確定它的絕對名字

  對於老的DOS文件系統每個文件都有一個唯一確定的路徑名這個路徑名就相當於它的絕對名字它的格式通常就是C:\windows\system\…\文件名而單獨的樣式的文件名字則是相對名

  對於新的Windows x系統這種DOS方式的路徑名已經不夠用了它無法描述控制面板這類外殼對象的名字為此微軟公司給出了兩個新的數據結構每個元素的相對名字用一個TShItemID記錄來標識當需要時我們可以合並這些記錄從概念上類似於用\連接DOS路徑名而一連串的這些記錄就是項目標識符列表(IDLItem Identifier List)在Delphi中使用TItemIDList來標識它因為IDL主要是通過指針來進行操作的因此通常主要使用的是它的指針形式PIDL在Delphi中定義為PItemIDListPIDL就是在外殼命名空間確定唯一一個元素的通用方法所有這些Delphi數據結構都定義在ShlObj單元中

  同DOS樣式的字符串類型的路徑不同的是PIDL是二進制類型的數據同時TShItemID 和 TItemIDList 是變長的數據類型其中TShItemID的定義如下

TShItemID = packed record
cb: Word; // 記錄的大小
abID: array[] of Byte; // 外殼對象 ID數據
end;

  第一個記錄成員是cbcb 中應該存放整個TShItemID記錄的尺寸而abID 被定義為只有一個元素的字節數組但這並不意味著數組中只有一個元素它可以擴展為cb個元素另外TItemIDList 定義如下

TItemIDList = packed record
mkid: TShItemID;
end;

  它只是有一個TShItemID類型的數據成員構成需要注意的是這種定義方法意味著記錄並不僅是一個TShItemID成員而是一個TShItemID結構的列表一個挨著一個最後要使用一個cb為的TshItemID標識列表的結束下表中給出了一個TItemIDList的示意圖它由個TShItemID 記錄組成注意cb 總是比abID的字節大除了列表結束的標志記錄的cb這是因為cb 應該包含cb成員本身的字節大小而它正好是

cb abID cb abID cb abID cb abID bytes bytes bytes bytes

  從表中就可以清楚地知道cb的用途了它可以被用來作為可靠的路標來遍歷一個TItemIDListPItemIDList指針指向TItemIDList記錄的第一個字節除非PItemIDList 為nil否則列表中至少會有一個TShItemID 然後通過cb的值就可以知道列表中下一個TShItemID的起始位置如果cb為就表明列表結束了

  下面的代碼用PItemIDList作為參數然後遍歷整個TItemIDList並返回整個列表的尺寸當需要復制列表時獲得的信息可以用來確定復制所需緩沖區大小

function GetPIDLSize(PIDL: PItemIDList): Integer;
var
CurrentID: PShItemID;
begin
// 判斷PIDL是否為nil
if (PIDL <> nil) then
begin
// 對於終止的標志的cb至少為
Result := SizeOf(CurrentIDcb);
// 初始化item id 指針並遍歷列表直到碰到cb = 才終止
// 把碰到的每個cb的值添加到結果中
CurrentID := PShItemID(PIDL);
while (CurrentIDcb <> ) do begin
Inc(Result CurrentIDcb);
Inc(PChar(CurrentID) CurrentIDcb);
end
end
else
// 如果PIDL為nil返回
Result := ;
end;

  如同有相對和絕對路徑一樣同樣也有相對和絕對的PIDL一個絕對的PIDL是從命名空間的根節點桌面開始算起的而相對PIDL通常是從其直接父對象算起的

  外殼中的文件夾可以通過一個IShellFolder COM接口來進行控制這個接口提供了許多方法這些方法的參數通常就是相對PIDL因為接口本身就代表了父文件夾而以Sh開頭的Shell API函數通常則使用絕對PIDL作為參數因為它們不是類無法代表類因此只能使用絕對PIDL我們在應用中一定要搞清楚兩者的區別

PIDL的內存分配

  在實際應用中PIDL經常是在一個模塊中被分配而在另一個模塊中被釋放比如外殼API經常會在函數內部分配並返回一個PIDL這時我們的程序就要負責在使用後進行釋放這意味著內存的分配和釋放必須是語言無關的也就是說可以用C++寫PIDL分配模塊而用Delphi寫釋放模塊

  但實際上不同的開發語言的內存管理函數是完全不兼容的如果使用Delphi的FreeMem 過程來釋放一些C語言的Malloc函數分配的內存的話產生的糟糕後果就是會破壞整個堆為了解決這一問題操作系統提供了外殼任務分配器(shell task allocator)來統一外殼內存管理外殼任務分配器是通過IMalloc COM接口實現的IMalloc實現了一個非常完整的內存分配引擎它定義在ActiveX單元中獲得一個IMalloc接口實例最簡單的辦法是使用SHGetMalloc API函數這個函數定義在ShlObj 單元中這些聲明定義如下

IMalloc = interface(IUnknown)
[{ C }]
function Alloc(cb: Longint): Pointer; stdcall;
function Realloc(pv: Pointer; cb: Longint):Pointer; stdcall;
procedure Free(pv: Pointer); stdcall;
function GetSize(pv: Pointer): Longint; stdcall;
function DidAlloc(pv: Pointer): Integer; stdcall;
procedure HeapMinimize; stdcall;
end;
function SHGetMalloc(var ppMalloc: IMalloc):HResult; stdcall;

  下面是一個使用分配引擎的例子

var
Allocator: IMalloc;
Buffer: Pointer;
begin
// 獲得IMalloc 接口
SHGetMalloc(Allocator);
// 分配個字節的緩沖區
Buffer := AllocatorAlloc();
// 擴展緩沖區為 字節
Buffer := AllocatorRealloc(Buffer);
//釋放緩沖區
AllocatorFree(Buffer);
end;

  如果不需要IMalloc接口提供的全部功能而只是想分配或釋放內存的話有兩個未經公開的函數SHAlloc 和SHFree封裝了對IMalloc接口的調用來分配和釋放內存它們在SHELLDLL中的索引分別為當要想釋放一個PIDL時可以使用ILFree 這個未公開的函數它的索引值為三個函數的定義如下

function SHAlloc(BufferSize: ULONG): Pointer; stdcall;
procedure SHFree(Buffer: Pointer); stdcall;
procedure ILFree(Buffer: PItemIDList); stdcall;

路徑和PIDL之間的相互轉換

  如何將文件系統的路徑轉化為外殼形式的PIDL呢?微軟公司的文檔中記載的標准方式是先獲得桌面的IShellFolder 接口然後把要轉化的路徑名轉化為PWideChar 類型的以null結尾的UNICODE字符串然後作為參數調用桌面的IShellFolder接口的ParseDisplayName 方法才能獲得PIDL實際應用起來太復雜不過不要緊有三個未公開的函數可以幫助我們簡化這一功能的實現

function SHILCreateFromPath(Path: Pointer;PIDL: PItemIDList;
var Attributes: ULONG):HResult; stdcall;
function ILCreateFromPath(Path: Pointer):PItemIDList; stdcall;
function SHSimpleIDListFromPath(Path: Pointer):PItemIDList; stdcall;

  SHILCreateFromPath 函數實際上就是對桌面的IShellFolder接口的ParseDisplayName方法進行簡單封裝而ILCreateFromPath函數則是對SHILCreateFromPath調用的簡單封裝而SHSimpleIDListFromPath函數則實現了整個過程它們的索引分別是

  其中SHSimpleIDListFromPath 相對要快一些因為它並不校驗路徑參數的有效性而SHILCreateFromPath 和ILCreateFromPath 在轉化前都要校驗路徑的有效性如果提供的路徑是無效的就會返回一個nil

  由於SHSimpleIDListFromPath 不校驗路徑所以可以從任何路徑獲得一個PIDL而不會引起錯誤但是有時這個函數返回的PIDL不完全正確比如用它產生的PIDL來調用SHBrowseForFolder 函數顯示浏覽對話框的時候偶爾結果顯示的名字和圖標是不正確的

  當想從一個絕對PIDL獲得一個文件系統路徑時就相對簡單多了有一個公開的函數SHGetPathFromIDList可以實現這一功能它定義在ShlObj單元中(有AnsiChar和widechar兩個版本)

function SHGetPathFromIDList(PIDL: PItemIDList;Path: PAnsiChar): BOOL; stdcall;
function SHGetPathFromIDListW(PIDL: PItemIDList;Path: PWideChar): BOOL; stdcall;

  注意path參數對應的指針應該指向一個可以容納MAX_PATH+個字符的緩沖區以避免越界讀寫

顯示名稱

  如果想要獲得一個PIDL對應的顯示名稱文檔中介紹的方法是使用IShellFolder接口的GetDisplayNameOf方法來完成另外使用SHGetFileInfo API函數也能獲得顯示名不過有一個未公開的API調用ILGetDisplayName函數使用起來是最方便的它實際上就是調用桌面的IShellFolder接口的GetDisplayNameOf 方法同時調用的標志值為SHGDN_FORPARSINGILGetDisplayName 函數的索引值為不過這個函數不會返回通常的短顯示名而是返回包含了相應路徑的長顯示名如果想得到的是短文件名的話最好使用SHGetFileInfo函數下面是函數的定義

function ILGetDisplayName(PIDL: PItemIDList;Name: Pointer): LongBool; stdcall;

Windows NT和PWideChar

  回頭看一下已經定義的未公開的函數就會發現通常字符串類型的變量並沒有定義為Pchar而是定義為Pointer這是因為對於未公開的函數來說在Windows x上字符串變量都是PAnsiChar類型的而在NT上都是PWideChar類型的沒有辦法像公開的函數那樣可以任選ANSI或UNICODE版本的函數未公開函數在Windows x上只能使用ANSI版本在Windows NT 上只能使用UNICODE版本的函數如果想在所有版本的操作系統上都能正常工作就必須在運行時檢查操作系統類型SysUtils單元中的WinPlatform 全局變量可以用來判斷操作系統類型如果程序是運行在Windows NT上的在調用前就需要把字符串變量轉化為PWideChar 類型當函數返回時又需要把返回字符串變回PAnsiChar這種轉化比較麻煩但這就是使用未公開函數調用的代價

  如果想確定兩個PIDL是否相同標准方法是使用IShellFolder接口的CompareIDs 方法相對的PIDLs 可以用他們父文件夾的IShellFolder接口而絕對PIDLs的比較必須使用桌面的IShellFolder接口同樣的系統也提供了未公開的快捷方法要想確定兩個PIDL是否相等可以使用ILIsEqual 函數如果想確定一個PIDL是否是另一個PIDL的子對象可以使用ILIsParent 函數如果希望判斷子對象是否是父對象的最直接的子對象的話需要設定函數的ImmediateParent 參數為True下面的就是函數的定義

function ILIsEqual(PIDL: PItemIDList; PIDL: PItemIDList):LongBool; stdcall;
function ILIsParent(PIDL: PItemIDList;PIDL: PItemIDList; ImmediateParent: LongBool):LongBool; stdcall;

  這兩個函數的索引值分別為要注意的是通過二進制的比較是無法判斷兩個PIDL是否相等的因為相等的PIDL可能會有不同的二進制結構

解析PIDL

  有時我們會想要分解一個PIDL為單獨的ID列表沒有公開的函數可以實現這項功能很顯然微軟公司希望程序員自己實現切割PIDL的功能幸運的是還是有未公開的函數可以簡化開發

  如果我們想確定PIDL中所有標識符的尺寸可以使用ILGetSize 函數如果想遍歷PIDL中每一個項目標識符的話可以使用ILGetNext 函數當給定一個PIDL後函數會返回一個指向列表中下一個項目標識符的指針如果PIDL為nil或已經指向了列表中的最後一項函數會返回nil要想返回列表中最後一項item identifier可以使用未公開的ILFindLastID函數

  一個更專業的查找函數是ILFindChild 給定一個父PIDL和一個子PIDL它將返回一個指向子PIDL獨特部分的指針比如如果你把目錄 C:\DIR的PIDL作為父PIDL而把C:\DIR\FILETXT 的PIDL作為子PIDL的話它會返回一個指針指向代表FILETXT的子PIDL如果給定的子PIDL不是父PIDL的子對象函數返回nil這些函數的索引值分別為函數定義如下

function ILGetSize(PIDL: PItemIDList): UINT; stdcall;k
function ILGetNext(PIDL: PItemIDList):PItemIDList; stdcall;
function ILFindLastID(PIDL: PItemIDList):PItemIDList; stdcall;
function ILFindChild(ParentPIDL: PItemIDList;ChildPIDL: PItemIDList): PItemIDList; stdcall;

復制和合並

  有時在進行外殼編程的時候需要制作一個PIDL的拷貝給定一個已有的PIDL ILClone 函數將會分配並返回一個新的PIDL的克隆而ILCloneFirst 函數可以從源PIDL中生成一個只包含第一個item identifier的PIDL如果想獲得最後一個item identifier的拷貝組合使用ILFindLastID和ILCloneFirst函數調用就可以了對於PIDL的其他部分就需要不斷調用ILGetNext和ILCloneFirst函數了這兩個函數定義如下其索引值為

function ILClone(PIDL: PItemIDList): PItemIDList; stdcall;
function ILCloneFirst(PIDL: PItemIDList):
PItemIDList; stdcall;

  如果想合並兩個PIDL則可以使用ILCombine 函數給定兩個PIDL它會創建一個包含兩個源列表的新的PIDL如果想把一個單獨的item identifier同PIDL合並可能需要使用ILAppendID 函數它可以把一個TItemID 記錄添加到一個已有的PIDL的開頭或結尾然而同ILCombine不同原來的PIDL在操作後將被銷毀ILAppendID 函數中的PIDL參數甚至可以為nil這兩個函數的索引值分別為函數定義如下

function ILCombine(PIDL: PItemIDList; PIDL: PItemIDList):PItemIDList; stdcall;
function ILAppendID(PIDL: PItemIDList; ItemID: PShItemID;
AddToEnd: LongBool): PItemIDList; stdcall;

全局內存克隆

  前面已經提到了為PIDL分配內存需要使用外殼內存分配器系統中有兩個未公開的函數提供了不同的分配和釋放內存的方法它們是ILGlobalClone和ILGlobalFree 函數(索引值為函數定義如下

function ILGlobalClone(PIDL: PItemIDList):PItemIDList; stdcall;
procedure ILGlobalFree(PIDL: PItemIDList); stdcall;

  在Windows NT中這兩個函數使用缺省進程的堆(由GetProcessHeap得到的)堆的分配在某些方面比外殼分配器效率更高而外殼在內部使用全局分配函數可以提高效率

  在Windows x 上外殼中的絕大多數內部結構都需要在DLL的所有實例中共享同樣PIDL使用的內存也應該是可共享的ILGlobalClone 使用一個可共享的堆來分配PIDL的內存使得可以從任何地方存取PIDL的指針

刪改

  如果想刪除整個PIDL只要使用ILFree 函數就可以了如果想從列表的末尾刪除最後一個item identifier可以使用ILRemoveLastID 函數

function ILRemoveLastID(PIDL: PItemIDList):LongBool; stdcall;

  它的索引值為要注意的是它並不真的釋放任何內存它只是重置了列表的最後位置它是唯一一個刪除相關操作的函數如果我們想從PIDL的開始刪除一個item identifier就只能使用ILGetNext 和ILClone 來生成一個從原始PIDL的第二個ID開始的拷貝了然後使用ILFree刪除源PIDL從列表的中間刪除一個ID顯然更加麻煩了但幸運的是在實際中幾乎不存在這種需要

深入命名空間

  現在我們對PIDL已經有了一定程度的了解了接下來就是研究如何遍歷命名空間桌面是遍歷命名空間的根節點從桌面開始可以枚舉外殼中的所有對象在開始遍歷命名空間前需要獲得桌面對象的IShellFolder接口下面的代碼演示了如何獲得桌面接口

var
Desktop: IShellFolder;
Begin
OleCheck(SHGetDesktopFolder(Desktop));

  IShellFolder 可以用來枚舉外殼中的內容設定或取得外殼對象的名字查詢它們的屬性並通過界面元素進行交互下面是一個使用IShellFolder 接口的例子

type
TItemListArray = array of PItemIDList;

function GetShellItems(Folder: IShellFolder): TItemListArray;
Const
SHCONTF_ALL=SHCONTF_FOLDERSorSHCONTF_NONFOLDERSor
SHCONTF_INCLUDEHIDDEN;
Var
EnumList: IEnumIDList;
NewItem: PItemIDList;
Dummy: Cardinal;
I: Integer;
Begin
Result := nil;
I := ;
if FolderEnumObjects( SHCONTF_ALL EnumList) = S_OK then
while EnumListNext( NewItem Dummy) = S_OK do
begin
Inc(I);
SetLength(Result I);
Result[I ] := NewItem;
end;
end;

  GetShellFolders 函數返回一組相對於父文件夾的PIDL列表通過EnumObjects方法可以獲得PIDL枚舉接口不過最終要負責釋放全部結果中的項目 

function GetShellObjectName(Folder: IShellFolder;
ItemList: PItemIDList): string;
Var
StrRet: TStrRet;
Begin
FolderGetDisplayNameOf(ItemList SHGDN_INFOLDER StrRet);
case StrRetuType of STRRET_WSTR:
Begin
Result := WideCharToString(StrRetpOleStr);
CoTaskMemFree(StrRetpOleStr);
end;
STRRET_OFFSET: Result := PChar(Cardinal(ItemList) + StrRetuOffset);
STRRET_CSTR: Result := StrRetcStr;
end;
end;

  GetShellObjectName 函數則返回一個相對的PIDL的字符串表達把這些代碼集成起來就可以編寫一個過程來輸出指定深度的外殼命名空間的層次關系了

procedure EnumShellNamespace(Strings: TStrings; Depth: Integer;
Folder: IShellFolder = nil);
procedure AddObjectName(Folder: IShellFolder; ItemList: PItemIDList;
Level: Integer);
Var
S: string;
Begin
SetLength(S Level * );
FillChar(PChar(S)^ Length(S) );
StringsAdd(S + GetShellObjectName(Folder ItemList));
end;
procedure EnumItems(Folder: IShellFolder; Level: Integer);
var
Items: TItemListArray;
ItemList: PItemIDList;
Flags: Cardinal;
SubFolder: IShellFolder;
I: Integer;
Begin
Inc(Level);
Items := GetShellItems(Folder);
Try
for I := to Length(Items) do
begin
ItemList := Items[I];
AddObjectName(Folder ItemList Level);
if Level < Depth then
begin
Flags := SFGAO_HASSUBFOLDER;
OleCheck(FolderGetAttributesOf( ItemList Flags));
if Flags and SFGAO_HASSUBFOLDER = SFGAO_HASSUBFOLDER then
Begin
OleCheck(FolderBindToObject(
ItemList nilIID_IShellFolder SubFolder));
EnumItems(SubFolder Level);
end;
end;
end;
finally
for I := to Length(Items) do
ILFree(Items[I]);
end;
begin
StringsBeginUpdate;
Try
StringsClear;
if Folder = nil then
begin
OleCheck(SHGetDesktopFolder(Folder));
AddObjectName(Folder nil );
end;
if Depth > then
EnumItems(Folder );
Finally
StringsEndUpdate;
end;
end;
end

  對於Delphi來說由於其提供了一個非常友好的對象框架所以這裡對IShellFolder的功能進行了封裝實現了一個TShellNode 類對TShellNode類進行了描述

AbsoluteList 標識節點的絕對PIDL指針
CountItems屬性中的節點數
HasChildren表示是否有子節點
Item子TShellNode對象
Name節點的顯示名稱
Path節點對應的系統文件路徑
Parent對象的父節點對桌面來說這個屬性為nil
RelativeList相對PIDL指針
ShellFolder對應於節點的IShellFolder接口
Create創建節點
CreateFromList根據PIDL創建節點
CreateFromFolder由特殊路徑創建節點
Destroy析構函數
Assign復制節點信息
Clear釋放節點的子對象
Initialize初始化方法

  TShellNode被設計成一個基類可以從它繼承更加有用的類來一些在表中列出的屬性和方法是protected的需要在繼承類中聲明為public衍生類不應該重新定義constructors過程但可以重載Initialize方法

  擴展TShellNode 的類可以添加系統圖像列表索引屬性查找能力等等這完全取決於你的想像力還有一點是除了桌面外微軟公司還定義了一組CoClasses對象它們都暴露了IShellFolder 接口我們也可以從它們出發來遍歷命名空間下面列出這些CoClass的定義和描述

CLSID_NetworkPlaces網絡鄰居
CLSID_NetworkDomain網絡域
CLSID_NetworkServer網絡服務器
CLSID_NetworkShare網絡共享
CLSID_MyComputer我的電腦
CLSID_Internet我的網絡
CLSID_ShellFSFolder文件系統的桌面目錄
CLSID_RecycleBin回收站
CLSID_ControlPanel控制面板
CLSID_Printers打印機
CLSID_MyDocuments我的文檔

  舉例來說可以使用下面代碼來創建一個簡單的打印機選擇組合列表框

EnumShellNamespace(ComboBoxItems

CreateCOMObject(CLSID_Printers) as IShellFolder);

  在例子程序中我們從TShellNode類又衍生了一個TShellTreeNode 類添加了圖像索引和Strings屬性ImageIndex 屬性對應於系統圖像列表中的節點的圖像索引Strings 屬性則保存著節點的絕對PIDL列表中每一項的顯示名稱程序允許我們在絕對和相對PIDL察看模式間切換下圖就是程序中顯示的外殼對象樹的示意圖

  例子程序的主要目的是演示如何進行PIDL的操作在GetItemListStrings過程中演示了如何使用ILCloneILFindChildILFreeILGetCountILIsRoot和ILRemoveLastID等例程

顯示屬性頁

  IShellFolder接口不僅提供對外殼內部數據結構的存取也可以調用界面元素進行交互例如使用IShellFolderGetUIObjectOf 方法可以請求上下文相關菜單在下面代碼中演示了如何操作PIDL來獲得IContextMenu 接口並通過IContextMenu來調用菜單命令比如顯示屬性頁調用我的電腦的屬性命令顯示屬性頁的示意圖下圖所示

procedure ShowProperties(Handle: HWND; ItemList: PItemIDList); overload;
var
Desktop: IShellFolder;
Folder: IShellFolder;
ParentList: PItemIDList;
RelativeList: PItemIDList;
ContextMenu: IContextMenu;
CommandInfo: TCMInvokeCommandInfo;
Begin
ParentList := ILClone(ItemList);
if ParentList <> nil then
try
ILRemoveLastID(ParentList);
OleCheck(SHGetDesktopFolder(Desktop));
OleCheck(DesktopBindToObject(ParentList nil IID_IShellFolderFolder));
RelativeList := ILFindChild(ParentList ItemList);
OleCheck(FolderGetUIObjectOf(Handle RelativeList
IID_IContextMenu nil ContextMenu));
FillChar(CommandInfo SizeOf(TCMInvokeCommandInfo) #);
with CommandInfo do
begin
cbSize := SizeOf(TCMInvokeCommandInfo);
hwnd := Handle;
lpVerb := Properties;
nShow := SW_SHOW;
end;
OleCheck(ContextMenuInvokeCommand(CommandInfo));
Finally
ILFree(ParentList);
end;
end;
procedure ShowProperties(Handle: HWND;
const DisplayName: string); overload;
var
ItemList: PItemIDList;
Begin
ItemList := ILCreateFromPath(PChar(DisplayName));
Try
ShowProperties(Handle ItemList)
Finally
ILFree(ItemList);
end;
end;

PIDL的其他用途

  IShellFolder並不是使用PIDL的唯一接口其他像文件快捷方式外殼擴展等都利用PIDL來擴展或嵌入外殼Windows還提供了一組公開的使用PIDL的函數比如調用SHGetSpecialFolderLocation 函數就可以由PIDL獲得特色文件夾的相應文件路徑而用SHGetDataFromIDList 函數可以查詢文件系統或網絡資源中的PIDL來獲得相應屬性

結論

  PIDL是理解外殼的一把關鍵的鑰匙幾乎所有外殼功能的實現都離不開PIDL因此掌握PIDL的相關編程知識就顯得至關重要了進一步研究下去我們還可以使用命名空間來查詢網絡設備存取虛擬文件夾中的對象從現在開始研究PIDL明天你將獲得更加靈活的同外殼結合更加緊密的應用程序


From:http://tw.wingwit.com/Article/program/Delphi/201311/24786.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.