http://www.cnblogs.com/lemony/category/88555.html
原文见上面链接中的1-9系列
在win32中是以外壳名字空间的形式来组织文件系统的,在外壳名字空间里的每一个对象(注)都实现了一个IShellFolder的接口,通过这个接口我们可以直接查询或间接得到其他相关的接口。
(注:这里的对象指的是外壳名字空间中的一个节点,对象有可能是一个文件夹,有可能是一个文件,也有可能是一个虚拟文件夹,例如:我的电脑,网上邻居,控制面板等)
首先我们必须了解,在外壳编程中,要使用 PIDL 路径代替普通路径(如果对 PIDL 不熟悉,请看Windows外壳名字空间的浏览)。
“桌面”是最顶级的文件夹,外壳名字空间中其他各项都可以用从“桌面”开始的 PIDL 加以表示。
如何获取“桌面”的 PIDL 和其 IShellFolder 接口呢,可以通过 API SHGetDesktopFolder:
首先是桌面的Ishellfolder 通过这个我们找我们自己目标路径的PIDL及[IshellFolder可以通过 IShellFolder 的 ParseDisplayName 和 BindToObject 函数来实现:]
(void Ishellfolder .GetDisplayNameOf( ntPtr pidl, SHGNO uFlags, IntPtr lpName)为ParseDisplayName 的逆函数,两者实现从文件名到pidl和从pidl到文件名的一个抓换)
拿到了我们自己目标路径的ishellFolder 就可以通过EnumObjects 获得IEnumIDList进而通过循环获取其子项(子文件和子文件夹)的pidl:
获取到了pidl通过SHGetFileInfo来获取对应的文件或文件夹信息
2 - 解释,从“桌面”开始展开
对1的类似递归的利用,展开所有的文件夹及文件信息
类似“桌面”、“我的文档”这种既是普通文件夹又是特殊对象的绝对路径如何获得,这里就要用到 SHGetSpecialFolderPath API 了
3 - 上下文菜单(iContextMenu)(一)右键菜单
对象的上下文菜单相关的接口是IContextMenu,通过对象的父文件夹的IShellFolder.GetUIObjectOf方法可得到该接口。得到该接口后,可以用IContextMenu.QueryContextMenu方法来生成上下文菜单的菜单项,用IContextMenu.InvokeCommand调用相应的命令。
首先假设我们已经获得某个 IShellFolder 对象的 PIDL 和其上级 IShellFolder 对象:
IShellFolder IParent;
然后我们定义一个存放 PIDL 的数组:
pidls[ 0 ] = PIDL;
没错,我们的确要用到 PIDL 数组。可以理解,你在资源管理器中选择了多个文件/文件夹,再点击右键,弹出的上下文菜单将有所不同。你可以根据需要,把同一级的多个 PIDL 放到数组里面,实现这个效果。由于我们在例2的树中弹出菜单,所以只存放一个节点的 PIDL。
然后,通过 IParent 的 GetUIObjectOf 方法我们可以得到该节点的一个或多个指定子节点的 IContextMenu 接口:
得到 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;