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

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

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

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

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

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

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

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

ContractedBlock.gif ExpandedBlockStart.gif IShellFolder.cs
None.gifusing System;
None.gif
using System.Collections.Generic;
None.gif
using System.Text;
None.gif
using System.Runtime.InteropServices;
None.gif
None.gif
namespace WinShell
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    [ComImport]
InBlock.gif    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
InBlock.gif    [Guid(
"000214E6-0000-0000-C000-000000000046")]
InBlock.gif    
public interface IShellFolder
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

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

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

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

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

None.gif [DllImport( " shell32.dll " )]
None.gif        
public   static   extern  Int32 SHGetDesktopFolder( out  IntPtr ppshf);
None.gif
ExpandedBlockStart.gifContractedBlock.gif
/**/ /// <summary>
InBlock.gif        
/// 获得桌面 Shell
ExpandedBlockEnd.gif        
/// </summary>

None.gif          public   static  IShellFolder GetDesktopFolder( out  IntPtr ppshf)
ExpandedBlockStart.gifContractedBlock.gif        
dot.gif {
InBlock.gif            SHGetDesktopFolder(
out ppshf);
InBlock.gif            Object obj 
= Marshal.GetObjectForIUnknown(ppshf);
InBlock.gif            
return (IShellFolder)obj;
ExpandedBlockEnd.gif        }

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

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

None.gif void  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);
None.gif
None.gif
void  BindToObject(
None.gif            IntPtr pidl,
None.gif            IntPtr pbc,
None.gif            [In()] 
ref  Guid riid,
None.gif            
out  IShellFolder ppv);


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

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

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

None.gif // 循环查找 C 盘下面的文件/文件夹的 PIDL
None.gif
            IEnumIDList fileEnum  =   null ;
None.gif            IEnumIDList folderEnum 
=   null ;
None.gif            IntPtr fileEnumPtr 
=  IntPtr.Zero;
None.gif            IntPtr folderEnumPtr 
=  IntPtr.Zero;
None.gif            IntPtr pidlSub;
None.gif            
int  celtFetched;
None.gif
None.gif            
// 获取子文件夹
None.gif
             if  (Root.EnumObjects( this .Handle, SHCONTF.FOLDERS  |  SHCONTF.INCLUDEHIDDEN,  out  fileEnumPtr)  ==  API.S_OK)
ExpandedBlockStart.gifContractedBlock.gif            
dot.gif {
InBlock.gif                fileEnum 
= (IEnumIDList)Marshal.GetObjectForIUnknown(fileEnumPtr);
InBlock.gif                
while (fileEnum.Next(1out pidlSub, out celtFetched) == 0 && celtFetched == API.S_FALSE)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
//获取显示名称
InBlock.gif
                    string name = API.GetNameByPIDL(pidlSub);
InBlock.gif                    lvFile.Items.Add(name, 
1);
ExpandedSubBlockEnd.gif                }

ExpandedBlockEnd.gif            }

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

ExpandedBlockEnd.gif            }

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

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



源代码: /Files/lemony/WinShell.rar
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值