diff --git a/PInvoke/Shared/Collections/IEnumFromCom.cs b/PInvoke/Shared/Collections/IEnumFromCom.cs new file mode 100644 index 00000000..0e9d08a7 --- /dev/null +++ b/PInvoke/Shared/Collections/IEnumFromCom.cs @@ -0,0 +1,120 @@ +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; + + /// 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. + public IEnumFromCom(ComTryGetNext next, Action reset) : base() + { + if (next is null || reset is null) + throw new ArgumentNullException(); + cnext = next; + 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 bool TryGet(out TItem item) + { + var res = new TItem[1]; + 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); + } + } +} \ No newline at end of file diff --git a/PInvoke/Shared/Collections/IEnumGenerics.cs b/PInvoke/Shared/Collections/IEnumGenerics.cs index 92627a87..41f41d30 100644 --- a/PInvoke/Shared/Collections/IEnumGenerics.cs +++ b/PInvoke/Shared/Collections/IEnumGenerics.cs @@ -2,8 +2,6 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using Vanara.PInvoke; namespace Vanara.Collections { @@ -25,46 +23,6 @@ namespace Vanara.Collections /// true if an item is returned, otherwise false. public delegate bool TryGetNext(TIEnum enumObj, out TItem value); - /// 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. - /// - // https://docs.microsoft.com/en-us/windows/desktop/api/objidl/nf-objidl-ienumunknown-reset HRESULT Reset( ); - void Reset(); - */ - } - /// An implementation the interface that can iterate through next and reset methods. public class IEnumeratorFromNext : IEnumerator where TIEnum : class { @@ -127,74 +85,6 @@ namespace Vanara.Collections } } - /// - /// 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 object comObj; - - /// 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. - public IEnumFromCom(ComTryGetNext next, Action reset) : base() - { - if (next is null || reset is null) - throw new ArgumentNullException(); - cnext = next; - base.next = TryGet; - base.reset = reset; - } - - /// Initializes a new instance of the class from a COM enumeration interface instance. - /// The COM enumeration interface instance. - public IEnumFromCom(ICOMEnum enumObj) : base() - { - if (enumObj is null) - throw new ArgumentNullException(nameof(enumObj)); - comObj = enumObj; - cnext = ComObjTryGetNext; - next = TryGet; - reset = ComObjReset; - } - - /// - /// 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); - - private HRESULT ComObjTryGetNext(uint celt, TItem[] rgelt, out uint celtFetched) - { - var para = new object[] { celt, rgelt, 0U }; - var hr = (HRESULT)comObj.GetType().InvokeMember("Next", BindingFlags.InvokeMethod, null, comObj, para); - celtFetched = (uint)para[2]; - return hr; - } - - private void ComObjReset() => comObj.GetType().InvokeMember("Reset", BindingFlags.InvokeMethod, null, comObj, null); - - private bool TryGet(out TItem item) - { - var res = new TItem[1]; - item = default; - if (cnext(1, res, out var ret) != HRESULT.S_OK) - return false; - item = res[0]; - return true; - } - } - /// /// Creates an enumerable class from a counter and an indexer. Useful if a class doesn't support or /// like some COM objects.