/animeplugin3/MyAnimePlugin3/ShellItem.cs
C# | 2334 lines | 2317 code | 7 blank | 10 comment | 90 complexity | 9b807895a5af4736537348782ed636b8 MD5 | raw file
- /*
- * This class is converted VB.Net from the CScItem
- * from http://www.codeproject.com/KB/cpp/VbNetExpTree.aspx
- */
-
- using System.IO;
- using System.Runtime.InteropServices;
- using System.Text;
- using System;
- using System.Collections;
- using System.Windows.Forms;
- using System.Diagnostics;
- using System.Management;
-
- public class ShellItem : IDisposable, IComparable
- {
-
- #region " Shared Private Fields"
- //This class has occasion to refer to the TypeName as reported by
- // ShellDll.SHGetFileInfo. It needs to compare this to the string
- // (in English) "System Folder"
- //on non-English systems, we do not know, in the general case,
- // what the equivalent string is to compare against
- //The following variable is set by Sub New() to the string that
- // corresponds to "System Folder" on the current machine
- // Sub New() depends on the existance of My Computer(ShellDll.CSIDL.DRIVES),
- // to determine what the equivalent string is
- private static string m_strSystemFolder;
-
- //My Computer is also commonly used (though not internally),
- // so save & expose its name on the current machine
- private static string m_strMyComputer;
-
- //To get My Documents sorted first, we need to know the Locale
- //specific name of that folder.
- private static string m_strMyDocuments;
-
- // The DesktopBase is set up via Sub New() (one time only) and
- // disposed of only when DesktopBase is finally disposed of
- private static ShellItem DesktopBase;
-
- //We can avoid an extra ShellDll.SHGetFileInfo call once this is set up
- private static int OpenFolderIconIndex = -1;
-
- // It is also useful to know if the OS is XP or above.
- // Set up in Sub New() to avoid multiple calls to find this info
- private static bool XPorAbove;
- // Likewise if OS is Win2K or Above
- private static bool Win2KOrAbove;
-
- // DragDrop, possibly among others, needs to know the Path of
- // the DeskTopDirectory in addition to the Desktop itself
- // Also need the actual ShellItem for the DeskTopDirectory, so get it
- private static ShellItem m_DeskTopDirectory;
-
-
- #endregion
-
- #region " Instance Private Fields"
- //m_Folder and m_Pidl must be released/freed at Dispose time
- private ShellDll.IShellFolder m_Folder;
- //if item is a folder, contains the Folder interface for this instance
- private IntPtr m_Pidl;
- //The Absolute PIDL for this item (not retained for files)
- private string m_DisplayName = "";
- private string m_Path;
- private string m_TypeName;
- private ShellItem m_Parent;
- //= Nothing
- private int m_IconIndexNormal;
- //index into the System Image list for Normal icon
- private int m_IconIndexOpen;
- //index into the SystemImage list for Open icon
- private bool m_IsBrowsable;
- private bool m_IsFileSystem;
- private bool m_IsFolder;
- private bool m_HasSubFolders;
- private bool m_IsLink;
- private bool m_IsDisk;
- private bool m_IsShared;
- private bool m_IsHidden;
- private bool m_IsNetWorkDrive;
- //= False
- private bool m_IsRemovable;
- //= False
- private bool m_IsReadOnly;
- //= False
- //Properties of interest to Drag Operations
- private bool m_CanMove;
- //= False
- private bool m_CanCopy;
- //= False
- private bool m_CanDelete;
- //= False
- private bool m_CanLink;
- //= False
- private bool m_IsDropTarget;
- //= False
- private ShellDll.SFGAO m_Attributes;
- //the original, returned from GetAttributesOf
-
- private int m_SortFlag;
- //= 0 'Used in comparisons
-
- private ArrayList m_Directories;
-
- //The following elements are only filled in on demand
- private bool m_XtrInfo;
- //= False
- private DateTime m_LastWriteTime;
- private DateTime m_CreationTime;
- private DateTime m_LastAccessTime;
- private long m_Length;
-
- //Indicates whether DisplayName, TypeName, SortFlag have been set up
- private bool m_HasDispType;
- //= False
-
- //Indicates whether IsReadOnly has been set up
- private bool m_IsReadOnlySetup;
- //= False
-
- //Holds a byte() representation of m_PIDL -- filled when needed
- private cPidl m_cPidl;
-
- //Flags for Dispose state
- private bool m_Disposed;
-
- #endregion
-
- #region " Destructor"
- /// <summary>
- /// Summary of Dispose.
- /// </summary>
- ///
- public void Dispose()
- {
- Dispose(true);
- // Take yourself off of the finalization queue
- // to prevent finalization code for this object
- // from executing a second time.
- GC.SuppressFinalize(this);
- }
- /// <summary>
- /// Deallocates CoTaskMem contianing m_Pidl and removes reference to m_Folder
- /// </summary>
- /// <param name="disposing"></param>
- ///
- protected virtual void Dispose(bool disposing)
- {
- // Allow your Dispose method to be called multiple times,
- // but throw an exception if the object has been disposed.
- // Whenever you do something with this class,
- // check to see if it has been disposed.
- if (!(m_Disposed)) {
- // If disposing equals true, dispose all managed
- // and unmanaged resources.
- m_Disposed = true;
- if ((disposing)) {
- }
- // Release unmanaged resources. If disposing is false,
- // only the following code is executed.
- if ((m_Folder != null)) {
- Marshal.ReleaseComObject(m_Folder);
- }
- if (!m_Pidl.Equals(IntPtr.Zero)) {
- Marshal.FreeCoTaskMem(m_Pidl);
- }
- }
- else {
- throw new Exception("ShellItem Disposed more than once");
- }
- }
-
- // This Finalize method will run only if the
- // Dispose method does not get called.
- // By default, methods are NotOverridable.
- // This prevents a derived class from overriding this method.
- /// <summary>
- /// Summary of Finalize.
- /// </summary>
- ///
- ~ShellItem()
- {
- // Do not re-create Dispose clean-up code here.
- // Calling Dispose(false) is optimal in terms of
- // readability and maintainability.
- Dispose(false);
- }
-
- #endregion
-
- #region " Constructors"
-
- #region " Private Sub New(ByVal folder As ShellDll.IShellFolder, ByVal pidl As IntPtr, ByVal parent As ShellItem)"
- /// <summary>
- /// Private Constructor, creates new ShellItem from the item's parent folder and
- /// the item's PIDL relative to that folder.</summary>
- /// <param name="folder">the folder interface of the parent</param>
- /// <param name="pidl">the Relative PIDL of this item</param>
- /// <param name="parent">the CShitem of the parent</param>
- ///
- private ShellItem(ShellDll.IShellFolder folder, IntPtr pidl, ShellItem parent)
- {
- if ((DesktopBase == null)) {
- //This initializes the Desktop folder
- DesktopBase = new ShellItem();
- }
- m_Parent = parent;
- m_Pidl = concatPidls(parent.PIDL, pidl);
-
- //Get some attributes
- SetUpAttributes(folder, pidl);
-
- //Set unfetched value for IconIndex....
- m_IconIndexNormal = -1;
- m_IconIndexOpen = -1;
- //finally, set up my Folder
- if (m_IsFolder) {
- int HR = 0;
- HR = folder.BindToObject(pidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
- if (HR != ShellDll.NOERROR) {
- Marshal.ThrowExceptionForHR(HR);
- }
- }
- }
- #endregion
-
- #region " Sub New()"
- /// <summary>
- /// Private Constructor. Creates ShellItem of the Desktop
- /// </summary>
- ///
- private ShellItem()
- {
- //only used when desktopfolder has not been intialized
- if ((DesktopBase != null)) {
- throw new Exception("Attempt to initialize ShellItem for second time");
- }
-
- int HR = 0;
- //firstly determine what the local machine calls a "System Folder" and "My Computer"
- IntPtr tmpPidl = default(IntPtr);
- HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ShellDll.CSIDL.DRIVES, ref tmpPidl);
- ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
- int dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
- int dwAttr = 0;
- ShellDll.SHGetFileInfo(tmpPidl, dwAttr, ref shfi, (int)ShellDll.cbFileInfo, dwflag);
- m_strSystemFolder = shfi.szTypeName;
- m_strMyComputer = shfi.szDisplayName;
- Marshal.FreeCoTaskMem(tmpPidl);
- //set OS version info
- XPorAbove = ShellDll.IsXpOrAbove();
- Win2KOrAbove = ShellDll.Is2KOrAbove();
-
- //With That done, now set up Desktop ShellItem
- m_Path = "::{" + ShellDll.DesktopGUID.ToString() + "}";
- m_IsFolder = true;
- m_HasSubFolders = true;
- m_IsBrowsable = false;
- HR = ShellDll.SHGetDesktopFolder(ref m_Folder);
- m_Pidl = ShellDll.GetSpecialFolderLocation(IntPtr.Zero, (int)ShellDll.CSIDL.DESKTOP);
- dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.SYSICONINDEX | ShellDll.SHGFI.PIDL);
- dwAttr = 0;
- IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, (int)ShellDll.cbFileInfo, dwflag);
- m_DisplayName = shfi.szDisplayName;
- m_TypeName = strSystemFolder;
- //not returned correctly by ShellDll.SHGetFileInfo
- m_IconIndexNormal = shfi.iIcon;
- m_IconIndexOpen = shfi.iIcon;
- m_HasDispType = true;
- m_IsDropTarget = true;
- m_IsReadOnly = false;
- m_IsReadOnlySetup = true;
-
- //also get local name for "My Documents"
- int pchEaten = 0;
- tmpPidl = IntPtr.Zero;
- int tmp = 0;
- HR = m_Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref tmpPidl, ref tmp);
- shfi = new ShellDll.SHFILEINFO();
- dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
- dwAttr = 0;
- ShellDll.SHGetFileInfo(tmpPidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
- m_strMyDocuments = shfi.szDisplayName;
- Marshal.FreeCoTaskMem(tmpPidl);
- //this must be done after getting "My Documents" string
- m_SortFlag = ComputeSortFlag();
- //Set DesktopBase
- DesktopBase = this;
- // Lastly, get the Path and ShellItem of the DesktopDirectory -- useful for DragDrop
- m_DeskTopDirectory = new ShellItem(ShellDll.CSIDL.DESKTOPDIRECTORY);
- }
- #endregion
-
- #region " New(ByVal ID As ShellDll.CSIDL)"
- /// <summary>Create instance based on a non-desktop ShellDll.CSIDL.
- /// Will create based on any ShellDll.CSIDL Except the DeskTop ShellDll.CSIDL</summary>
- /// <param name="ID">Value from ShellDll.CSIDL enumeration denoting the folder to create this ShellItem of.</param>
- ///
- public ShellItem(ShellDll.CSIDL ID)
- {
- if ((DesktopBase == null)) {
- //This initializes the Desktop folder
- DesktopBase = new ShellItem();
- }
- int HR = 0;
- if (ID == ShellDll.CSIDL.MYDOCUMENTS) {
- int pchEaten = 0;
- int tmp = 0;
- HR = DesktopBase.m_Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref m_Pidl, ref tmp);
- }
- else {
- HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ID, ref m_Pidl);
- }
- if (HR == ShellDll.NOERROR) {
- ShellDll.IShellFolder pParent = default(ShellDll.IShellFolder);
- IntPtr relPidl = IntPtr.Zero;
-
- pParent = GetParentOf(m_Pidl, ref relPidl);
- //Get the Attributes
- SetUpAttributes(pParent, relPidl);
- //Set unfetched value for IconIndex....
- m_IconIndexNormal = -1;
- m_IconIndexOpen = -1;
- //finally, set up my Folder
- if (m_IsFolder) {
- HR = pParent.BindToObject(relPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
- if (HR != ShellDll.NOERROR) {
- Marshal.ThrowExceptionForHR(HR);
- }
- }
- Marshal.ReleaseComObject(pParent);
- //if PidlCount(m_Pidl) = 1 then relPidl is same as m_Pidl, don't release
- if (PidlCount(m_Pidl) > 1) Marshal.FreeCoTaskMem(relPidl);
- }
- else {
- Marshal.ThrowExceptionForHR(HR);
- }
- }
- #endregion
-
- #region " New(ByVal path As String)"
- /// <summary>Create a new ShellItem based on a Path Must be a valid FileSystem Path</summary>
- /// <param name="path"></param>
- ///
- public ShellItem(string path)
- {
- if ((DesktopBase == null)) {
- //This initializes the Desktop folder
- DesktopBase = new ShellItem();
- }
- //Removal of following code allows Path(GUID) of Special FOlders to serve
- // as a valid Path for ShellItem creation (part of Calum's refresh code needs this
- //If Not Directory.Exists(path) AndAlso Not File.Exists(path) Then
- // Throw New Exception("ShellItem -- Invalid Path specified")
- //End If
- int HR = 0;
- int tmp1 = 0, tmp2 = 0;
- HR = DesktopBase.m_Folder.ParseDisplayName(0, IntPtr.Zero, path, ref tmp1, ref m_Pidl, ref tmp2);
- if (!(HR == ShellDll.NOERROR)) Marshal.ThrowExceptionForHR(HR);
- ShellDll.IShellFolder pParent = default(ShellDll.IShellFolder);
- IntPtr relPidl = IntPtr.Zero;
-
- pParent = GetParentOf(m_Pidl, ref relPidl);
-
- //Get the Attributes
- SetUpAttributes(pParent, relPidl);
- //Set unfetched value for IconIndex....
- m_IconIndexNormal = -1;
- m_IconIndexOpen = -1;
- //finally, set up my Folder
- if (m_IsFolder) {
- HR = pParent.BindToObject(relPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
- if (HR != ShellDll.NOERROR) {
- Marshal.ThrowExceptionForHR(HR);
- }
- }
- Marshal.ReleaseComObject(pParent);
- //if PidlCount(m_Pidl) = 1 then relPidl is same as m_Pidl, don't release
- if (PidlCount(m_Pidl) > 1) {
- Marshal.FreeCoTaskMem(relPidl);
- }
- }
- #endregion
-
- #region " ShellItem(byte[] FoldBytes, byte[] ItemBytes)"
- ///<Summary>Given a Byte() containing the Pidl of the parent
- /// folder and another Byte() containing the Pidl of the Item,
- /// relative to the Folder, Create a ShellItem for the Item.
- /// This is of primary use in dealing with "Shell IDList Array"
- /// formatted info passed in a Drag Operation
- /// </Summary>
- public ShellItem(byte[] FoldBytes, byte[] ItemBytes)
- {
- if ((DesktopBase == null)) {
- //This initializes the Desktop folder
- DesktopBase = new ShellItem();
- }
- ShellDll.IShellFolder pParent = MakeFolderFromBytes(FoldBytes);
- IntPtr ipParent = cPidl.BytesToPidl(FoldBytes);
- IntPtr ipItem = cPidl.BytesToPidl(ItemBytes);
- if ((pParent == null)) {
- //m_PIDL will = IntPtr.Zero for really bad CShitem
- goto XIT;
- }
- if (ipParent.Equals(IntPtr.Zero) | ipItem.Equals(IntPtr.Zero)) {
- goto XIT;
- }
- // Now process just like sub new(folder,pidl,parent) version
- m_Pidl = concatPidls(ipParent, ipItem);
-
- //Get some attributes
- SetUpAttributes(pParent, ipItem);
-
- //Set unfetched value for IconIndex....
- m_IconIndexNormal = -1;
- m_IconIndexOpen = -1;
- //finally, set up my Folder
- if (m_IsFolder) {
- int HR = 0;
- HR = pParent.BindToObject(ipItem, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
- #if Debug
- if (HR != NOERROR) {
- Marshal.ThrowExceptionForHR(HR);
- }
- #endif
- }
- XIT:
- //On any kind of exit, free the allocated memory
- #if Debug
- if (m_Pidl.Equals(IntPtr.Zero)) {
- Debug.WriteLine("CShItem.New(FoldBytes,ItemBytes) Failed");
- }
- else {
- Debug.WriteLine("CShItem.New(FoldBytes,ItemBytes) Created " + this.Path);
- }
- #endif
- if (!ipParent.Equals(IntPtr.Zero)) {
- Marshal.FreeCoTaskMem(ipParent);
- }
- if (!ipItem.Equals(IntPtr.Zero)) {
- Marshal.FreeCoTaskMem(ipItem);
- }
- }
-
- #endregion
-
- #region " Utility functions used in Constructors"
-
- #region " IsValidPidl"
- ///<Summary>It is impossible to validate a PIDL completely since its contents
- /// are arbitrarily defined by the creating Shell Namespace. However, it
- /// is possible to validate the structure of a PIDL.</Summary>
- public static bool IsValidPidl(byte[] b)
- {
- bool IsValidPidl = false;
- //assume failure
- int bMax = b.Length - 1;
- //max value that index can have
- if (bMax < 1) return IsValidPidl;
-
- //min Size is 2 bytes
- int cb = b[0] + (b[1] * 256);
- int indx = 0;
- while (cb > 0) {
- if ((indx + cb + 1) > bMax) return IsValidPidl;
-
- //an error
- indx += cb;
- cb = b[indx] + (b[indx + 1] * 256);
- }
- // on fall thru, it is ok as far as we can check
- return true;
- }
- #endregion
-
- #region " MakeFolderFromBytes"
- public static ShellDll.IShellFolder MakeFolderFromBytes(byte[] b)
- {
- ShellDll.IShellFolder functionReturnValue = default(ShellDll.IShellFolder);
- functionReturnValue = null;
- //get rid of VS2005 warning
- if (!IsValidPidl(b)) return null;
- if (b.Length == 2 && ((b[0] == 0) & (b[1] == 0))) {
- //this is the desktop
- return DesktopBase.Folder;
- }
- else if (b.Length == 0) {
- //Also indicates the desktop
- return DesktopBase.Folder;
- }
- else {
- IntPtr ptr = Marshal.AllocCoTaskMem(b.Length);
- if (ptr.Equals(IntPtr.Zero)) return null;
- Marshal.Copy(b, 0, ptr, b.Length);
- //the next statement assigns a IshellFolder object to the function return, or has an error
- int hr = DesktopBase.Folder.BindToObject(ptr, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref functionReturnValue);
- if (hr != 0) functionReturnValue = null;
- Marshal.FreeCoTaskMem(ptr);
- }
- return functionReturnValue;
- }
- #endregion
-
- #region " GetParentOf"
-
- ///<Summary>Returns both the ShellDll.IShellFolder interface of the parent folder
- /// and the relative pidl of the input PIDL</Summary>
- ///<remarks>Several internal functions need this information and do not have
- /// it readily available. GetParentOf serves those functions</remarks>
- private static ShellDll.IShellFolder GetParentOf(IntPtr pidl, ref IntPtr relPidl)
- {
- ShellDll.IShellFolder functionReturnValue = default(ShellDll.IShellFolder);
- functionReturnValue = null;
- //avoid VB2005 warning
- int HR = 0;
- int itemCnt = PidlCount(pidl);
- if (itemCnt == 1) {
- //parent is desktop
- HR = ShellDll.SHGetDesktopFolder(ref functionReturnValue);
- relPidl = pidl;
- }
- else {
- IntPtr tmpPidl = default(IntPtr);
- tmpPidl = TrimPidl(pidl, ref relPidl);
- HR = DesktopBase.m_Folder.BindToObject(tmpPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref functionReturnValue);
- Marshal.FreeCoTaskMem(tmpPidl);
- }
- if (!(HR == ShellDll.NOERROR)) Marshal.ThrowExceptionForHR(HR);
- return functionReturnValue;
- }
- #endregion
-
- #region " SetUpAttributes"
- /// <summary>Get the base attributes of the folder/file that this ShellItem represents</summary>
- /// <param name="folder">Parent Folder of this Item</param>
- /// <param name="pidl">Relative Pidl of this Item.</param>
- ///
- private void SetUpAttributes(ShellDll.IShellFolder folder, IntPtr pidl)
- {
- ShellDll.SFGAO attrFlag = default(ShellDll.SFGAO);
- attrFlag = ShellDll.SFGAO.BROWSABLE;
- attrFlag = attrFlag | ShellDll.SFGAO.FILESYSTEM;
- attrFlag = attrFlag | ShellDll.SFGAO.HASSUBFOLDER;
- attrFlag = attrFlag | ShellDll.SFGAO.FOLDER;
- attrFlag = attrFlag | ShellDll.SFGAO.LINK;
- attrFlag = attrFlag | ShellDll.SFGAO.SHARE;
- attrFlag = attrFlag | ShellDll.SFGAO.HIDDEN;
- attrFlag = attrFlag | ShellDll.SFGAO.REMOVABLE;
- //attrFlag = attrFlag Or ShellDll.SFGAO.RDONLY 'made into an on-demand attribute
- attrFlag = attrFlag | ShellDll.SFGAO.CANCOPY;
- attrFlag = attrFlag | ShellDll.SFGAO.CANDELETE;
- attrFlag = attrFlag | ShellDll.SFGAO.CANLINK;
- attrFlag = attrFlag | ShellDll.SFGAO.CANMOVE;
- attrFlag = attrFlag | ShellDll.SFGAO.DROPTARGET;
- //Note: for GetAttributesOf, we must provide an array, in all cases with 1 element
- IntPtr[] aPidl = new IntPtr[1];
- aPidl[0] = pidl;
- folder.GetAttributesOf(1, aPidl, ref attrFlag);
- m_Attributes = attrFlag;
- m_IsBrowsable = ((attrFlag & ShellDll.SFGAO.BROWSABLE) != 0);
- m_IsFileSystem = ((attrFlag & ShellDll.SFGAO.FILESYSTEM) != 0);
- m_HasSubFolders = ((attrFlag & ShellDll.SFGAO.HASSUBFOLDER) != 0);
- m_IsFolder = ((attrFlag & ShellDll.SFGAO.FOLDER) != 0);
- m_IsLink = ((attrFlag & ShellDll.SFGAO.LINK) != 0);
- m_IsShared = ((attrFlag & ShellDll.SFGAO.SHARE) != 0);
- m_IsHidden = ((attrFlag & ShellDll.SFGAO.HIDDEN) != 0);
- m_IsRemovable = ((attrFlag & ShellDll.SFGAO.REMOVABLE) != 0);
- //m_IsReadOnly = CBool(attrFlag And ShellDll.SFGAO.RDONLY) 'made into an on-demand attribute
- m_CanCopy = ((attrFlag & ShellDll.SFGAO.CANCOPY) != 0);
- m_CanDelete = ((attrFlag & ShellDll.SFGAO.CANDELETE) != 0);
- m_CanLink = ((attrFlag & ShellDll.SFGAO.CANLINK) != 0);
- m_CanMove = ((attrFlag & ShellDll.SFGAO.CANMOVE) != 0);
- m_IsDropTarget = ((attrFlag & ShellDll.SFGAO.DROPTARGET) != 0);
-
- //Get the Path
- IntPtr strr = Marshal.AllocCoTaskMem(ShellDll.MAX_PATH * 2 + 4);
- Marshal.WriteInt32(strr, 0, 0);
- StringBuilder buf = new StringBuilder(ShellDll.MAX_PATH);
- ShellDll.SHGDN itemflags = ShellDll.SHGDN.FORPARSING;
- folder.GetDisplayNameOf(pidl, itemflags, strr);
- int HR = ShellDll.StrRetToBuf(strr, pidl, buf, ShellDll.MAX_PATH);
- Marshal.FreeCoTaskMem(strr);
- //now done with it
- if (HR == ShellDll.NOERROR) {
- m_Path = buf.ToString();
- //check for zip file = folder on xp, leave it a file
- if (m_IsFolder && m_IsFileSystem && XPorAbove) {
- //Note:meaning of ShellDll.SFGAO.STREAM changed between win2k and winXP
- //Version 20 code
- //If File.Exists(m_Path) Then
- // m_IsFolder = False
- //End If
- //Version 21 code
- aPidl[0] = pidl;
- attrFlag = ShellDll.SFGAO.STREAM;
- folder.GetAttributesOf(1, aPidl, ref attrFlag);
- if ((attrFlag & ShellDll.SFGAO.STREAM) != 0) {
- m_IsFolder = false;
- }
- }
- if (m_Path.Length == 3 && m_Path.Substring(1).Equals(":\\")) {
- m_IsDisk = true;
- }
- }
- else {
- Marshal.ThrowExceptionForHR(HR);
- }
- }
-
- #endregion
-
- #endregion
-
- #region " Public Shared Function GetCShItem(ByVal path As String) As ShellItem"
- public static ShellItem GetCShItem(string path)
- {
- ShellItem functionReturnValue = default(ShellItem);
- functionReturnValue = null;
- //assume failure
- int HR = 0;
- IntPtr tmpPidl = default(IntPtr);
- int tmp1 = 0, tmp2 = 0;
- HR = GetDeskTop().Folder.ParseDisplayName(0, IntPtr.Zero, path, ref tmp1, ref tmpPidl, ref tmp2);
- if (HR == 0) {
- functionReturnValue = FindCShItem(tmpPidl);
- if ((functionReturnValue == null))
- {
- try {
- functionReturnValue = new ShellItem(path);
- }
- catch {
- functionReturnValue = null;
- }
- }
- }
- if (!tmpPidl.Equals(IntPtr.Zero)) {
- Marshal.FreeCoTaskMem(tmpPidl);
- }
- return functionReturnValue;
- }
- #endregion
-
- #region " Public Shared Function GetCShItem(ByVal ID As ShellDll.CSIDL) As ShellItem"
- public static ShellItem GetCShItem(ShellDll.CSIDL ID)
- {
- ShellItem functionReturnValue = default(ShellItem);
- functionReturnValue = null;
- //avoid VB2005 Warning
- if (ID == ShellDll.CSIDL.DESKTOP) {
- return GetDeskTop();
- }
- int HR = 0;
- IntPtr tmpPidl = default(IntPtr);
- if (ID == ShellDll.CSIDL.MYDOCUMENTS) {
- int pchEaten = 0;
- int tmp = 0;
- HR = GetDeskTop().Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref tmpPidl, ref tmp);
- }
- else {
- HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ID, ref tmpPidl);
- }
- if (HR == ShellDll.NOERROR) {
- functionReturnValue = FindCShItem(tmpPidl);
- if ((functionReturnValue == null))
- {
- try {
- functionReturnValue = new ShellItem(ID);
- }
- catch {
- functionReturnValue = null;
- }
- }
- }
- if (!tmpPidl.Equals(IntPtr.Zero)) {
- Marshal.FreeCoTaskMem(tmpPidl);
- }
- return functionReturnValue;
- }
- #endregion
-
- #region " Public Shared Function GetCShItem(ByVal FoldBytes() As Byte, ByVal ItemBytes() As Byte) As ShellItem"
- public static ShellItem GetCShItem(byte[] FoldBytes, byte[] ItemBytes)
- {
- ShellItem functionReturnValue = default(ShellItem);
- functionReturnValue = null;
- //assume failure
- byte[] b = cPidl.JoinPidlBytes(FoldBytes, ItemBytes);
- if ((b == null)) return functionReturnValue;
-
- //can do no more with invalid pidls
- //otherwise do like below, skipping unnecessary validation check
- IntPtr thisPidl = Marshal.AllocCoTaskMem(b.Length);
- if (thisPidl.Equals(IntPtr.Zero)) return null;
- Marshal.Copy(b, 0, thisPidl, b.Length);
- functionReturnValue = FindCShItem(thisPidl);
- Marshal.FreeCoTaskMem(thisPidl);
- if ((functionReturnValue == null))
- {
- //didn't find it, make new
- try {
- functionReturnValue = new ShellItem(FoldBytes, ItemBytes);
- }
- catch {
-
- }
- }
- if (functionReturnValue.PIDL.Equals(IntPtr.Zero)) functionReturnValue = null;
- return functionReturnValue;
- }
- #endregion
-
- #region " Public Shared Function FindCShItem(ByVal b() As Byte) As ShellItem"
- public static ShellItem FindCShItem(byte[] b)
- {
- ShellItem functionReturnValue = default(ShellItem);
- if (!IsValidPidl(b)) return null;
- IntPtr thisPidl = Marshal.AllocCoTaskMem(b.Length);
- if (thisPidl.Equals(IntPtr.Zero)) return null;
- Marshal.Copy(b, 0, thisPidl, b.Length);
- functionReturnValue = FindCShItem(thisPidl);
- Marshal.FreeCoTaskMem(thisPidl);
- return functionReturnValue;
- }
- #endregion
-
- #region " Public Shared Function FindCShItem(ByVal ptr As IntPtr) As ShellItem"
- public static ShellItem FindCShItem(IntPtr ptr)
- {
- ShellItem functionReturnValue = default(ShellItem);
- functionReturnValue = null;
- //avoid VB2005 Warning
- ShellItem BaseItem = ShellItem.GetDeskTop();
- //ShellItem CSI = default(ShellItem);
- bool FoundIt = false;
- //True if we found item or an ancestor
- while (!(FoundIt)) {
- foreach (var obj in BaseItem.GetDirectories(true)) {
- ShellItem CSI = obj as ShellItem;
- if (IsAncestorOf(CSI.PIDL, ptr, false)) {
- if (ShellItem.IsEqual(CSI.PIDL, ptr)) {
- //we found the desired item
- return CSI;
- }
- else {
- BaseItem = CSI;
- FoundIt = true;
- break; // TODO: might not be correct. Was : Exit For
- }
- }
- }
- if (!FoundIt) return null;
- //didn't find an ancestor
- //The complication is that the desired item may not be a directory
- if (!IsAncestorOf(BaseItem.PIDL, ptr, true)) {
- //Don't have immediate ancestor
- //go around again
- FoundIt = false;
- }
- else {
- foreach (var obj in BaseItem.GetItems()) {
- ShellItem CSI = obj as ShellItem;
- if (ShellItem.IsEqual(CSI.PIDL, ptr))
- {
- return CSI;
- }
- }
- //fall thru here means it doesn't exist or we can't find it because of funny PIDL from SHParseDisplayName
- return null;
- }
- }
- return functionReturnValue;
- }
- #endregion
-
- #endregion
-
- #region " Icomparable -- for default Sorting"
-
- /// <summary>Computes the Sort key of this ShellItem, based on its attributes</summary>
- ///
- private int ComputeSortFlag()
- {
- int rVal = 0;
- if (m_IsDisk) rVal = 0x100000;
- if (m_TypeName.Equals(strSystemFolder)) {
- if (!m_IsBrowsable) {
- rVal = rVal | 0x10000;
- if (m_strMyDocuments.Equals(m_DisplayName)) {
- rVal = rVal | 0x1;
- }
- }
- else {
- rVal = rVal | 0x1000;
- }
- }
- if (m_IsFolder) rVal = rVal | 0x100;
- return rVal;
- }
-
- ///<Summary> CompareTo(obj as object)
- /// Compares obj to this instance based on SortFlag-- obj must be a ShellItem</Summary>
- ///<SortOrder> (low)Disks,non-browsable System Folders,
- /// browsable System Folders,
- /// Directories, Files, Nothing (high)</SortOrder>
- public virtual int CompareTo(object obj)
- {
- ShellItem Other = obj as ShellItem;
- if ((Other == null)) return 1;
- //non-existant is always low
- if (!m_HasDispType) SetDispType();
- int cmp = Other.SortFlag - m_SortFlag;
- //Note the reversal
- if (cmp != 0) {
- return cmp;
- }
- else {
- if (m_IsDisk) {
- //implies that both are
- return string.Compare(m_Path, Other.Path);
- }
- else {
- return string.Compare(m_DisplayName, Other.DisplayName);
- }
- }
- }
- #endregion
-
- #region " Properties"
-
- #region " Shared Properties"
- public static string strMyComputer {
- get { return m_strMyComputer; }
- }
-
- public static string strSystemFolder {
- get { return m_strSystemFolder; }
- }
-
- public static string DesktopDirectoryPath {
- get { return m_DeskTopDirectory.Path; }
- }
-
- #endregion
-
- #region " Normal Properties"
- public IntPtr PIDL {
- get { return m_Pidl; }
- }
-
- public ShellDll.IShellFolder Folder {
- get { return m_Folder; }
- }
-
- public string Path {
- get { return m_Path; }
- }
- public ShellItem Parent {
- get { return m_Parent; }
- }
-
- public ShellDll.SFGAO Attributes {
- get { return m_Attributes; }
- }
- public bool IsBrowsable {
- get { return m_IsBrowsable; }
- }
- public bool IsFileSystem {
- get { return m_IsFileSystem; }
- }
- public bool IsFolder {
- get { return m_IsFolder; }
- }
- public bool HasSubFolders {
- get { return m_HasSubFolders; }
- }
- public bool IsDisk {
- get { return m_IsDisk; }
- }
- public bool IsLink {
- get { return m_IsLink; }
- }
- public bool IsShared {
- get { return m_IsShared; }
- }
- public bool IsHidden {
- get { return m_IsHidden; }
- }
- public bool IsRemovable {
- get { return m_IsRemovable; }
- }
-
- #region " Drag Ops Properties"
- public bool CanMove {
- get { return m_CanMove; }
- }
- public bool CanCopy {
- get { return m_CanCopy; }
- }
- public bool CanDelete {
- get { return m_CanDelete; }
- }
- public bool CanLink {
- get { return m_CanLink; }
- }
- public bool IsDropTarget {
- get { return m_IsDropTarget; }
- }
- #endregion
-
- #endregion
-
- #region " Filled on Demand Properties"
-
- #region " Filled based on m_HasDispType"
- /// <summary>
- /// Set DisplayName, TypeName, and SortFlag when actually needed
- /// </summary>
- ///
- private void SetDispType()
- {
- //Get Displayname, TypeName
- ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
- int dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
- int dwAttr = 0;
- if (m_IsFileSystem & !m_IsFolder) {
- dwflag = dwflag | (int)ShellDll.SHGFI.USEFILEATTRIBUTES;
- dwAttr = (int)ShellDll.FILE_ATTRIBUTE_NORMAL;
- }
- IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
- m_DisplayName = shfi.szDisplayName;
- m_TypeName = shfi.szTypeName;
- //fix DisplayName
- if (m_DisplayName.Equals("")) {
- m_DisplayName = m_Path;
- }
- //Fix TypeName
- //If m_IsFolder And m_TypeName.Equals("File") Then
- // m_TypeName = "File Folder"
- //End If
- m_SortFlag = ComputeSortFlag();
- m_HasDispType = true;
- }
-
- public string DisplayName {
- get {
- if (!m_HasDispType) SetDispType();
- return m_DisplayName;
- }
- }
-
- private int SortFlag {
- get {
- if (!m_HasDispType) SetDispType();
- return m_SortFlag;
- }
- }
-
- public string TypeName {
- get {
- if (!m_HasDispType) SetDispType();
- return m_TypeName;
- }
- }
- #endregion
-
- #region " IconIndex properties"
- public int IconIndexNormal {
- get {
- if (m_IconIndexNormal < 0) {
- if (!m_HasDispType) SetDispType();
- ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
- int dwflag = (int)(ShellDll.SHGFI.PIDL | ShellDll.SHGFI.SYSICONINDEX);
- int dwAttr = 0;
- if (m_IsFileSystem & !m_IsFolder) {
- dwflag = dwflag | (int)ShellDll.SHGFI.USEFILEATTRIBUTES;
- dwAttr = (int)ShellDll.FILE_ATTRIBUTE_NORMAL;
- }
- IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
- m_IconIndexNormal = shfi.iIcon;
- }
- return m_IconIndexNormal;
- }
- }
- // IconIndexOpen is Filled on demand
- public int IconIndexOpen {
- get {
- if (m_IconIndexOpen < 0) {
- if (!m_HasDispType) SetDispType();
- if (!m_IsDisk & m_IsFileSystem & m_IsFolder) {
- if (OpenFolderIconIndex < 0) {
- int dwflag = (int)(ShellDll.SHGFI.SYSICONINDEX | ShellDll.SHGFI.PIDL);
- ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
- IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, 0, ref shfi, ShellDll.cbFileInfo, dwflag | (int)ShellDll.SHGFI.OPENICON);
- m_IconIndexOpen = shfi.iIcon;
- }
- else {
- //If m_TypeName.Equals("File Folder") Then
- // OpenFolderIconIndex = shfi.iIcon
- //End If
- m_IconIndexOpen = OpenFolderIconIndex;
- }
- }
- else {
- m_IconIndexOpen = m_IconIndexNormal;
- }
- }
- return m_IconIndexOpen;
- }
- }
- #endregion
-
- #region " FileInfo type Information"
-
- /// <summary>
- /// Obtains information available from FileInfo.
- /// </summary>
- ///
- private void FillDemandInfo()
- {
- if (m_IsDisk) {
- try {
- //See if this is a network drive
- //NoRoot = 1
- //Removable = 2
- //LocalDisk = 3
- //Network = 4
- //CD = 5
- //RAMDrive = 6
- System.Management.ManagementObject disk = new System.Management.ManagementObject("win32_logicaldisk.deviceid=\"" + m_Path.Substring(0, 2) + "\"");
- m_Length = (long)(UInt64)disk["Size"];
- if ((UInt32)disk["DriveType"] == 4) {
- m_IsNetWorkDrive = true;
- }
- }
- catch (Exception) {
- //Disconnected Network Drives etc. will generate
- //an error here, just assume that it is a network
- //drive
- m_IsNetWorkDrive = true;
- }
- finally {
- m_XtrInfo = true;
- }
- }
- else if (!m_IsDisk & m_IsFileSystem & !m_IsFolder) {
- //in this case, it's a file
- if (File.Exists(m_Path)) {
- FileInfo fi = new FileInfo(m_Path);
- m_LastWriteTime = fi.LastWriteTime;
- m_LastAccessTime = fi.LastAccessTime;
- m_CreationTime = fi.CreationTime;
- m_Length = fi.Length;
- m_XtrInfo = true;
- }
- }
- else {
- if (m_IsFileSystem & m_IsFolder) {
- if (Directory.Exists(m_Path)) {
- DirectoryInfo di = new DirectoryInfo(m_Path);
- m_LastWriteTime = di.LastWriteTime;
- m_LastAccessTime = di.LastAccessTime;
- m_CreationTime = di.CreationTime;
- m_XtrInfo = true;
- }
- }
- }
- }
-
- public DateTime LastWriteTime {
- get {
- if (!m_XtrInfo) {
- FillDemandInfo();
- }
- return m_LastWriteTime;
- }
- }
- public DateTime LastAccessTime {
- get {
- if (!m_XtrInfo) {
- FillDemandInfo();
- }
- return m_LastAccessTime;
- }
- }
- public DateTime CreationTime {
- get {
- if (!m_XtrInfo) {
- FillDemandInfo();
- }
- return m_CreationTime;
- }
- }
- public long Length {
- get {
- if (!m_XtrInfo) {
- FillDemandInfo();
- }
- return m_Length;
- }
- }
- public bool IsNetworkDrive {
- get {
- if (!m_XtrInfo) {
- FillDemandInfo();
- }
- return m_IsNetWorkDrive;
- }
- }
- #endregion
-
- #region " cPidl information"
- public cPidl clsPidl {
- get {
- if ((m_cPidl == null)) {
- m_cPidl = new cPidl(m_Pidl);
- }
- return m_cPidl;
- }
- }
- #endregion
-
- #region " IsReadOnly and IsSystem"
- ///<Summary>The IsReadOnly attribute causes an annoying access to any floppy drives
- /// on the system. To postpone this (or avoid, depending on user action),
- /// the attribute is only queried when asked for</Summary>
- public bool IsReadOnly {
- get {
- if (m_IsReadOnlySetup) {
- return m_IsReadOnly;
- }
- else {
- ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
- shfi.dwAttributes = (int)ShellDll.SFGAO.RDONLY;
- int dwflag = (int)(ShellDll.SHGFI.PIDL | ShellDll.SHGFI.ATTRIBUTES | ShellDll.SHGFI.ATTR_SPECIFIED);
- int dwAttr = 0;
- IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
- if (H.ToInt32() != ShellDll.NOERROR && H.ToInt32() != 1) {
- Marshal.ThrowExceptionForHR(H.ToInt32());
- }
- m_IsReadOnly = ((shfi.dwAttributes & (int)ShellDll.SFGAO.RDONLY) != 0);
- //If m_IsReadOnly Then Debug.WriteLine("IsReadOnly -- " & m_Path)
- m_IsReadOnlySetup = true;
- return m_IsReadOnly;
- }
- }
- //If Not m_XtrInfo Then
- // FillDemandInfo()
- //End If
- //Return m_Attributes And FileAttributes.ReadOnly = FileAttributes.ReadOnly
- }
- ///<Summary>The IsSystem attribute is seldom used, but required by DragDrop operations.
- /// Since there is no way of getting ONLY the System attribute without getting
- /// the RO attribute (which forces a reference to the floppy drive), we pay
- /// the price of getting its own File/DirectoryInfo for this purpose alone.
- ///</Summary>
- public bool IsSystem {
- get {
- //true once we have gotten this attr
- //the value of this attr once we have it
- if (!static_IsSystem_HaveSysInfo) {
- try {
- static_IsSystem_m_IsSystem = (File.GetAttributes(m_Path) & FileAttributes.System) == FileAttributes.System;
- static_IsSystem_HaveSysInfo = true;
- }
- catch (Exception) {
- static_IsSystem_HaveSysInfo = true;
- }
- }
- Debug.WriteLine("In IsSystem -- Path = " + m_Path + " IsSystem = " + static_IsSystem_m_IsSystem);
- return static_IsSystem_m_IsSystem;
- }
- }
- static bool static_IsSystem_m_IsSystem = false;
- static bool static_IsSystem_HaveSysInfo = false;
-
- #endregion
-
- #endregion
-
- #endregion
-
- #region " Public Methods"
-
- #region " Shared Public Methods"
-
- #region " GetDeskTop"
- /// <summary>
- /// If not initialized, then build DesktopBase
- /// once done, or if initialized already,
- /// </summary>
- ///<returns>The DesktopBase ShellItem representing the desktop</returns>
- ///
- public static ShellItem GetDeskTop()
- {
- if ((DesktopBase == null)) {
- DesktopBase = new ShellItem();
- }
- return DesktopBase;
- }
- #endregion
-
- #region " IsAncestorOf"
- ///<Summary>IsAncestorOf returns True if ShellItem ancestor is an ancestor of ShellItem current
- /// if OS is Win2K or above, uses the ILIsParent API, otherwise uses the
- /// cPidl function StartsWith. This is necessary since ILIsParent in only available
- /// in Win2K or above systems AND StartsWith fails on some folders on XP systems (most
- /// obviously some Network Folder Shortcuts, but also Control Panel. Note, StartsWith
- /// always works on systems prior to XP.
- /// NOTE: if ancestor and current reference the same Item, both
- /// methods return True</Summary>
- public static bool IsAncestorOf(ShellItem ancestor, ShellItem current, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
- bool fParent)
- {
- return IsAncestorOf(ancestor.PIDL, current.PIDL, fParent);
- }
- ///<Summary> Compares a candidate Ancestor PIDL with a Child PIDL and
- /// returns True if Ancestor is an ancestor of the child.
- /// if fParent is True, then only return True if Ancestor is the immediate
- /// parent of the Child</Summary>
- public static bool IsAncestorOf(IntPtr AncestorPidl, IntPtr ChildPidl, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
- bool fParent)
- {
- bool functionReturnValue = false;
- if (ShellDll.Is2KOrAbove()) {
- return ShellDll.ILIsParent(AncestorPidl, ChildPidl, fParent);
- }
- else {
- cPidl Child = new cPidl(ChildPidl);
- cPidl Ancestor = new cPidl(AncestorPidl);
- functionReturnValue = Child.StartsWith(Ancestor);
- if (!functionReturnValue) return functionReturnValue;
-
- if (fParent) {
- // check for immediate ancestor, if desired
- object[] oAncBytes = Ancestor.Decompose();
- object[] oChildBytes = Child.Decompose();
- if (oAncBytes.Length != (oChildBytes.Length - 1)) {
- functionReturnValue = false;
- }
- }
- }
- return functionReturnValue;
- }
- #endregion
-
- #region " AllFolderWalk"
- ///<Summary>The WalkAllCallBack delegate defines the signature of
- /// the routine to be passed to DirWalker
- /// Usage: dim d1 as new CshItem.WalkAllCallBack(addressof yourroutine)
- /// Callback function receives a ShellItem for each file and Directory in
- /// Starting Directory and each sub-directory of this directory and
- /// each sub-dir of each sub-dir ....
- ///</Summary>
- public delegate bool WalkAllCallBack(ShellItem info, int UserLevel, int Tag);
- ///<Summary>
- /// AllFolderWalk recursively walks down directories from cStart, calling its
- /// callback routine, WalkAllCallBack, for each Directory and File encountered, including those in
- /// cStart. UserLevel is incremented by 1 for each level of dirs that DirWalker
- /// recurses thru. Tag in an Integer that is simply passed, unmodified to the
- /// callback, with each ShellItem encountered, both File and Directory CShItems.
- /// </Summary>
- /// <param name="cStart"></param>
- /// <param name="cback"></param>
- /// <param name="UserLevel"></param>
- /// <param name="Tag"></param>
- ///
- public static bool AllFolderWalk(ShellItem cStart, WalkAllCallBack cback, int UserLevel, int Tag)
- {
- if ((cStart != null) && cStart.IsFolder) {
- //ShellItem cItem = default(ShellItem);
- //first processes all files in this directory
- foreach (var obj in cStart.GetFiles()) {
- ShellItem cItem = obj as ShellItem;
- if (!cback(cItem, UserLevel, Tag)) {
- //user said stop
- return false;
- }
- }
- //then process all dirs in this directory, recursively
- foreach (var obj in cStart.GetDirectories(true)) {
- ShellItem cItem = obj as ShellItem;
- if (!cback(cItem, UserLevel + 1, Tag)) {
- //user said stop
- return false;
- }
- else {
- if (!AllFolderWalk(cItem, cback, UserLevel + 1, Tag)) {
- return false;
- }
- }
- }
- return true;
- }
- else {
- //Invalid call
- throw new ApplicationException("AllFolderWalk -- Invalid Start Directory");
- }
- }
- #endregion
-
- #endregion
-
- #region " Public Instance Methods"
-
- #region " Equals"
- public bool Equals(ShellItem other)
- {
- return this.Path.Equals(other.Path);
- }
- #endregion
-
- #region " GetDirectories"
- /// <summary>
- /// Returns the Directories of this sub-folder as an ArrayList of CShitems
- /// </summary>
- /// <param name="doRefresh">Optional, default=True, Refresh the directories</param>
- /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
- /// <remarks>revised to alway return an up-to-date list unless
- /// specifically instructed not to (useful in constructs like:
- /// if CSI.RefreshDirectories then
- /// Dirs = CSI.GetDirectories(False) ' just did a Refresh </remarks>
- public ArrayList GetDirectories([System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(true)] // ERROR: Optional parameters aren't supported in C#
- bool doRefresh)
- {
- if (m_IsFolder) {
- if (doRefresh) {
- // return an up-to-date List
- RefreshDirectories();
- }
- else if (m_Directories == null) {
- RefreshDirectories();
- }
- return m_Directories;
- }
- else {
- //if it is not a Folder, then return empty arraylist
- return new ArrayList();
- }
- }
-
- #endregion
-
- #region " GetFiles"
- /// <summary>
- /// Returns the Files of this sub-folder as an
- /// ArrayList of CShitems
- /// Note: we do not keep the arraylist of files, Generate it each time
- /// </summary>
- /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
- ///
- public ArrayList GetFiles()
- {
- if (m_IsFolder) {
- return GetContents(ShellDll.SHCONTF.NONFOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
- }
- else {
- return new ArrayList();
- }
- }
-
- /// <summary>
- /// Returns the Files of this sub-folder, filtered by a filtering string, as an
- /// ArrayList of CShitems
- /// Note: we do not keep the arraylist of files, Generate it each time
- /// </summary>
- /// <param name="Filter">A filter string (for example: *.Doc)</param>
- /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
- ///
- public ArrayList GetFiles(string Filter)
- {
- if (m_IsFolder) {
- ArrayList dummy = new ArrayList();
- string[] fileentries = null;
- fileentries = Directory.GetFiles(m_Path, Filter);
- //string vFile = null;
- foreach (var obj in fileentries) {
- string vFile = obj as string;
- dummy.Add(new ShellItem(vFile));
- }
- return dummy;
- }
- else {
- return new ArrayList();
- }
- }
- #endregion
-
- #region " GetItems"
- /// <summary>
- /// Returns the Directories and Files of this sub-folder as an
- /// ArrayList of CShitems
- /// Note: we do not keep the arraylist of files, Generate it each time
- /// </summary>
- /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
- public ArrayList GetItems()
- {
- ArrayList rVal = new ArrayList();
- if (m_IsFolder) {
- rVal.AddRange(this.GetDirectories(true));
- rVal.AddRange(this.GetContents(ShellDll.SHCONTF.NONFOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false));
- rVal.Sort();
- return rVal;
- }
- else {
- return rVal;
- }
- }
- #endregion
-
- #region " GetFileName"
- ///<Summary>GetFileName returns the Full file name of this item.
- /// Specifically, for a link file (xxx.txt.lnk for example) the
- /// DisplayName property will return xxx.txt, this method will
- /// return xxx.txt.lnk. In most cases this is equivalent of
- /// System.IO.Path.GetFileName(m_Path). However, some m_Paths
- /// actually are GUIDs. In that case, this routine returns the
- /// DisplayName</Summary>
- public string GetFileName()
- {
- if (m_Path.StartsWith("::{")) {
- //Path is really a GUID
- return this.DisplayName;
- }
- else {
- if (m_IsDisk) {
- return m_Path.Substring(0, 1);
- }
- else {
- return System.IO.Path.GetFileName(m_Path);
- }
- }
- }
- #endregion
-
- #region " ReFreshDirectories"
- ///<Summary> A lower cost way of Refreshing the Directories of this ShellItem</Summary>
- ///<returns> Returns True if there were any changes</returns>
- public bool RefreshDirectories()
- {
- bool functionReturnValue = false;
- functionReturnValue = false;
- //value unless there were changes
- if (m_IsFolder) {
- //if not a folder, then return false
- ArrayList InvalidDirs = new ArrayList();
- //holds CShItems of not found dirs
- if ((m_Directories == null)) {
- m_Directories = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
- //changed from unexamined to examined
- functionReturnValue = true;
- }
- else {
- //Get relative PIDLs from current directory items
- ArrayList curPidls = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, true);
- //IntPtr iptr = default(IntPtr);
- //used below
- if (curPidls.Count < 1) {
- if (m_Directories.Count > 0) {
- m_Directories = new ArrayList();
- //nothing there anymore
- //Changed from had some to have none
- functionReturnValue = true;
- }
- else {
- //Empty before, Empty now, do nothing -- just a logic marker
- }
- }
- else {
- //still has some. Are they the same?
- if (m_Directories.Count < 1) {
- //didn't have any before, so different
- m_Directories = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
- //changed from had none to have some
- functionReturnValue = true;
- }
- else {
- //some before, some now. Same? This is the complicated part
- //Firstly, build ArrayLists of Relative Pidls
- ArrayList compList = new ArrayList(curPidls);
- //Since we are only comparing relative PIDLs, build a list of
- // the relative PIDLs of the old content -- saving repeated building
- int iOld = 0;
- IntPtr[] OldRel = new IntPtr[m_Directories.Count];
- for (iOld = 0; iOld <= m_Directories.Count - 1; iOld++) {
- //GetLastID returns a ptr into an EXISTING IDLIST -- never release that ptr
- // and never release the EXISTING IDLIST before thru with OldRel
- OldRel[iOld] = GetLastID(((ShellItem)m_Directories[iOld]).PIDL);
- }
- int iNew = 0;
- for (iOld = 0; iOld <= m_Directories.Count - 1; iOld++) {
- for (iNew = 0; iNew <= compList.Count - 1; iNew++) {
- if (IsEqual((IntPtr)compList[iNew], OldRel[iOld])) {
- compList.RemoveAt(iNew);
- //Match, don't look at this one again
- //content item exists in both
- goto NXTOLD;
- }
- }
- //falling thru here means couldn't find iOld entry
- InvalidDirs.Add(m_Directories[iOld]);
- //save off the unmatched ShellItem
- functionReturnValue = true;
- NXTOLD: functionReturnValue = !!functionReturnValue;
- }
- //any not found should be removed from m_Directories
- //ShellItem csi = default(ShellItem);
- foreach (var obj in InvalidDirs) {
- ShellItem csi = obj as ShellItem;
- m_Directories.Remove(csi);
- }
- //anything remaining in compList is a new entry
- if (compList.Count > 0) {
- functionReturnValue = true;
- foreach (var obj in compList) {
- IntPtr iptr = (IntPtr)obj;
- //these are relative PIDLs
- m_Directories.Add(new ShellItem(m_Folder, iptr, this));
- }
- }
- if (functionReturnValue) {
- //something added or removed, resort
- m_Directories.Sort();
- }
- }
- //we obtained some new relative PIDLs in curPidls, so free them
- foreach (var obj in curPidls) {
- IntPtr iptr = (IntPtr)obj;
- Marshal.FreeCoTaskMem(iptr);
- }
- //end of content comparison
- }
- //end of IsNothing test
- }
- }
- //end of IsFolder test
- return functionReturnValue;
- }
-
- #endregion
-
- #region " ToString"
- /// <summary>
- /// Returns the DisplayName as the normal ToString value
- /// </summary>
- ///
- public override string ToString()
- {
- return m_DisplayName;
- }
- #endregion
-
- #region " Debug Dumper"
- /// <summary>
- /// Summary of DebugDump.
- /// </summary>
- ///
- public void DebugDump()
- {
- Debug.WriteLine("DisplayName = " + m_DisplayName);
- Debug.WriteLine("PIDL = " + m_Pidl.ToString());
- Debug.WriteLine("\tPath = " + m_Path);
- Debug.WriteLine("\tTypeName = " + this.TypeName);
- Debug.WriteLine("\tiIconNormal = " + m_IconIndexNormal);
- Debug.WriteLine("\tiIconSelect = " + m_IconIndexOpen);
- Debug.WriteLine("\tIsBrowsable = " + m_IsBrowsable);
- Debug.WriteLine("\tIsFileSystem= " + m_IsFileSystem);
- Debug.WriteLine("\tIsFolder = " + m_IsFolder);
- Debug.WriteLine("\tIsLink = " + m_IsLink);
- Debug.WriteLine("\tIsDropTarget = " + m_IsDropTarget);
- Debug.WriteLine("\tIsReadOnly = " + this.IsReadOnly);
- Debug.WriteLine("\tCanCopy = " + this.CanCopy);
- Debug.WriteLine("\tCanLink = " + this.CanLink);
- Debug.WriteLine("\tCanMove = " + this.CanMove);
- Debug.WriteLine("\tCanDelete = " + this.CanDelete);
- if (m_IsFolder) {
- if ((m_Directories != null)) {
- Debug.WriteLine("\tDirectory Count = " + m_Directories.Count);
- }
- else {
- Debug.WriteLine("\tDirectory Count Not yet set");
- }
- }
- }
- #endregion
-
- #region " GetDropTargetOf"
- public IDropTarget GetDropTargetOf(Control tn)
- {
- //if ((m_Folder == null)) return null;
- //IntPtr[] apidl = new IntPtr[1];
- //int HR = 0;
- IDropTarget theInterface = null;
- //IntPtr tnH = tn.Handle;
- //HR = m_Folder.CreateViewObject(tnH, ref ShellDll.IID_IDropTarget, ref theInterface);
- //if (HR != 0) {
- // Marshal.ThrowExceptionForHR(HR);
- //}
- return theInterface;
- }
- #endregion
-
- #endregion
-
- #endregion
-
- #region " Private Instance Methods"
-
- #region " GetContents"
- ///<Summary>
- /// Returns the requested Items of this Folder as an ArrayList of CShitems
- /// unless the IntPtrOnly flag is set. If IntPtrOnly is True, return an
- /// ArrayList of PIDLs.
- ///</Summary>
- /// <param name="flags">A set of one or more ShellDll.SHCONTF flags indicating which items to return</param>
- /// <param name="IntPtrOnly">True to suppress generation of CShItems, returning only an
- /// ArrayList of IntPtrs to RELATIVE PIDLs for the contents of this Folder</param>
- private ArrayList GetContents(ShellDll.SHCONTF flags, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
- bool IntPtrOnly)
- {
- ArrayList rVal = new ArrayList();
- int HR = 0;
- ShellDll.IEnumIDList IEnum = null;
- HR = m_Folder.EnumObjects(0, flags, ref IEnum);
- if (HR == ShellDll.NOERROR) {
- IntPtr item = IntPtr.Zero;
- int itemCnt = 0;
- HR = IEnum.GetNext(1, ref item, ref itemCnt);
- if (HR == ShellDll.NOERROR) {
- while (itemCnt > 0 && !item.Equals(IntPtr.Zero)) {
- //there is no setting to exclude folders from enumeration,
- // just one to include non-folders
- // so we have to screen the results to return only
- // non-folders if folders are not wanted
- if ((flags & ShellDll.SHCONTF.FOLDERS) == 0) {
- //don't want folders. see if this is one
- ShellDll.SFGAO attrFlag = default(ShellDll.SFGAO);
- attrFlag = attrFlag | ShellDll.SFGAO.FOLDER | ShellDll.SFGAO.STREAM;
- //Note: for GetAttributesOf, we must provide an array, in all cases with 1 element
- IntPtr[] aPidl = new IntPtr[1];
- aPidl[0] = item;
- m_Folder.GetAttributesOf(1, aPidl, ref attrFlag);
- if (!XPorAbove) {
- if ((attrFlag & ShellDll.SFGAO.FOLDER) != 0) {
- //Don't need it
- goto SKIPONE;
- }
- }
- else {
- //XP or above
- if ((attrFlag & ShellDll.SFGAO.FOLDER) != 0 && (attrFlag & ShellDll.SFGAO.STREAM) == 0) {
- goto SKIPONE;
- }
- }
- }
- if (IntPtrOnly) {
- //just relative pidls for fast look, no CShITem overhead
- //caller must free
- rVal.Add(PIDLClone(item));
- }
- else {
- rVal.Add(new ShellItem(m_Folder, item, this));
- }
- SKIPONE:
- Marshal.FreeCoTaskMem(item);
- //if New kept it, it kept a copy
- item = IntPtr.Zero;
- itemCnt = 0;
- // Application.DoEvents()
- HR = IEnum.GetNext(1, ref item, ref itemCnt);
- }
- }
- else {
- //1 means no more
- if (HR != 1) goto HRError;
- }
- }
- else {
- goto HRError;
- }
- NORMAL:
- //Normal Exit
- if ((IEnum != null)) {
- Marshal.ReleaseComObject(IEnum);
- }
- rVal.TrimToSize();
- return rVal;
- HRError:
-
- // Error Exit for all Com errors
- //not ready disks will return the following error
- //If HR = &HFFFFFFFF800704C7 Then
- // GoTo NORMAL
- //ElseIf HR = &HFFFFFFFF80070015 Then
- // GoTo NORMAL
- // 'unavailable net resources will return these
- //ElseIf HR = &HFFFFFFFF80040E96 Or HR = &HFFFFFFFF80040E19 Then
- // GoTo NORMAL
- //ElseIf HR = &HFFFFFFFF80004001 Then 'Certain "Not Implemented" features will return this
- // GoTo NORMAL
- //ElseIf HR = &HFFFFFFFF80004005 Then
- // GoTo NORMAL
- //ElseIf HR = &HFFFFFFFF800704C6 Then
- // GoTo NORMAL
- //End If
- if ((IEnum != null)) Marshal.ReleaseComObject(IEnum);
- //#If Debug Then
- // Marshal.ThrowExceptionForHR(HR)
- //#End If
- rVal = new ArrayList();
- //sometimes it is a non-fatal error,ignored
- goto NORMAL;
- }
- #endregion
-
- #region " Really nasty Pidl manipulation"
-
- /// <Summary>
- /// Get FileSize in bytes of the first (possibly only)
- /// SHItem in an ID list. Note: the full Size of
- /// the item is the sum of the sizes of all SHItems
- /// in the list!!
- /// </Summary>
- /// <param name="pidl"></param>
- ///
- private static int ItemIDSize(IntPtr pidl)
- {
- if (!pidl.Equals(IntPtr.Zero)) {
- byte[] b = new byte[2];
- Marshal.Copy(pidl, b, 0, 2);
- return b[1] * 256 + b[0];
- }
- else {
- return 0;
- }
- }
-
- /// <summary>
- /// computes the actual Size of the ItemIDList pointed to by pidl
- /// </summary>
- /// <param name="pidl">The pidl pointing to an ItemIDList</param>
- ///<returns> Returns actual Size of the ItemIDList, less the terminating nulnul</returns>
- public static int ItemIDListSize(IntPtr pidl)
- {
- if (!pidl.Equals(IntPtr.Zero)) {
- int i = ItemIDSize(pidl);
- int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
- while (b > 0) {
- i += b;
- b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
- }
- return i;
- }
- else {
- return 0;
- }
- }
- /// <summary>
- /// Counts the total number of SHItems in input pidl
- /// </summary>
- /// <param name="pidl">The pidl to obtain the count for</param>
- /// <returns> Returns the count of SHItems pointed to by pidl</returns>
- public static int PidlCount(IntPtr pidl)
- {
- if (!pidl.Equals(IntPtr.Zero)) {
- int cnt = 0;
- int i = 0;
- int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
- while (b > 0) {
- cnt += 1;
- i += b;
- b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
- }
- return cnt;
- }
- else {
- return 0;
- }
- }
-
- ///<Summary>GetLastId -- returns a pointer to the last ITEMID in a valid
- /// ITEMIDLIST. Returned pointer SHOULD NOT be released since it
- /// points to place within the original PIDL</Summary>
- ///<returns>IntPtr pointing to last ITEMID in ITEMIDLIST structure,
- /// Returns IntPtr.Zero if given a null pointer.
- /// If given a pointer to the Desktop, will return same pointer.</returns>
- ///<remarks>This is what the API ILFindLastID does, however IL...
- /// functions are not supported before Win2K.</remarks>
- public static IntPtr GetLastID(IntPtr pidl)
- {
- if (!pidl.Equals(IntPtr.Zero)) {
- int prev = 0;
- int i = 0;
- int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
- while (b > 0) {
- prev = i;
- i += b;
- b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
- }
- return new IntPtr(pidl.ToInt32() + prev);
- }
- else {
- return IntPtr.Zero;
- }
- }
-
- public static IntPtr[] DecomposePIDL(IntPtr pidl)
- {
- int lim = ItemIDListSize(pidl);
- IntPtr[] PIDLs = new IntPtr[PidlCount(pidl)];
- int i = 0;
- int curB = 0;
- int offSet = 0;
- while (curB < lim) {
- IntPtr thisPtr = new IntPtr(pidl.ToInt32() + curB);
- offSet = Marshal.ReadByte(thisPtr) + (Marshal.ReadByte(thisPtr, 1) * 256);
- PIDLs[i] = Marshal.AllocCoTaskMem(offSet + 2);
- byte[] b = new byte[offSet + 2];
- Marshal.Copy(thisPtr, b, 0, offSet);
- b[offSet] = 0;
- b[offSet + 1] = 0;
- Marshal.Copy(b, 0, PIDLs[i], offSet + 2);
- //DumpPidl(PIDLs(i))
- curB += offSet;
- i += 1;
- }
- return PIDLs;
- }
-
- private static IntPtr PIDLClone(IntPtr pidl)
- {
- IntPtr functionReturnValue = default(IntPtr);
- int cb = ItemIDListSize(pidl);
- byte[] b = new byte[cb + 2];
- Marshal.Copy(pidl, b, 0, cb);
- //not including terminating nulnul
- b[cb] = 0;
- b[cb + 1] = 0;
- //force to nulnul
- functionReturnValue = Marshal.AllocCoTaskMem(cb + 2);
- Marshal.Copy(b, 0, functionReturnValue, cb + 2);
- return functionReturnValue;
- }
-
- public static bool IsEqual(IntPtr Pidl1, IntPtr Pidl2)
- {
- if (Win2KOrAbove) {
- return ShellDll.ILIsEqual(Pidl1, Pidl2);
- }
- else {
- //do hard way, may not work for some folders on XP
-
- int cb1 = 0;
- int cb2 = 0;
- cb1 = ItemIDListSize(Pidl1);
- cb2 = ItemIDListSize(Pidl2);
- if (cb1 != cb2) return false;
- int lim32 = cb1 / 4;
-
- int i = 0;
- for (i = 0; i <= lim32 - 1; i++) {
- if (Marshal.ReadInt32(Pidl1, i) != Marshal.ReadInt32(Pidl2, i)) {
- return false;
- }
- }
- int limB = cb1 % 4;
- int offset = lim32 * 4;
- for (i = 0; i <= limB - 1; i++) {
- if (Marshal.ReadByte(Pidl1, offset + i) != Marshal.ReadByte(Pidl2, offset + i)) {
- return false;
- }
- }
- //made it to here, so they are equal
- return true;
- }
- }
-
- /// <summary>
- /// Concatenates the contents of two pidls into a new Pidl (ended by 00)
- /// allocating CoTaskMem to hold the result,
- /// placing the concatenation (followed by 00) into the
- /// allocated Memory,
- /// and returning an IntPtr pointing to the allocated mem
- /// </summary>
- /// <param name="pidl1">IntPtr to a well formed SHItemIDList or IntPtr.Zero</param>
- /// <param name="pidl2">IntPtr to a well formed SHItemIDList or IntPtr.Zero</param>
- /// <returns>Returns a ptr to an ItemIDList containing the
- /// concatenation of the two (followed by the req 2 zeros
- /// Caller must Free this pidl when done with it</returns>
- public static IntPtr concatPidls(IntPtr pidl1, IntPtr pidl2)
- {
- int cb1 = 0;
- int cb2 = 0;
- cb1 = ItemIDListSize(pidl1);
- cb2 = ItemIDListSize(pidl2);
- int rawCnt = cb1 + cb2;
- if ((rawCnt) > 0) {
- byte[] b = new byte[rawCnt + 2];
- if (cb1 > 0) {
- Marshal.Copy(pidl1, b, 0, cb1);
- }
- if (cb2 > 0) {
- Marshal.Copy(pidl2, b, cb1, cb2);
- }
- IntPtr rVal = Marshal.AllocCoTaskMem(cb1 + cb2 + 2);
- b[rawCnt] = 0;
- b[rawCnt + 1] = 0;
- Marshal.Copy(b, 0, rVal, rawCnt + 2);
- return rVal;
- }
- else {
- return IntPtr.Zero;
- }
- }
-
- /// <summary>
- /// Returns an ItemIDList with the last ItemID trimed off
- /// This is necessary since I cannot get SHBindToParent to work
- /// It's purpose is to generate an ItemIDList for the Parent of a
- /// Special Folder which can then be processed with DesktopBase.BindToObject,
- /// yeilding a Folder for the parent of the Special Folder
- /// It also creates and passes back a RELATIVE pidl for this item
- /// </summary>
- /// <param name="pidl">A pointer to a well formed ItemIDList. The PIDL to trim</param>
- /// <param name="relPidl">BYREF IntPtr which will point to a new relative pidl
- /// containing the contents of the last ItemID in the ItemIDList
- /// terminated by the required 2 nulls.</param>
- /// <returns> an ItemIDList with the last element removed.
- /// Caller must Free this item when through with it
- /// Also returns the new relative pidl in the 2nd parameter
- /// Caller must Free this pidl as well, when through with it
- ///</returns>
- public static IntPtr TrimPidl(IntPtr pidl, ref IntPtr relPidl)
- {
- int cb = ItemIDListSize(pidl);
- byte[] b = new byte[cb + 2];
- Marshal.Copy(pidl, b, 0, cb);
- int prev = 0;
- int i = b[0] + (b[1] * 256);
- //Do While i < cb AndAlso b(i) <> 0
- while (i > 0 && i < cb) {
- //Changed code
- prev = i;
- i += b[i] + (b[i + 1] * 256);
- }
- if ((prev + 1) < cb) {
- //first set up the relative pidl
- b[cb] = 0;
- b[cb + 1] = 0;
- int cb1 = b[prev] + (b[prev + 1] * 256);
- relPidl = Marshal.AllocCoTaskMem(cb1 + 2);
- Marshal.Copy(b, prev, relPidl, cb1 + 2);
- b[prev] = 0;
- b[prev + 1] = 0;
- IntPtr rVal = Marshal.AllocCoTaskMem(prev + 2);
- Marshal.Copy(b, 0, rVal, prev + 2);
- return rVal;
- }
- else {
- return IntPtr.Zero;
- }
- }
-
- #region " DumpPidl Routines"
- /// <summary>
- /// Dumps, to the Debug output, the contents of the mem block pointed to by
- /// a PIDL. Depends on the internal structure of a PIDL
- /// </summary>
- /// <param name="pidl">The IntPtr(a PIDL) pointing to the block to dump</param>
- ///
- public static void DumpPidl(IntPtr pidl)
- {
- int cb = ItemIDListSize(pidl);
- Debug.WriteLine("PIDL " + pidl.ToString() + " contains " + cb + " bytes");
- if (cb > 0) {
- byte[] b = new byte[cb + 2];
- Marshal.Copy(pidl, b, 0, cb + 1);
- int pidlCnt = 1;
- int i = b[0] + (b[1] * 256);
- int curB = 0;
- while (i > 0) {
- Debug.Write("ItemID #" + pidlCnt + " Length = " + i);
- DumpHex(b, curB, curB + i - 1);
- pidlCnt += 1;
- curB += i;
- i = b[curB] + (b[curB + 1] * 256);
- }
- }
- }
-
- ///<Summary>Dump a portion or all of a Byte Array to Debug output</Summary>
- ///<param name = "b">A single dimension Byte Array</param>
- ///<param name = "sPos">Optional start index of area to dump (default = 0)</param>
- ///<param name = "epos">Optional last index position to dump (default = end of array)</param>
- ///<Remarks>
- ///</Remarks>
- public static void DumpHex(byte[] b, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(0)] // ERROR: Optional parameters aren't supported in C#
- int sPos, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(0)] // ERROR: Optional parameters aren't supported in C#
- int ePos)
- {
- if (ePos == 0) ePos = b.Length - 1;
- int j = 0;
- int curB = sPos;
- string sTmp = null;
- char ch = '\0';
- StringBuilder SBH = new StringBuilder();
- StringBuilder SBT = new StringBuilder();
- for (j = 0; j <= ePos - sPos; j++) {
- if (j % 16 == 0) {
- Debug.WriteLine(SBH.ToString() + SBT.ToString());
- SBH = new StringBuilder();
- SBT = new StringBuilder(" ");
- SBH.Append(HexNum(j + sPos, 4) + "). ");
- }
- if (b[curB] < 16) {
- sTmp = "0" + Convert.ToString(b[curB], 16);
- }
- else {
- sTmp = Convert.ToString(b[curB], 16);
- }
- SBH.Append(sTmp);
- SBH.Append(" ");
- ch = (char)(b[curB]);
- if (char.IsControl(ch)) {
- SBT.Append(".");
- }
- else {
- SBT.Append(ch);
- }
- curB += 1;
- }
-
- int fill = (j) % 16;
- if (fill != 0) {
- SBH.Append(' ', 48 - (3 * ((j) % 16)));
- }
- Debug.WriteLine(SBH.ToString() + SBT.ToString());
- }
-
- public static string HexNum(int num, int nrChrs)
- {
- string h = Convert.ToString(num, 16);
- StringBuilder SB = new StringBuilder();
- int i = 0;
- for (i = 1; i <= nrChrs - h.Length; i++) {
- SB.Append("0");
- }
- SB.Append(h);
- return SB.ToString();
- }
- #endregion
-
- #endregion
-
- #endregion
-
- #region " TagComparer Class"
- ///<Summary> It is sometimes useful to sort a list of TreeNodes,
- /// ListViewItems, or other objects in an order based on CShItems in their Tag
- /// use this Icomparer for that Sort</Summary>
- public class TagComparer : IComparer
- {
- public int Compare(object x, object y)
- {
- //ShellItem xTag = x.tag;
- //ShellItem yTag = y.tag;
- //return xTag.CompareTo(y.tag);
- return 0;
- }
- }
- #endregion
-
- #region " cPidl Class"
- ///<Summary>cPidl class contains a Byte() representation of a PIDL and
- /// certain Methods and Properties for comparing one cPidl to another</Summary>
- public class cPidl : IEnumerable
- {
-
- #region " Private Fields"
- byte[] m_bytes = null;
- //The local copy of the PIDL
- int m_ItemCount = 0;
- //the # of ItemIDs in this ItemIDList (PIDL)
- #endregion
-
- #region " Constructor"
- public cPidl(IntPtr pidl)
- {
- int cb = ItemIDListSize(pidl);
- if (cb > 0) {
- m_bytes = new byte[cb + 2];
- Marshal.Copy(pidl, m_bytes, 0, cb);
- //DumpPidl(pidl)
- }
- else {
- m_bytes = new byte[2]; //This is the DeskTop (we hope)
- }
- //ensure nulnul
- m_bytes[m_bytes.Length - 2]= 0;
- m_bytes[m_bytes.Length - 1] = 0;
- m_ItemCount = PidlCount(pidl);
- }
- #endregion
-
- #region " Public Properties"
- public byte[] PidlBytes {
- get { return m_bytes; }
- }
-
- public int Length {
- get { return m_bytes.Length; }
- }
-
- public int ItemCount {
- get { return m_ItemCount; }
- }
-
- #endregion
-
- #region " Public Intstance Methods -- ToPIDL, Decompose, and IsEqual"
-
- ///<Summary> Copy the contents of a byte() containing a pidl to
- /// CoTaskMemory, returning an IntPtr that points to that mem block
- /// Assumes that this cPidl is properly terminated, as all New
- /// cPidls are.
- /// Caller must Free the returned IntPtr when done with mem block.
- ///</Summary>
- public IntPtr ToPIDL()
- {
- return BytesToPidl(m_bytes);
- }
-
- ///<Summary>Returns an object containing a byte() for each of this cPidl's
- /// ITEMIDs (individual PIDLS), in order such that obj(0) is
- /// a byte() containing the bytes of the first ITEMID, etc.
- /// Each ITEMID is properly terminated with a nulnul
- ///</Summary>
- public object[] Decompose()
- {
- object[] bArrays = new object[this.ItemCount];
- ICPidlEnumerator eByte = this.GetEnumerator() as ICPidlEnumerator;
- int i = 0;
- while (eByte.MoveNext()) {
- bArrays[i] = eByte.Current;
- i += 1;
- }
- return bArrays;
- }
-
- ///<Summary>Returns True if input cPidl's content exactly match the
- /// contents of this instance</Summary>
- public bool IsEqual(cPidl other)
- {
- bool functionReturnValue = false;
- functionReturnValue = false;
- //assume not
- if (other.Length != this.Length) return functionReturnValue;
-
- byte[] ob = other.PidlBytes;
- int i = 0;
- for (i = 0; i <= this.Length - 1; i++) {
- //note: we look at nulnul also
- if (ob[i] != m_bytes[i]) return functionReturnValue;
-
- }
- //all equal on fall thru
- return true;
- }
- #endregion
-
- #region " Public Shared Methods"
-
- #region " JoinPidlBytes"
- ///<Summary> Join two byte arrays containing PIDLS, returning a
- /// Byte() containing the resultant ITEMIDLIST. Both Byte() must
- /// be properly terminated (nulnul)
- /// Returns NOTHING if error
- /// </Summary>
- public static byte[] JoinPidlBytes(byte[] b1, byte[] b2)
- {
- if (IsValidPidl(b1) & IsValidPidl(b2)) {
- byte[] b = new byte[b1.Length + b2.Length - 2];
- //allow for leaving off first nulnul
- Array.Copy(b1, b, b1.Length - 2);
- Array.Copy(b2, 0, b, b1.Length - 2, b2.Length);
- if (IsValidPidl(b)) {
- return b;
- }
- else {
- return null;
- }
- }
- else {
- return null;
- }
- }
- #endregion
-
- #region " BytesToPidl"
- ///<Summary> Copy the contents of a byte() containing a pidl to
- /// CoTaskMemory, returning an IntPtr that points to that mem block
- /// Caller must free the IntPtr when done with it
- ///</Summary>
- public static IntPtr BytesToPidl(byte[] b)
- {
- IntPtr functionReturnValue = default(IntPtr);
- functionReturnValue = IntPtr.Zero;
- //assume failure
- if (IsValidPidl(b)) {
- int bLen = b.Length;
- functionReturnValue = Marshal.AllocCoTaskMem(bLen);
- if (functionReturnValue.Equals(IntPtr.Zero)) return functionReturnValue;
-
- //another bad error
- Marshal.Copy(b, 0, functionReturnValue, bLen);
- }
- return functionReturnValue;
- }
- #endregion
-
- #region " StartsWith"
- ///<Summary>returns True if the beginning of pidlA matches PidlB exactly for pidlB's entire length</Summary>
- public static bool StartsWith(IntPtr pidlA, IntPtr pidlB)
- {
- return cPidl.StartsWith(new cPidl(pidlA), new cPidl(pidlB));
- }
-
- ///<Summary>returns True if the beginning of A matches B exactly for B's entire length</Summary>
- public static bool StartsWith(cPidl A, cPidl B)
- {
- return A.StartsWith(B);
- }
-
- ///<Summary>Returns true if the CPidl input parameter exactly matches the
- /// beginning of this instance of CPidl</Summary>
- public bool StartsWith(cPidl cp)
- {
- byte[] b = cp.PidlBytes;
- if (b.Length > m_bytes.Length) return false;
- //input is longer
- int i = 0;
- for (i = 0; i <= b.Length - 3; i++) {
- //allow for nulnul at end of cp.PidlBytes
- if (b[i] != m_bytes[i]) return false;
- }
- return true;
- }
- #endregion
-
- #endregion
-
- #region " GetEnumerator"
- public System.Collections.IEnumerator GetEnumerator()
- {
- return new ICPidlEnumerator(m_bytes);
- }
- #endregion
-
- #region " PIDL enumerator Class"
- private class ICPidlEnumerator : IEnumerator
- {
-
- private int m_sPos;
- //the first index in the current PIDL
- private int m_ePos;
- //the last index in the current PIDL
- private byte[] m_bytes;
- //the local copy of the PIDL
- private bool m_NotEmpty = false;
- //the desktop PIDL is zero length
-
- public ICPidlEnumerator(byte[] b)
- {
- m_bytes = b;
- if (b.Length > 0) m_NotEmpty = true;
- m_sPos = -1;
- m_ePos = -1;
- }
-
- public object Current {
- get {
- if (m_sPos < 0) throw new InvalidOperationException("ICPidlEnumerator --- attempt to get Current with invalidated list");
- byte[] b = new byte[(m_ePos - m_sPos) + 3];
- //room for nulnul
- Array.Copy(m_bytes, m_sPos, b, 0, b.Length - 2);
- b[b.Length - 2] = 0;
- b[b.Length - 1] = 0;
- //add nulnul
- return b;
- }
- }
-
- public bool MoveNext()
- {
- if (m_NotEmpty) {
- if (m_sPos < 0) {
- m_sPos = 0;
- m_ePos = -1;
- }
- else {
- m_sPos = m_ePos + 1;
- }
- if (m_bytes.Length < m_sPos + 1) throw new InvalidCastException("Malformed PIDL");
- int cb = m_bytes[m_sPos] + m_bytes[m_sPos + 1] * 256;
- if (cb == 0) {
- //have passed all back
- return false;
- }
- else {
- m_ePos += cb;
- }
- }
- else {
- m_sPos = 0;
- m_ePos = 0;
- //in this case, we have exhausted the list of 0 ITEMIDs
- return false;
- }
- return true;
- }
-
- public void Reset()
- {
- m_sPos = -1;
- m_ePos = -1;
- }
- }
- #endregion
-
- }
- #endregion
-
- }