PageRenderTime 76ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/animeplugin3/MyAnimePlugin3/ShellItem.cs

https://bitbucket.org/gibwar/jmm-test
C# | 2334 lines | 2317 code | 7 blank | 10 comment | 90 complexity | 9b807895a5af4736537348782ed636b8 MD5 | raw file
  1. /*
  2. * This class is converted VB.Net from the CScItem
  3. * from http://www.codeproject.com/KB/cpp/VbNetExpTree.aspx
  4. */
  5. using System.IO;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System;
  9. using System.Collections;
  10. using System.Windows.Forms;
  11. using System.Diagnostics;
  12. using System.Management;
  13. public class ShellItem : IDisposable, IComparable
  14. {
  15.    
  16.     #region " Shared Private Fields"
  17.     //This class has occasion to refer to the TypeName as reported by
  18.     // ShellDll.SHGetFileInfo. It needs to compare this to the string
  19.     // (in English) "System Folder"
  20.     //on non-English systems, we do not know, in the general case,
  21.     // what the equivalent string is to compare against
  22.     //The following variable is set by Sub New() to the string that
  23.     // corresponds to "System Folder" on the current machine
  24.     // Sub New() depends on the existance of My Computer(ShellDll.CSIDL.DRIVES),
  25.     // to determine what the equivalent string is
  26.     private static string m_strSystemFolder;
  27.    
  28.     //My Computer is also commonly used (though not internally),
  29.     // so save & expose its name on the current machine
  30.     private static string m_strMyComputer;
  31.    
  32.     //To get My Documents sorted first, we need to know the Locale
  33.     //specific name of that folder.
  34.     private static string m_strMyDocuments;
  35.    
  36.     // The DesktopBase is set up via Sub New() (one time only) and
  37.     // disposed of only when DesktopBase is finally disposed of
  38.     private static ShellItem DesktopBase;
  39.    
  40.     //We can avoid an extra ShellDll.SHGetFileInfo call once this is set up
  41.     private static int OpenFolderIconIndex = -1;
  42.    
  43.     // It is also useful to know if the OS is XP or above.
  44.     // Set up in Sub New() to avoid multiple calls to find this info
  45.     private static bool XPorAbove;
  46.     // Likewise if OS is Win2K or Above
  47.     private static bool Win2KOrAbove;
  48.    
  49.     // DragDrop, possibly among others, needs to know the Path of
  50.     // the DeskTopDirectory in addition to the Desktop itself
  51.     // Also need the actual ShellItem for the DeskTopDirectory, so get it
  52.     private static ShellItem m_DeskTopDirectory;
  53.    
  54.    
  55.     #endregion
  56.    
  57.     #region " Instance Private Fields"
  58.     //m_Folder and m_Pidl must be released/freed at Dispose time
  59.     private ShellDll.IShellFolder m_Folder;
  60.     //if item is a folder, contains the Folder interface for this instance
  61.     private IntPtr m_Pidl;
  62.     //The Absolute PIDL for this item (not retained for files)
  63.     private string m_DisplayName = "";
  64.     private string m_Path;
  65.     private string m_TypeName;
  66.     private ShellItem m_Parent;
  67.     //= Nothing
  68.     private int m_IconIndexNormal;
  69.     //index into the System Image list for Normal icon
  70.     private int m_IconIndexOpen;
  71.     //index into the SystemImage list for Open icon
  72.     private bool m_IsBrowsable;
  73.     private bool m_IsFileSystem;
  74.     private bool m_IsFolder;
  75.     private bool m_HasSubFolders;
  76.     private bool m_IsLink;
  77.     private bool m_IsDisk;
  78.     private bool m_IsShared;
  79.     private bool m_IsHidden;
  80.     private bool m_IsNetWorkDrive;
  81.     //= False
  82.     private bool m_IsRemovable;
  83.     //= False
  84.     private bool m_IsReadOnly;
  85.     //= False
  86.     //Properties of interest to Drag Operations
  87.     private bool m_CanMove;
  88.     //= False
  89.     private bool m_CanCopy;
  90.     //= False
  91.     private bool m_CanDelete;
  92.     //= False
  93.     private bool m_CanLink;
  94.     //= False
  95.     private bool m_IsDropTarget;
  96.     //= False
  97.     private ShellDll.SFGAO m_Attributes;
  98.     //the original, returned from GetAttributesOf
  99.    
  100.     private int m_SortFlag;
  101.     //= 0 'Used in comparisons
  102.    
  103.     private ArrayList m_Directories;
  104.    
  105.     //The following elements are only filled in on demand
  106.     private bool m_XtrInfo;
  107.     //= False
  108.     private DateTime m_LastWriteTime;
  109.     private DateTime m_CreationTime;
  110.     private DateTime m_LastAccessTime;
  111.     private long m_Length;
  112.    
  113.     //Indicates whether DisplayName, TypeName, SortFlag have been set up
  114.     private bool m_HasDispType;
  115.     //= False
  116.    
  117.     //Indicates whether IsReadOnly has been set up
  118.     private bool m_IsReadOnlySetup;
  119.     //= False
  120.    
  121.     //Holds a byte() representation of m_PIDL -- filled when needed
  122.     private cPidl m_cPidl;
  123.    
  124.     //Flags for Dispose state
  125.     private bool m_Disposed;
  126.    
  127.     #endregion
  128.    
  129.     #region " Destructor"
  130.     /// <summary>
  131.     /// Summary of Dispose.
  132.     /// </summary>
  133.     ///
  134.     public void Dispose()
  135.     {
  136.         Dispose(true);
  137.         // Take yourself off of the finalization queue
  138.         // to prevent finalization code for this object
  139.         // from executing a second time.
  140.         GC.SuppressFinalize(this);
  141.     }
  142.     /// <summary>
  143.     /// Deallocates CoTaskMem contianing m_Pidl and removes reference to m_Folder
  144.     /// </summary>
  145.     /// <param name="disposing"></param>
  146.     ///
  147.     protected virtual void Dispose(bool disposing)
  148.     {
  149.         // Allow your Dispose method to be called multiple times,
  150.         // but throw an exception if the object has been disposed.
  151.         // Whenever you do something with this class,
  152.         // check to see if it has been disposed.
  153.         if (!(m_Disposed)) {
  154.             // If disposing equals true, dispose all managed
  155.             // and unmanaged resources.
  156.             m_Disposed = true;
  157.             if ((disposing)) {
  158.             }
  159.             // Release unmanaged resources. If disposing is false,
  160.             // only the following code is executed.
  161.             if ((m_Folder != null)) {
  162.                 Marshal.ReleaseComObject(m_Folder);
  163.             }
  164.             if (!m_Pidl.Equals(IntPtr.Zero)) {
  165.                 Marshal.FreeCoTaskMem(m_Pidl);
  166.             }
  167.         }
  168.         else {
  169.             throw new Exception("ShellItem Disposed more than once");
  170.         }
  171.     }
  172.    
  173.     // This Finalize method will run only if the
  174.     // Dispose method does not get called.
  175.     // By default, methods are NotOverridable.
  176.     // This prevents a derived class from overriding this method.
  177.     /// <summary>
  178.     /// Summary of Finalize.
  179.     /// </summary>
  180.     ///
  181.     ~ShellItem()
  182.     {
  183.         // Do not re-create Dispose clean-up code here.
  184.         // Calling Dispose(false) is optimal in terms of
  185.         // readability and maintainability.
  186.         Dispose(false);
  187.     }
  188.    
  189.     #endregion
  190.    
  191.     #region " Constructors"
  192.    
  193.     #region " Private Sub New(ByVal folder As ShellDll.IShellFolder, ByVal pidl As IntPtr, ByVal parent As ShellItem)"
  194.     /// <summary>
  195.     /// Private Constructor, creates new ShellItem from the item's parent folder and
  196.     /// the item's PIDL relative to that folder.</summary>
  197.     /// <param name="folder">the folder interface of the parent</param>
  198.     /// <param name="pidl">the Relative PIDL of this item</param>
  199.     /// <param name="parent">the CShitem of the parent</param>
  200.     ///
  201.     private ShellItem(ShellDll.IShellFolder folder, IntPtr pidl, ShellItem parent)
  202.     {
  203.         if ((DesktopBase == null)) {
  204.                 //This initializes the Desktop folder
  205.             DesktopBase = new ShellItem();
  206.         }
  207.         m_Parent = parent;
  208.         m_Pidl = concatPidls(parent.PIDL, pidl);
  209.        
  210.         //Get some attributes
  211.         SetUpAttributes(folder, pidl);
  212.        
  213.         //Set unfetched value for IconIndex....
  214.         m_IconIndexNormal = -1;
  215.         m_IconIndexOpen = -1;
  216.         //finally, set up my Folder
  217.         if (m_IsFolder) {
  218.             int HR = 0;
  219.             HR = folder.BindToObject(pidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  220.             if (HR != ShellDll.NOERROR) {
  221.                 Marshal.ThrowExceptionForHR(HR);
  222.             }
  223.         }
  224.     }
  225.     #endregion
  226.    
  227.     #region " Sub New()"
  228.     /// <summary>
  229.     /// Private Constructor. Creates ShellItem of the Desktop
  230.     /// </summary>
  231.     ///
  232.     private ShellItem()
  233.     {
  234.         //only used when desktopfolder has not been intialized
  235.         if ((DesktopBase != null)) {
  236.             throw new Exception("Attempt to initialize ShellItem for second time");
  237.         }
  238.        
  239.         int HR = 0;
  240.         //firstly determine what the local machine calls a "System Folder" and "My Computer"
  241.         IntPtr tmpPidl = default(IntPtr);
  242.         HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ShellDll.CSIDL.DRIVES, ref tmpPidl);
  243.         ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  244.         int dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
  245.         int dwAttr = 0;
  246. ShellDll.SHGetFileInfo(tmpPidl, dwAttr, ref shfi, (int)ShellDll.cbFileInfo, dwflag);
  247.         m_strSystemFolder = shfi.szTypeName;
  248.         m_strMyComputer = shfi.szDisplayName;
  249.         Marshal.FreeCoTaskMem(tmpPidl);
  250.         //set OS version info
  251.         XPorAbove = ShellDll.IsXpOrAbove();
  252.         Win2KOrAbove = ShellDll.Is2KOrAbove();
  253.        
  254.         //With That done, now set up Desktop ShellItem
  255. m_Path = "::{" + ShellDll.DesktopGUID.ToString() + "}";
  256.         m_IsFolder = true;
  257.         m_HasSubFolders = true;
  258.         m_IsBrowsable = false;
  259. HR = ShellDll.SHGetDesktopFolder(ref m_Folder);
  260. m_Pidl = ShellDll.GetSpecialFolderLocation(IntPtr.Zero, (int)ShellDll.CSIDL.DESKTOP);
  261.         dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.SYSICONINDEX | ShellDll.SHGFI.PIDL);
  262.         dwAttr = 0;
  263. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, (int)ShellDll.cbFileInfo, dwflag);
  264.         m_DisplayName = shfi.szDisplayName;
  265.         m_TypeName = strSystemFolder;
  266.         //not returned correctly by ShellDll.SHGetFileInfo
  267.         m_IconIndexNormal = shfi.iIcon;
  268.         m_IconIndexOpen = shfi.iIcon;
  269.         m_HasDispType = true;
  270.         m_IsDropTarget = true;
  271.         m_IsReadOnly = false;
  272.         m_IsReadOnlySetup = true;
  273.        
  274.         //also get local name for "My Documents"
  275.         int pchEaten = 0;
  276.         tmpPidl = IntPtr.Zero;
  277. int tmp = 0;
  278.         HR = m_Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref tmpPidl, ref tmp);
  279.         shfi = new ShellDll.SHFILEINFO();
  280.         dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
  281.         dwAttr = 0;
  282. ShellDll.SHGetFileInfo(tmpPidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  283.         m_strMyDocuments = shfi.szDisplayName;
  284.         Marshal.FreeCoTaskMem(tmpPidl);
  285.         //this must be done after getting "My Documents" string
  286.         m_SortFlag = ComputeSortFlag();
  287.         //Set DesktopBase
  288.         DesktopBase = this;
  289.         // Lastly, get the Path and ShellItem of the DesktopDirectory -- useful for DragDrop
  290.         m_DeskTopDirectory = new ShellItem(ShellDll.CSIDL.DESKTOPDIRECTORY);
  291.     }
  292.     #endregion
  293.    
  294.     #region " New(ByVal ID As ShellDll.CSIDL)"
  295.     /// <summary>Create instance based on a non-desktop ShellDll.CSIDL.
  296.     /// Will create based on any ShellDll.CSIDL Except the DeskTop ShellDll.CSIDL</summary>
  297.     /// <param name="ID">Value from ShellDll.CSIDL enumeration denoting the folder to create this ShellItem of.</param>
  298.     ///
  299.     public ShellItem(ShellDll.CSIDL ID)
  300.     {
  301.         if ((DesktopBase == null)) {
  302.                 //This initializes the Desktop folder
  303.             DesktopBase = new ShellItem();
  304.         }
  305.         int HR = 0;
  306.         if (ID == ShellDll.CSIDL.MYDOCUMENTS) {
  307.             int pchEaten = 0;
  308. int tmp = 0;
  309.             HR = DesktopBase.m_Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref m_Pidl, ref tmp);
  310.         }
  311.         else {
  312.             HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ID, ref m_Pidl);
  313.         }
  314.         if (HR == ShellDll.NOERROR) {
  315.             ShellDll.IShellFolder pParent = default(ShellDll.IShellFolder);
  316.             IntPtr relPidl = IntPtr.Zero;
  317. pParent = GetParentOf(m_Pidl, ref relPidl);
  318.             //Get the Attributes
  319.             SetUpAttributes(pParent, relPidl);
  320.             //Set unfetched value for IconIndex....
  321.             m_IconIndexNormal = -1;
  322.             m_IconIndexOpen = -1;
  323.             //finally, set up my Folder
  324.             if (m_IsFolder) {
  325.                 HR = pParent.BindToObject(relPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  326.                 if (HR != ShellDll.NOERROR) {
  327.                     Marshal.ThrowExceptionForHR(HR);
  328.                 }
  329.             }
  330.             Marshal.ReleaseComObject(pParent);
  331.             //if PidlCount(m_Pidl) = 1 then relPidl is same as m_Pidl, don't release
  332.             if (PidlCount(m_Pidl) > 1) Marshal.FreeCoTaskMem(relPidl);
  333.         }
  334.         else {
  335.             Marshal.ThrowExceptionForHR(HR);
  336.         }
  337.     }
  338.     #endregion
  339.    
  340.     #region " New(ByVal path As String)"
  341.     /// <summary>Create a new ShellItem based on a Path Must be a valid FileSystem Path</summary>
  342.     /// <param name="path"></param>
  343.     ///
  344.     public ShellItem(string path)
  345.     {
  346.         if ((DesktopBase == null)) {
  347.                 //This initializes the Desktop folder
  348.             DesktopBase = new ShellItem();
  349.         }
  350.         //Removal of following code allows Path(GUID) of Special FOlders to serve
  351.         // as a valid Path for ShellItem creation (part of Calum's refresh code needs this
  352.         //If Not Directory.Exists(path) AndAlso Not File.Exists(path) Then
  353.         // Throw New Exception("ShellItem -- Invalid Path specified")
  354.         //End If
  355.         int HR = 0;
  356. int tmp1 = 0, tmp2 = 0;
  357.         HR = DesktopBase.m_Folder.ParseDisplayName(0, IntPtr.Zero, path, ref tmp1, ref m_Pidl, ref tmp2);
  358.         if (!(HR == ShellDll.NOERROR)) Marshal.ThrowExceptionForHR(HR);
  359.         ShellDll.IShellFolder pParent = default(ShellDll.IShellFolder);
  360.         IntPtr relPidl = IntPtr.Zero;
  361.        
  362.         pParent = GetParentOf(m_Pidl, ref relPidl);
  363.        
  364.         //Get the Attributes
  365.         SetUpAttributes(pParent, relPidl);
  366.         //Set unfetched value for IconIndex....
  367.         m_IconIndexNormal = -1;
  368.         m_IconIndexOpen = -1;
  369.         //finally, set up my Folder
  370.         if (m_IsFolder) {
  371.             HR = pParent.BindToObject(relPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  372.             if (HR != ShellDll.NOERROR) {
  373.                 Marshal.ThrowExceptionForHR(HR);
  374.             }
  375.         }
  376.         Marshal.ReleaseComObject(pParent);
  377.         //if PidlCount(m_Pidl) = 1 then relPidl is same as m_Pidl, don't release
  378.         if (PidlCount(m_Pidl) > 1) {
  379.             Marshal.FreeCoTaskMem(relPidl);
  380.         }
  381.     }
  382.     #endregion
  383. #region " ShellItem(byte[] FoldBytes, byte[] ItemBytes)"
  384. ///<Summary>Given a Byte() containing the Pidl of the parent
  385. /// folder and another Byte() containing the Pidl of the Item,
  386. /// relative to the Folder, Create a ShellItem for the Item.
  387. /// This is of primary use in dealing with "Shell IDList Array"
  388. /// formatted info passed in a Drag Operation
  389. /// </Summary>
  390. public ShellItem(byte[] FoldBytes, byte[] ItemBytes)
  391. {
  392.         if ((DesktopBase == null)) {
  393.                 //This initializes the Desktop folder
  394.             DesktopBase = new ShellItem();
  395.         }
  396.         ShellDll.IShellFolder pParent = MakeFolderFromBytes(FoldBytes);
  397.        IntPtr ipParent = cPidl.BytesToPidl(FoldBytes);
  398.         IntPtr ipItem = cPidl.BytesToPidl(ItemBytes);
  399.       if ((pParent == null)) {
  400.                 //m_PIDL will = IntPtr.Zero for really bad CShitem
  401.             goto XIT;
  402.         }
  403.         if (ipParent.Equals(IntPtr.Zero) | ipItem.Equals(IntPtr.Zero)) {
  404.             goto XIT;
  405.         }
  406.         // Now process just like sub new(folder,pidl,parent) version
  407.         m_Pidl = concatPidls(ipParent, ipItem);
  408.        
  409.         //Get some attributes
  410.         SetUpAttributes(pParent, ipItem);
  411.        
  412.         //Set unfetched value for IconIndex....
  413.         m_IconIndexNormal = -1;
  414.         m_IconIndexOpen = -1;
  415.         //finally, set up my Folder
  416.         if (m_IsFolder) {
  417.             int HR = 0;
  418.             HR = pParent.BindToObject(ipItem, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  419.             #if Debug
  420.             if (HR != NOERROR) {
  421.                 Marshal.ThrowExceptionForHR(HR);
  422.             }
  423.             #endif
  424.         }
  425.         XIT:
  426.         //On any kind of exit, free the allocated memory
  427.         #if Debug
  428.         if (m_Pidl.Equals(IntPtr.Zero)) {
  429.             Debug.WriteLine("CShItem.New(FoldBytes,ItemBytes) Failed");
  430.         }
  431.         else {
  432.             Debug.WriteLine("CShItem.New(FoldBytes,ItemBytes) Created " + this.Path);
  433.         }
  434.         #endif
  435.         if (!ipParent.Equals(IntPtr.Zero)) {
  436.             Marshal.FreeCoTaskMem(ipParent);
  437.         }
  438.         if (!ipItem.Equals(IntPtr.Zero)) {
  439.             Marshal.FreeCoTaskMem(ipItem);
  440.         }
  441. }
  442. #endregion
  443.    
  444.     #region " Utility functions used in Constructors"
  445.    
  446.     #region " IsValidPidl"
  447.     ///<Summary>It is impossible to validate a PIDL completely since its contents
  448.     /// are arbitrarily defined by the creating Shell Namespace. However, it
  449.     /// is possible to validate the structure of a PIDL.</Summary>
  450.     public static bool IsValidPidl(byte[] b)
  451.     {
  452.         bool IsValidPidl = false;
  453.         //assume failure
  454.         int bMax = b.Length - 1;
  455.         //max value that index can have
  456. if (bMax < 1) return IsValidPidl;
  457.         //min Size is 2 bytes
  458.         int cb = b[0] + (b[1] * 256);
  459.         int indx = 0;
  460.         while (cb > 0) {
  461. if ((indx + cb + 1) > bMax) return IsValidPidl;
  462.             //an error
  463.             indx += cb;
  464.             cb = b[indx] + (b[indx + 1] * 256);
  465.         }
  466.             // on fall thru, it is ok as far as we can check
  467.         return true;
  468.     }
  469.     #endregion
  470.    
  471.     #region " MakeFolderFromBytes"
  472.     public static ShellDll.IShellFolder MakeFolderFromBytes(byte[] b)
  473.     {
  474.         ShellDll.IShellFolder functionReturnValue = default(ShellDll.IShellFolder);
  475.         functionReturnValue = null;
  476.         //get rid of VS2005 warning
  477.         if (!IsValidPidl(b)) return null;
  478.         if (b.Length == 2 && ((b[0] == 0) & (b[1] == 0))) {
  479.             //this is the desktop
  480.             return DesktopBase.Folder;
  481.         }
  482.         else if (b.Length == 0) {
  483.             //Also indicates the desktop
  484.             return DesktopBase.Folder;
  485.         }
  486.         else {
  487.             IntPtr ptr = Marshal.AllocCoTaskMem(b.Length);
  488.             if (ptr.Equals(IntPtr.Zero)) return null;
  489.             Marshal.Copy(b, 0, ptr, b.Length);
  490.             //the next statement assigns a IshellFolder object to the function return, or has an error
  491. int hr = DesktopBase.Folder.BindToObject(ptr, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref functionReturnValue);
  492.             if (hr != 0) functionReturnValue = null;
  493.             Marshal.FreeCoTaskMem(ptr);
  494.         }
  495.         return functionReturnValue;
  496.     }
  497.     #endregion
  498.    
  499.     #region " GetParentOf"
  500.    
  501.     ///<Summary>Returns both the ShellDll.IShellFolder interface of the parent folder
  502.     /// and the relative pidl of the input PIDL</Summary>
  503.     ///<remarks>Several internal functions need this information and do not have
  504.     /// it readily available. GetParentOf serves those functions</remarks>
  505.     private static ShellDll.IShellFolder GetParentOf(IntPtr pidl, ref IntPtr relPidl)
  506.     {
  507.         ShellDll.IShellFolder functionReturnValue = default(ShellDll.IShellFolder);
  508.         functionReturnValue = null;
  509.         //avoid VB2005 warning
  510.         int HR = 0;
  511.         int itemCnt = PidlCount(pidl);
  512.         if (itemCnt == 1) {
  513.             //parent is desktop
  514. HR = ShellDll.SHGetDesktopFolder(ref functionReturnValue);
  515.             relPidl = pidl;
  516.         }
  517.         else {
  518.             IntPtr tmpPidl = default(IntPtr);
  519.             tmpPidl = TrimPidl(pidl, ref relPidl);
  520.             HR = DesktopBase.m_Folder.BindToObject(tmpPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref functionReturnValue);
  521.             Marshal.FreeCoTaskMem(tmpPidl);
  522.         }
  523.         if (!(HR == ShellDll.NOERROR)) Marshal.ThrowExceptionForHR(HR);
  524.         return functionReturnValue;
  525.     }
  526.     #endregion
  527.    
  528.     #region " SetUpAttributes"
  529.     /// <summary>Get the base attributes of the folder/file that this ShellItem represents</summary>
  530.     /// <param name="folder">Parent Folder of this Item</param>
  531.     /// <param name="pidl">Relative Pidl of this Item.</param>
  532.     ///
  533.     private void SetUpAttributes(ShellDll.IShellFolder folder, IntPtr pidl)
  534.     {
  535.         ShellDll.SFGAO attrFlag = default(ShellDll.SFGAO);
  536.         attrFlag = ShellDll.SFGAO.BROWSABLE;
  537.         attrFlag = attrFlag | ShellDll.SFGAO.FILESYSTEM;
  538.         attrFlag = attrFlag | ShellDll.SFGAO.HASSUBFOLDER;
  539.         attrFlag = attrFlag | ShellDll.SFGAO.FOLDER;
  540.         attrFlag = attrFlag | ShellDll.SFGAO.LINK;
  541.         attrFlag = attrFlag | ShellDll.SFGAO.SHARE;
  542.         attrFlag = attrFlag | ShellDll.SFGAO.HIDDEN;
  543.         attrFlag = attrFlag | ShellDll.SFGAO.REMOVABLE;
  544.         //attrFlag = attrFlag Or ShellDll.SFGAO.RDONLY 'made into an on-demand attribute
  545.         attrFlag = attrFlag | ShellDll.SFGAO.CANCOPY;
  546.         attrFlag = attrFlag | ShellDll.SFGAO.CANDELETE;
  547.         attrFlag = attrFlag | ShellDll.SFGAO.CANLINK;
  548.         attrFlag = attrFlag | ShellDll.SFGAO.CANMOVE;
  549.         attrFlag = attrFlag | ShellDll.SFGAO.DROPTARGET;
  550.         //Note: for GetAttributesOf, we must provide an array, in all cases with 1 element
  551.         IntPtr[] aPidl = new IntPtr[1];
  552.         aPidl[0] = pidl;
  553.         folder.GetAttributesOf(1, aPidl, ref attrFlag);
  554.         m_Attributes = attrFlag;
  555.         m_IsBrowsable = ((attrFlag & ShellDll.SFGAO.BROWSABLE) != 0);
  556.         m_IsFileSystem = ((attrFlag & ShellDll.SFGAO.FILESYSTEM) != 0);
  557.         m_HasSubFolders = ((attrFlag & ShellDll.SFGAO.HASSUBFOLDER) != 0);
  558.         m_IsFolder = ((attrFlag & ShellDll.SFGAO.FOLDER) != 0);
  559.         m_IsLink = ((attrFlag & ShellDll.SFGAO.LINK) != 0);
  560.         m_IsShared = ((attrFlag & ShellDll.SFGAO.SHARE) != 0);
  561.         m_IsHidden = ((attrFlag & ShellDll.SFGAO.HIDDEN) != 0);
  562.         m_IsRemovable = ((attrFlag & ShellDll.SFGAO.REMOVABLE) != 0);
  563.         //m_IsReadOnly = CBool(attrFlag And ShellDll.SFGAO.RDONLY) 'made into an on-demand attribute
  564.         m_CanCopy = ((attrFlag & ShellDll.SFGAO.CANCOPY) != 0);
  565.         m_CanDelete = ((attrFlag & ShellDll.SFGAO.CANDELETE) != 0);
  566.         m_CanLink = ((attrFlag & ShellDll.SFGAO.CANLINK) != 0);
  567.         m_CanMove = ((attrFlag & ShellDll.SFGAO.CANMOVE) != 0);
  568. m_IsDropTarget = ((attrFlag & ShellDll.SFGAO.DROPTARGET) != 0);
  569.        
  570.         //Get the Path
  571.         IntPtr strr = Marshal.AllocCoTaskMem(ShellDll.MAX_PATH * 2 + 4);
  572.         Marshal.WriteInt32(strr, 0, 0);
  573.         StringBuilder buf = new StringBuilder(ShellDll.MAX_PATH);
  574.         ShellDll.SHGDN itemflags = ShellDll.SHGDN.FORPARSING;
  575.         folder.GetDisplayNameOf(pidl, itemflags, strr);
  576.         int HR = ShellDll.StrRetToBuf(strr, pidl, buf, ShellDll.MAX_PATH);
  577.         Marshal.FreeCoTaskMem(strr);
  578.         //now done with it
  579.         if (HR == ShellDll.NOERROR) {
  580.             m_Path = buf.ToString();
  581.             //check for zip file = folder on xp, leave it a file
  582.             if (m_IsFolder && m_IsFileSystem && XPorAbove) {
  583.                 //Note:meaning of ShellDll.SFGAO.STREAM changed between win2k and winXP
  584.                 //Version 20 code
  585.                 //If File.Exists(m_Path) Then
  586.                 // m_IsFolder = False
  587.                 //End If
  588.                 //Version 21 code
  589.                 aPidl[0] = pidl;
  590.                 attrFlag = ShellDll.SFGAO.STREAM;
  591.                 folder.GetAttributesOf(1, aPidl, ref attrFlag);
  592.                 if ((attrFlag & ShellDll.SFGAO.STREAM) != 0) {
  593.                     m_IsFolder = false;
  594.                 }
  595.             }
  596.             if (m_Path.Length == 3 && m_Path.Substring(1).Equals(":\\")) {
  597.                 m_IsDisk = true;
  598.             }
  599.         }
  600.         else {
  601.             Marshal.ThrowExceptionForHR(HR);
  602.         }
  603.     }
  604.    
  605.     #endregion
  606.    
  607.     #endregion
  608.    
  609.     #region " Public Shared Function GetCShItem(ByVal path As String) As ShellItem"
  610.     public static ShellItem GetCShItem(string path)
  611.     {
  612.         ShellItem functionReturnValue = default(ShellItem);
  613.         functionReturnValue = null;
  614.         //assume failure
  615.         int HR = 0;
  616.         IntPtr tmpPidl = default(IntPtr);
  617. int tmp1 = 0, tmp2 = 0;
  618.         HR = GetDeskTop().Folder.ParseDisplayName(0, IntPtr.Zero, path, ref tmp1, ref tmpPidl, ref tmp2);
  619.         if (HR == 0) {
  620.             functionReturnValue = FindCShItem(tmpPidl);
  621. if ((functionReturnValue == null))
  622. {
  623.                 try {
  624.                     functionReturnValue = new ShellItem(path);
  625.                 }
  626.                 catch {
  627.                     functionReturnValue = null;
  628.                 }
  629.             }
  630.         }
  631.         if (!tmpPidl.Equals(IntPtr.Zero)) {
  632.             Marshal.FreeCoTaskMem(tmpPidl);
  633.         }
  634.         return functionReturnValue;
  635.     }
  636.     #endregion
  637.    
  638.     #region " Public Shared Function GetCShItem(ByVal ID As ShellDll.CSIDL) As ShellItem"
  639.     public static ShellItem GetCShItem(ShellDll.CSIDL ID)
  640.     {
  641.         ShellItem functionReturnValue = default(ShellItem);
  642.         functionReturnValue = null;
  643.         //avoid VB2005 Warning
  644.         if (ID == ShellDll.CSIDL.DESKTOP) {
  645.             return GetDeskTop();
  646.         }
  647.         int HR = 0;
  648.         IntPtr tmpPidl = default(IntPtr);
  649.         if (ID == ShellDll.CSIDL.MYDOCUMENTS) {
  650.             int pchEaten = 0;
  651. int tmp = 0;
  652.             HR = GetDeskTop().Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref tmpPidl, ref tmp);
  653.         }
  654.         else {
  655.             HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ID, ref tmpPidl);
  656.         }
  657.         if (HR == ShellDll.NOERROR) {
  658.             functionReturnValue = FindCShItem(tmpPidl);
  659. if ((functionReturnValue == null))
  660. {
  661.                 try {
  662.                     functionReturnValue = new ShellItem(ID);
  663.                 }
  664.                 catch {
  665.                     functionReturnValue = null;
  666.                 }
  667.             }
  668.         }
  669.         if (!tmpPidl.Equals(IntPtr.Zero)) {
  670.             Marshal.FreeCoTaskMem(tmpPidl);
  671.         }
  672.         return functionReturnValue;
  673.     }
  674.     #endregion
  675.    
  676.     #region " Public Shared Function GetCShItem(ByVal FoldBytes() As Byte, ByVal ItemBytes() As Byte) As ShellItem"
  677.     public static ShellItem GetCShItem(byte[] FoldBytes, byte[] ItemBytes)
  678.     {
  679.         ShellItem functionReturnValue = default(ShellItem);
  680.         functionReturnValue = null;
  681.         //assume failure
  682.         byte[] b = cPidl.JoinPidlBytes(FoldBytes, ItemBytes);
  683.         if ((b == null)) return functionReturnValue;
  684.         //can do no more with invalid pidls
  685.         //otherwise do like below, skipping unnecessary validation check
  686.         IntPtr thisPidl = Marshal.AllocCoTaskMem(b.Length);
  687.         if (thisPidl.Equals(IntPtr.Zero)) return null;
  688.         Marshal.Copy(b, 0, thisPidl, b.Length);
  689.         functionReturnValue = FindCShItem(thisPidl);
  690.         Marshal.FreeCoTaskMem(thisPidl);
  691. if ((functionReturnValue == null))
  692. {
  693.             //didn't find it, make new
  694.             try {
  695.                 functionReturnValue = new ShellItem(FoldBytes, ItemBytes);
  696.             }
  697.             catch {
  698.                
  699.             }
  700.         }
  701.         if (functionReturnValue.PIDL.Equals(IntPtr.Zero)) functionReturnValue = null;
  702.         return functionReturnValue;
  703.     }
  704.     #endregion
  705.    
  706.     #region " Public Shared Function FindCShItem(ByVal b() As Byte) As ShellItem"
  707.     public static ShellItem FindCShItem(byte[] b)
  708.     {
  709.         ShellItem functionReturnValue = default(ShellItem);
  710.         if (!IsValidPidl(b)) return null;
  711.         IntPtr thisPidl = Marshal.AllocCoTaskMem(b.Length);
  712.         if (thisPidl.Equals(IntPtr.Zero)) return null;
  713.         Marshal.Copy(b, 0, thisPidl, b.Length);
  714.         functionReturnValue = FindCShItem(thisPidl);
  715.         Marshal.FreeCoTaskMem(thisPidl);
  716.         return functionReturnValue;
  717.     }
  718.     #endregion
  719.    
  720.     #region " Public Shared Function FindCShItem(ByVal ptr As IntPtr) As ShellItem"
  721.     public static ShellItem FindCShItem(IntPtr ptr)
  722.     {
  723.         ShellItem functionReturnValue = default(ShellItem);
  724.         functionReturnValue = null;
  725.         //avoid VB2005 Warning
  726.         ShellItem BaseItem = ShellItem.GetDeskTop();
  727.         //ShellItem CSI = default(ShellItem);
  728.         bool FoundIt = false;
  729.         //True if we found item or an ancestor
  730.         while (!(FoundIt)) {
  731.             foreach (var obj in BaseItem.GetDirectories(true)) {
  732. ShellItem CSI = obj as ShellItem;
  733.                 if (IsAncestorOf(CSI.PIDL, ptr, false)) {
  734.                     if (ShellItem.IsEqual(CSI.PIDL, ptr)) {
  735.                         //we found the desired item
  736.                         return CSI;
  737.                     }
  738.                     else {
  739.                         BaseItem = CSI;
  740.                         FoundIt = true;
  741.                         break; // TODO: might not be correct. Was : Exit For
  742.                     }
  743.                 }
  744.             }
  745.             if (!FoundIt) return null;
  746.             //didn't find an ancestor
  747.             //The complication is that the desired item may not be a directory
  748.             if (!IsAncestorOf(BaseItem.PIDL, ptr, true)) {
  749.                 //Don't have immediate ancestor
  750.                     //go around again
  751.                 FoundIt = false;
  752.             }
  753.             else {
  754.                 foreach (var obj in BaseItem.GetItems()) {
  755. ShellItem CSI = obj as ShellItem;
  756. if (ShellItem.IsEqual(CSI.PIDL, ptr))
  757. {
  758.                         return CSI;
  759.                     }
  760.                 }
  761.                 //fall thru here means it doesn't exist or we can't find it because of funny PIDL from SHParseDisplayName
  762.                 return null;
  763.             }
  764.         }
  765.         return functionReturnValue;
  766.     }
  767.     #endregion
  768.    
  769.     #endregion
  770.    
  771.     #region " Icomparable -- for default Sorting"
  772.    
  773.     /// <summary>Computes the Sort key of this ShellItem, based on its attributes</summary>
  774.     ///
  775.     private int ComputeSortFlag()
  776.     {
  777.         int rVal = 0;
  778.         if (m_IsDisk) rVal = 0x100000;
  779.         if (m_TypeName.Equals(strSystemFolder)) {
  780.             if (!m_IsBrowsable) {
  781.                 rVal = rVal | 0x10000;
  782.                 if (m_strMyDocuments.Equals(m_DisplayName)) {
  783.                     rVal = rVal | 0x1;
  784.                 }
  785.             }
  786.             else {
  787.                 rVal = rVal | 0x1000;
  788.             }
  789.         }
  790.         if (m_IsFolder) rVal = rVal | 0x100;
  791.         return rVal;
  792.     }
  793.    
  794.     ///<Summary> CompareTo(obj as object)
  795.     /// Compares obj to this instance based on SortFlag-- obj must be a ShellItem</Summary>
  796.     ///<SortOrder> (low)Disks,non-browsable System Folders,
  797.     /// browsable System Folders,
  798.     /// Directories, Files, Nothing (high)</SortOrder>
  799.     public virtual int CompareTo(object obj)
  800.     {
  801.         ShellItem Other = obj as ShellItem;
  802.         if ((Other == null)) return 1;
  803.         //non-existant is always low
  804.         if (!m_HasDispType) SetDispType();
  805.         int cmp = Other.SortFlag - m_SortFlag;
  806.         //Note the reversal
  807.         if (cmp != 0) {
  808.             return cmp;
  809.         }
  810.         else {
  811.             if (m_IsDisk) {
  812.                 //implies that both are
  813.                 return string.Compare(m_Path, Other.Path);
  814.             }
  815.             else {
  816.                 return string.Compare(m_DisplayName, Other.DisplayName);
  817.             }
  818.         }
  819.     }
  820.     #endregion
  821.    
  822.     #region " Properties"
  823.    
  824.     #region " Shared Properties"
  825.     public static string strMyComputer {
  826.         get { return m_strMyComputer; }
  827.     }
  828.    
  829.     public static string strSystemFolder {
  830.         get { return m_strSystemFolder; }
  831.     }
  832.    
  833.     public static string DesktopDirectoryPath {
  834.         get { return m_DeskTopDirectory.Path; }
  835.     }
  836.    
  837.     #endregion
  838.    
  839.     #region " Normal Properties"
  840.     public IntPtr PIDL {
  841.         get { return m_Pidl; }
  842.     }
  843.    
  844.     public ShellDll.IShellFolder Folder {
  845.         get { return m_Folder; }
  846.     }
  847.    
  848.     public string Path {
  849.         get { return m_Path; }
  850.     }
  851.     public ShellItem Parent {
  852.         get { return m_Parent; }
  853.     }
  854.    
  855.     public ShellDll.SFGAO Attributes {
  856.         get { return m_Attributes; }
  857.     }
  858.     public bool IsBrowsable {
  859.         get { return m_IsBrowsable; }
  860.     }
  861.     public bool IsFileSystem {
  862.         get { return m_IsFileSystem; }
  863.     }
  864.     public bool IsFolder {
  865.         get { return m_IsFolder; }
  866.     }
  867.     public bool HasSubFolders {
  868.         get { return m_HasSubFolders; }
  869.     }
  870.     public bool IsDisk {
  871.         get { return m_IsDisk; }
  872.     }
  873.     public bool IsLink {
  874.         get { return m_IsLink; }
  875.     }
  876.     public bool IsShared {
  877.         get { return m_IsShared; }
  878.     }
  879.     public bool IsHidden {
  880.         get { return m_IsHidden; }
  881.     }
  882.     public bool IsRemovable {
  883.         get { return m_IsRemovable; }
  884.     }
  885.    
  886.     #region " Drag Ops Properties"
  887.     public bool CanMove {
  888.         get { return m_CanMove; }
  889.     }
  890.     public bool CanCopy {
  891.         get { return m_CanCopy; }
  892.     }
  893.     public bool CanDelete {
  894.         get { return m_CanDelete; }
  895.     }
  896.     public bool CanLink {
  897.         get { return m_CanLink; }
  898.     }
  899.     public bool IsDropTarget {
  900.         get { return m_IsDropTarget; }
  901.     }
  902.     #endregion
  903.    
  904.     #endregion
  905.    
  906.     #region " Filled on Demand Properties"
  907.    
  908.     #region " Filled based on m_HasDispType"
  909.     /// <summary>
  910.     /// Set DisplayName, TypeName, and SortFlag when actually needed
  911.     /// </summary>
  912.     ///
  913.     private void SetDispType()
  914.     {
  915.         //Get Displayname, TypeName
  916.         ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  917.         int dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
  918.         int dwAttr = 0;
  919.         if (m_IsFileSystem & !m_IsFolder) {
  920.             dwflag = dwflag | (int)ShellDll.SHGFI.USEFILEATTRIBUTES;
  921. dwAttr = (int)ShellDll.FILE_ATTRIBUTE_NORMAL;
  922.         }
  923. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  924.         m_DisplayName = shfi.szDisplayName;
  925.         m_TypeName = shfi.szTypeName;
  926.         //fix DisplayName
  927.         if (m_DisplayName.Equals("")) {
  928.             m_DisplayName = m_Path;
  929.         }
  930.         //Fix TypeName
  931.         //If m_IsFolder And m_TypeName.Equals("File") Then
  932.         // m_TypeName = "File Folder"
  933.         //End If
  934.         m_SortFlag = ComputeSortFlag();
  935.         m_HasDispType = true;
  936.     }
  937.    
  938.     public string DisplayName {
  939.         get {
  940.             if (!m_HasDispType) SetDispType();
  941.             return m_DisplayName;
  942.         }
  943.     }
  944.    
  945.     private int SortFlag {
  946.         get {
  947.             if (!m_HasDispType) SetDispType();
  948.             return m_SortFlag;
  949.         }
  950.     }
  951.    
  952.     public string TypeName {
  953.         get {
  954.             if (!m_HasDispType) SetDispType();
  955.             return m_TypeName;
  956.         }
  957.     }
  958.     #endregion
  959.    
  960.     #region " IconIndex properties"
  961.     public int IconIndexNormal {
  962.         get {
  963.             if (m_IconIndexNormal < 0) {
  964.                 if (!m_HasDispType) SetDispType();
  965.                 ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  966.                 int dwflag = (int)(ShellDll.SHGFI.PIDL | ShellDll.SHGFI.SYSICONINDEX);
  967.                 int dwAttr = 0;
  968.                 if (m_IsFileSystem & !m_IsFolder) {
  969.                     dwflag = dwflag | (int)ShellDll.SHGFI.USEFILEATTRIBUTES;
  970.                     dwAttr = (int)ShellDll.FILE_ATTRIBUTE_NORMAL;
  971.                 }
  972.                 IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  973.                 m_IconIndexNormal = shfi.iIcon;
  974.             }
  975.             return m_IconIndexNormal;
  976.         }
  977.     }
  978.     // IconIndexOpen is Filled on demand
  979.     public int IconIndexOpen {
  980.         get {
  981.             if (m_IconIndexOpen < 0) {
  982.                 if (!m_HasDispType) SetDispType();
  983.                 if (!m_IsDisk & m_IsFileSystem & m_IsFolder) {
  984.                     if (OpenFolderIconIndex < 0) {
  985.                         int dwflag = (int)(ShellDll.SHGFI.SYSICONINDEX | ShellDll.SHGFI.PIDL);
  986.                         ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  987.                         IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, 0, ref shfi, ShellDll.cbFileInfo, dwflag | (int)ShellDll.SHGFI.OPENICON);
  988.                         m_IconIndexOpen = shfi.iIcon;
  989.                     }
  990.                     else {
  991.                         //If m_TypeName.Equals("File Folder") Then
  992.                         // OpenFolderIconIndex = shfi.iIcon
  993.                         //End If
  994.                         m_IconIndexOpen = OpenFolderIconIndex;
  995.                     }
  996.                 }
  997.                 else {
  998.                     m_IconIndexOpen = m_IconIndexNormal;
  999.                 }
  1000.             }
  1001.             return m_IconIndexOpen;
  1002.         }
  1003.     }
  1004.     #endregion
  1005.    
  1006.     #region " FileInfo type Information"
  1007.    
  1008.     /// <summary>
  1009.     /// Obtains information available from FileInfo.
  1010.     /// </summary>
  1011.     ///
  1012.     private void FillDemandInfo()
  1013.     {
  1014.         if (m_IsDisk) {
  1015.             try {
  1016.                 //See if this is a network drive
  1017.                 //NoRoot = 1
  1018.                 //Removable = 2
  1019.                 //LocalDisk = 3
  1020.                 //Network = 4
  1021.                 //CD = 5
  1022.                 //RAMDrive = 6
  1023.                 System.Management.ManagementObject disk = new System.Management.ManagementObject("win32_logicaldisk.deviceid=\"" + m_Path.Substring(0, 2) + "\"");
  1024.                 m_Length = (long)(UInt64)disk["Size"];
  1025.                 if ((UInt32)disk["DriveType"] == 4) {
  1026.                     m_IsNetWorkDrive = true;
  1027.                 }
  1028.             }
  1029.             catch (Exception) {
  1030.                 //Disconnected Network Drives etc. will generate
  1031.                 //an error here, just assume that it is a network
  1032.                 //drive
  1033.                 m_IsNetWorkDrive = true;
  1034.             }
  1035.             finally {
  1036.                 m_XtrInfo = true;
  1037.             }
  1038.         }
  1039.         else if (!m_IsDisk & m_IsFileSystem & !m_IsFolder) {
  1040.             //in this case, it's a file
  1041.             if (File.Exists(m_Path)) {
  1042.                 FileInfo fi = new FileInfo(m_Path);
  1043.                 m_LastWriteTime = fi.LastWriteTime;
  1044.                 m_LastAccessTime = fi.LastAccessTime;
  1045.                 m_CreationTime = fi.CreationTime;
  1046.                 m_Length = fi.Length;
  1047.                 m_XtrInfo = true;
  1048.             }
  1049.         }
  1050.         else {
  1051.             if (m_IsFileSystem & m_IsFolder) {
  1052.                 if (Directory.Exists(m_Path)) {
  1053.                     DirectoryInfo di = new DirectoryInfo(m_Path);
  1054.                     m_LastWriteTime = di.LastWriteTime;
  1055.                     m_LastAccessTime = di.LastAccessTime;
  1056.                     m_CreationTime = di.CreationTime;
  1057.                     m_XtrInfo = true;
  1058.                 }
  1059.             }
  1060.         }
  1061.     }
  1062.    
  1063.     public DateTime LastWriteTime {
  1064.         get {
  1065.             if (!m_XtrInfo) {
  1066.                 FillDemandInfo();
  1067.             }
  1068.             return m_LastWriteTime;
  1069.         }
  1070.     }
  1071.     public DateTime LastAccessTime {
  1072.         get {
  1073.             if (!m_XtrInfo) {
  1074.                 FillDemandInfo();
  1075.             }
  1076.             return m_LastAccessTime;
  1077.         }
  1078.     }
  1079.     public DateTime CreationTime {
  1080.         get {
  1081.             if (!m_XtrInfo) {
  1082.                 FillDemandInfo();
  1083.             }
  1084.             return m_CreationTime;
  1085.         }
  1086.     }
  1087.     public long Length {
  1088.         get {
  1089.             if (!m_XtrInfo) {
  1090.                 FillDemandInfo();
  1091.             }
  1092.             return m_Length;
  1093.         }
  1094.     }
  1095.     public bool IsNetworkDrive {
  1096.         get {
  1097.             if (!m_XtrInfo) {
  1098.                 FillDemandInfo();
  1099.             }
  1100.             return m_IsNetWorkDrive;
  1101.         }
  1102.     }
  1103.     #endregion
  1104.    
  1105.     #region " cPidl information"
  1106.     public cPidl clsPidl {
  1107.         get {
  1108.             if ((m_cPidl == null)) {
  1109.                 m_cPidl = new cPidl(m_Pidl);
  1110.             }
  1111.             return m_cPidl;
  1112.         }
  1113.     }
  1114.     #endregion
  1115.    
  1116.     #region " IsReadOnly and IsSystem"
  1117.     ///<Summary>The IsReadOnly attribute causes an annoying access to any floppy drives
  1118.     /// on the system. To postpone this (or avoid, depending on user action),
  1119.     /// the attribute is only queried when asked for</Summary>
  1120.     public bool IsReadOnly {
  1121.         get {
  1122.             if (m_IsReadOnlySetup) {
  1123.                 return m_IsReadOnly;
  1124.             }
  1125.             else {
  1126.                 ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  1127.                 shfi.dwAttributes = (int)ShellDll.SFGAO.RDONLY;
  1128.                 int dwflag = (int)(ShellDll.SHGFI.PIDL | ShellDll.SHGFI.ATTRIBUTES | ShellDll.SHGFI.ATTR_SPECIFIED);
  1129.                 int dwAttr = 0;
  1130.                 IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  1131.                 if (H.ToInt32() != ShellDll.NOERROR && H.ToInt32() != 1) {
  1132.                     Marshal.ThrowExceptionForHR(H.ToInt32());
  1133.                 }
  1134.                 m_IsReadOnly = ((shfi.dwAttributes & (int)ShellDll.SFGAO.RDONLY) != 0);
  1135.                 //If m_IsReadOnly Then Debug.WriteLine("IsReadOnly -- " & m_Path)
  1136.                 m_IsReadOnlySetup = true;
  1137.                 return m_IsReadOnly;
  1138.             }
  1139.         }
  1140.         //If Not m_XtrInfo Then
  1141.         // FillDemandInfo()
  1142.         //End If
  1143.         //Return m_Attributes And FileAttributes.ReadOnly = FileAttributes.ReadOnly
  1144.     }
  1145.     ///<Summary>The IsSystem attribute is seldom used, but required by DragDrop operations.
  1146.     /// Since there is no way of getting ONLY the System attribute without getting
  1147.     /// the RO attribute (which forces a reference to the floppy drive), we pay
  1148.     /// the price of getting its own File/DirectoryInfo for this purpose alone.
  1149.     ///</Summary>
  1150.     public bool IsSystem {
  1151.         get {
  1152.             //true once we have gotten this attr
  1153.             //the value of this attr once we have it
  1154.             if (!static_IsSystem_HaveSysInfo) {
  1155.                 try {
  1156.                     static_IsSystem_m_IsSystem = (File.GetAttributes(m_Path) & FileAttributes.System) == FileAttributes.System;
  1157.                     static_IsSystem_HaveSysInfo = true;
  1158.                 }
  1159.                 catch (Exception) {
  1160.                     static_IsSystem_HaveSysInfo = true;
  1161.                 }
  1162.             }
  1163. Debug.WriteLine("In IsSystem -- Path = " + m_Path + " IsSystem = " + static_IsSystem_m_IsSystem);
  1164.             return static_IsSystem_m_IsSystem;
  1165.         }
  1166.     }
  1167.     static bool static_IsSystem_m_IsSystem = false;
  1168.     static bool static_IsSystem_HaveSysInfo = false;
  1169.    
  1170.     #endregion
  1171.    
  1172.     #endregion
  1173.    
  1174.     #endregion
  1175.    
  1176.     #region " Public Methods"
  1177.    
  1178.     #region " Shared Public Methods"
  1179.    
  1180.     #region " GetDeskTop"
  1181.     /// <summary>
  1182.     /// If not initialized, then build DesktopBase
  1183.     /// once done, or if initialized already,
  1184.     /// </summary>
  1185.     ///<returns>The DesktopBase ShellItem representing the desktop</returns>
  1186.     ///
  1187.     public static ShellItem GetDeskTop()
  1188.     {
  1189.         if ((DesktopBase == null)) {
  1190.             DesktopBase = new ShellItem();
  1191.         }
  1192.         return DesktopBase;
  1193.     }
  1194.     #endregion
  1195.    
  1196.     #region " IsAncestorOf"
  1197.     ///<Summary>IsAncestorOf returns True if ShellItem ancestor is an ancestor of ShellItem current
  1198.     /// if OS is Win2K or above, uses the ILIsParent API, otherwise uses the
  1199.     /// cPidl function StartsWith. This is necessary since ILIsParent in only available
  1200.     /// in Win2K or above systems AND StartsWith fails on some folders on XP systems (most
  1201.     /// obviously some Network Folder Shortcuts, but also Control Panel. Note, StartsWith
  1202.     /// always works on systems prior to XP.
  1203.     /// NOTE: if ancestor and current reference the same Item, both
  1204.     /// methods return True</Summary>
  1205.     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#
  1206. bool fParent)
  1207.     {
  1208.         return IsAncestorOf(ancestor.PIDL, current.PIDL, fParent);
  1209.     }
  1210.     ///<Summary> Compares a candidate Ancestor PIDL with a Child PIDL and
  1211.     /// returns True if Ancestor is an ancestor of the child.
  1212.     /// if fParent is True, then only return True if Ancestor is the immediate
  1213.     /// parent of the Child</Summary>
  1214.     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#
  1215. bool fParent)
  1216.     {
  1217.         bool functionReturnValue = false;
  1218.         if (ShellDll.Is2KOrAbove()) {
  1219.             return ShellDll.ILIsParent(AncestorPidl, ChildPidl, fParent);
  1220.         }
  1221.         else {
  1222.             cPidl Child = new cPidl(ChildPidl);
  1223.             cPidl Ancestor = new cPidl(AncestorPidl);
  1224.             functionReturnValue = Child.StartsWith(Ancestor);
  1225.             if (!functionReturnValue) return functionReturnValue;
  1226.             if (fParent) {
  1227.                 // check for immediate ancestor, if desired
  1228.                 object[] oAncBytes = Ancestor.Decompose();
  1229.                 object[] oChildBytes = Child.Decompose();
  1230.                 if (oAncBytes.Length != (oChildBytes.Length - 1)) {
  1231.                     functionReturnValue = false;
  1232.                 }
  1233.             }
  1234.         }
  1235.         return functionReturnValue;
  1236.     }
  1237.     #endregion
  1238.    
  1239.     #region " AllFolderWalk"
  1240.     ///<Summary>The WalkAllCallBack delegate defines the signature of
  1241.     /// the routine to be passed to DirWalker
  1242.     /// Usage: dim d1 as new CshItem.WalkAllCallBack(addressof yourroutine)
  1243.     /// Callback function receives a ShellItem for each file and Directory in
  1244.     /// Starting Directory and each sub-directory of this directory and
  1245.     /// each sub-dir of each sub-dir ....
  1246.     ///</Summary>
  1247.     public delegate bool WalkAllCallBack(ShellItem info, int UserLevel, int Tag);
  1248.     ///<Summary>
  1249.     /// AllFolderWalk recursively walks down directories from cStart, calling its
  1250.     /// callback routine, WalkAllCallBack, for each Directory and File encountered, including those in
  1251.     /// cStart. UserLevel is incremented by 1 for each level of dirs that DirWalker
  1252.     /// recurses thru. Tag in an Integer that is simply passed, unmodified to the
  1253.     /// callback, with each ShellItem encountered, both File and Directory CShItems.
  1254.     /// </Summary>
  1255.     /// <param name="cStart"></param>
  1256.     /// <param name="cback"></param>
  1257.     /// <param name="UserLevel"></param>
  1258.     /// <param name="Tag"></param>
  1259.     ///
  1260.     public static bool AllFolderWalk(ShellItem cStart, WalkAllCallBack cback, int UserLevel, int Tag)
  1261.     {
  1262.         if ((cStart != null) && cStart.IsFolder) {
  1263.             //ShellItem cItem = default(ShellItem);
  1264.             //first processes all files in this directory
  1265.             foreach (var obj in cStart.GetFiles()) {
  1266. ShellItem cItem = obj as ShellItem;
  1267.                 if (!cback(cItem, UserLevel, Tag)) {
  1268.                         //user said stop
  1269.                     return false;
  1270.                 }
  1271.             }
  1272.             //then process all dirs in this directory, recursively
  1273.             foreach (var obj in cStart.GetDirectories(true)) {
  1274. ShellItem cItem = obj as ShellItem;
  1275.                 if (!cback(cItem, UserLevel + 1, Tag)) {
  1276.                         //user said stop
  1277.                     return false;
  1278.                 }
  1279.                 else {
  1280.                     if (!AllFolderWalk(cItem, cback, UserLevel + 1, Tag)) {
  1281.                         return false;
  1282.                     }
  1283.                 }
  1284.             }
  1285.             return true;
  1286.         }
  1287.         else {
  1288.             //Invalid call
  1289.             throw new ApplicationException("AllFolderWalk -- Invalid Start Directory");
  1290.         }
  1291.     }
  1292.     #endregion
  1293.    
  1294.     #endregion
  1295.    
  1296.     #region " Public Instance Methods"
  1297.    
  1298.     #region " Equals"
  1299.     public bool Equals(ShellItem other)
  1300.     {
  1301.         return this.Path.Equals(other.Path);
  1302.     }
  1303.     #endregion
  1304.    
  1305.     #region " GetDirectories"
  1306.     /// <summary>
  1307.     /// Returns the Directories of this sub-folder as an ArrayList of CShitems
  1308.     /// </summary>
  1309.     /// <param name="doRefresh">Optional, default=True, Refresh the directories</param>
  1310.     /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1311.     /// <remarks>revised to alway return an up-to-date list unless
  1312.     /// specifically instructed not to (useful in constructs like:
  1313.     /// if CSI.RefreshDirectories then
  1314.     /// Dirs = CSI.GetDirectories(False) ' just did a Refresh </remarks>
  1315.     public ArrayList GetDirectories([System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(true)] // ERROR: Optional parameters aren't supported in C#
  1316. bool doRefresh)
  1317.     {
  1318.         if (m_IsFolder) {
  1319.             if (doRefresh) {
  1320.                     // return an up-to-date List
  1321.                 RefreshDirectories();
  1322.             }
  1323.             else if (m_Directories == null) {
  1324.                 RefreshDirectories();
  1325.             }
  1326.             return m_Directories;
  1327.         }
  1328.         else {
  1329.             //if it is not a Folder, then return empty arraylist
  1330.             return new ArrayList();
  1331.         }
  1332.     }
  1333.    
  1334.     #endregion
  1335.    
  1336.     #region " GetFiles"
  1337.     /// <summary>
  1338.     /// Returns the Files of this sub-folder as an
  1339.     /// ArrayList of CShitems
  1340.     /// Note: we do not keep the arraylist of files, Generate it each time
  1341.     /// </summary>
  1342.     /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1343.     ///
  1344.     public ArrayList GetFiles()
  1345.     {
  1346.         if (m_IsFolder) {
  1347.             return GetContents(ShellDll.SHCONTF.NONFOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
  1348.         }
  1349.         else {
  1350.             return new ArrayList();
  1351.         }
  1352.     }
  1353.    
  1354.     /// <summary>
  1355.     /// Returns the Files of this sub-folder, filtered by a filtering string, as an
  1356.     /// ArrayList of CShitems
  1357.     /// Note: we do not keep the arraylist of files, Generate it each time
  1358.     /// </summary>
  1359.     /// <param name="Filter">A filter string (for example: *.Doc)</param>
  1360.     /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1361.     ///
  1362.     public ArrayList GetFiles(string Filter)
  1363.     {
  1364.         if (m_IsFolder) {
  1365.             ArrayList dummy = new ArrayList();
  1366.             string[] fileentries = null;
  1367.             fileentries = Directory.GetFiles(m_Path, Filter);
  1368.             //string vFile = null;
  1369.             foreach (var obj in fileentries) {
  1370. string vFile = obj as string;
  1371.                 dummy.Add(new ShellItem(vFile));
  1372.             }
  1373.             return dummy;
  1374.         }
  1375.         else {
  1376.             return new ArrayList();
  1377.         }
  1378.     }
  1379.     #endregion
  1380.    
  1381.     #region " GetItems"
  1382.     /// <summary>
  1383.     /// Returns the Directories and Files of this sub-folder as an
  1384.     /// ArrayList of CShitems
  1385.     /// Note: we do not keep the arraylist of files, Generate it each time
  1386.     /// </summary>
  1387.     /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1388.     public ArrayList GetItems()
  1389.     {
  1390.         ArrayList rVal = new ArrayList();
  1391.         if (m_IsFolder) {
  1392.             rVal.AddRange(this.GetDirectories(true));
  1393.             rVal.AddRange(this.GetContents(ShellDll.SHCONTF.NONFOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false));
  1394.             rVal.Sort();
  1395.             return rVal;
  1396.         }
  1397.         else {
  1398.             return rVal;
  1399.         }
  1400.     }
  1401.     #endregion
  1402.    
  1403.     #region " GetFileName"
  1404.     ///<Summary>GetFileName returns the Full file name of this item.
  1405.     /// Specifically, for a link file (xxx.txt.lnk for example) the
  1406.     /// DisplayName property will return xxx.txt, this method will
  1407.     /// return xxx.txt.lnk. In most cases this is equivalent of
  1408.     /// System.IO.Path.GetFileName(m_Path). However, some m_Paths
  1409.     /// actually are GUIDs. In that case, this routine returns the
  1410.     /// DisplayName</Summary>
  1411.     public string GetFileName()
  1412.     {
  1413.         if (m_Path.StartsWith("::{")) {
  1414.             //Path is really a GUID
  1415.             return this.DisplayName;
  1416.         }
  1417.         else {
  1418.             if (m_IsDisk) {
  1419.                 return m_Path.Substring(0, 1);
  1420.             }
  1421.             else {
  1422.                 return System.IO.Path.GetFileName(m_Path);
  1423.             }
  1424.         }
  1425.     }
  1426.     #endregion
  1427.    
  1428.     #region " ReFreshDirectories"
  1429.     ///<Summary> A lower cost way of Refreshing the Directories of this ShellItem</Summary>
  1430.     ///<returns> Returns True if there were any changes</returns>
  1431.     public bool RefreshDirectories()
  1432.     {
  1433.         bool functionReturnValue = false;
  1434.         functionReturnValue = false;
  1435.         //value unless there were changes
  1436.         if (m_IsFolder) {
  1437.             //if not a folder, then return false
  1438.             ArrayList InvalidDirs = new ArrayList();
  1439.             //holds CShItems of not found dirs
  1440.             if ((m_Directories == null)) {
  1441.                 m_Directories = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
  1442.                     //changed from unexamined to examined
  1443.                 functionReturnValue = true;
  1444.             }
  1445.             else {
  1446.                 //Get relative PIDLs from current directory items
  1447.                 ArrayList curPidls = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, true);
  1448.                 //IntPtr iptr = default(IntPtr);
  1449.                 //used below
  1450.                 if (curPidls.Count < 1) {
  1451.                     if (m_Directories.Count > 0) {
  1452.                         m_Directories = new ArrayList();
  1453.                         //nothing there anymore
  1454.                             //Changed from had some to have none
  1455.                         functionReturnValue = true;
  1456.                     }
  1457.                     else {
  1458.                         //Empty before, Empty now, do nothing -- just a logic marker
  1459.                     }
  1460.                 }
  1461.                 else {
  1462.                     //still has some. Are they the same?
  1463.                     if (m_Directories.Count < 1) {
  1464.                         //didn't have any before, so different
  1465.                         m_Directories = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
  1466.                             //changed from had none to have some
  1467.                         functionReturnValue = true;
  1468.                     }
  1469.                     else {
  1470.                         //some before, some now. Same? This is the complicated part
  1471.                         //Firstly, build ArrayLists of Relative Pidls
  1472.                         ArrayList compList = new ArrayList(curPidls);
  1473.                         //Since we are only comparing relative PIDLs, build a list of
  1474.                         // the relative PIDLs of the old content -- saving repeated building
  1475.                         int iOld = 0;
  1476.                         IntPtr[] OldRel = new IntPtr[m_Directories.Count];
  1477.                         for (iOld = 0; iOld <= m_Directories.Count - 1; iOld++) {
  1478.                             //GetLastID returns a ptr into an EXISTING IDLIST -- never release that ptr
  1479.                             // and never release the EXISTING IDLIST before thru with OldRel
  1480.                             OldRel[iOld] = GetLastID(((ShellItem)m_Directories[iOld]).PIDL);
  1481.                         }
  1482.                         int iNew = 0;
  1483.                         for (iOld = 0; iOld <= m_Directories.Count - 1; iOld++) {
  1484.                             for (iNew = 0; iNew <= compList.Count - 1; iNew++) {
  1485.                                 if (IsEqual((IntPtr)compList[iNew], OldRel[iOld])) {
  1486.                                     compList.RemoveAt(iNew);
  1487.                                     //Match, don't look at this one again
  1488.                                         //content item exists in both
  1489.                                     goto NXTOLD;
  1490.                                 }
  1491.                             }
  1492.                             //falling thru here means couldn't find iOld entry
  1493.                             InvalidDirs.Add(m_Directories[iOld]);
  1494.                             //save off the unmatched ShellItem
  1495.                             functionReturnValue = true;
  1496. NXTOLD: functionReturnValue = !!functionReturnValue;
  1497.                         }
  1498.                         //any not found should be removed from m_Directories
  1499.                         //ShellItem csi = default(ShellItem);
  1500.                         foreach (var obj in InvalidDirs) {
  1501. ShellItem csi = obj as ShellItem;
  1502.                             m_Directories.Remove(csi);
  1503.                         }
  1504.                         //anything remaining in compList is a new entry
  1505.                         if (compList.Count > 0) {
  1506.                             functionReturnValue = true;
  1507.                             foreach (var obj in compList) {
  1508. IntPtr iptr = (IntPtr)obj;
  1509.                                 //these are relative PIDLs
  1510.                                 m_Directories.Add(new ShellItem(m_Folder, iptr, this));
  1511.                             }
  1512.                         }
  1513.                         if (functionReturnValue) {
  1514.                             //something added or removed, resort
  1515.                             m_Directories.Sort();
  1516.                         }
  1517.                     }
  1518.                     //we obtained some new relative PIDLs in curPidls, so free them
  1519.                     foreach (var obj in curPidls) {
  1520. IntPtr iptr = (IntPtr)obj;
  1521. Marshal.FreeCoTaskMem(iptr);
  1522.                     }
  1523.                     //end of content comparison
  1524.                 }
  1525.                 //end of IsNothing test
  1526.             }
  1527.         }
  1528.             //end of IsFolder test
  1529.         return functionReturnValue;
  1530.     }
  1531.    
  1532.     #endregion
  1533.    
  1534.     #region " ToString"
  1535.     /// <summary>
  1536.     /// Returns the DisplayName as the normal ToString value
  1537.     /// </summary>
  1538.     ///
  1539.     public override string ToString()
  1540.     {
  1541.         return m_DisplayName;
  1542.     }
  1543.     #endregion
  1544.    
  1545.     #region " Debug Dumper"
  1546.     /// <summary>
  1547.     /// Summary of DebugDump.
  1548.     /// </summary>
  1549.     ///
  1550.     public void DebugDump()
  1551.     {
  1552.         Debug.WriteLine("DisplayName = " + m_DisplayName);
  1553.         Debug.WriteLine("PIDL = " + m_Pidl.ToString());
  1554.         Debug.WriteLine("\tPath = " + m_Path);
  1555. Debug.WriteLine("\tTypeName = " + this.TypeName);
  1556. Debug.WriteLine("\tiIconNormal = " + m_IconIndexNormal);
  1557. Debug.WriteLine("\tiIconSelect = " + m_IconIndexOpen);
  1558. Debug.WriteLine("\tIsBrowsable = " + m_IsBrowsable);
  1559. Debug.WriteLine("\tIsFileSystem= " + m_IsFileSystem);
  1560. Debug.WriteLine("\tIsFolder = " + m_IsFolder);
  1561. Debug.WriteLine("\tIsLink = " + m_IsLink);
  1562. Debug.WriteLine("\tIsDropTarget = " + m_IsDropTarget);
  1563. Debug.WriteLine("\tIsReadOnly = " + this.IsReadOnly);
  1564. Debug.WriteLine("\tCanCopy = " + this.CanCopy);
  1565. Debug.WriteLine("\tCanLink = " + this.CanLink);
  1566. Debug.WriteLine("\tCanMove = " + this.CanMove);
  1567. Debug.WriteLine("\tCanDelete = " + this.CanDelete);
  1568.         if (m_IsFolder) {
  1569.             if ((m_Directories != null)) {
  1570.                 Debug.WriteLine("\tDirectory Count = " + m_Directories.Count);
  1571.             }
  1572.             else {
  1573.                 Debug.WriteLine("\tDirectory Count Not yet set");
  1574.             }
  1575.         }
  1576.     }
  1577.     #endregion
  1578.    
  1579.     #region " GetDropTargetOf"
  1580.     public IDropTarget GetDropTargetOf(Control tn)
  1581.     {
  1582. //if ((m_Folder == null)) return null;
  1583. //IntPtr[] apidl = new IntPtr[1];
  1584. //int HR = 0;
  1585.         IDropTarget theInterface = null;
  1586. //IntPtr tnH = tn.Handle;
  1587. //HR = m_Folder.CreateViewObject(tnH, ref ShellDll.IID_IDropTarget, ref theInterface);
  1588. //if (HR != 0) {
  1589. // Marshal.ThrowExceptionForHR(HR);
  1590. //}
  1591.         return theInterface;
  1592.     }
  1593.     #endregion
  1594.    
  1595.     #endregion
  1596.    
  1597.     #endregion
  1598.    
  1599.     #region " Private Instance Methods"
  1600.    
  1601.     #region " GetContents"
  1602.     ///<Summary>
  1603.     /// Returns the requested Items of this Folder as an ArrayList of CShitems
  1604.     /// unless the IntPtrOnly flag is set. If IntPtrOnly is True, return an
  1605.     /// ArrayList of PIDLs.
  1606.     ///</Summary>
  1607.     /// <param name="flags">A set of one or more ShellDll.SHCONTF flags indicating which items to return</param>
  1608.     /// <param name="IntPtrOnly">True to suppress generation of CShItems, returning only an
  1609.     /// ArrayList of IntPtrs to RELATIVE PIDLs for the contents of this Folder</param>
  1610.     private ArrayList GetContents(ShellDll.SHCONTF flags, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
  1611. bool IntPtrOnly)
  1612.     {
  1613.         ArrayList rVal = new ArrayList();
  1614.         int HR = 0;
  1615. ShellDll.IEnumIDList IEnum = null;
  1616.         HR = m_Folder.EnumObjects(0, flags, ref IEnum);
  1617.         if (HR == ShellDll.NOERROR) {
  1618.             IntPtr item = IntPtr.Zero;
  1619.             int itemCnt = 0;
  1620.             HR = IEnum.GetNext(1, ref item, ref itemCnt);
  1621.             if (HR == ShellDll.NOERROR) {
  1622.                 while (itemCnt > 0 && !item.Equals(IntPtr.Zero)) {
  1623.                     //there is no setting to exclude folders from enumeration,
  1624.                     // just one to include non-folders
  1625.                     // so we have to screen the results to return only
  1626.                     // non-folders if folders are not wanted
  1627.                     if ((flags & ShellDll.SHCONTF.FOLDERS) == 0) {
  1628.                         //don't want folders. see if this is one
  1629.                         ShellDll.SFGAO attrFlag = default(ShellDll.SFGAO);
  1630.                         attrFlag = attrFlag | ShellDll.SFGAO.FOLDER | ShellDll.SFGAO.STREAM;
  1631.                         //Note: for GetAttributesOf, we must provide an array, in all cases with 1 element
  1632.                         IntPtr[] aPidl = new IntPtr[1];
  1633.                         aPidl[0] = item;
  1634.                         m_Folder.GetAttributesOf(1, aPidl, ref attrFlag);
  1635.                         if (!XPorAbove) {
  1636.                             if ((attrFlag & ShellDll.SFGAO.FOLDER) != 0) {
  1637.                                 //Don't need it
  1638.                                 goto SKIPONE;
  1639.                             }
  1640.                         }
  1641.                         else {
  1642.                             //XP or above
  1643.                             if ((attrFlag & ShellDll.SFGAO.FOLDER) != 0 && (attrFlag & ShellDll.SFGAO.STREAM) == 0) {
  1644.                                 goto SKIPONE;
  1645.                             }
  1646.                         }
  1647.                     }
  1648.                     if (IntPtrOnly) {
  1649.                         //just relative pidls for fast look, no CShITem overhead
  1650.                             //caller must free
  1651.                         rVal.Add(PIDLClone(item));
  1652.                     }
  1653.                     else {
  1654.                         rVal.Add(new ShellItem(m_Folder, item, this));
  1655.                     }
  1656.                     SKIPONE:
  1657.                     Marshal.FreeCoTaskMem(item);
  1658.                     //if New kept it, it kept a copy
  1659.                     item = IntPtr.Zero;
  1660.                     itemCnt = 0;
  1661.                     // Application.DoEvents()
  1662.                     HR = IEnum.GetNext(1, ref item, ref itemCnt);
  1663.                 }
  1664.             }
  1665.             else {
  1666.                     //1 means no more
  1667.                 if (HR != 1) goto HRError;
  1668.             }
  1669.         }
  1670.         else {
  1671.             goto HRError;
  1672.         }
  1673.         NORMAL:
  1674.         //Normal Exit
  1675.         if ((IEnum != null)) {
  1676.             Marshal.ReleaseComObject(IEnum);
  1677.         }
  1678.         rVal.TrimToSize();
  1679.         return rVal;
  1680.         HRError:
  1681.        
  1682.         // Error Exit for all Com errors
  1683.         //not ready disks will return the following error
  1684.         //If HR = &HFFFFFFFF800704C7 Then
  1685.         // GoTo NORMAL
  1686.         //ElseIf HR = &HFFFFFFFF80070015 Then
  1687.         // GoTo NORMAL
  1688.         // 'unavailable net resources will return these
  1689.         //ElseIf HR = &HFFFFFFFF80040E96 Or HR = &HFFFFFFFF80040E19 Then
  1690.         // GoTo NORMAL
  1691.         //ElseIf HR = &HFFFFFFFF80004001 Then 'Certain "Not Implemented" features will return this
  1692.         // GoTo NORMAL
  1693.         //ElseIf HR = &HFFFFFFFF80004005 Then
  1694.         // GoTo NORMAL
  1695.         //ElseIf HR = &HFFFFFFFF800704C6 Then
  1696.         // GoTo NORMAL
  1697.         //End If
  1698.         if ((IEnum != null)) Marshal.ReleaseComObject(IEnum);
  1699.         //#If Debug Then
  1700.         // Marshal.ThrowExceptionForHR(HR)
  1701.         //#End If
  1702.         rVal = new ArrayList();
  1703.         //sometimes it is a non-fatal error,ignored
  1704.         goto NORMAL;
  1705.     }
  1706.     #endregion
  1707.    
  1708.     #region " Really nasty Pidl manipulation"
  1709.    
  1710.     /// <Summary>
  1711.     /// Get FileSize in bytes of the first (possibly only)
  1712.     /// SHItem in an ID list. Note: the full Size of
  1713.     /// the item is the sum of the sizes of all SHItems
  1714.     /// in the list!!
  1715.     /// </Summary>
  1716.     /// <param name="pidl"></param>
  1717.     ///
  1718.     private static int ItemIDSize(IntPtr pidl)
  1719.     {
  1720.         if (!pidl.Equals(IntPtr.Zero)) {
  1721.             byte[] b = new byte[2];
  1722.             Marshal.Copy(pidl, b, 0, 2);
  1723.             return b[1] * 256 + b[0];
  1724.         }
  1725.         else {
  1726.             return 0;
  1727.         }
  1728.     }
  1729.    
  1730.     /// <summary>
  1731.     /// computes the actual Size of the ItemIDList pointed to by pidl
  1732.     /// </summary>
  1733.     /// <param name="pidl">The pidl pointing to an ItemIDList</param>
  1734.     ///<returns> Returns actual Size of the ItemIDList, less the terminating nulnul</returns>
  1735.     public static int ItemIDListSize(IntPtr pidl)
  1736.     {
  1737.         if (!pidl.Equals(IntPtr.Zero)) {
  1738.             int i = ItemIDSize(pidl);
  1739.             int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1740.             while (b > 0) {
  1741.                 i += b;
  1742.                 b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1743.             }
  1744.             return i;
  1745.         }
  1746.         else {
  1747.             return 0;
  1748.         }
  1749.     }
  1750.     /// <summary>
  1751.     /// Counts the total number of SHItems in input pidl
  1752.     /// </summary>
  1753.     /// <param name="pidl">The pidl to obtain the count for</param>
  1754.     /// <returns> Returns the count of SHItems pointed to by pidl</returns>
  1755.     public static int PidlCount(IntPtr pidl)
  1756.     {
  1757.         if (!pidl.Equals(IntPtr.Zero)) {
  1758.             int cnt = 0;
  1759.             int i = 0;
  1760.             int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1761.             while (b > 0) {
  1762.                 cnt += 1;
  1763.                 i += b;
  1764.                 b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1765.             }
  1766.             return cnt;
  1767.         }
  1768.         else {
  1769.             return 0;
  1770.         }
  1771.     }
  1772.    
  1773.     ///<Summary>GetLastId -- returns a pointer to the last ITEMID in a valid
  1774.     /// ITEMIDLIST. Returned pointer SHOULD NOT be released since it
  1775.     /// points to place within the original PIDL</Summary>
  1776.     ///<returns>IntPtr pointing to last ITEMID in ITEMIDLIST structure,
  1777.     /// Returns IntPtr.Zero if given a null pointer.
  1778.     /// If given a pointer to the Desktop, will return same pointer.</returns>
  1779.     ///<remarks>This is what the API ILFindLastID does, however IL...
  1780.     /// functions are not supported before Win2K.</remarks>
  1781.     public static IntPtr GetLastID(IntPtr pidl)
  1782.     {
  1783.         if (!pidl.Equals(IntPtr.Zero)) {
  1784.             int prev = 0;
  1785.             int i = 0;
  1786.             int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1787.             while (b > 0) {
  1788.                 prev = i;
  1789.                 i += b;
  1790.                 b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1791.             }
  1792.             return new IntPtr(pidl.ToInt32() + prev);
  1793.         }
  1794.         else {
  1795.             return IntPtr.Zero;
  1796.         }
  1797.     }
  1798.    
  1799.     public static IntPtr[] DecomposePIDL(IntPtr pidl)
  1800.     {
  1801.         int lim = ItemIDListSize(pidl);
  1802.         IntPtr[] PIDLs = new IntPtr[PidlCount(pidl)];
  1803.         int i = 0;
  1804.         int curB = 0;
  1805.         int offSet = 0;
  1806.         while (curB < lim) {
  1807.             IntPtr thisPtr = new IntPtr(pidl.ToInt32() + curB);
  1808.             offSet = Marshal.ReadByte(thisPtr) + (Marshal.ReadByte(thisPtr, 1) * 256);
  1809.             PIDLs[i] = Marshal.AllocCoTaskMem(offSet + 2);
  1810.             byte[] b = new byte[offSet + 2];
  1811.             Marshal.Copy(thisPtr, b, 0, offSet);
  1812.             b[offSet] = 0;
  1813.             b[offSet + 1] = 0;
  1814.             Marshal.Copy(b, 0, PIDLs[i], offSet + 2);
  1815.             //DumpPidl(PIDLs(i))
  1816.             curB += offSet;
  1817.             i += 1;
  1818.         }
  1819.         return PIDLs;
  1820.     }
  1821.    
  1822.     private static IntPtr PIDLClone(IntPtr pidl)
  1823.     {
  1824.         IntPtr functionReturnValue = default(IntPtr);
  1825.         int cb = ItemIDListSize(pidl);
  1826.         byte[] b = new byte[cb + 2];
  1827.         Marshal.Copy(pidl, b, 0, cb);
  1828.         //not including terminating nulnul
  1829.         b[cb] = 0;
  1830.         b[cb + 1] = 0;
  1831.         //force to nulnul
  1832.         functionReturnValue = Marshal.AllocCoTaskMem(cb + 2);
  1833.         Marshal.Copy(b, 0, functionReturnValue, cb + 2);
  1834.         return functionReturnValue;
  1835.     }
  1836.    
  1837.     public static bool IsEqual(IntPtr Pidl1, IntPtr Pidl2)
  1838.     {
  1839.         if (Win2KOrAbove) {
  1840.             return ShellDll.ILIsEqual(Pidl1, Pidl2);
  1841.         }
  1842.         else {
  1843.             //do hard way, may not work for some folders on XP
  1844.            
  1845.             int cb1 = 0;
  1846.             int cb2 = 0;
  1847.             cb1 = ItemIDListSize(Pidl1);
  1848.             cb2 = ItemIDListSize(Pidl2);
  1849.             if (cb1 != cb2) return false;
  1850.             int lim32 = cb1 / 4;
  1851.            
  1852.             int i = 0;
  1853.             for (i = 0; i <= lim32 - 1; i++) {
  1854.                 if (Marshal.ReadInt32(Pidl1, i) != Marshal.ReadInt32(Pidl2, i)) {
  1855.                     return false;
  1856.                 }
  1857.             }
  1858.             int limB = cb1 % 4;
  1859.             int offset = lim32 * 4;
  1860.             for (i = 0; i <= limB - 1; i++) {
  1861.                 if (Marshal.ReadByte(Pidl1, offset + i) != Marshal.ReadByte(Pidl2, offset + i)) {
  1862.                     return false;
  1863.                 }
  1864.             }
  1865.                 //made it to here, so they are equal
  1866.             return true;
  1867.         }
  1868.     }
  1869.    
  1870.     /// <summary>
  1871.     /// Concatenates the contents of two pidls into a new Pidl (ended by 00)
  1872.     /// allocating CoTaskMem to hold the result,
  1873.     /// placing the concatenation (followed by 00) into the
  1874.     /// allocated Memory,
  1875.     /// and returning an IntPtr pointing to the allocated mem
  1876.     /// </summary>
  1877.     /// <param name="pidl1">IntPtr to a well formed SHItemIDList or IntPtr.Zero</param>
  1878.     /// <param name="pidl2">IntPtr to a well formed SHItemIDList or IntPtr.Zero</param>
  1879.     /// <returns>Returns a ptr to an ItemIDList containing the
  1880.     /// concatenation of the two (followed by the req 2 zeros
  1881.     /// Caller must Free this pidl when done with it</returns>
  1882.     public static IntPtr concatPidls(IntPtr pidl1, IntPtr pidl2)
  1883.     {
  1884.         int cb1 = 0;
  1885.         int cb2 = 0;
  1886.         cb1 = ItemIDListSize(pidl1);
  1887.         cb2 = ItemIDListSize(pidl2);
  1888.         int rawCnt = cb1 + cb2;
  1889.         if ((rawCnt) > 0) {
  1890.             byte[] b = new byte[rawCnt + 2];
  1891.             if (cb1 > 0) {
  1892.                 Marshal.Copy(pidl1, b, 0, cb1);
  1893.             }
  1894.             if (cb2 > 0) {
  1895.                 Marshal.Copy(pidl2, b, cb1, cb2);
  1896.             }
  1897.             IntPtr rVal = Marshal.AllocCoTaskMem(cb1 + cb2 + 2);
  1898.             b[rawCnt] = 0;
  1899.             b[rawCnt + 1] = 0;
  1900.             Marshal.Copy(b, 0, rVal, rawCnt + 2);
  1901.             return rVal;
  1902.         }
  1903.         else {
  1904.             return IntPtr.Zero;
  1905.         }
  1906.     }
  1907.    
  1908.     /// <summary>
  1909.     /// Returns an ItemIDList with the last ItemID trimed off
  1910.     /// This is necessary since I cannot get SHBindToParent to work
  1911.     /// It's purpose is to generate an ItemIDList for the Parent of a
  1912.     /// Special Folder which can then be processed with DesktopBase.BindToObject,
  1913.     /// yeilding a Folder for the parent of the Special Folder
  1914.     /// It also creates and passes back a RELATIVE pidl for this item
  1915.     /// </summary>
  1916.     /// <param name="pidl">A pointer to a well formed ItemIDList. The PIDL to trim</param>
  1917.     /// <param name="relPidl">BYREF IntPtr which will point to a new relative pidl
  1918.     /// containing the contents of the last ItemID in the ItemIDList
  1919.     /// terminated by the required 2 nulls.</param>
  1920.     /// <returns> an ItemIDList with the last element removed.
  1921.     /// Caller must Free this item when through with it
  1922.     /// Also returns the new relative pidl in the 2nd parameter
  1923.     /// Caller must Free this pidl as well, when through with it
  1924.     ///</returns>
  1925.     public static IntPtr TrimPidl(IntPtr pidl, ref IntPtr relPidl)
  1926.     {
  1927.         int cb = ItemIDListSize(pidl);
  1928.         byte[] b = new byte[cb + 2];
  1929.         Marshal.Copy(pidl, b, 0, cb);
  1930.         int prev = 0;
  1931.         int i = b[0] + (b[1] * 256);
  1932.         //Do While i < cb AndAlso b(i) <> 0
  1933.         while (i > 0 && i < cb) {
  1934.             //Changed code
  1935.             prev = i;
  1936.             i += b[i] + (b[i + 1] * 256);
  1937.         }
  1938.         if ((prev + 1) < cb) {
  1939.             //first set up the relative pidl
  1940.             b[cb] = 0;
  1941.             b[cb + 1] = 0;
  1942.             int cb1 = b[prev] + (b[prev + 1] * 256);
  1943.             relPidl = Marshal.AllocCoTaskMem(cb1 + 2);
  1944.             Marshal.Copy(b, prev, relPidl, cb1 + 2);
  1945.             b[prev] = 0;
  1946.             b[prev + 1] = 0;
  1947.             IntPtr rVal = Marshal.AllocCoTaskMem(prev + 2);
  1948.             Marshal.Copy(b, 0, rVal, prev + 2);
  1949.             return rVal;
  1950.         }
  1951.         else {
  1952.             return IntPtr.Zero;
  1953.         }
  1954.     }
  1955.    
  1956.     #region " DumpPidl Routines"
  1957.     /// <summary>
  1958.     /// Dumps, to the Debug output, the contents of the mem block pointed to by
  1959.     /// a PIDL. Depends on the internal structure of a PIDL
  1960.     /// </summary>
  1961.     /// <param name="pidl">The IntPtr(a PIDL) pointing to the block to dump</param>
  1962.     ///
  1963.     public static void DumpPidl(IntPtr pidl)
  1964.     {
  1965.         int cb = ItemIDListSize(pidl);
  1966.         Debug.WriteLine("PIDL " + pidl.ToString() + " contains " + cb + " bytes");
  1967.         if (cb > 0) {
  1968.             byte[] b = new byte[cb + 2];
  1969.             Marshal.Copy(pidl, b, 0, cb + 1);
  1970.             int pidlCnt = 1;
  1971.             int i = b[0] + (b[1] * 256);
  1972.             int curB = 0;
  1973.             while (i > 0) {
  1974.                 Debug.Write("ItemID #" + pidlCnt + " Length = " + i);
  1975.                 DumpHex(b, curB, curB + i - 1);
  1976.                 pidlCnt += 1;
  1977.                 curB += i;
  1978.                 i = b[curB] + (b[curB + 1] * 256);
  1979.             }
  1980.         }
  1981.     }
  1982.    
  1983.     ///<Summary>Dump a portion or all of a Byte Array to Debug output</Summary>
  1984.     ///<param name = "b">A single dimension Byte Array</param>
  1985.     ///<param name = "sPos">Optional start index of area to dump (default = 0)</param>
  1986.     ///<param name = "epos">Optional last index position to dump (default = end of array)</param>
  1987.     ///<Remarks>
  1988.     ///</Remarks>
  1989.     public static void DumpHex(byte[] b, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(0)] // ERROR: Optional parameters aren't supported in C#
  1990. int sPos, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(0)] // ERROR: Optional parameters aren't supported in C#
  1991. int ePos)
  1992.     {
  1993.         if (ePos == 0) ePos = b.Length - 1;
  1994.         int j = 0;
  1995.         int curB = sPos;
  1996.         string sTmp = null;
  1997.         char ch = '\0';
  1998.         StringBuilder SBH = new StringBuilder();
  1999.         StringBuilder SBT = new StringBuilder();
  2000.         for (j = 0; j <= ePos - sPos; j++) {
  2001.             if (j % 16 == 0) {
  2002.                 Debug.WriteLine(SBH.ToString() + SBT.ToString());
  2003.                 SBH = new StringBuilder();
  2004.                 SBT = new StringBuilder(" ");
  2005.                 SBH.Append(HexNum(j + sPos, 4) + "). ");
  2006.             }
  2007.             if (b[curB] < 16) {
  2008. sTmp = "0" + Convert.ToString(b[curB], 16);
  2009.             }
  2010.             else {
  2011.                 sTmp = Convert.ToString(b[curB], 16);
  2012.             }
  2013.             SBH.Append(sTmp);
  2014.             SBH.Append(" ");
  2015.             ch = (char)(b[curB]);
  2016.             if (char.IsControl(ch)) {
  2017.                 SBT.Append(".");
  2018.             }
  2019.             else {
  2020.                 SBT.Append(ch);
  2021.             }
  2022.             curB += 1;
  2023.         }
  2024.        
  2025.         int fill = (j) % 16;
  2026.         if (fill != 0) {
  2027.             SBH.Append(' ', 48 - (3 * ((j) % 16)));
  2028.         }
  2029.         Debug.WriteLine(SBH.ToString() + SBT.ToString());
  2030. }
  2031.    
  2032.     public static string HexNum(int num, int nrChrs)
  2033.     {
  2034. string h = Convert.ToString(num, 16);
  2035.         StringBuilder SB = new StringBuilder();
  2036.         int i = 0;
  2037.         for (i = 1; i <= nrChrs - h.Length; i++) {
  2038.             SB.Append("0");
  2039.         }
  2040.         SB.Append(h);
  2041.         return SB.ToString();
  2042.     }
  2043.     #endregion
  2044.    
  2045.     #endregion
  2046.    
  2047.     #endregion
  2048.    
  2049.     #region " TagComparer Class"
  2050.     ///<Summary> It is sometimes useful to sort a list of TreeNodes,
  2051.     /// ListViewItems, or other objects in an order based on CShItems in their Tag
  2052.     /// use this Icomparer for that Sort</Summary>
  2053.     public class TagComparer : IComparer
  2054.     {
  2055.         public int Compare(object x, object y)
  2056.         {
  2057. //ShellItem xTag = x.tag;
  2058. //ShellItem yTag = y.tag;
  2059. //return xTag.CompareTo(y.tag);
  2060. return 0;
  2061.         }
  2062.     }
  2063.     #endregion
  2064.    
  2065.     #region " cPidl Class"
  2066.     ///<Summary>cPidl class contains a Byte() representation of a PIDL and
  2067.     /// certain Methods and Properties for comparing one cPidl to another</Summary>
  2068.     public class cPidl : IEnumerable
  2069.     {
  2070.        
  2071.         #region " Private Fields"
  2072.         byte[] m_bytes = null;
  2073.         //The local copy of the PIDL
  2074.         int m_ItemCount = 0;
  2075.         //the # of ItemIDs in this ItemIDList (PIDL)
  2076.         #endregion
  2077.        
  2078.         #region " Constructor"
  2079.         public cPidl(IntPtr pidl)
  2080.         {
  2081.             int cb = ItemIDListSize(pidl);
  2082.             if (cb > 0) {
  2083. m_bytes = new byte[cb + 2];
  2084.                 Marshal.Copy(pidl, m_bytes, 0, cb);
  2085. //DumpPidl(pidl)
  2086.             }
  2087.             else {
  2088. m_bytes = new byte[2]; //This is the DeskTop (we hope)
  2089.             }
  2090.             //ensure nulnul
  2091.             m_bytes[m_bytes.Length - 2]= 0;
  2092.             m_bytes[m_bytes.Length - 1] = 0;
  2093.             m_ItemCount = PidlCount(pidl);
  2094.         }
  2095.         #endregion
  2096.        
  2097.         #region " Public Properties"
  2098.         public byte[] PidlBytes {
  2099.             get { return m_bytes; }
  2100.         }
  2101.        
  2102.         public int Length {
  2103.             get { return m_bytes.Length; }
  2104.         }
  2105.        
  2106.         public int ItemCount {
  2107.             get { return m_ItemCount; }
  2108.         }
  2109.        
  2110.         #endregion
  2111.        
  2112.         #region " Public Intstance Methods -- ToPIDL, Decompose, and IsEqual"
  2113.        
  2114.         ///<Summary> Copy the contents of a byte() containing a pidl to
  2115.         /// CoTaskMemory, returning an IntPtr that points to that mem block
  2116.         /// Assumes that this cPidl is properly terminated, as all New
  2117.         /// cPidls are.
  2118.         /// Caller must Free the returned IntPtr when done with mem block.
  2119.         ///</Summary>
  2120.         public IntPtr ToPIDL()
  2121.         {
  2122.             return BytesToPidl(m_bytes);
  2123.         }
  2124.        
  2125.         ///<Summary>Returns an object containing a byte() for each of this cPidl's
  2126.         /// ITEMIDs (individual PIDLS), in order such that obj(0) is
  2127.         /// a byte() containing the bytes of the first ITEMID, etc.
  2128.         /// Each ITEMID is properly terminated with a nulnul
  2129.         ///</Summary>
  2130.         public object[] Decompose()
  2131.         {
  2132.             object[] bArrays = new object[this.ItemCount];
  2133. ICPidlEnumerator eByte = this.GetEnumerator() as ICPidlEnumerator;
  2134.             int i = 0;
  2135.             while (eByte.MoveNext()) {
  2136.                 bArrays[i] = eByte.Current;
  2137.                 i += 1;
  2138.             }
  2139.             return bArrays;
  2140.         }
  2141.        
  2142.         ///<Summary>Returns True if input cPidl's content exactly match the
  2143.         /// contents of this instance</Summary>
  2144.         public bool IsEqual(cPidl other)
  2145.         {
  2146.             bool functionReturnValue = false;
  2147.             functionReturnValue = false;
  2148.             //assume not
  2149.             if (other.Length != this.Length) return functionReturnValue;
  2150.             byte[] ob = other.PidlBytes;
  2151.             int i = 0;
  2152.             for (i = 0; i <= this.Length - 1; i++) {
  2153.                 //note: we look at nulnul also
  2154.                 if (ob[i] != m_bytes[i]) return functionReturnValue;
  2155.             }
  2156.                 //all equal on fall thru
  2157.             return true;
  2158. }
  2159.         #endregion
  2160.        
  2161.         #region " Public Shared Methods"
  2162.        
  2163.         #region " JoinPidlBytes"
  2164.         ///<Summary> Join two byte arrays containing PIDLS, returning a
  2165.         /// Byte() containing the resultant ITEMIDLIST. Both Byte() must
  2166.         /// be properly terminated (nulnul)
  2167.         /// Returns NOTHING if error
  2168.         /// </Summary>
  2169.         public static byte[] JoinPidlBytes(byte[] b1, byte[] b2)
  2170.         {
  2171.             if (IsValidPidl(b1) & IsValidPidl(b2)) {
  2172.                 byte[] b = new byte[b1.Length + b2.Length - 2];
  2173.                 //allow for leaving off first nulnul
  2174.                 Array.Copy(b1, b, b1.Length - 2);
  2175.                 Array.Copy(b2, 0, b, b1.Length - 2, b2.Length);
  2176.                 if (IsValidPidl(b)) {
  2177.                     return b;
  2178.                 }
  2179.                 else {
  2180.                     return null;
  2181.                 }
  2182.             }
  2183.             else {
  2184.                 return null;
  2185.             }
  2186.         }
  2187.         #endregion
  2188.        
  2189.         #region " BytesToPidl"
  2190.         ///<Summary> Copy the contents of a byte() containing a pidl to
  2191.         /// CoTaskMemory, returning an IntPtr that points to that mem block
  2192.         /// Caller must free the IntPtr when done with it
  2193.         ///</Summary>
  2194.         public static IntPtr BytesToPidl(byte[] b)
  2195.         {
  2196.             IntPtr functionReturnValue = default(IntPtr);
  2197.             functionReturnValue = IntPtr.Zero;
  2198.             //assume failure
  2199.             if (IsValidPidl(b)) {
  2200.                 int bLen = b.Length;
  2201.                 functionReturnValue = Marshal.AllocCoTaskMem(bLen);
  2202.                 if (functionReturnValue.Equals(IntPtr.Zero)) return functionReturnValue;
  2203.                 //another bad error
  2204.                 Marshal.Copy(b, 0, functionReturnValue, bLen);
  2205.             }
  2206.             return functionReturnValue;
  2207.         }
  2208.         #endregion
  2209.        
  2210.         #region " StartsWith"
  2211.         ///<Summary>returns True if the beginning of pidlA matches PidlB exactly for pidlB's entire length</Summary>
  2212.         public static bool StartsWith(IntPtr pidlA, IntPtr pidlB)
  2213.         {
  2214.             return cPidl.StartsWith(new cPidl(pidlA), new cPidl(pidlB));
  2215.         }
  2216.        
  2217.         ///<Summary>returns True if the beginning of A matches B exactly for B's entire length</Summary>
  2218.         public static bool StartsWith(cPidl A, cPidl B)
  2219.         {
  2220.             return A.StartsWith(B);
  2221.         }
  2222.        
  2223.         ///<Summary>Returns true if the CPidl input parameter exactly matches the
  2224.         /// beginning of this instance of CPidl</Summary>
  2225.         public bool StartsWith(cPidl cp)
  2226.         {
  2227.             byte[] b = cp.PidlBytes;
  2228.             if (b.Length > m_bytes.Length) return false;
  2229.             //input is longer
  2230.             int i = 0;
  2231.             for (i = 0; i <= b.Length - 3; i++) {
  2232.                 //allow for nulnul at end of cp.PidlBytes
  2233.                 if (b[i] != m_bytes[i]) return false;
  2234.             }
  2235.             return true;
  2236.         }
  2237.         #endregion
  2238.        
  2239.         #endregion
  2240.        
  2241.         #region " GetEnumerator"
  2242.         public System.Collections.IEnumerator GetEnumerator()
  2243.         {
  2244.             return new ICPidlEnumerator(m_bytes);
  2245.         }
  2246.         #endregion
  2247.        
  2248.         #region " PIDL enumerator Class"
  2249.         private class ICPidlEnumerator : IEnumerator
  2250.         {
  2251.            
  2252.             private int m_sPos;
  2253.             //the first index in the current PIDL
  2254.             private int m_ePos;
  2255.             //the last index in the current PIDL
  2256.             private byte[] m_bytes;
  2257.             //the local copy of the PIDL
  2258.             private bool m_NotEmpty = false;
  2259.             //the desktop PIDL is zero length
  2260.            
  2261.             public ICPidlEnumerator(byte[] b)
  2262.             {
  2263.                 m_bytes = b;
  2264.                 if (b.Length > 0) m_NotEmpty = true;
  2265.                 m_sPos = -1;
  2266.                 m_ePos = -1;
  2267.             }
  2268.            
  2269.             public object Current {
  2270.                 get {
  2271.                     if (m_sPos < 0) throw new InvalidOperationException("ICPidlEnumerator --- attempt to get Current with invalidated list");
  2272.                     byte[] b = new byte[(m_ePos - m_sPos) + 3];
  2273.                     //room for nulnul
  2274.                     Array.Copy(m_bytes, m_sPos, b, 0, b.Length - 2);
  2275.                     b[b.Length - 2] = 0;
  2276.                     b[b.Length - 1] = 0;
  2277.                     //add nulnul
  2278.                     return b;
  2279.                 }
  2280.             }
  2281.            
  2282.             public bool MoveNext()
  2283.             {
  2284.                 if (m_NotEmpty) {
  2285.                     if (m_sPos < 0) {
  2286.                         m_sPos = 0;
  2287.                         m_ePos = -1;
  2288.                     }
  2289.                     else {
  2290.                         m_sPos = m_ePos + 1;
  2291.                     }
  2292.                     if (m_bytes.Length < m_sPos + 1) throw new InvalidCastException("Malformed PIDL");
  2293.                     int cb = m_bytes[m_sPos] + m_bytes[m_sPos + 1] * 256;
  2294.                     if (cb == 0) {
  2295.                             //have passed all back
  2296.                         return false;
  2297.                     }
  2298.                     else {
  2299.                         m_ePos += cb;
  2300.                     }
  2301.                 }
  2302.                 else {
  2303.                     m_sPos = 0;
  2304.                     m_ePos = 0;
  2305.                         //in this case, we have exhausted the list of 0 ITEMIDs
  2306.                     return false;
  2307.                 }
  2308.                 return true;
  2309.             }
  2310.            
  2311.             public void Reset()
  2312.             {
  2313.                 m_sPos = -1;
  2314.                 m_ePos = -1;
  2315.             }
  2316.         }
  2317.         #endregion
  2318.        
  2319.     }
  2320.     #endregion
  2321.    
  2322. }