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;
}
}
}
}
}