using Microsoft.Win32.SafeHandles; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Text; using Vanara.Extensions; using Vanara.InteropServices; //using static Vanara.Shell32.PIDLUtil; // ReSharper disable InconsistentNaming namespace Vanara.PInvoke { public static partial class Shell32 { /// /// Represents a managed pointer to an ITEMIDLIST. /// /// /// [PInvokeData("Shtypes.h", MSDNShortId = "bb773321")] public class PIDL : SafeHandleZeroOrMinusOneIsInvalid, IEnumerable, IEquatable, IEquatable { /// Initializes a new instance of the class. /// The raw pointer to a native ITEMIDLIST. /// if set to true clone the list before storing it. /// if set to true will release the memory associated with the ITEMIDLIST when done. public PIDL(IntPtr pidl, bool clone = false, bool own = true) : base(clone || own) { SetHandle(clone ? IntILClone(pidl) : pidl); } /// Initializes a new instance of the class. /// An existing that will be copied and managed. public PIDL(PIDL pidl) : this(pidl.handle, true) { } /// Initializes a new instance of the class from a file path. /// A string that contains the path. public PIDL(string path) : base(true) { SetHandle(IntILCreateFromPath(path)); } /// Initializes a new instance of the class. internal PIDL() : base(true) { } /// Gets a value indicating whether this list is empty. /// true if this list is empty; otherwise, false. public bool IsEmpty => ILIsEmpty(handle); /// Gets the last SHITEMID in this ITEMIDLIST. /// The last SHITEMID. public PIDL LastId => ILFindLastID(handle); /// Gets a value representing a NULL PIDL. /// The null equivalent. public static PIDL Null { get; } = new PIDL(); /// Performs an explicit conversion from to . /// The current . /// The result of the conversion. public static explicit operator IntPtr(PIDL pidl) => pidl.handle; /// Appends the specified to the existing list. /// The to append. public void Append(PIDL appendPidl) { var newPidl = IntILCombine(handle, appendPidl.DangerousGetHandle()); Marshal.FreeCoTaskMem(handle); SetHandle(newPidl); } /// Combines the specified instances to create a new one. /// The first . /// The second . /// A managed instance that contains both supplied lists in their respective order. public static PIDL Combine(PIDL firstPidl, PIDL secondPidl) => ILCombine(firstPidl.handle, secondPidl.handle); /// Determines whether the specified , is equal to this instance. /// The to compare with this instance. /// true if the specified is equal to this instance; otherwise, false. public override bool Equals(object obj) { switch (obj) { case null: return IsEmpty; case PIDL pidl: return Equals(pidl); case IntPtr ptr: return Equals(ptr); default: return base.Equals(obj); } } /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// /// true if the current object is equal to the parameter; otherwise, false. /// public bool Equals(IntPtr other) => ILIsEqual(handle, other); /// /// Indicates whether the current object is equal to another object of the same type. /// /// An object to compare with this object. /// /// true if the current object is equal to the parameter; otherwise, false. /// public bool Equals(PIDL other) => Equals(other?.handle ?? IntPtr.Zero); //public static bool operator ==(PIDL a, PIDL b) => (a == null && b == null) || (a?.Equals(b) ?? false); //public static bool operator !=(PIDL a, PIDL b) => !(a == b); /// Returns an enumerator that iterates through the collection. /// A that can be used to iterate through the collection. public IEnumerator GetEnumerator() => new InternalEnumerator(handle); /// Returns an enumerator that iterates through a collection. /// An object that can be used to iterate through the collection. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); // ReSharper disable once BaseObjectGetHashCodeCallInGetHashCode /// Returns a hash code for this instance. /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. public override int GetHashCode() => base.GetHashCode(); /// Inserts the specified before the existing list. /// The to insert. public void Insert(PIDL insertPidl) { var newPidl = IntILCombine(insertPidl.handle, handle); Marshal.FreeCoTaskMem(handle); SetHandle(newPidl); } /// /// Removes the last identifier from the list. /// public bool RemoveLastId() => ILRemoveLastID(handle); /// Returns a that represents this instance. /// A that represents this instance. public override string ToString() => ToString(SIGDN.SIGDN_NORMALDISPLAY); /// Returns a that represents this instance base according to the format provided by . /// The desired display name format. /// A that represents this instance. public string ToString(SIGDN displayNameFormat) { try { SHGetNameFromIDList(this, displayNameFormat, out SafeCoTaskMemHandle name); return name.ToString(-1); } catch (Exception ex) { Debug.WriteLine($"Error: PIDL:ToString = {ex}"); } return null; } /// Dumps this instance to a string a list of binary values. /// A binary string of the contents. public string Dump() { var sb = new StringBuilder(); foreach (var pidl in this) { var sz = ILGetItemSize(pidl.handle); var bytes = pidl.handle.ToIEnum(sz); sb.AppendLine(string.Join(" ", bytes.Select(b => $"{b:X2}").ToArray())); } return sb.ToString(); } /// Performs an implicit conversion from to . /// The pointer to a raw ITEMIDLIST. /// The result of the conversion. public static implicit operator PIDL(IntPtr p) => new PIDL(p); /// When overridden in a derived class, executes the code required to free the handle. /// /// true if the handle is released successfully; otherwise, in the event of a catastrophic failure, false. In this case, it generates a /// releaseHandleFailed MDA Managed Debugging Assistant. /// protected override bool ReleaseHandle() { try { Marshal.FreeCoTaskMem(handle); } catch { } handle = IntPtr.Zero; return true; } private class InternalEnumerator : IEnumerator { private readonly IntPtr pidl; private IntPtr currentPidl; private bool start; public InternalEnumerator(IntPtr handle) { start = true; pidl = handle; currentPidl = IntPtr.Zero; } public PIDL Current => currentPidl == IntPtr.Zero ? null : ILCloneFirst(currentPidl); object IEnumerator.Current => Current; public void Dispose() { } public bool MoveNext() { if (start) { start = false; currentPidl = pidl; return true; } var newPidl = ILNext(currentPidl); if (ILIsEmpty(newPidl)) return false; currentPidl = newPidl; return true; } public void Reset() { start = true; currentPidl = IntPtr.Zero; } } } } }