context: using System;
  using System.Collections.Generic;
  using System.Text;
  using System.Runtime.InteropServices;
  using System.Collections;
  
  namespace ShellDll
  {
      internal class PIDL : IEnumerable
      {
          private IntPtr pidl = IntPtr.Zero;
  
          #region Constructors
  
          public PIDL(IntPtr pidl, bool clone)
          {
              if (clone)
                  this.pidl = ILClone(pidl);
              else
                  this.pidl = pidl;
          }
  
          public PIDL(PIDL pidl, bool clone)
          {
              if (clone)
                  this.pidl = ILClone(pidl.Ptr);
              else
                  this.pidl = pidl.Ptr;
          }
  
          #endregion
  
          #region Public
  
          public IntPtr Ptr { get { return pidl; } }
  
          public void Append(IntPtr appendPidl)
          {
              IntPtr newPidl = ILCombine(pidl, appendPidl);
  
              Marshal.FreeCoTaskMem(pidl);
              pidl = newPidl;
          }
  
          public void Insert(IntPtr insertPidl)
          {
              IntPtr newPidl = ILCombine(insertPidl, pidl);
              
              Marshal.FreeCoTaskMem(pidl);
              pidl = newPidl;
          }
  
          public static bool IsEmpty(IntPtr pidl)
          {
              if (pidl == IntPtr.Zero)
                  return true;
  
              byte[] bytes = new byte[2];
              Marshal.Copy(pidl, bytes, 0, 2);
              int size = bytes[0] + bytes[1] * 256;
              return (size <= 2);
          }
  
          public static bool SplitPidl(IntPtr pidl, out IntPtr parent, out IntPtr child)
          {
              parent = ILClone(pidl);
              child = ILClone(ILFindLastID(pidl));
  
              if (!ILRemoveLastID(parent))
              {
                  Marshal.FreeCoTaskMem(parent);
                  Marshal.FreeCoTaskMem(child);
                  return false;
              }
              else
              {
                  return true;
              }
          }
  
          public static void Write(IntPtr pidl)
          {
              StringBuilder path = new StringBuilder(256);
              ShellAPI.SHGetPathFromIDList(pidl, path);
              Console.Out.WriteLine("Pidl: {0}", path);
          }
  
          public static void WriteBytes(IntPtr pidl)
          {
              int size = Marshal.ReadByte(pidl, 0) + Marshal.ReadByte(pidl, 1) * 256 - 2;
  
              for (int i = 0; i < size; i++)
              {
                  Console.Out.WriteLine(Marshal.ReadByte(pidl, i + 2));
              }
  
              Console.Out.WriteLine(Marshal.ReadByte(pidl, size + 2));
              Console.Out.WriteLine(Marshal.ReadByte(pidl, size + 3));
          }
  
          public void Free()
          {
              if (pidl != IntPtr.Zero)
              {
                  Marshal.FreeCoTaskMem(pidl);
                  pidl = IntPtr.Zero;
              }
          }
  
          #endregion
  
          #region Shell
  
          private static int ItemIDSize(IntPtr pidl)
          {
              if (!pidl.Equals(IntPtr.Zero))
              {
                  byte[] buffer = new byte[2];
                  Marshal.Copy(pidl, buffer, 0, 2);
                  return buffer[1] * 256 + buffer[0];
              }
              else
                  return 0;
          }
  
          private static int ItemIDListSize(IntPtr pidl)
          {
              if (pidl.Equals(IntPtr.Zero))
                  return 0;
              else
              {
                  int size = ItemIDSize(pidl);
                  int nextSize = Marshal.ReadByte(pidl, size) + (Marshal.ReadByte(pidl, size + 1) * 256);
                  while (nextSize > 0)
                  {
                      size += nextSize;
                      nextSize = Marshal.ReadByte(pidl, size) + (Marshal.ReadByte(pidl, size + 1) * 256);
                  }
  
                  return size;
              }
          }
  
          // Clones an ITEMIDLIST structure
          public static IntPtr ILClone(IntPtr pidl)
          {
              int size = ItemIDListSize(pidl);
  
              byte[] bytes = new byte[size + 2];
              Marshal.Copy(pidl, bytes, 0, size);
  
              IntPtr newPidl = Marshal.AllocCoTaskMem(size + 2);
              Marshal.Copy(bytes, 0, newPidl, size + 2);
  
              return newPidl;
          }
  
          // Clones the first SHITEMID structure in an ITEMIDLIST structure
          public static IntPtr ILCloneFirst(IntPtr pidl)
          {
              int size = ItemIDSize(pidl);
  
              byte[] bytes = new byte[size + 2];
              Marshal.Copy(pidl, bytes, 0, size);
  
              IntPtr newPidl = Marshal.AllocCoTaskMem(size + 2);
              Marshal.Copy(bytes, 0, newPidl, size + 2);
  
              return newPidl;
          }
  
          // Gets the next SHITEMID structure in an ITEMIDLIST structure
          public static IntPtr ILGetNext(IntPtr pidl)
          {
              int size = ItemIDSize(pidl);
              IntPtr nextPidl = new IntPtr((int)pidl + size);
              return nextPidl;
          }
  
          // Returns a pointer to the last SHITEMID structure in an ITEMIDLIST structure
          public static IntPtr ILFindLastID(IntPtr pidl)
          {
              IntPtr ptr1 = pidl;
              IntPtr ptr2 = ILGetNext(ptr1);
  
              while (ItemIDSize(ptr2) > 0)
              {
                  ptr1 = ptr2;
                  ptr2 = ILGetNext(ptr1);
              }
  
              return ptr1;
          }
  
          // Removes the last SHITEMID structure from an ITEMIDLIST structure
          public static bool ILRemoveLastID(IntPtr pidl)
          {
              IntPtr lastPidl = ILFindLastID(pidl);
  
              if (lastPidl != pidl)
              {
                  int newSize = (int)lastPidl - (int)pidl + 2;
                  Marshal.ReAllocCoTaskMem(pidl, newSize);
                  Marshal.Copy(new byte[] { 0, 0 }, 0, new IntPtr((int)pidl + newSize - 2), 2);
  
                  return true;
              }
              else
                  return false;
          }
  
          // Combines two ITEMIDLIST structures
          public static IntPtr ILCombine(IntPtr pidl1, IntPtr pidl2)
          {
              int size1 = ItemIDListSize(pidl1);
              int size2 = ItemIDListSize(pidl2);
  
              IntPtr newPidl = Marshal.AllocCoTaskMem(size1 + size2 + 2);
              byte[] bytes = new byte[size1 + size2 + 2];
  
              Marshal.Copy(pidl1, bytes, 0, size1);
              Marshal.Copy(pidl2, bytes, size1, size2);
  
              Marshal.Copy(bytes, 0, newPidl, bytes.Length);
  
              return newPidl;
          }
  
          #endregion
  
          #region IEnumerable Members
  
          public IEnumerator GetEnumerator()
          {
              return new PIDLEnumerator(pidl);
          }
  
          #endregion
  
          #region Override
  
          public override bool Equals(object obj)
          {
              try
              {
                  if (obj is IntPtr)
                      return ShellAPI.ILIsEqual(this.Ptr, (IntPtr)obj);
                  if (obj is PIDL)
                      return ShellAPI.ILIsEqual(this.Ptr, ((PIDL)obj).Ptr);
                  else
                      return false;
              }
              catch (Exception)
              {
                  return false;
              }
          }
  
          public override int GetHashCode()
          {
              return pidl.GetHashCode();
          }
  
          #endregion
  
          public class PIDLEnumerator : IEnumerator
          {
              private IntPtr pidl;
              private IntPtr currentPidl;
              private IntPtr clonePidl;
              private bool start;
  
              public PIDLEnumerator(IntPtr pidl)
              {
                  start = true;
                  this.pidl = pidl;
                  currentPidl = pidl;
                  clonePidl = IntPtr.Zero;
              }
  
              #region IEnumerator Members
  
              public object Current
              {
                  get
                  {
                      if (clonePidl != IntPtr.Zero)
                      {
                          Marshal.FreeCoTaskMem(clonePidl);
                          clonePidl = IntPtr.Zero;
                      }
  
                      clonePidl = PIDL.ILCloneFirst(currentPidl);
                      return clonePidl;
                  }
              }
  
              public bool MoveNext()
              {
  
gt:                 if (clonePidl != IntPtr.Zero)