![]() |
Desktop Development »
Files and Folders »
General
Beginner
License: The GNU Lesser General Public License
Rewrite DirectoryInfo using IShellFolderBy Leung Yat ChunThis article describes how to uses IShellFolder to list special / virtual directories using C# |
C#, Windows (Win2K, WinXP, Win2003, Vista, Win2008, Win 7, Win2008 R2), .NET (.NET 2.0, .NET 3.0, .NET 3.5, .NET 4.0), Win32, WPF, Dev
|
|
Advanced Search Add to IE Search |
|
|
|
||||||||||||||||
DirectoryInfo is a class to represent a folder in disk, it's suitable to list file system entries, but it cannot be used to represent a special folder (e.g. virtual folder that doesn't exist in the disk). You may have to use IShellFolder to enumerate these directories. DirectoryInfoEx is written to support these folders.
The project is a rewrite based on Steven Roebert's C# File Browser's code and article. His code does even more than my project, his article emphasizes on how to take advantage of the shell once you have a complete implementation (e.g. context menu, shell drag and drop, preview handler, etc), but lacks documentation about how to create one. I rewrite part of his code (the core part) to learn how it works, and this article explain how to do the basic file operations using IShellFolder interface, in C#.
Because of my lack of knowledge, and the nature of DirectoryInfo / FileInfo, the new class is a lot simpler than CShellItem.
IShellFolder interface IStorage interface IStream interface DirectoryInfoEx implementation Folders and Files in shell namespace can be located by PIDL (ITEMIDLIST), just like folder path (or DisplayName), there are Relative PIDL and Full PIDL, just like relative path (abc.txt) and full path (c:\abc.txt).
To obtain PIDL from a string path, you can use Desktop's IShellFolder."http://msdn.microsoft.com/en-us/library/bb775090%28VS.85%29.aspx" target="_blank" rel="nofollow">ParseDisplayName() (see below):
ShellAPI.SFGAO pdwAttributes = 0;
DesktopShellFolder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero,
path, ref pchEaten, out pidlPtr, ref pdwAttributes);
PIDL pidl = new PIDL(pidlPtr, false);
For special directories (e.g. virtual ones like DRIVES, or special file system directories like PROFILE), CSIDL enum has a list of them, you can use SHGetSpecialFolderLocation() to obtain it's PIDL.
int RetVal = ShellAPI.SHGetSpecialFolderLocation(IntPtr.Zero, csidl, out ptrAddr);
if (ptrAddr != IntPtr.Zero)
{
pidl = new PIDL(ptrAddr, false);
return pidl;
}
Desktop is the root of all shell namespace folder, you can use SHGetDesktopFolder() to obtain the IShellFolder interface for Desktop.
IntPtr ptrShellFolder = IntPtr.Zero;
if (ShellAPI.SHGetDesktopFolder(out ptrShellFolder) == ShellAPI.S_OK)
iShellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(ptrShellFolder,
typeof(IShellFolder));
As for other directories (including non-file directory, e.g. MyComputer), you can use BindToObject().
if (Parent.ShellFolder.BindToObject(pidl.Ptr, IntPtr.Zero,
ref ShellAPI.IID_IShellFolder, out ptrShellFolder) == ShellAPI.S_OK)
iShellFolder = (IShellFolder)Marshal.GetTypedObjectForIUnknown(ptrShellFolder,
typeof(IShellFolder));
This contains methods to manage the folder, e.g.:
static ShellAPI.SHCONTF folderflag = ShellAPI.SHCONTF.FOLDERS |
ShellAPI.SHCONTF.INCLUDEHIDDEN;
/* Specify to include folders only */
if (iShellFolder.EnumObjects(IntPtr.Zero, folderflag, out ptrEnum)
== ShellAPI.S_OK) //return the pointer of IEnumIDList
{
IEnumIDList IEnum = (IEnumIDList)Marshal.GetTypedObjectForIUnknown(ptrEnum,
typeof(IEnumIDList));
IntPtr pidlSubItem;
int celtFetched;
while (IEnum.Next(1, out pidlSubItem, out celtFetched) == ShellAPI.S_OK
&& celtFetched == 1)
dirList.Add(new PIDL(pidlSubItem, false));
/* Add PIDL of each subdirectory to dirList, noted that in normal case you
* should release pidlSubItem but Steven Roebert's PIDL class is a IDisposable
* class which will dispose it for you.
* Also noted that the pidl is relative instead of full.
* Use GetDisplayNameOf() (see below) to get it's name. */
if (IEnum != null) //Release resource
{
Marshal.ReleaseComObject(IEnum);
Marshal.Release(ptrEnum);
}
}
IntPtr ptrStr = Marshal.AllocCoTaskMem(ShellAPI.MAX_PATH * 2 + 4);
Marshal.WriteInt32(ptrStr, 0, 0);
StringBuilder buf = new StringBuilder(ShellAPI.MAX_PATH);
try
{
/* uflags is a SHGNO enum that allow you to get different folder names,
* e.g. "My Documents"(SHGNO.NORMAL) folder is named as
* "Documents"(SHGNO.FORPARSING) in file system.
* StrRetToBuf() convert STRRET structure to
* buffer usable by StringBuilder */
if (iShellFolder.GetDisplayNameOf(pidl, uFlags, ptrStr) == ShellAPI.S_OK)
ShellAPI.StrRetToBuf(ptrStr, pidl, buf, ShellAPI.MAX_PATH);
}
finally
{
if (ptrStr != IntPtr.Zero)
Marshal.FreeCoTaskMem(ptrStr);
ptrStr = IntPtr.Zero;
}
Console.WriteLine(buf.ToString());
IShellFolder / IStorage interface for a subfolder :
Using SHBindToParent() :
IntPtr pidlLast = IntPtr.Zero;
retVal = ShellAPI.SHBindToParent(dir.PIDLRel.Ptr, ShellAPI.IID_IStorage,
out storagePtr, ref pidlLast);
Or BindToStorage():
retVal = dir.Parent.ShellFolder.BindToStorage(
dir.PIDLRel.Ptr, IntPtr.Zero, ref ShellAPI.IID_IStorage,
out storagePtr);
/* Beside IID_IStorage interface, there is IID_IStream and
IID_IPropertySetStorage as well. */
if ((retVal == ShellAPI.S_OK))
{
IStorage storage = (IStorage)Marshal.GetTypedObjectForIUnknown(storagePtr,
typeof(IStorage));
/* Your work here, free the pointer and interface when done. */
}
This contains methods for creation or to manage items / subitems in the folder, e.g.
SrcStorage.MoveElementTo(SourceFileName, DestStorage,
DestFilename, ShellAPI.STGMOVE.MOVE) != ShellAPI.S_OK)
ParentStorage.DestroyElement(name);
/* FileStreamEx class is a customized IDisposable Stream class,
* which uses IStream interface of a file. */
FileStreamEx stream = new FileStreamEx(path, mode, access);
StreamReader sr = new StreamReader(stream);
Console.WriteLine(sr.ReadToEnd());
This allows you to read / write data to stream objects.
IStream interface, use OpenStream() (Read/Write) or CreateStream() (Create New):
if (parentStorage.OpenStream(filename, IntPtr.Zero, grfmode, 0,
out streamPtr) == ShellAPI.S_OK)
stream = (IStream)Marshal.GetTypedObjectForIUnknown(streamPtr,
typeof(IStream));
IStream contains methods like seek(), read(), write(). All interface objects should be released when done.
The implementation is simpler than CShellItem, however, as FileSystemInfoEx's PIDL is exposed (via PIDLRel and PIDL property), you can implement custom operation (e.g. Extract Icon, Context Menu) externally. IShellFolder and IStorage (generate on demand) are also exposed in DirectoryInfoEx, they are automatically destroyed when disposed, do not free them yourself.
Both DirectoryInfoEx and FileInfoEx are inherited from FileSystemInfoEx, they are used as enumeration (listing) subitems, DirectoryInfoEx contains GetFiles() and GetDirectories() method for this purpose. To modify a file, use FileEx class for managing files (DirectoryEx is not implemented yet, use System.IO.Directory at this time), and FileStreamEx class for read/write files.
DirectoryInfoEx (and FileInfoEx)'s constructor accept a Path or PIDL. A number of special directories are defined in DirectoryInfoEx, including DesktopDirectory, MyComputerDirectory, CurrentUserDirectory, SharedDirectory and NetworkDirectory. For other special directories, you can obtain it's PIDL by calling DirectoryInfoEx.CSIDLtoPIDL().
This demo is a simple WPF application that lists the subdirectories below Desktop. The Icons are obtained using SHGetFileInfo(), which takes a full PIDL parameter.
DirectoryEx static class is not implemented. IShellFolder directly, current implementation looks up from Desktop directory (e.g. C:\ = Desktop, Computer, C:\ = 3 times), which is slower. | You must Sign In to use this message board. | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 3 Nov 2009 Editor: Deeksha Shenoy |
Copyright 2009 by Leung Yat Chun Everything else Copyright © CodeProject, 1999-2009 Web21 | Advertise on the Code Project |