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

接上一节:(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();
        }
SHGNO

事实上,只要修改 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
/// <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

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

GetUIObjectOf
//得到 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,让我们可以插入自己的菜单,或者直接调用菜单命令。

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

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

    (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单 上一节说到如 ...

  2. (C#)Windows Shell 外壳编程系列5 - 获取图标

    (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令 有 ...

  3. (C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单

    原文 (C#)Windows Shell 外壳编程系列7 - ContextMenu 注册文件右键菜单 (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windo ...

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

    (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一篇:(C#)Windows Shell 外壳编程系列1 - 基础,浏览一个文件夹 让我们详细解释一下 Shell 编程中最基本 ...

  5. (C#)Windows Shell 外壳编程系列8 - 同后缀名不同图标?

    原文 (C#)Windows Shell 外壳编程系列8 - 同后缀名不同图标? (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windows Shell 外壳 ...

  6. (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示

    原文 (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示 (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windows She ...

  7. (C#)Windows Shell 外壳编程系列6 - 执行

    原文(C#)Windows Shell 外壳编程系列6 - 执行 (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢-) 接上一节:(C#)Windows Shell 外壳编程系列5 - ...

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

    1 - 基础,浏览一个文件夹 我们知道,在win32中是以外壳名字空间的形式来组织文件系统的,在外壳名字空间里的每一个对象(注)都实现了一个IShellFolder的接口,通过这个接口我们可以直接查询 ...

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

    原文 (C#)Windows Shell 编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单 接上一节:(C#)Windows Shell 编程系列2 - 解释,从“桌面”开始展开这 ...

随机推荐

  1. tcp/ip 中的分组和分片

    osi 大家应该都知道osi七层模型吧,物理层 链路层 网络层 传输层 会话层 表示层 应用层ip 属于网络层,tcp 属于传输层,你可以把每一层想像成粽子的粽叶,包裹了七层的粽子最外面的就是物理层, ...

  2. Windos Server Tomcat 双开配置

    Tomcat 双开配置 tomcat_1   server.mxl文件 # 修改端口 <Connector port=" protocol="HTTP/1.1" c ...

  3. QGIS3.0.3+Qt5.9+VS2015_x64编译

    QGIS3.0.3+Qt5.9+VS2015_x64编译 参考:https://blog.csdn.net/u010670734/article/details/80241615 https://ww ...

  4. JZ2440专用dnw 支持xp、win7、win8和win10系统【转】

    本文转载自:https://blog.csdn.net/czg13548930186/article/details/76999152 学习于韦东山百问网公司 本文用于解决win7以上系统使用dnw难 ...

  5. 数据库系统概论学习4-SQL 语句和关系代数(二)单表查询

    4.12 字符匹配 精确查询和模糊查询 在这一节之前,我们学习的查询几乎都是精确查询,这就需要我们明确地知道某些属性的具体值.例如我们需要查询 'Wangxiaoxiao' 同学的信息,就需要在WHE ...

  6. 在Linux系统下使用Docker以及Weave搭建Nginx反向代理

    Hi, 今天我们将会学习如何使用 Weave 和 Docker 搭建 Nginx 的反向代理/负载均衡服务器.Weave 可以创建一个虚拟网络将 Docker 容器彼此连接在一起,支持跨主机部署及自动 ...

  7. MongoDB快速入门(二)- 数据库

    创建数据库 MongoDB use DATABASE_NAME 用于创建数据库.该命令如果数据库不存在,将创建一个新的数据库, 否则将返回现有的数据库. 语法 use DATABASE语句的基本语法如 ...

  8. java异常和错误类总结(2016.5)

    看到以前2016.5.写的一点笔记,拿过来放在一起. java异常和错误类总结 最近由于考试和以前的面试经常会遇到java当中异常类的继承层次的问题,弄得非常头大,因为java的异常实在是有点多,很难 ...

  9. js实现级联菜单(没有后台)

    html代码: <!-- js级联菜单 --> <div id="cascMenu"> <select id="select" o ...

  10. HDFS-查看文件属性+文件名称过滤

    package com.zhen.hdfs; import java.io.IOException; import java.io.OutputStream; import java.net.URI; ...