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