using System; using System.Runtime.InteropServices; using Vanara.Extensions; namespace Vanara.PInvoke { public static partial class Shell32 { /// Utilities to work with and raw ITEMIDLIST pointers. public static class PIDLUtil { /// Clones an ITEMIDLIST structure public static PIDL ILClone(IntPtr pidl) => new PIDL(IntILClone(pidl)); /// Clones the first SHITEMID structure in an ITEMIDLIST structure /// A pointer to the ITEMIDLIST structure that you want to clone. /// /// A pointer to an ITEMIDLIST structure that contains the first SHITEMID structure from the ITEMIDLIST structure specified by /// pidl. Returns NULL on failure. /// public static PIDL ILCloneFirst(IntPtr pidl) { var size = ItemIdSize(pidl); var bytes = new byte[size + 2]; Marshal.Copy(pidl, bytes, 0, size); var newPidl = Marshal.AllocCoTaskMem(size + 2); Marshal.Copy(bytes, 0, newPidl, size + 2); return new PIDL(newPidl); } /// Combines two ITEMIDLIST structures. /// A pointer to the first ITEMIDLIST structure. /// /// A pointer to the second ITEMIDLIST structure. This structure is appended to the structure pointed to by pidl1. /// /// /// Returns an ITEMIDLIST containing the combined structures. If you set either pidl1 or pidl2 to NULL, the returned ITEMIDLIST /// structure is a clone of the non-NULL parameter. Returns NULL if pidl1 and pidl2 are both set to NULL. /// public static PIDL ILCombine(IntPtr pidl1, IntPtr pidl2) => new PIDL(IntILCombine(pidl1, pidl2)); /// Returns a pointer to the last SHITEMID structure in an ITEMIDLIST structure /// A pointer to an ITEMIDLIST structure. /// A pointer to the last SHITEMID structure in pidl. public static IntPtr ILFindLastId(IntPtr pidl) { var ptr1 = pidl; var ptr2 = ILGetNext(ptr1); while (ItemIdSize(ptr2) > 0) { ptr1 = ptr2; ptr2 = ILGetNext(ptr1); } return ptr1; } /// Gets the next SHITEMID structure in an ITEMIDLIST structure /// A pointer to a particular SHITEMID structure in a larger ITEMIDLIST structure. /// /// Returns a pointer to the SHITEMID structure that follows the one specified by pidl. Returns NULL if pidl points to the last /// SHITEMID structure. /// public static IntPtr ILGetNext(IntPtr pidl) { var size = ItemIdSize(pidl); return size == 0 ? IntPtr.Zero : pidl.Offset(size); } /// Determines whether the specified ITEMIDLIST has no children. /// A pointer to the ITEMIDLIST structure to be evaluated. /// true if the specified ITEMIDLIST is empty; otherwise, false. public static bool ILIsEmpty(IntPtr pidl) => ItemIdListSize(pidl) == 0; /// Removes the last SHITEMID structure from an ITEMIDLIST structure /// /// A pointer to the ITEMIDLIST structure to be shortened. When the function returns, this variable points to the shortened structure. /// /// Returns TRUE if successful, FALSE otherwise. public static bool ILRemoveLastId(IntPtr pidl) { var lastPidl = ILFindLastId(pidl); if (lastPidl == pidl) return false; var newSize = (int)lastPidl - (int)pidl + 2; Marshal.ReAllocCoTaskMem(pidl, newSize); Marshal.Copy(new byte[] { 0, 0 }, 0, pidl.Offset(newSize - 2), 2); return true; } /// Separates an ITEMIDLIST into the parent SHITEMID and the children SHITEMIDs /// A pointer to the ITEMIDLIST structure to be evaluated. /// The parent. /// The children. /// Returns TRUE if successful, FALSE otherwise. public static bool SplitPidl(IntPtr pidl, out IntPtr parent, out IntPtr child) { parent = IntILClone(pidl); child = IntILClone(ILFindLastId(pidl)); if (ILRemoveLastId(parent)) return true; Marshal.FreeCoTaskMem(parent); Marshal.FreeCoTaskMem(child); return false; } /// Clones an ITEMIDLIST structure internal static IntPtr IntILClone(IntPtr pidl) { var size = ItemIdListSize(pidl); var bytes = new byte[size + 2]; Marshal.Copy(pidl, bytes, 0, size); var newPidl = Marshal.AllocCoTaskMem(size + 2); Marshal.Copy(bytes, 0, newPidl, size + 2); return newPidl; } /// Combines two ITEMIDLIST structures internal static IntPtr IntILCombine(IntPtr pidl1, IntPtr pidl2) { var size1 = ItemIdListSize(pidl1); var size2 = ItemIdListSize(pidl2); var newPidl = Marshal.AllocCoTaskMem(size1 + size2 + 2); var 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; } /*public static void WriteBytes(IntPtr handle) { var size = Marshal.ReadByte(handle, 0) + Marshal.ReadByte(handle, 1)*256 - 2; for (var i = 0; i < size; i++) { Console.Out.WriteLine(Marshal.ReadByte(handle, i + 2)); } Console.Out.WriteLine(Marshal.ReadByte(handle, size + 2)); Console.Out.WriteLine(Marshal.ReadByte(handle, size + 3)); }*/ internal static int ItemIdListSize(IntPtr handle) { if (handle.Equals(IntPtr.Zero)) return 0; var size = ItemIdSize(handle); var nextSize = Marshal.ReadInt16(handle, size); while (nextSize > 0) { size += nextSize; nextSize = Marshal.ReadInt16(handle, size); } return size; } internal static int ItemIdSize(IntPtr handle) => handle.Equals(IntPtr.Zero) ? 0 : Marshal.ReadInt16(handle); } } }