天天看點

(C#)Windows Shell 外殼程式設計系列3 - 上下文菜單(iContextMenu)(一)右鍵菜單

(本系列文章由檸檬的(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,讓我們可以插入自己的菜單,或者直接調用菜單指令。