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

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

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

Windows Shell 编程,即 Windows 外壳编程。我们所看到的资源管理器以及整个桌面,都是一个 Shell。

关于 Windows 外壳的基本概念,我这里不做详细介绍,不了解的朋友,可以看看 姜伟华 的 Windows外壳名字空间的浏览
好,现在让我们从基础学起,早日做出一个强大的资源管理器软件。(偶也是初学者,多多指教)

1 - 基础,浏览一个文件夹

我们知道,在win32中是以外壳名字空间的形式来组织文件系统的,在外壳名字空间里的每一个对象(注)都实现了一个IShellFolder的接口,通过这个接口我们可以直接查询或间接得到其他相关的接口。 (注:这里的对象指的是外壳名字空间中的一个节点,对象有可能是一个文件夹,有可能是一个文件,也有可能是一个虚拟文件夹,例如:我的电脑,网上邻居,控制面板等)
在C#中,我们这样定义 IShellFolder 接口:

 

当然,这个接口还没有列出细节函数。我们要做的仅仅是从最基础开始。
首先我们必须了解,在外壳编程中,要使用 PIDL 路径代替普通路径(如果对 PIDL 不熟悉,请看Windows外壳名字空间的浏览)。
“桌面”是最*的文件夹,外壳名字空间中其他各项都可以用从“桌面”开始的 PIDL 加以表示。
如何获取“桌面”的 PIDL 和其 IShellFolder 接口呢,可以通过 API SHGetDesktopFolder:

(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹[DllImport("shell32.dll")] (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        public static extern Int32 SHGetDesktopFolder(out IntPtr ppshf); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹/// <summary> (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        /// 获得桌面 Shell (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        /// </summary> (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        public static IShellFolder GetDesktopFolder(out IntPtr ppshf) (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        { (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            SHGetDesktopFolder(out ppshf); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            Object obj = Marshal.GetObjectForIUnknown(ppshf); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            return (IShellFolder)obj; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        }
(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹//获得桌面 PIDL (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr desktopPtr; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IShellFolder desktop = API.GetDesktopFolder(out desktopPtr);


好的,我们取得“桌面”的 IShellFolder 接口,就已经成功了一半。现在我需要通过“桌面”,来获取“C:/”这个路径的 PIDL 和 IShellFolder 接口,可以通过 IShellFolder 的 ParseDisplayName 和 BindToObject 函数来实现:

(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹void ParseDisplayName( (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr hwnd, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr pbc, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            [MarshalAs(UnmanagedType.LPWStr)] string pszDisplayName, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            out uint pchEaten, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            out IntPtr ppidl, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            ref uint pdwAttributes); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹void BindToObject( (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr pidl, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr pbc, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            [In()] ref Guid riid, (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            out IShellFolder ppv);
(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹//获取 C 盘的 PIDL (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            string FolderPath = @"C:/"; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr Pidl = IntPtr.Zero; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IShellFolder Root; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            uint i, j = 0; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            desktop.ParseDisplayName(Handle, IntPtr.Zero, FolderPath, out i, out Pidl, ref j); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            desktop.BindToObject(Pidl, IntPtr.Zero, ref Guids.IID_IShellFolder, out Root);


前提是你应该保证路径存在,因为我没有做任何出错控制。这样我们就获得了一个 Root,它表示C盘。通过这个Root,我们可以用 EnumObjects 来循环获取其子项(子文件和子文件夹):

(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹[PreserveSig] (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹        int EnumObjects(IntPtr hWnd, SHCONTF flags, out IntPtr enumIDList);

 

(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹//循环查找 C 盘下面的文件/文件夹的 PIDL (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IEnumIDList fileEnum = null; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IEnumIDList folderEnum = null; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr fileEnumPtr = IntPtr.Zero; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr folderEnumPtr = IntPtr.Zero; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            IntPtr pidlSub; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            int celtFetched; (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            //获取子文件夹 (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            if (Root.EnumObjects(this.Handle, SHCONTF.FOLDERS | SHCONTF.INCLUDEHIDDEN, out fileEnumPtr) == API.S_OK) (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            { (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                fileEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(fileEnumPtr); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                while (fileEnum.Next(1out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE) (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                { (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                    //获取显示名称 (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                    string name = API.GetNameByPIDL(pidlSub); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                    lvFile.Items.Add(name, 1); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                } (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            } (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹(C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            //获取子文件 (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            if (Root.EnumObjects(this.Handle, SHCONTF.NONFOLDERS | SHCONTF.INCLUDEHIDDEN, out folderEnumPtr) == API.S_OK) (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            { (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                folderEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(folderEnumPtr); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                while (folderEnum.Next(1out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE) (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                { (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                    string name = API.GetNameByPIDL(pidlSub); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                    lvFile.Items.Add(name, 0); (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹                } (C#)Windows Shell 编程系列1 - 基础,浏览一个文件夹            }


事实上,代码到此结束。然而我发现有太多的结构体和枚举没有介绍(以后会有更多),有兴趣的朋友可以自己查 MSDN ,否则就等待我下一节再介绍了。

最后把图和代码贴上,下一节再详细介绍。

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

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

上一篇:一文读懂SSL证书以及https对于网站安全的重要性


下一篇:C++进程注入