博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
(C#)Windows Shell 外壳编程系列2 - 解释,从“桌面”开始展开
阅读量:6074 次
发布时间:2019-06-20

本文共 4003 字,大约阅读时间需要 13 分钟。

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

让我们详细解释一下 Shell 编程中最基本的一些函数、结构体和枚举。
SHGetDesktopFolder
获取桌面的 IShellFolder 接口

None.gif[DllImport("shell32.dll")]

None.gif public static extern Int32 SHGetDesktopFolder(out IntPtr ppshf);

要使用这个函数,必须先定义一个 IntPtr 指针。然后通过指针,使用 GetObjectForIUnknown 返回通过指向 COM 对象的 IShellFolder 接口的指针实例。于是需要编写以下函数:

None.gifpublic static IShellFolder GetDesktopFolder(out IntPtr ppshf)

ExpandedBlockStart.gifContractedBlock.gifdot.gif{
InBlock.gif            SHGetDesktopFolder(out ppshf);
InBlock.gif            Object obj = Marshal.GetObjectForIUnknown(ppshf);
InBlock.gif return (IShellFolder)obj;
ExpandedBlockEnd.gif        }

ParseDisplayName

获得对象的PIDL,即便对象在目录树中处于当前目录下一层或更多层。例如,对于文件对象来说,它的解析名就是它的路径,我们用文件系统对象的完全路径名来调用桌面的IshellFolder接口的 ParseDisplayName 方法,它会返回这个对象的完全PIDL。定义:

None.gifvoid ParseDisplayName(

None.gif            IntPtr hwnd,
None.gif            IntPtr pbc,
None.gif            [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName,
None.gif out uint pchEaten,
None.gif out IntPtr ppidl,
None.gif ref uint pdwAttributes);

里面最重要的参数就是 out IntPtr ppidl 了,它返回 pszDisplayName 指定路径对应的 PIDL。然而仅仅是 PIDL 并不能让你做更多的事情。这时候还需要调用 BindToObject 来返回 IShellFolder 接口。

BindToObject
根据 PIDL 创建和初始化 IShellFolder 对象。定义:

None.gifvoid BindToObject(

None.gif            IntPtr pidl,
None.gif            IntPtr pbc,
None.gif            [In()] ref Guid riid,
None.gif out IShellFolder ppv);

里面有一个 [In()] ref Guid riid 参数,表示接口的接口标识符 (IID)。GUID其实就是一个唯一的标识符。世界上的任何两台计算机都不会生成重复的 GUID 值。GUID 主要用于在拥有多个节点、多台计算机的网络或系统中,分配必须具有唯一性的标识符。我们这里使用 IID_IShellFolder 表示它获取的是一个 IShellFolder 接口。

None.gifpublic static Guid IID_IShellFolder = new Guid("{000214E6-0000-0000-C000-000000000046}");

另外介绍 IEnumIDList 接口。IEnumIDList 接口使资源管理器获得文件夹包含的全部对象的PIDL,PIDL然后可以用来获得这些对象的信息。

因此,我们使用 EnumObjects 函数返回的将是 IEnumIDList 的指针:

None.gifint EnumObjects(IntPtr hWnd, SHCONTF flags, out IntPtr enumIDList);

其中 flags 是 SHCONTF 枚举类型,它决定了枚举的内容:

ContractedBlock.gifExpandedBlockStart.gifSHCONTF

None.gifpublic enum SHCONTF
ExpandedBlockStart.gifContractedBlock.gifdot.gif{
InBlock.gif        FOLDERS = 0x20,
InBlock.gif        NONFOLDERS = 0x40,
InBlock.gif        INCLUDEHIDDEN = 0x80,
InBlock.gif        INIT_ON_FIRST_NEXT = 0x100,
InBlock.gif        NETPRINTERSRCH = 0x200,
InBlock.gif        SHAREABLE = 0x400,
InBlock.gif        STORAGE = 0x800
ExpandedBlockEnd.gif    }

因此,我们可以通过 flags 的不同来分别列举子文件和子目录。这里会遇到一个问题,怎么获取 PIDL 对象的名称呢。这里编写了2个函数,可以通过 PIDL 或者 IShellFolder 返回对象的名称(详细解释留到下一节):

ContractedBlock.gifExpandedBlockStart.gif获取名称

ExpandedBlockStart.gifContractedBlock.gif/** <summary>
InBlock.gif /// 获取显示名称
ExpandedBlockEnd.gif /// </summary>
None.gif public static string GetNameByIShell(IShellFolder Root, IntPtr pidlSub)
ExpandedBlockStart.gifContractedBlock.gifdot.gif{
InBlock.gif            IntPtr strr = Marshal.AllocCoTaskMem(MAX_PATH * 2 + 4);
InBlock.gif            Marshal.WriteInt32(strr, 0, 0);
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 return buf.ToString();
ExpandedBlockEnd.gif        }
None.gif
ExpandedBlockStart.gifContractedBlock.gif /** <summary>
InBlock.gif /// 根据 PIDL 获取显示名称
ExpandedBlockEnd.gif /// </summary>
None.gif public static string GetNameByPIDL(IntPtr pidl)
ExpandedBlockStart.gifContractedBlock.gifdot.gif{
InBlock.gif            SHFILEINFO info = new SHFILEINFO();
InBlock.gif            API.SHGetFileInfo(pidl, 0, ref info, Marshal.SizeOf(typeof(SHFILEINFO)),
InBlock.gif                SHGFI.PIDL | SHGFI.DISPLAYNAME | SHGFI.TYPENAME);
InBlock.gif return info.szDisplayName;
ExpandedBlockEnd.gif        }

例子二,从“桌面”开始展开

这个例子将使你深入理解之前的内容。它是这样的一个例子,允许你从“桌面”开始,一直展开到最深层的对象。

ContractedBlock.gifExpandedBlockStart.gif例2

None.gifpublic partial class Sample2 : Form
ExpandedBlockStart.gifContractedBlock.gifdot.gif{
InBlock.gif private IShellFolder deskTop;
InBlock.gif
InBlock.gif public Sample2()
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif            InitializeComponent();
ExpandedSubBlockEnd.gif        }
InBlock.gif
InBlock.gif private void Form1_Load(object sender, EventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif //获得桌面 PIDL
InBlock.gif            IntPtr deskTopPtr;
InBlock.gif            deskTop = API.GetDesktopFolder(out deskTopPtr);
InBlock.gif
InBlock.gif //添加 桌面 节点
InBlock.gif            TreeNode tnDesktop = new TreeNode("桌面");
InBlock.gif            tnDesktop.Tag = deskTop;
InBlock.gif            tnDesktop.Nodes.Add("dot.gif");
InBlock.gif
InBlock.gif //把节点添加到树中
InBlock.gif            Tree1.Nodes.Add(tnDesktop);
InBlock.gif            tnDesktop.Expand();
ExpandedSubBlockEnd.gif        }
InBlock.gif
InBlock.gif private void Tree1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
ContractedSubBlock.gifExpandedSubBlockStart.gif 判断节点是否已经展开#region 判断节点是否已经展开
InBlock.gif if (e.Node.Nodes.Count != 1)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif return;
ExpandedSubBlockEnd.gif            }
InBlock.gif else
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif if (e.Node.FirstNode.Text != "dot.gif")
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif return;
ExpandedSubBlockEnd.gif                }
ExpandedSubBlockEnd.gif            }
InBlock.gif
InBlock.gif            e.Node.Nodes.Clear(); 
ExpandedSubBlockEnd.gif #endregion
InBlock.gif
InBlock.gif            IShellFolder root = (IShellFolder)e.Node.Tag;
InBlock.gif
InBlock.gif //循环查找子项
InBlock.gif            IEnumIDList Enum = null;
InBlock.gif            IntPtr EnumPtr = IntPtr.Zero;
InBlock.gif            IntPtr pidlSub;
InBlock.gif int celtFetched;
InBlock.gif
InBlock.gif if (root.EnumObjects(this.Handle, SHCONTF.FOLDERS, out EnumPtr) == API.S_OK)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif                Enum = (IEnumIDList)Marshal.GetObjectForIUnknown(EnumPtr);
InBlock.gif while (Enum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif string name = API.GetNameByIShell(root, pidlSub);
InBlock.gif                    IShellFolder iSub;
InBlock.gif                    root.BindToObject(pidlSub, IntPtr.Zero, ref Guids.IID_IShellFolder, out iSub);
InBlock.gif
InBlock.gif                    TreeNode nodeSub = new TreeNode(name);
InBlock.gif                    nodeSub.Tag = iSub;
InBlock.gif                    nodeSub.Nodes.Add("dot.gif");
InBlock.gif                    e.Node.Nodes.Add(nodeSub);
ExpandedSubBlockEnd.gif                }
ExpandedSubBlockEnd.gif            }
ExpandedSubBlockEnd.gif        }
InBlock.gif
InBlock.gif private void Sample2_FormClosing(object sender, FormClosingEventArgs e)
ExpandedSubBlockStart.gifContractedSubBlock.gifdot.gif{
InBlock.gif //释放资源
InBlock.gif            Marshal.ReleaseComObject(deskTop);
ExpandedSubBlockEnd.gif        }
InBlock.gif
ExpandedBlockEnd.gif    }

照例,附图片和源代码:

shell2.jpg
源代码:

你可能感兴趣的文章
[转摘]使用异步方式调用同步方法
查看>>
四则运算的在线答题(判断对错,记录错题)
查看>>
Python基础教程(第2版 修订版) pdf
查看>>
python数据类型之二
查看>>
ubuntu下更改默认python版本的方法
查看>>
针对文件系统和网络性能的测试
查看>>
专题5.外汇市场分析与投资
查看>>
24、AES RSA加密处理记录
查看>>
处理结果集的相关属性和方法?
查看>>
javascript原型深入解析2--Object和Function,先有鸡先有蛋
查看>>
POJ 1001 高精度乘法
查看>>
json+underscore+Node 小例子
查看>>
Google Play市场考察报告
查看>>
***浅析JQuery中的html(),text(),val()区别
查看>>
数组与对象的转换
查看>>
Entity Framework Code-First(9.1):DataAnnotations - Key Attribute
查看>>
yii2异常
查看>>
未能加载文件或程序集“Oracle.DataAccess”或它的某一个依赖项.试图加载格式不正确的程序...
查看>>
Java基础之 反射是什么?
查看>>
pycharm 激活码激活
查看>>