(本系列文章由檸檬的(lc_mtt)原創,轉載請注明出處,謝謝~)
這裡解釋上一節中擷取名稱的方法
GetDisplayNameOf 定義:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 void GetDisplayNameOf(
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr pidl,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 SHGNO uFlags,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr lpName);
該方法是用來轉換PIDL成為可顯示的名稱字元串。PIDL必須是相對于對象的父目錄的。換句話說,它必須包含一個非空的SHITEMID 結構。因為有多種命名對象的方式,資料總管通過在uFlags參數中定義SHGNO辨別的組合來表示名稱類型。SHGDN_NORMAL或SHGDN_INFOLDER将被用來指定名稱是相對于檔案夾的還是相對于桌面的。其他三個值SHGDN_FOREDITING、SHGDN_FORADDRESSBAR和SHGDN_FORPARSING可以用來指定名稱的用途。 名稱必須按STRRET的結構形式傳回,如果SHGDN_FOREDITING、SHGDN_FORADDRESSBAR和 SHGDN_FORPARSING沒有設定,就傳回外殼對象的顯示名稱。
具體實作方法:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 /// <summary>
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 /// 擷取顯示名稱
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 /// </summary>
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 public static string GetNameByIShell(IShellFolder Root, IntPtr pidlSub)
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 {
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr strr = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 Marshal.WriteInt32(strr, 0, 0);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 StringBuilder buf = new StringBuilder(MAX_PATH);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 Root.GetDisplayNameOf(pidlSub, SHGNO.INFOLDER, strr);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 API.StrRetToBuf(strr, pidlSub, buf, MAX_PATH);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 Marshal.FreeCoTaskMem(strr);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 return buf.ToString();
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 }
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 SHGNO
事實上,隻要修改 SHGNO ,就可以擷取其絕對路徑:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 /// 根據路徑擷取 IShellFolder 和 PIDL
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 public static IShellFolder GetShellFolder(IShellFolder desktop, string path, out IntPtr Pidl)
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IShellFolder IFolder;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 uint i, j = 0;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 desktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, path, out i, out Pidl, ref j);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 desktop.BindToObject(Pidl, IntPtr.Zero, ref Guids.IID_IShellFolder, out IFolder);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 return IFolder;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 但我們還關心類似“桌面”、“我的檔案”這種既是普通檔案夾又是特殊對象的絕對路徑如何獲得,這裡就要用到 SHGetSpecialFolderPath API 了。
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 [DllImport("Shell32.Dll")]
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 private static extern bool SHGetSpecialFolderPath(
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr hwndOwner,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 StringBuilder lpszPath,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 ShellSpecialFolders nFolder,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 bool fCreate);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 ShellSpecialFolders
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 /// 擷取特殊檔案夾的路徑
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 public static string GetSpecialFolderPath(IntPtr hwnd, ShellSpecialFolders nFolder)
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 StringBuilder sb = new StringBuilder(MAX_PATH);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 SHGetSpecialFolderPath(hwnd, sb, nFolder, false);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 return sb.ToString();
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 上下文菜單
對象的上下文菜單相關的接口是IContextMenu,通過對象的父檔案夾的IShellFolder.GetUIObjectOf方法可得到該接口。得到該接口後,可以用IContextMenu.QueryContextMenu方法來生成上下文菜單的菜單項,用IContextMenu.InvokeCommand調用相應的指令。
好,讓我們一步一步來實作 IShellFolder 對象的上下文菜單彈出。
首先假設我們已經獲得某個 IShellFolder 對象的 PIDL 和其上級 IShellFolder 對象:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr PIDL;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IShellFolder IParent;
然後我們定義一個存放 PIDL 的數組:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr[] pidls = new IntPtr[1];
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 pidls[0] = PIDL;
沒錯,我們的确要用到 PIDL 數組。可以了解,你在資料總管中選擇了多個檔案/檔案夾,再點選右鍵,彈出的上下文菜單将有所不同。你可以根據需要,把同一級的多個 PIDL 放到數組裡面,實作這個效果。由于我們在例2的樹中彈出菜單,是以隻存放一個節點的 PIDL。
IContextMenu 是一個接口,我們這樣定義:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IContextMenu.cs
然後,通過 IParent 的 GetUIObjectOf 方法我們可以得到該節點的一個或多個指定子節點的 IContextMenu 接口:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 GetUIObjectOf
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 //得到 IContextMenu 接口
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr iContextMenuPtr = IntPtr.Zero;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 iContextMenuPtr = IParent.GetUIObjectOf(IntPtr.Zero, (uint)pidls.Length,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 pidls, ref Guids.IID_IContextMenu, out iContextMenuPtr);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IContextMenu iContextMenu = (IContextMenu)Marshal.GetObjectForIUnknown(iContextMenuPtr);
得到 IContextMenu 後我們需要提供一個彈出式菜單的句柄,并把他傳給 IContextMenu.QueryContextMenu,如果該方法執行成功的話,會在我們的菜單裡加入相應的菜單項。
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 //提供一個彈出式菜單的句柄
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 IntPtr contextMenu = API.CreatePopupMenu();
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 iContextMenu.QueryContextMenu(contextMenu, 0,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 API.CMD_FIRST, API.CMD_LAST, CMF.NORMAL | CMF.EXPLORE);
有了菜單項,我們就可以彈出該菜單了,我們用 TPM_RETURNCMD 标志指定 TrackPopupMenu 必須傳回使用者所選菜單項的 ID,以便稍後通過IContextMenu.InvokeCommand 來執行菜單指令:
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 //彈出菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 uint cmd = API.TrackPopupMenuEx(contextMenu,TPM.RETURNCMD,
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 MousePosition.X, MousePosition.Y, this.Handle, IntPtr.Zero);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 //擷取指令序号,執行菜單指令
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 if (cmd >= API.CMD_FIRST)
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 {
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX();
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 invoke.cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX));
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 invoke.lpVerb = (IntPtr)(cmd - 1);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 invoke.lpDirectory = string.Empty;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 invoke.fMask = 0;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 invoke.ptInvoke = new POINT(MousePosition.X, MousePosition.Y);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 invoke.nShow = 1;
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 iContextMenu.InvokeCommand(ref invoke);
(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單 }
慣例附上圖檔和源代碼:
下一節深入講述 iContextMenu,讓我們可以插入自己的菜單,或者直接調用菜單指令。