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

转载 2014年08月15日 19:08:24
接上一节:(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, 
00);
            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 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令

接上一节:(C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单 上一节说到如何弹出 IShellFolder 的上下文菜单,也就是 I...

Windows Shell编程实现右键菜单-VS2005,Win7 32位

参考资料: windows右键菜单开发--with vs2008(2005) or vc9(vc8) 前言:其实最近一直在看微软的刻录函数 imapi2,发现大量的接口函数...

VC Windows shell扩展编程(为系统右键菜单添加二级菜单傻瓜教程)

转自:VC Windows shell扩展编程(为系统右键菜单添加二级菜单傻瓜教程) 2012-08-07 16:26 650人阅读 评论(0) 收藏 举报 windowsshe...
  • worldy
  • worldy
  • 2013年12月24日 11:50
  • 1485

自己动手写一个简单的Windows shell扩展程序

作者:朱金灿来源:http://blog.csdn.net/clever101                关于什么叫Windowsshell扩展程序,这里不作介绍,不懂的同学请google之。  ...

Windows Shell 编程 第二章

“Shell 编程”的大伞之下有大量的API函数和COM接口。这个种类繁多的‘命令’集允许你用不同的方法对Windows Shell进行编程。函数和接口并不是两种提供相同功能的等价途径,相反,它们在不...

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

接上一节:(C#)Windows Shell 外壳编程系列5 - 获取图标  执行     许多人都知道 ShellExecute ,用于执行一个外部命令。但对于  IShellFol...

ASP.Net 获取服务器的当前日期时间的综合处理函数!

//获取日期+时间 DateTime.Now.ToString(); // 2008-9-4 20:02:10 DateTime.Now.ToLocalTime().ToSt...
  • dxnn520
  • dxnn520
  • 2012年11月24日 06:51
  • 6314

VC++ 外壳扩展编程之windows右键菜单

VC++ 外壳扩展编程之windows右键菜单 推荐指数:★★★☆ 文档人气:1961 本周人气:17 发布日期:2006-12-13      这次我想和大家一起讨论一下 Wi...
  • aasmfox
  • aasmfox
  • 2015年12月01日 20:44
  • 636

(转)VC++ 外壳扩展编程之windows右键菜单

所属类别:VC++ 文章作者:李晓飞 推荐指数:★★★☆ 文档人气:4379 本周人气:4 发布日期:2006-12-13 这次我想和大家一起讨论一下 Windows 的 Sh...
  • gxj1680
  • gxj1680
  • 2012年08月09日 12:12
  • 977
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:(C#)Windows Shell 外壳编程系列3 - 上下文菜单(iContextMenu)(一)右键菜单
举报原因:
原因补充:

(最多只允许输入30个字)