#pragma warning disable IL2050 // Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Vanara.PInvoke;
public static partial class Shell32
{
/// Represents a managed pointer to an ITEMIDLIST.
[PInvokeData("Shtypes.h", MSDNShortId = "bb773321")]
[DebuggerDisplay("[{ToString()}]")]
public class PIDL : SafeHANDLE, 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 ? IntILClone(pidl) : pidl, clone || own) { }
/// 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) : this(IntILCreateFromPath(path)) { }
/// Initializes a new instance of the class from an array of bytes.
/// The bytes.
public PIDL(byte[] bytes) { using var p = new PinnedObject(bytes); SetHandle(IntILClone(p)); }
/// Initializes a new instance of the class from a stream holding an absolute ITEMIDLIST.
/// The stream interface instance from which the ITEMIDLIST loads.
public PIDL(System.Runtime.InteropServices.ComTypes.IStream stream)
{
ILLoadFromStreamEx(stream, out PIDL p).ThrowIfFailed();
SetHandle(p.ReleaseOwnership());
}
/// Initializes a new instance of the class.
internal PIDL() { }
/// Gets a value representing a NULL PIDL.
/// The null equivalent.
public static PIDL Null { get; } = new PIDL();
/// 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 => new(ILFindLastID(handle), true);
/// Gets an ITEMIDLIST with the last ID removed. If this is the topmost ID, a clone of the current is returned.
public PIDL Parent
{
get
{
var p = new PIDL(this);
p.RemoveLastId();
return p;
}
}
/// Gets the size, in bytes, of the ITEMIDLIST.
public uint Size => ILGetSize(handle);
/// 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);
/// Performs an explicit conversion from to .
/// The current .
/// The result of the conversion.
public static explicit operator IntPtr(PIDL pidl) => pidl.handle;
/// 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(p);
/// 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 operator +(PIDL first, PIDL second) => Combine(first, second);
/// Implements the operator ==.
/// The first .
/// The second .
/// The result of the operator.
public static bool operator ==(PIDL first, PIDL second) => first?.Equals(second) ?? second is null;
/// Implements the operator !=.
/// The first .
/// The second .
/// The result of the operator.
public static bool operator !=(PIDL first, PIDL second) => !(first == second);
/// Appends the specified to the existing list.
/// The to append.
public void Append(PIDL appendPidl) => UpdateHandle(IntILCombine(handle, appendPidl.DangerousGetHandle()));
/// Dumps this instance to a string a list of binary values.
/// A binary string of the contents.
public string Dump() => string.Join(" ", handle.ToIEnum((int)Size).Select(b => $"{b:X2}").ToArray());
/// 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) => obj switch
{
null => IsEmpty,
PIDL pidl => Equals(pidl),
IntPtr ptr => Equals(ptr),
_ => 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) => ReferenceEquals(this, other) || Equals(other?.handle ?? IntPtr.Zero);
/// Gets the ID list as an array of bytes.
/// An array of bytes representing the ID list.
public byte[] GetBytes() => handle.ToByteArray((int)Size) ?? new byte[0];
/// 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 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() => new Vanara.Collections.EnumerableEqualityComparer().GetHashCode(handle.ToIEnum((int)Size));
/// Inserts the specified before the existing list.
/// The to insert.
public void Insert(PIDL insertPidl) => UpdateHandle(IntILCombine(insertPidl.handle, handle));
/// Determines if this instance is a parent or ancestor of a supplied PIDL.
/// Child instance to test.
/// If true, narrows test to immediate children only.
/// true if this instance is a parent or ancestor of a supplied PIDL.
public bool IsParentOf(PIDL childPidl, bool immediate = true) => ILIsParent((IntPtr)this, (IntPtr)childPidl, immediate);
/// Removes the last identifier from the list.
public bool RemoveLastId() => ILRemoveLastID(handle);
/// Saves an ITEMIDLIST structure to a stream.
/// An IStream instance where the ITEMIDLIST is saved.
/// The stream must be opened for writing, or returns an error.
public void SaveToStream(System.Runtime.InteropServices.ComTypes.IStream stream) => ILSaveToStream(stream, new(handle, false, false)).ThrowIfFailed();
/// 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 var name).ThrowIfFailed();
return name!;
}
catch (Exception ex) { Debug.WriteLine($"Error: PIDL:ToString = {ex}"); }
return string.Empty;
}
/// Returns an enumerator that iterates through a collection.
/// An object that can be used to iterate through the collection.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
///
/// Internal method that actually releases the handle. This is called by
/// for valid handles and afterwards zeros the handle.
///
/// true to indicate successful release of the handle; false otherwise.
protected override bool InternalReleaseHandle()
{
ILFree(handle);
return true;
}
/// Updates the handle after freeing the existing handle.
/// The new handle.
protected virtual void UpdateHandle(IntPtr newHandle)
{
ILFree(handle);
SetHandle(newHandle);
}
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 ? PIDL.Null : ILCloneFirst(currentPidl);
object IEnumerator.Current => Current;
public void Dispose()
{
}
public bool MoveNext()
{
if (start)
{
start = false;
currentPidl = pidl;
return true;
}
IntPtr newPidl = ILNext(currentPidl);
if (ILIsEmpty(newPidl)) return false;
currentPidl = newPidl;
return true;
}
public void Reset()
{
start = true;
currentPidl = IntPtr.Zero;
}
}
}
}