(C#)Windows Shell 外殼編程系列3 – 操作功能表(iContextMenu)(一)右鍵菜單

來源:互聯網
上載者:User

(本系列文章由檸檬的(lc_mtt)原創,轉載請註明出處,謝謝~)

這裡解釋上一節中擷取名稱的方法

GetDisplayNameOf
定義:

void GetDisplayNameOf(
            IntPtr pidl,
            SHGNO uFlags,
            IntPtr lpName);

該方法是用來轉換PIDL成為可顯示的名稱字串。PIDL必須是相對於對象的父目錄的。換句話說,它必須包含一個非空的SHITEMID 結構。因為有多種命名物件的方式,資源管理員通過在uFlags參數中定義SHGNO標識的組合來表示名稱類型。SHGDN_NORMAL或SHGDN_INFOLDER將被用來指定名稱是相對於檔案夾的還是相對於案頭的。其他三個值SHGDN_FOREDITING、SHGDN_FORADDRESSBAR和SHGDN_FORPARSING可以用來指定名稱的用途。 名稱必須按STRRET的結構形式返回,如果SHGDN_FOREDITING、SHGDN_FORADDRESSBAR和 SHGDN_FORPARSING沒有設定,就返回外殼對象的顯示名稱。
具體實現方法:

/**//// <summary>
/// 擷取顯示名稱
/// </summary>
public static string GetNameByIShell(IShellFolder Root, IntPtr pidlSub)
{
            IntPtr strr = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
            Marshal.WriteInt32(strr, 0, 0);
            StringBuilder buf = new StringBuilder(MAX_PATH);
            Root.GetDisplayNameOf(pidlSub, SHGNO.INFOLDER, strr);
            API.StrRetToBuf(strr, pidlSub, buf, MAX_PATH);
            Marshal.FreeCoTaskMem(strr);
return buf.ToString();
        }

SHGNO
public enum SHGNO
{
        NORMAL = 0x0,
        INFOLDER = 0x1,
        FOREDITING = 0x1000,
        FORADDRESSBAR = 0x4000,
        FORPARSING = 0x8000,
    }

事實上,只要修改 SHGNO ,就可以擷取其絕對路徑:

/**//// <summary>
/// 根據路徑擷取 IShellFolder 和 PIDL
/// </summary>
public static IShellFolder GetShellFolder(IShellFolder desktop, string path, out IntPtr Pidl)
{
            IShellFolder IFolder;
uint i, j = 0;
            desktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, path, out i, out Pidl, ref j);
            desktop.BindToObject(Pidl, IntPtr.Zero, ref Guids.IID_IShellFolder, out IFolder);
return IFolder;
        }

但我們還關心類似“案頭”、“我的文件”這種既是普通檔案夾又是特殊對象的絕對路徑如何獲得,這裡就要用到 SHGetSpecialFolderPath API 了。

[DllImport("Shell32.Dll")]
private static extern bool SHGetSpecialFolderPath(
            IntPtr hwndOwner, 
            StringBuilder lpszPath,
            ShellSpecialFolders nFolder,
bool fCreate);

ShellSpecialFolders
public enum ShellSpecialFolders
{
        DESKTOP = 0x0000,         // <desktop>
        INTERNET = 0x0001,
        PROGRAMS = 0x0002,        // Start Menu\Programs
        CONTROLS = 0x0003,        // My Computer\Control Panel
        PRINTERS = 0x0004,        // My Computer\Printers
        PERSONAL = 0x0005,        // My Documents
        FAVORITES = 0x0006,        // <user name>\Favorites
        STARTUP = 0x0007,        // Start Menu\Programs\Startup
        RECENT = 0x0008,        // <user name>\Recent
        SENDTO = 0x0009,        // <user name>\SendTo
        BITBUCKET = 0x000a,        // <desktop>\Recycle Bin
        STARTMENU = 0x000b,        // <user name>\Start Menu
        MYDOCUMENTS = 0x000c,        // logical "My Documents" desktop icon
        MYMUSIC = 0x000d,        // "My Music" folder
        MYVIDEO = 0x000e,        // "My Videos" folder
        DESKTOPDIRECTORY = 0x0010,        // <user name>\Desktop
        DRIVES = 0x0011,        // My Computer
        NETWORK = 0x0012,        // Network Neighborhood (My Network Places)
        NETHOOD = 0x0013,        // <user name>\nethood
        FONTS = 0x0014,        // windows\fonts
        TEMPLATES = 0x0015,
        COMMON_STARTMENU = 0x0016,        // All Users\Start Menu
        COMMON_PROGRAMS = 0X0017,        // All Users\Start Menu\Programs
        COMMON_STARTUP = 0x0018,        // All Users\Startup
        COMMON_DESKTOPDIRECTORY = 0x0019,        // All Users\Desktop
        APPDATA = 0x001a,        // <user name>\Application Data
        PRINTHOOD = 0x001b,        // <user name>\PrintHood
        LOCAL_APPDATA = 0x001c,        // <user name>\Local Settings\Applicaiton Data (non roaming)
        ALTSTARTUP = 0x001d,        // non localized startup
        COMMON_ALTSTARTUP = 0x001e,        // non localized common startup
        COMMON_FAVORITES = 0x001f,
        INTERNET_CACHE = 0x0020,
        COOKIES = 0x0021,
        HISTORY = 0x0022,
        COMMON_APPDATA = 0x0023,        // All Users\Application Data
        WINDOWS = 0x0024,        // GetWindowsDirectory()
        SYSTEM = 0x0025,        // GetSystemDirectory()
        PROGRAM_FILES = 0x0026,        // C:\Program Files
        MYPICTURES = 0x0027,        // C:\Program Files\My Pictures
        PROFILE = 0x0028,        // USERPROFILE
        SYSTEMX86 = 0x0029,        // x86 system directory on RISC
        PROGRAM_FILESX86 = 0x002a,        // x86 C:\Program Files on RISC
        PROGRAM_FILES_COMMON = 0x002b,        // C:\Program Files\Common
        PROGRAM_FILES_COMMONX86 = 0x002c,        // x86 Program Files\Common on RISC
        COMMON_TEMPLATES = 0x002d,        // All Users\Templates
        COMMON_DOCUMENTS = 0x002e,        // All Users\Documents
        COMMON_ADMINTOOLS = 0x002f,        // All Users\Start Menu\Programs\Administrative Tools
        ADMINTOOLS = 0x0030,        // <user name>\Start Menu\Programs\Administrative Tools
        CONNECTIONS = 0x0031,        // Network and Dial-up Connections
        COMMON_MUSIC = 0x0035,        // All Users\My Music
        COMMON_PICTURES = 0x0036,        // All Users\My Pictures
        COMMON_VIDEO = 0x0037,        // All Users\My Video
        RESOURCES = 0x0038,        // Resource Direcotry
        RESOURCES_LOCALIZED = 0x0039,        // Localized Resource Direcotry
        COMMON_OLINKS = 0x003a,        // Links to All Users OEM specific apps
        CDBURN_AREA = 0x003b,        // USERPROFILE\Local Settings\Application Data\Microsoft\CD Burning
        COMPUTERSNEARME = 0x003d,        // Computers Near Me (computered from Workgroup membership)
        FLAG_CREATE = 0x8000,        // combine with  value to force folder creation in SHGetFolderPath()
        FLAG_DONT_VERIFY = 0x4000,        // combine with  value to return an unverified folder path
        FLAG_NO_ALIAS = 0x1000,        // combine with  value to insure non-alias versions of the pidl
        FLAG_PER_USER_INIT = 0x0800,        // combine with  value to indicate per-user init (eg. upgrade)
        FLAG_MASK = 0xFF00,        // mask for all possible flag values
    }

/**//// <summary>
/// 擷取特殊檔案夾的路徑
/// </summary>
public static string GetSpecialFolderPath(IntPtr hwnd, ShellSpecialFolders nFolder)
{
            StringBuilder sb = new StringBuilder(MAX_PATH);
            SHGetSpecialFolderPath(hwnd, sb, nFolder, false);
return sb.ToString();
        }

操作功能表
對象的操作功能表相關的介面是IContextMenu,通過對象的父資料夾的IShellFolder.GetUIObjectOf方法可得到該介面。得到該介面後,可以用IContextMenu.QueryContextMenu方法來產生操作功能表的功能表項目,用IContextMenu.InvokeCommand調用相應的命令。
好,讓我們一步一步來實現 IShellFolder 對象的操作功能表彈出。
首先假設我們已經獲得某個 IShellFolder 對象的 PIDL 和其上級 IShellFolder 對象:

IntPtr PIDL;
IShellFolder IParent;

然後我們定義一個存放 PIDL 的數組:

IntPtr[] pidls = new IntPtr[1];
pidls[0] = PIDL;

沒錯,我們的確要用到 PIDL 數組。可以理解,你在資源管理員中選擇了多個檔案/檔案夾,再點擊右鍵,彈出的操作功能表將有所不同。你可以根據需要,把同一級的多個 PIDL 放到數組裡面,實現這個效果。由於我們在例2的樹中快顯功能表,所以只存放一個節點的 PIDL。
IContextMenu 是一個介面,我們這樣定義:

IContextMenu.cs
using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace WinShell
{
    [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), GuidAttribute("000214e4-0000-0000-c000-000000000046")]
public interface IContextMenu
{
        [PreserveSig()]
        Int32 QueryContextMenu(
            IntPtr hmenu,
uint iMenu,
uint idCmdFirst,
uint idCmdLast,
            CMF uFlags);

        [PreserveSig()]
        Int32 InvokeCommand(
ref CMINVOKECOMMANDINFOEX info);

        [PreserveSig()]
void GetCommandString(
int idcmd,
            GetCommandStringInformations uflags,
int reserved,
            StringBuilder commandstring,
int cch);
    }
}

然後,通過 IParent 的 GetUIObjectOf 方法我們可以得到該節點的一個或多個指定子節點的 IContextMenu 介面:

GetUIObjectOf
IntPtr GetUIObjectOf(
            IntPtr hwndOwner,
uint cidl,
            [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl,
            [In()] ref Guid riid,
out IntPtr rgfReserved);

//得到 IContextMenu 介面
                    IntPtr iContextMenuPtr = IntPtr.Zero;
                    iContextMenuPtr = IParent.GetUIObjectOf(IntPtr.Zero, (uint)pidls.Length, 
                        pidls, ref Guids.IID_IContextMenu, out iContextMenuPtr);
                    IContextMenu iContextMenu = (IContextMenu)Marshal.GetObjectForIUnknown(iContextMenuPtr);

得到 IContextMenu 後我們需要提供一個彈出式菜單的控制代碼,並把他傳給 IContextMenu.QueryContextMenu,如果該方法執行成功的話,會在我們的菜單裡加入相應的功能表項目。

//提供一個彈出式菜單的控制代碼
IntPtr contextMenu = API.CreatePopupMenu();
iContextMenu.QueryContextMenu(contextMenu, 0,
API.CMD_FIRST, API.CMD_LAST, CMF.NORMAL | CMF.EXPLORE);

有了功能表項目,我們就可以彈出該菜單了,我們用 TPM_RETURNCMD 標誌指定 TrackPopupMenu 必須返回使用者所選功能表項目的 ID,以便稍後通過IContextMenu.InvokeCommand 來執行功能表命令:

//快顯功能表
uint cmd = API.TrackPopupMenuEx(contextMenu,TPM.RETURNCMD,
MousePosition.X, MousePosition.Y, this.Handle, IntPtr.Zero);

//擷取命令序號,執行功能表命令
if (cmd >= API.CMD_FIRST)
{
    CMINVOKECOMMANDINFOEX invoke = new CMINVOKECOMMANDINFOEX();
    invoke.cbSize = Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX));
    invoke.lpVerb = (IntPtr)(cmd - 1);
    invoke.lpDirectory = string.Empty;
    invoke.fMask = 0;
    invoke.ptInvoke = new POINT(MousePosition.X, MousePosition.Y);
    invoke.nShow = 1;
    iContextMenu.InvokeCommand(ref invoke);
}

慣例附片和原始碼:

原始碼:/Files/lemony/WinShell3.rar

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.