Использование Shell?! Прекрасный вопрос. Рекомендуется использовать системные функции. Это немного сложнее, потому что придется описывать необходимые системные интерфейсы, функции и структуры, которые в .NET не описаны. Но зато результат будет мощным и универсальным. В системе много функций работы с файловой системой, но какие из них нам понадобятся? Я помогу вам разобраться.
Если тебе нужно просто получить список файлов в директории, то лучше воспользоваться классом Directory. Совместно с File он предоставит тебе базовые возможности для работы с файловой системой. Но этого не всегда достаточно, и если нужно что-то большее - хорошие системные иконки, снимки файлов и т.д., то придётся опускаться на системный уровень, о чем мы и поговорим в этой статье.
Создайте новый проект и поместите на форму компонент ListView. В него мы будет загружать имена файлов и папок, которые найдем на рабочем столе. Теперь мы должны описать два интерфейса, которые нам понадобяться: IShellFolder и IEnumIDList. Создайте новый файл для интерфейса IShellFolder. Чтобы сделать это щелкните правой кнопкой по имени проекта в окне Solution Explorer и выберите меню New Item из меню Add. Перед вами откроется окно Add New:

Выберите иконку Interface в списке Templates и введите имя нового интерфейса IShellFolder в поле Name:

Сохраните новый файл под именем IShellFolder. Содержимое файла должно быть таким (можете скопировать его из браузера):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ShellExample
{
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("000214E6-0000-0000-C000-000000000046")]
public interface IShellFolder
{
[PreserveSig]
Int32 ParseDisplayName(IntPtr hwnd,
IntPtr pbc,
[MarshalAs(UnmanagedType.LPWStr)]
string pszDisplayName,
ref uint pchEaten,
out IntPtr ppidl,
ref ShellAPI.SFGAO pdwAttributes);
[PreserveSig]
Int32 EnumObjects(IntPtr hwnd,
ShellAPI.SHCONTF grfFlags,
out IntPtr enumIDList);
[PreserveSig]
Int32 BindToObject(IntPtr pidl,
IntPtr pbc,
ref Guid riid,
out IntPtr ppv);
[PreserveSig]
Int32 BindToStorage(IntPtr pidl,
IntPtr pbc,
ref Guid riid,
out IntPtr ppv);
[PreserveSig]
Int32 CompareIDs(IntPtr lParam,
IntPtr pidl1,
IntPtr pidl2);
[PreserveSig]
Int32 CreateViewObject(IntPtr hwndOwner,
Guid riid,
out IntPtr ppv);
[PreserveSig]
Int32 GetAttributesOf(uint cidl,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr[] apidl,
ref ShellAPI.SFGAO rgfInOut);
[PreserveSig]
Int32 GetUIObjectOf(IntPtr hwndOwner,
uint cidl,
[MarshalAs(UnmanagedType.LPArray)]
IntPtr[] apidl,
ref Guid riid,
IntPtr rgfReserved,
out IntPtr ppv);
[PreserveSig()]
Int32 GetDisplayNameOf(IntPtr pidl,
ShellAPI.SHGNO uFlags,
IntPtr lpName);
[PreserveSig]
Int32 SetNameOf(IntPtr hwnd,
IntPtr pidl,
[MarshalAs(UnmanagedType.LPWStr)]
string pszName,
ShellAPI.SHGNO uFlags,
out IntPtr ppidlOut);
}
}
Теперь создайте еще один файл интерфейса с именем IEnumIDList. Его код должен быть таким:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ShellExample
{
[ComImportAttribute()]
[InterfaceTypeAttribute(ComInterfaceType
.InterfaceIsIUnknown)]
[Guid("000214F2-0000-0000-C000-000000000046")]
public interface IEnumIDList
{
[PreserveSig()]
Int32 Next(int celt,
out IntPtr rgelt,
out int pceltFetched);
[PreserveSig()]
Int32 Skip(int celt);
[PreserveSig()]
Int32 Reset();
[PreserveSig()]
Int32 Clone(out IEnumIDList ppenum);
}
}
Теперь у нас есть необходимые интерфейсы, но нужны еще функции и структуры. Давайте создадим файл ShellAPI.cs в котором и опишем необходимые функции. Чтобы сделать это, щелкните правой кнопкой мыши по имени проекта в окне Solution Explorer, как мы это делали для создания интерфейсов. Выберите New Item из меню Add. Снова появиться окно Add New Item. Теперь выберите иконку Class в списке Templates и напишите имя класса ShellAPI в поле Name. Код нового файла должен быть таким:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ShellExample
{
public class ShellAPI
{
public const int S_OK = 0;
public const int S_FALSE = 1;
public const int MAX_PATH = 260;
public static int cbFileInfo =
Marshal.SizeOf(typeof(SHFILEINFO));
[Flags]
public enum SHCONTF
{
FOLDERS = 0x0020,
NONFOLDERS = 0x0040,
INCLUDEHIDDEN = 0x0080,
INIT_ON_FIRST_NEXT = 0x0100,
NETPRINTERSRCH = 0x0200,
SHAREABLE = 0x0400,
STORAGE = 0x0800,
}
[Flags]
public enum SHGNO
{
NORMAL = 0x0000,
INFOLDER = 0x0001,
FOREDITING = 0x1000,
FORADDRESSBAR = 0x4000,
FORPARSING = 0x8000
}
[Flags]
public enum SHGFI : uint
{
ADDOVERLAYS = 0x20,
ATTR_SPECIFIED = 0x20000,
ATTRIBUTES = 0x800,
DISPLAYNAME = 0x200,
EXETYPE = 0x2000,
ICON = 0x100,
ICONLOCATION = 0x1000,
LARGEICON = 0,
LINKOVERLAY = 0x8000,
OPENICON = 2,
OVERLAYINDEX = 0x40,
PIDL = 8,
SELECTED = 0x10000,
SHELLICONSIZE = 4,
SMALLICON = 1,
SYSICONINDEX = 0x4000,
TYPENAME = 0x400,
USEFILEATTRIBUTES = 0x10
}
[Flags]
public enum SFGAO : uint
{
BROWSABLE = 0x8000000,
CANCOPY = 1,
CANDELETE = 0x20,
CANLINK = 4,
CANMONIKER = 0x400000,
CANMOVE = 2,
CANRENAME = 0x10,
CAPABILITYMASK = 0x177,
COMPRESSED = 0x4000000,
CONTENTSMASK = 0x80000000,
DISPLAYATTRMASK = 0xfc000,
DROPTARGET = 0x100,
ENCRYPTED = 0x2000,
FILESYSANCESTOR = 0x10000000,
FILESYSTEM = 0x40000000,
FOLDER = 0x20000000,
GHOSTED = 0x8000,
HASPROPSHEET = 0x40,
HASSTORAGE = 0x400000,
HASSUBFOLDER = 0x80000000,
HIDDEN = 0x80000,
ISSLOW = 0x4000,
LINK = 0x10000,
NEWCONTENT = 0x200000,
NONENUMERATED = 0x100000,
READONLY = 0x40000,
REMOVABLE = 0x2000000,
SHARE = 0x20000,
STORAGE = 8,
STORAGEANCESTOR = 0x800000,
STORAGECAPMASK = 0x70c50008,
STREAM = 0x400000,
VALIDATE = 0x1000000
}
[Flags]
public enum FILE_ATTRIBUTE
{
READONLY = 0x00000001,
HIDDEN = 0x00000002,
SYSTEM = 0x00000004,
DIRECTORY = 0x00000010,
ARCHIVE = 0x00000020,
DEVICE = 0x00000040,
NORMAL = 0x00000080,
TEMPORARY = 0x00000100,
SPARSE_FILE = 0x00000200,
REPARSE_POINT = 0x00000400,
COMPRESSED = 0x00000800,
OFFLINE = 0x00001000,
NOT_CONTENT_INDEXED = 0x00002000,
ENCRYPTED = 0x00004000
}
[StructLayout(LayoutKind.Sequential,
CharSet = CharSet.Auto)]
public struct SHFILEINFO
{
public IntPtr hIcon;
public int iIcon;
public SFGAO dwAttributes;
[MarshalAs(UnmanagedType.ByValTStr,
SizeConst = MAX_PATH)]
public string szDisplayName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
public string szTypeName;
}
[DllImport("shell32.dll")]
public static extern Int32 SHGetDesktopFolder(
out IntPtr ppshf);
[DllImport("shell32", EntryPoint = "SHGetFileInfo",
ExactSpelling = false, CharSet = CharSet.Auto,
SetLastError = true)]
public static extern IntPtr SHGetFileInfo(
string pszPath,
FILE_ATTRIBUTE dwFileAttributes,
ref SHFILEINFO sfi,
int cbFileInfo,
SHGFI uFlags);
[DllImport("shell32", EntryPoint = "SHGetFileInfo",
ExactSpelling = false, CharSet = CharSet.Auto,
SetLastError = true)]
public static extern IntPtr SHGetFileInfo(
IntPtr ppidl,
FILE_ATTRIBUTE dwFileAttributes,
ref SHFILEINFO sfi,
int cbFileInfo,
SHGFI uFlags);
}
}
Теперь у нас есть все необходимое и можно переходить к рассмотрению функций получения списка файлов. Комментарии помогут вам разобраться с кодом:
// получить папку рабочего стола
IntPtr shellFolderPtr;
ShellAPI.SHGetDesktopFolder(out shellFolderPtr);
IShellFolder shellFolder = (IShellFolder)Marshal
.GetTypedObjectForIUnknown(shellFolderPtr,
typeof(IShellFolder));
// очистить содержимое listView1
listView1.Items.Clear();
// мы будем перечислять папки и файлы
ShellAPI.SHCONTF fileFlag =
ShellAPI.SHCONTF.NONFOLDERS | ShellAPI.SHCONTF.FOLDERS;
// если результат равен S_OK, то все прекрасно
IntPtr fileEnumPtr = IntPtr.Zero;
if(shellFolder.EnumObjects(IntPtr.Zero,
fileFlag, out fileEnumPtr)
== ShellAPI.S_OK)
{
IEnumIDList fileEnum =
(IEnumIDList)Marshal.GetTypedObjectForIUnknown(
fileEnumPtr, typeof(IEnumIDList));
IntPtr gelt;
int celtFetched;
//перечисляем найденные объекты
while(fileEnum.Next(1, out gelt, out celtFetched)
== ShellAPI.S_OK && celtFetched == 1)
{
//получить имя текущего объекта
ShellAPI.SHFILEINFO info =
new ShellAPI.SHFILEINFO();
ShellAPI.SHGetFileInfo(gelt, 0, ref info,
ShellAPI.cbFileInfo,
ShellAPI.SHGFI.DISPLAYNAME | ShellAPI.SHGFI.TYPENAME);
//добавить его в список
listView1.Items.Add(info.szDisplayName);
}
}
Вот результат:
