设为首页 收藏本站
查看: 2163|回复: 0

(C#)Windows Shell 外壳编程系列2

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-4-28 12:55:17 | 显示全部楼层 |阅读模式
(本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)

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

让我们详细解释一下 Shell 编程中最基本的一些函数、结构体和枚举。

SHGetDesktopFolder
获取桌面的 IShellFolder 接口


DSC0000.gif [DllImport("shell32.dll")]
        public static extern Int32 SHGetDesktopFolder(out IntPtr ppshf);

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


public static IShellFolder GetDesktopFolder(out IntPtr ppshf)
DSC0001.gif DSC0002.gif          DSC0003.gif {
DSC0004.gif             SHGetDesktopFolder(out ppshf);
            Object obj = Marshal.GetObjectForIUnknown(ppshf);
            return (IShellFolder)obj;
DSC0005.gif         }
ParseDisplayName
获得对象的PIDL,即便对象在目录树中处于当前目录下一层或更多层。例如,对于文件对象来说,它的解析名就是它的路径,我们用文件系统对象的完全路径名来调用桌面的IshellFolder接口的 ParseDisplayName 方法,它会返回这个对象的完全PIDL。定义:

void ParseDisplayName(
            IntPtr hwnd,
            IntPtr pbc,
            [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName,
            out uint pchEaten,
            out IntPtr ppidl,
            ref uint pdwAttributes);
里面最重要的参数就是 out IntPtr ppidl 了,它返回 pszDisplayName 指定路径对应的 PIDL。然而仅仅是 PIDL 并不能让你做更多的事情。这时候还需要调用 BindToObject 来返回 IShellFolder 接口。

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


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


public static Guid IID_IShellFolder = new Guid("{000214E6-0000-0000-C000-000000000046}");
另外介绍 IEnumIDList 接口。IEnumIDList 接口使资源管理器获得文件夹包含的全部对象的PIDL,PIDL然后可以用来获得这些对象的信息。

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


int EnumObjects(IntPtr hWnd, SHCONTF flags, out IntPtr enumIDList);
其中 flags 是 SHCONTF 枚举类型,它决定了枚举的内容:


SHCONTF
public enum SHCONTF
    {
        FOLDERS = 0x20,
        NONFOLDERS = 0x40,
        INCLUDEHIDDEN = 0x80,
        INIT_ON_FIRST_NEXT = 0x100,
        NETPRINTERSRCH = 0x200,
        SHAREABLE = 0x400,
        STORAGE = 0x800
    }
因此,我们可以通过 flags 的不同来分别列举子文件和子目录。这里会遇到一个问题,怎么获取 PIDL 对象的名称呢。这里编写了2个函数,可以通过 PIDL 或者 IShellFolder 返回对象的名称(详细解释留到下一节):


获取名称
/**////
        /// 获取显示名称
        ///
        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);
            return buf.ToString();
        }

        /**////
        /// 根据 PIDL 获取显示名称
        ///
        public static string GetNameByPIDL(IntPtr pidl)
        {
            SHFILEINFO info = new SHFILEINFO();
            API.SHGetFileInfo(pidl, 0, ref info, Marshal.SizeOf(typeof(SHFILEINFO)),
                SHGFI.PIDL | SHGFI.DISPLAYNAME | SHGFI.TYPENAME);
            return info.szDisplayName;
        }
例子二,从“桌面”开始展开

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


例2
public partial class Sample2 : Form
    {
        private IShellFolder deskTop;

        public Sample2()
DSC0006.gif DSC0007.gif         {
            InitializeComponent();
DSC0008.gif         }

        private void Form1_Load(object sender, EventArgs e)
        {
            //获得桌面 PIDL
            IntPtr deskTopPtr;
            deskTop = API.GetDesktopFolder(out deskTopPtr);

            //添加 桌面 节点
            TreeNode tnDesktop = new TreeNode("桌面");
            tnDesktop.Tag = deskTop;
            tnDesktop.Nodes.Add("");

            //把节点添加到树中
            Tree1.Nodes.Add(tnDesktop);
            tnDesktop.Expand();
        }

        private void Tree1_BeforeExpand(object sender, TreeViewCancelEventArgs e)
        {
            判断节点是否已经展开#region 判断节点是否已经展开
            if (e.Node.Nodes.Count != 1)
            {
                return;
            }
            else
            {
                if (e.Node.FirstNode.Text != "")
                {
                    return;
                }
            }

            e.Node.Nodes.Clear();
            #endregion

            IShellFolder root = (IShellFolder)e.Node.Tag;

            //循环查找子项
            IEnumIDList Enum = null;
            IntPtr EnumPtr = IntPtr.Zero;
            IntPtr pidlSub;
            int celtFetched;

            if (root.EnumObjects(this.Handle, SHCONTF.FOLDERS, out EnumPtr) == API.S_OK)
            {
                Enum = (IEnumIDList)Marshal.GetObjectForIUnknown(EnumPtr);
                while (Enum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
                {
                    string name = API.GetNameByIShell(root, pidlSub);
                    IShellFolder iSub;
                    root.BindToObject(pidlSub, IntPtr.Zero, ref Guids.IID_IShellFolder, out iSub);
                    
                    TreeNode nodeSub = new TreeNode(name);
                    nodeSub.Tag = iSub;
                    nodeSub.Nodes.Add("");
                    e.Node.Nodes.Add(nodeSub);
                }
            }
        }
        
        private void Sample2_FormClosing(object sender, FormClosingEventArgs e)
        {
            //释放资源
            Marshal.ReleaseComObject(deskTop);
        }

    }
照例,附图片和源代码:

DSC0009.jpg

源代码:/Files/lemony/WinShell2.rar

下一节将讲述 Shell 编程中的 IContextMenu ,也就是上下文菜单,将使你的应用程序列举 Shell 对象的同时,还能在右键操控它们的菜单。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-61580-1-1.html 上篇帖子: ||Shell脚本之sed&awk命令 下篇帖子: mac上的终端bash命令(一) Bourne-Again Shell简介
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表