(C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单

(本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)

接上一节: (C#)Windows Shell 外壳编程系列2 - 解释,从“桌面”开始展开

这里解释上一节中获取名称的方法

GetDisplayNameOf
定义:

None.gif void  GetDisplayNameOf(
None.gif            IntPtr pidl,
None.gif            SHGNO uFlags,
None.gif            IntPtr lpName);

该方法是用来转换PIDL成为可显示的名称字符串。PIDL必须是相对于对象的父目录的。换句话说,它必须包含一个非空的SHITEMID 结构。因为有多种命名对象的方式,资源管理器通过在uFlags参数中定义SHGNO标识的组合来表示名称类型。SHGDN_NORMAL或SHGDN_INFOLDER将被用来指定名称是相对于文件夹的还是相对于桌面的。其他三个值SHGDN_FOREDITING、SHGDN_FORADDRESSBAR和SHGDN_FORPARSING可以用来指定名称的用途。 名称必须按STRRET的结构形式返回,如果SHGDN_FOREDITING、SHGDN_FORADDRESSBAR和 SHGDN_FORPARSING没有设定,就返回外壳对象的显示名称。

具体实现方法:

ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
InBlock.gif        
/// 获取显示名称
ExpandedBlockEnd.gif        
/// </summary>

None.gif          public   static   string  GetNameByIShell(IShellFolder Root, IntPtr pidlSub)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif            IntPtr strr 
= Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
InBlock.gif            Marshal.WriteInt32(strr, 
00);
InBlock.gif            StringBuilder buf 
= new StringBuilder(MAX_PATH);
InBlock.gif            Root.GetDisplayNameOf(pidlSub, SHGNO.INFOLDER, strr);
InBlock.gif            API.StrRetToBuf(strr, pidlSub, buf, MAX_PATH);
InBlock.gif            Marshal.FreeCoTaskMem(strr);
InBlock.gif            
return buf.ToString();
ExpandedBlockEnd.gif        }

 

ContractedBlock.gif ExpandedBlockStart.gif SHGNO
None.gifpublic enum SHGNO
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif{
InBlock.gif        NORMAL 
= 0x0,
InBlock.gif        INFOLDER 
= 0x1,
InBlock.gif        FOREDITING 
= 0x1000,
InBlock.gif        FORADDRESSBAR 
= 0x4000,
InBlock.gif        FORPARSING 
= 0x8000,
ExpandedBlockEnd.gif    }

事实上,只要修改 SHGNO ,就可以获取其绝对路径:

ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
InBlock.gif        
/// 根据路径获取 IShellFolder 和 PIDL
ExpandedBlockEnd.gif        
/// </summary>

None.gif          public   static  IShellFolder GetShellFolder(IShellFolder desktop,  string  path,  out  IntPtr Pidl)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif            IShellFolder IFolder;
InBlock.gif            
uint i, j = 0;
InBlock.gif            desktop.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, path, 
out i, out Pidl, ref j);
InBlock.gif            desktop.BindToObject(Pidl, IntPtr.Zero, 
ref Guids.IID_IShellFolder, out IFolder);
InBlock.gif            
return IFolder;
ExpandedBlockEnd.gif        }

但我们还关心类似“桌面”、“我的文档”这种既是普通文件夹又是特殊对象的绝对路径如何获得,这里就要用到 SHGetSpecialFolderPath API 了。

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

 

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

 

ExpandedBlockStart.gif ContractedBlock.gif /**/ /// <summary>
InBlock.gif        
/// 获取特殊文件夹的路径
ExpandedBlockEnd.gif        
/// </summary>

None.gif          public   static   string  GetSpecialFolderPath(IntPtr hwnd, ShellSpecialFolders nFolder)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif            StringBuilder sb 
= new StringBuilder(MAX_PATH);
InBlock.gif            SHGetSpecialFolderPath(hwnd, sb, nFolder, 
false);
InBlock.gif            
return sb.ToString();
ExpandedBlockEnd.gif        }

上下文菜单

对象的上下文菜单相关的接口是IContextMenu,通过对象的父文件夹的IShellFolder.GetUIObjectOf方法可得到该接口。得到该接口后,可以用IContextMenu.QueryContextMenu方法来生成上下文菜单的菜单项,用IContextMenu.InvokeCommand调用相应的命令。

好,让我们一步一步来实现 IShellFolder 对象的上下文菜单弹出。

首先假设我们已经获得某个 IShellFolder 对象的 PIDL 和其上级 IShellFolder 对象:

None.gif IntPtr PIDL;
None.gifIShellFolder IParent;

然后我们定义一个存放 PIDL 的数组:

None.gif IntPtr[] pidls  =   new  IntPtr[ 1 ];
None.gifpidls[
0 =  PIDL;

没错,我们的确要用到 PIDL 数组。可以理解,你在资源管理器中选择了多个文件/文件夹,再点击右键,弹出的上下文菜单将有所不同。你可以根据需要,把同一级的多个 PIDL 放到数组里面,实现这个效果。由于我们在例2的树中弹出菜单,所以只存放一个节点的 PIDL。

IContextMenu 是一个接口,我们这样定义:

ContractedBlock.gif ExpandedBlockStart.gif IContextMenu.cs
None.gifusing System;
None.gif
using System.Collections.Generic;
None.gif
using System.Text;
None.gif
using System.Runtime.InteropServices;
None.gif
None.gif
namespace WinShell
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown), GuidAttribute(
"000214e4-0000-0000-c000-000000000046")]
InBlock.gif    
public interface IContextMenu
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        [PreserveSig()]
InBlock.gif        Int32 QueryContextMenu(
InBlock.gif            IntPtr hmenu,
InBlock.gif            
uint iMenu,
InBlock.gif            
uint idCmdFirst,
InBlock.gif            
uint idCmdLast,
InBlock.gif            CMF uFlags);
InBlock.gif
InBlock.gif        [PreserveSig()]
InBlock.gif        Int32 InvokeCommand(
InBlock.gif            
ref CMINVOKECOMMANDINFOEX info);
InBlock.gif
InBlock.gif        [PreserveSig()]
InBlock.gif        
void GetCommandString(
InBlock.gif            
int idcmd,
InBlock.gif            GetCommandStringInformations uflags,
InBlock.gif            
int reserved,
InBlock.gif            StringBuilder commandstring,
InBlock.gif            
int cch);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

None.gif

然后,通过 IParent 的 GetUIObjectOf 方法我们可以得到该节点的一个或多个指定子节点的 IContextMenu 接口:

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

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

得到 IContextMenu 后我们需要提供一个弹出式菜单的句柄,并把他传给 IContextMenu.QueryContextMenu,如果该方法执行成功的话,会在我们的菜单里加入相应的菜单项。

None.gif // 提供一个弹出式菜单的句柄
None.gif
IntPtr contextMenu  =  API.CreatePopupMenu();
None.gifiContextMenu.QueryContextMenu(contextMenu, 
0 ,
None.gifAPI.CMD_FIRST, API.CMD_LAST, CMF.NORMAL 
|  CMF.EXPLORE);

有了菜单项,我们就可以弹出该菜单了,我们用 TPM_RETURNCMD 标志指定 TrackPopupMenu 必须返回用户所选菜单项的 ID,以便稍后通过IContextMenu.InvokeCommand 来执行菜单命令:

None.gif // 弹出菜单
None.gif
uint  cmd  =  API.TrackPopupMenuEx(contextMenu,TPM.RETURNCMD,
None.gifMousePosition.X, MousePosition.Y, 
this .Handle, IntPtr.Zero);
None.gif
None.gif
// 获取命令序号,执行菜单命令
None.gif
if  (cmd  >=  API.CMD_FIRST)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    CMINVOKECOMMANDINFOEX invoke 
= new CMINVOKECOMMANDINFOEX();
InBlock.gif    invoke.cbSize 
= Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX));
InBlock.gif    invoke.lpVerb 
= (IntPtr)(cmd - 1);
InBlock.gif    invoke.lpDirectory 
= string.Empty;
InBlock.gif    invoke.fMask 
= 0;
InBlock.gif    invoke.ptInvoke 
= new POINT(MousePosition.X, MousePosition.Y);
InBlock.gif    invoke.nShow 
= 1;
InBlock.gif    iContextMenu.InvokeCommand(
ref invoke);
ExpandedBlockEnd.gif}

惯例附上图片和源代码:



源代码:/Files/lemony/WinShell3.rar

下一节深入讲述 iContextMenu,让我们可以插入自己的菜单,或者直接调用菜单命令。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值