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

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

接上一节:(C#)Windows Shell 编程系列2 - 解释,从“桌面”开始展开
这里解释上一节中获取名称的方法 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();         }

 

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);

 

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 是一个接口,我们这样定义:

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 接口:

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
下一节深入讲述 iContextMenu,让我们可以插入自己的菜单,或者直接调用菜单命令。

 

时间: 2024-08-18 14:20:00

(C#)Windows Shell 编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单的相关文章

(C#)Windows Shell 编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令

原文(C#)Windows Shell 编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令 (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windows Shell 编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单 上 一节说到如何弹出 IShellFolder 的上下文菜单,也就是 IContextMenu.有时候我们需要在这个菜单上面,加入一些属于自己的菜单项.举个例子,你打开资源管理器,查看左边目录树的

(C#)Windows Shell 编程系列2 - 解释,从“桌面”开始展开

 原文   (C#)Windows Shell 编程系列2 - 解释,从"桌面"开始展开   (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一篇:(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹 让我们详细解释一下 Shell 编程中最基本的一些函数.结构体和枚举. SHGetDesktopFolder  获取桌面的 IShellFolder 接口 [DllImport("shell32.dll")]        

(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹

原文 (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹  (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) Windows Shell 编程,即 Windows 外壳编程.我们所看到的资源管理器以及整个桌面,都是一个 Shell. 关于 Windows 外壳的基本概念,我这里不做详细介绍,不了解的朋友,可以看看 姜伟华 的 Windows外壳名字空间的浏览. 好,现在让我们从基础学起,早日做出一个强大的资源管理器软件.(偶也是初学者,多多指教) 1 -

c++-关于注册表添加右键菜单的测试。涉及Windows shell编程

问题描述 关于注册表添加右键菜单的测试.涉及Windows shell编程 关于注册表添加右键菜单的测试. 我在win7 64位 日语系统里面发现很奇怪的结论. 不知道是不是我弄错了. 1: [HKEY_CLASSES_ROOTDirectoryBackgroundshelltest] 1)右键桌面空白区域 有效果 2)右键文件夹 无效果 2 [HKEY_CLASSES_ROOTDirectoryBackgroundshellexTest] 1)右键桌面空白区域 无效果 2)右键文件夹 无效果

extjs-新手求救!EXTJS5的tree菜单如何实现右键菜单的问题

问题描述 新手求救!EXTJS5的tree菜单如何实现右键菜单的问题 EXTJS5的tree菜单右键点击节点时显示右键菜单,添加,删除,修改的操作 解决方案 var selectedRow; var menu = Ext.create('Ext.menu.Menu' { floating: true items: [{ text: '新建' handler: function () { console.log(selectedRow) ;/*....*/} } { text: '删除' hand

《windows核心编程系列》二谈谈ANSI和Unicode字符集 .

http://blog.csdn.net/ithzhang/article/details/7916732转载请注明出处!! 第二章:字符和字符串处理     使用vc编程时项目-->属性-->常规栏下我们可以设置项目字符集合,它可以是ANSI(多字节)字符集,也可以是unicode字符集.一般情况下说Unicode都是指UTF-16.也就是说每个字符编码为两个字节.65535个字符可以表示世界上大部分的语言.为了软件使国际化大家再编程时应该使用unicode字符集.由于原来学过c语言,不习惯

windows api编程-在别的地方弹出的系统菜单点击菜单项没有反应?

问题描述 在别的地方弹出的系统菜单点击菜单项没有反应? 去掉了标题栏,然后通过WM_HITTEST模拟标题栏,可是右键弹不出菜单,于是自己在WM_NCRBUTTONDOWN中弹出,代码如下case WM_NCRBUTTONDOWN: hMenu = GetSystemMenu(hWndFALSE); //AppendMenu(hMenuMF_SEPARATOR00); GetCursorPos(&pos); TrackPopupMenu(hMenuTPM_LEFTALIGN | TPM_LEFT

【转】Windows Shell扩展编程傻瓜手册大全:上下文菜单扩展

引用自:http://blog.163.com/yesaidu@126/blog/static/51819307200861853827582/ Part I: A step-by-step tutorial on writing shell extensions 第一节:Windows shell扩展初步:上下文菜单扩展   作者:Michael Dunn 译者:yesaidu   源代码下载:1       2   目录 ● README ● 系列绪言 ● 第一部分绪言 ● 从AppWiza

Windows 8开发入门(十六) Windows 8的右键菜单

在Windows 8中的控件中有TextBox等输入控件的ContextMenuOpening事件和Button等非输入控件的 RightTapped事件. 本文中将讲述者两个事件的用法.这两个事件的 PopupMenu是右键弹出菜单 的具体类. 首先我们看具体菜单类的实例化和获取项目代码 /// <summary> /// 设置右键点击点击具体处理细节 /// </summary> /// <param name="sender"></par