using System;
using System.Collections;
using System.Collections.Generic;
using Vanara.PInvoke;
namespace Vanara.Collections
{
/// A generic interface to identify matching COM enumerator interfaces
/// The type of the elem.
public interface ICOMEnum
{
/*
/// Retrieves the specified number of items in the enumeration sequence.
///
/// The number of items to be retrieved. If there are fewer than the requested number of items left in the sequence, this method
/// retrieves the remaining elements.
///
///
/// An array of enumerated items.
///
/// The enumerator is responsible for calling AddRef, and the caller is responsible for calling Release through each pointer
/// enumerated. If celt is greater than 1, the caller must also pass a non-NULL pointer passed to pceltFetched to know how many
/// pointers to release.
///
///
///
/// The number of items that were retrieved. This parameter is always less than or equal to the number of items requested.
///
/// If the method retrieves the number of items requested, the return value is S_OK. Otherwise, it is S_FALSE.
HRESULT Next(uint celt, TElem[] rgelt, out uint pceltFetched);
/// Skips over the specified number of items in the enumeration sequence.
/// The number of items to be skipped.
/// If the method skips the number of items requested, the return value is S_OK. Otherwise, it is S_FALSE.
HRESULT Skip(uint celt);
/// Resets the enumeration sequence to the beginning.
///
/// There is no guarantee that the same set of objects will be enumerated after the reset operation has completed. A static
/// collection is reset to the beginning, but it can be too expensive for some collections, such as files in a directory, to
/// guarantee this condition.
///
void Reset();
*/
}
///
/// Creates an enumerable class from a get next method in the form of HRESULT Next(uint, TItem[], out uint) and a reset method. Useful
/// if a class doesn't support or like some COM objects.
///
/// The type of the item.
public class IEnumFromCom : IEnumFromNext
{
private readonly ComTryGetNext cnext;
private readonly Func make;
/// Initializes a new instance of the class.
/// The method used to try to get the next item in the enumeration.
/// The method used to reset the enumeration to the first element.
/// The method used to create the default value placed in the array for .
public IEnumFromCom(ComTryGetNext next, Action reset, Func makeNew = null) : base()
{
if (next is null || reset is null)
throw new ArgumentNullException();
cnext = next;
make = makeNew ?? DefaultMaker;
base.next = TryGet;
base.reset = reset;
}
///
/// Delegate that gets the next value in an enumeration and returns true or returns false to indicate there are no more items in the enumeration.
///
/// The number of items requested.
/// An array of items to be returned.
/// The number of items retrieved in the parameter.
///
/// This method supports the following return values: S_OK = The number of items returned is equal to the number specified in the
/// parameter. S_FALSE = The number of items returned is less than the number specified in the parameter.
///
public delegate HRESULT ComTryGetNext(uint celt, TItem[] rgelt, out uint celtFetched);
/// Initializes a new instance of the class from a COM enumeration interface instance.
/// The COM enumeration interface instance.
public static IEnumFromCom Create(TIntf enumObj) where TIntf : class, ICOMEnum
{
if (enumObj is null)
throw new ArgumentNullException(nameof(enumObj));
var cew = new ComEnumWrapper(enumObj);
return new IEnumFromCom(cew.ComObjTryGetNext, cew.ComObjReset);
}
private static TItem DefaultMaker() => default;
private bool TryGet(out TItem item)
{
var res = new TItem[] { make() };
item = default;
if (cnext(1, res, out var ret) != HRESULT.S_OK)
return false;
item = res[0];
return true;
}
private class ComEnumWrapper where T : class, ICOMEnum
{
private readonly T obj;
public ComEnumWrapper(T o) => obj = o;
public void ComObjReset() => ComInvoke("Reset");
public HRESULT ComObjTryGetNext(uint celt, TItem[] rgelt, out uint celtFetched)
{
var para = new object[] { celt, rgelt, 0U };
var hr = (HRESULT)ComInvoke("Next", para);
celtFetched = (uint)para[2];
return hr;
}
private object ComInvoke(string meth, object[] p = null) => typeof(T).GetMethod(meth).Invoke(obj, p);
}
}
}