當前位置:編程學習大全網 - 編程語言 - Delphi深度探索之PItemIDList的基本概念

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

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路徑名 而壹連串的這些記錄就是項目標識符列表(IDL Item Identifier List) 在Delphi中使用TItemIDList來標識它 因為IDL主要是通過指針來進行操作的 因此通常主要使用的是它的指針形式PIDL 在Delphi中定義為PItemIDList PIDL就是在外殼命名空間確定唯壹壹個元素的通用方法 所有這些Delphi數據結構都定義在ShlObj單元中

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

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

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

TItemIDList = packed recordmkid: 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的用途了 它可以被用來作為可靠的路標來遍歷壹個TItemIDList PItemIDList指針指向TItemIDList記錄的第壹個字節 除非PItemIDList 為nil 否則列表中至少會有壹個TShItemID 然後通過cb的值就可以知道列表中下壹個TShItemID的起始位置 如果cb為 就表明列表結束了

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

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

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

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

PIDL的內存分配

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

 但實際上不同的開發語言的內存管理函數是完全不兼容的 如果使用Delphi的FreeMem 過程來釋放壹些C語言的Malloc函數分配的內存的話 產生的糟糕後果就是會破壞整個堆 為了解決這壹問題 操作系統提供了外殼任務分配器(shell task allocator)來統壹外殼內存管理 外殼任務分配器是通過IMalloc 接口實現的 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;

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

varAllocator: IMalloc;Buffer: Pointer;begin// 獲得IMalloc 接口 SHGetMalloc(Allocator); // 分配 個字節的緩沖區 Buffer := Allocator Alloc( );// 擴展緩沖區為 字節 Buffer := Allocator Realloc(Buffer ); //釋放緩沖區 Allocator Free(Buffer);end;

 如果不需要IMalloc接口提供的全部功能 而只是想分配或釋放內存的話 有兩個未經公開的函數SHAlloc 和SHFree封裝了對IMalloc接口的調用來分配和釋放內存 它們在SHELL DLL中的索引分別為 和 當要想釋放壹個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_FORPARSING ILGetDisplayName 函數的索引值為 不過這個函數不會返回通常的短顯示名 而是返回包含了相應路徑的長顯示名 如果想得到的是短文件名的話 最好使用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單元中的Win Platform 全局變量可以用來判斷操作系統類型 如果程序是運行在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\FILE TXT 的PIDL作為子PIDL的話 它會返回壹個指針指向代表FILE TXT的子PIDL 如果給定的子PIDL不是父PIDL的子對象 函數返回nil 這些函數的索引值分別為 和 函數定義如下

function ILGetSize(PIDL: PItemIDList): UINT; stdcall;kfunction 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接口 下面的代碼演示了如何獲得桌面接口

varDesktop: IShellFolder;BeginOleCheck(SHGetDesktopFolder(Desktop));

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

typeTItemListArray = array of PItemIDList; function GetShellItems(Folder: IShellFolder): TItemListArray;ConstSHCONTF_ALL=SHCONTF_FOLDERSorSHCONTF_NONFOLDERSorSHCONTF_INCLUDEHIDDEN;VarEnumList: IEnumIDList;NewItem: PItemIDList;Dummy: Cardinal;I: Integer;BeginResult := nil;I := ;if Folder EnumObjects( SHCONTF_ALL EnumList) = S_OK thenwhile EnumList Next( NewItem Dummy) = S_OK dobeginInc(I);SetLength(Result I);Result[I ] := NewItem;end;end;

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

function GetShellObjectName(Folder: IShellFolder;ItemList: PItemIDList): string;VarStrRet: TStrRet;BeginFolder GetDisplayNameOf(ItemList SHGDN_INFOLDER StrRet);case StrRet uType of STRRET_WSTR:BeginResult := WideCharToString(StrRet pOleStr);CoTaskMemFree(StrRet pOleStr);end;STRRET_OFFSET: Result := PChar(Cardinal(ItemList) + StrRet uOffset);STRRET_CSTR: Result := StrRet cStr;end;end;

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

procedure EnumShellNamespace(Strings: TStrings; Depth: Integer;Folder: IShellFolder = nil);procedure AddObjectName(Folder: IShellFolder; ItemList: PItemIDList; Level: Integer);VarS: string;BeginSetLength(S Level * );FillChar(PChar(S)^ Length(S) );Strings Add(S + GetShellObjectName(Folder ItemList));end;procedure EnumItems(Folder: IShellFolder; Level: Integer);varItems: TItemListArray;ItemList: PItemIDList;Flags: Cardinal;SubFolder: IShellFolder;I: Integer;BeginInc(Level);Items := GetShellItems(Folder);Tryfor I := to Length(Items) dobeginItemList := Items[I];AddObjectName(Folder ItemList Level);if Level < Depth thenbeginFlags := SFGAO_HASSUBFOLDER;OleCheck(Folder GetAttributesOf( ItemList Flags));if Flags and SFGAO_HASSUBFOLDER = SFGAO_HASSUBFOLDER thenBeginOleCheck(Folder BindToObject(ItemList nil IID_IShellFolder SubFolder));EnumItems(SubFolder Level);end;end;end;finallyfor I := to Length(Items) doILFree(Items[I]);end;beginStrings BeginUpdate;TryStrings Clear;if Folder = nil thenbeginOleCheck(SHGetDesktopFolder(Folder));AddObjectName(Folder nil );end;if Depth > thenEnumItems(Folder );FinallyStrings EndUpdate;end;end;end

 對於Delphi來說 由於其提供了壹個非常友好的對象框架 所以這裏對IShellFolder的功能進行了封裝 實現了壹個TShellNode 類 對TShellNode類進行了描述 AbsoluteList 標識節點的絕對PIDL指針Count Items屬性中的節點數HasChildren 表示是否有子節點Item 子TShellNode對象Name 節點的顯示名稱Path 節點對應的系統文件路徑Parent 對象的父節點 對桌面來說 這個屬性為nilRelativeList 相對PIDL指針ShellFolder 對應於節點的IShellFolder接口Create 創建節點CreateFromList 根據PIDL創建節點CreateFromFolder 由特殊路徑創建節點Destroy 析構函數Assign 復制節點信息Clear 釋放節點的子對象Initialize 初始化方法

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

 擴展TShellNode 的類可以添加系統圖像列表索引屬性 查找能力等等 這完全取決於妳的想像力 還有壹點是除了桌面外 微軟公司還定義了壹組CoClasses對象 它們都暴露了IShellFolder 接口 我們也可以從它們出發來遍歷命名空間 下面列出這些CoClass的定義和描述 CLSID_NeorkPlaces 網絡鄰居CLSID_NeorkDomain 網絡域CLSID_NeorkServer 網絡服務器CLSID_NeorkShare 網絡***享CLSID_MyComputer 我的電腦CLSID_Internet 我的網絡CLSID_ShellFSFolder 文件系統的桌面目錄CLSID_RecycleBin 回收站CLSID_ControlPanel 控制面板CLSID_Printers 打印機CLSID_MyDocuments 我的文檔

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

EnumShellNamespace(ComboBox Items

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

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

顯示屬性頁

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

procedure ShowProperties(Handle: HWND; ItemList: PItemIDList); overload;varDesktop: IShellFolder;Folder: IShellFolder;ParentList: PItemIDList;RelativeList: PItemIDList;ContextMenu: IContextMenu;CommandInfo: TCMInvokeCommandInfo;BeginParentList := ILClone(ItemList);if ParentList <> nil thentryILRemoveLastID(ParentList);OleCheck(SHGetDesktopFolder(Desktop));OleCheck(Desktop BindToObject(ParentList nil IID_IShellFolder Folder));RelativeList := ILFindChild(ParentList ItemList);OleCheck(Folder GetUIObjectOf(Handle RelativeList IID_IContextMenu nil ContextMenu));FillChar(CommandInfo SizeOf(TCMInvokeCommandInfo) # );with CommandInfo dobegincbSize := SizeOf(TCMInvokeCommandInfo);hwnd := Handle;lpVerb := Properties ;nShow := SW_SHOW;end;OleCheck(ContextMenu InvokeCommand(CommandInfo));FinallyILFree(ParentList);end;end;procedure ShowProperties(Handle: HWND; const DisplayName: string); overload;varItemList: PItemIDList;BeginItemList := ILCreateFromPath(PChar(DisplayName));TryShowProperties(Handle ItemList)FinallyILFree(ItemList);end;end;

PIDL的其他用途

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

結論

lishixinzhi/Article/program/Delphi/201311/24786

  • 上一篇:零基礎怎麽學習軟件開發
  • 下一篇:dbforge studio for sql server是什麽軟件
  • copyright 2024編程學習大全網