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

简介: (本系列文章由柠檬的(lc_mtt)原创,转载请注明出处,谢谢~)Windows Shell 编程,即 Windows 外壳编程。我们所看到的资源管理器以及整个桌面,都是一个 Shell。关于 Windows 外壳的基本概念,我这里不做详细介绍,不了解的朋友,可以看看 姜伟华 的 Windows外壳名字空间的浏览。

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

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

关于 Windows 外壳的基本概念,我这里不做详细介绍,不了解的朋友,可以看看 姜伟华 的 Windows外壳名字空间的浏览

好,现在让我们从基础学起,早日做出一个强大的资源管理器软件。(偶也是初学者,多多指教)

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

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

在C#中,我们这样定义 IShellFolder 接口:

IShellFolder.cs


当然,这个接口还没有列出细节函数。我们要做的仅仅是从最基础开始。

首先我们必须了解,在外壳编程中,要使用 PIDL 路径代替普通路径(如果对 PIDL 不熟悉,请看Windows外壳名字空间的浏览)。

“桌面”是最顶级的文件夹,外壳名字空间中其他各项都可以用从“桌面”开始的 PIDL 加以表示。

如何获取“桌面”的 PIDL 和其 IShellFolder 接口呢,可以通过 API SHGetDesktopFolder:

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

/// <summary>
        /// 获得桌面 Shell
        /// </summary>
        public static IShellFolder GetDesktopFolder(out IntPtr ppshf)
        {
            SHGetDesktopFolder(out ppshf);
            Object obj = Marshal.GetObjectForIUnknown(ppshf);
            return (IShellFolder)obj;
        }


//获得桌面 PIDL
            IntPtr desktopPtr;
            IShellFolder desktop = API.GetDesktopFolder(out desktopPtr);


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

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

void BindToObject(
            IntPtr pidl,
            IntPtr pbc,
            [In()] ref Guid riid,
            out IShellFolder ppv);



//获取 C 盘的 PIDL
            string FolderPath = @"C:\";
            IntPtr Pidl = IntPtr.Zero;
            IShellFolder Root;
            uint i, j = 0;
            desktop.ParseDisplayName(Handle, IntPtr.Zero, FolderPath, out i, out Pidl, ref j);
            desktop.BindToObject(Pidl, IntPtr.Zero, ref Guids.IID_IShellFolder, out Root);


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

[PreserveSig]
        int EnumObjects(IntPtr hWnd, SHCONTF flags, out IntPtr enumIDList);

 

//循环查找 C 盘下面的文件/文件夹的 PIDL
            IEnumIDList fileEnum = null;
            IEnumIDList folderEnum = null;
            IntPtr fileEnumPtr = IntPtr.Zero;
            IntPtr folderEnumPtr = IntPtr.Zero;
            IntPtr pidlSub;
            int celtFetched;

            //获取子文件夹
            if (Root.EnumObjects(this.Handle, SHCONTF.FOLDERS | SHCONTF.INCLUDEHIDDEN, out fileEnumPtr) == API.S_OK)
             {
                fileEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(fileEnumPtr);
                while (fileEnum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
                {
                    //获取显示名称
                    string name = API.GetNameByPIDL(pidlSub);
                    lvFile.Items.Add(name, 1);
                }
            }

            //获取子文件
            if (Root.EnumObjects(this.Handle, SHCONTF.NONFOLDERS | SHCONTF.INCLUDEHIDDEN, out folderEnumPtr) == API.S_OK)
            {
                folderEnum = (IEnumIDList)Marshal.GetObjectForIUnknown(folderEnumPtr);
                while (folderEnum.Next(1, out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
                {
                    string name = API.GetNameByPIDL(pidlSub);
                    lvFile.Items.Add(name, 0);
                }
            }


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

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



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

目录
相关文章
|
Go 分布式数据库 计算机视觉
.Net脱壳工具 de4dot参数说明/简易教程
原文:.Net脱壳工具 de4dot参数说明/简易教程 de4dot  /? 帮助原文  使用方法 de4dot "d:\xx.exe" -p xc -p xc 指定壳类型 , 这里是xc,表示Xenocode壳.
4553 0
|
JavaScript 关系型数据库 MySQL
盘点6个.NetCore+Vue前后端分离的开源项目
盘点6个.NetCore+Vue前后端分离的开源项目
2585 0
|
XML Java 数据格式
Java 实现汉字按照26个英文首字母分组排序(实际业务方法改造)
Java 实现汉字按照26个英文首字母分组排序(实际业务方法改造)
843 0
Java 实现汉字按照26个英文首字母分组排序(实际业务方法改造)
|
网络协议 API
让每个进程不同外网 IP,实现局部单窗口单 IP,驱动级网络加速器原理!
为解决游戏多开IP限制问题,可采用API HOOK、LSP劫持或驱动层拦截(WFP/TDI)结合SOCKS5转发数据,实现每个进程独立的外网IP。LSP方法需HOOK多个网络函数,如WSPCONNECT等。驱动层中,WFP适用于Win7以上系统,全面拦截,TDI兼容所有系统但拦截不全。提供了一个自编写的驱动层代理示例,支持全局和局部IP设置,适用于每个进程不同IP,长期维护,欢迎使用。下载链接:http://down.8u18.com/down/jsq.rar。附图片展示。
9644 0
|
4月前
|
XML 人工智能 安全
qq网警加入群聊恶搞代码, QQ卡片XML消息生成工具,装逼娱乐代码生成器
QQ装逼娱乐代码生成器是一款趣味性十足的Python工具,能生成黑客、AI、量子计算、区块链和元宇宙五种风格的专业级代码片段
|
安全 C++
C++: std::once_flag 和 std::call_once
`std::once_flag` 和 `std::call_once` 是 C++11 引入的同步原语,确保某个函数在多线程环境中仅执行一次。
|
开发框架 .NET API
在Windows Server 2008 R2上运行.Net 8应用
在Windows Server 2008 R2上成功运行.Net 8程序,需安装三个补丁:Windows Server 2008 R2 SP1 (KB976932)是基础更新;VC_redist.x64提供MSVC库支持;KB3063858解决.NET运行时加载`kernel.dll`的路径问题。KB3063858可能需要KB2533623。详细信息和下载链接在文中给出。
1147 4
|
Ubuntu Linux Shell
【OpenCV】如何在Linux操作系统下正确安装 OpenCV
【OpenCV】如何在Linux操作系统下正确安装 OpenCV
656 4
|
机器学习/深度学习 分布式计算 搜索推荐
推荐系统入门之使用协同过滤实现商品推荐
本场将介绍如何使用PAI基于协同过滤算法实现商品推荐。
|
安全 API 数据安全/隐私保护
[笔记]Windows安全之《二》Session0隔离及相关启动技术
[笔记]Windows安全之《二》Session0隔离及相关启动技术
492 0