|
1 | 1 | using System.Collections; |
2 | 2 | using System.Collections.Generic; |
3 | 3 | using System.Diagnostics.CodeAnalysis; |
| 4 | +using System.Runtime.InteropServices; |
4 | 5 | using Vanara.PInvoke; |
5 | 6 |
|
6 | 7 | namespace Vanara.Collections; |
@@ -45,8 +46,9 @@ public interface ICOMEnum<TElem> |
45 | 46 | } |
46 | 47 |
|
47 | 48 | /// <summary> |
48 | | -/// Creates an enumerable class from a get next method in the form of HRESULT Next(uint, TItem[], out uint) and a reset method. Useful |
49 | | -/// if a class doesn't support <see cref="IEnumerable"/> or <see cref="IEnumerable{T}"/> like some COM objects. |
| 49 | +/// Creates an enumerable class from a get next method and a reset method. Useful if a class doesn't support <see cref="IEnumerable"/> |
| 50 | +/// or <see cref="IEnumerable{T}"/> like some COM objects. The reflected Next method can expose its fetched-count parameter either as |
| 51 | +/// out uint or as IntPtr. |
50 | 52 | /// </summary> |
51 | 53 | /// <typeparam name="TItem">The type of the item.</typeparam> |
52 | 54 | public class IEnumFromCom<TItem> : IEnumFromNext<TItem> |
@@ -104,18 +106,40 @@ private bool TryGet([NotNullWhen(true)] out TItem? item) |
104 | 106 | return true; |
105 | 107 | } |
106 | 108 |
|
107 | | - private class ComEnumWrapper<T>(T o) where T : class, ICOMEnum<TItem> |
| 109 | + private class ComEnumWrapper<T> where T : class, ICOMEnum<TItem> |
108 | 110 | { |
109 | | - private readonly T obj = o; |
| 111 | + private readonly T obj; |
| 112 | + private readonly bool nextUsesIntPtr; |
| 113 | + |
| 114 | + public ComEnumWrapper(T o) |
| 115 | + { |
| 116 | + obj = o; |
| 117 | + var nextMethod = typeof(T).GetMethod("Next") ?? throw new MissingMethodException(typeof(T).FullName, "Next"); |
| 118 | + var nextParams = nextMethod.GetParameters(); |
| 119 | + nextUsesIntPtr = nextParams.Length > 2 && nextParams[2].ParameterType == typeof(IntPtr); |
| 120 | + } |
110 | 121 |
|
111 | 122 | public void ComObjReset() => ComInvoke("Reset"); |
112 | 123 |
|
113 | 124 | public HRESULT ComObjTryGetNext(uint celt, TItem?[] rgelt, out uint celtFetched) |
114 | 125 | { |
115 | | - var para = new object[] { celt, rgelt, 0U }; |
116 | | - var hr = (HRESULT?)ComInvoke("Next", para); |
117 | | - celtFetched = (uint)para[2]; |
118 | | - return hr ?? HRESULT.S_OK; |
| 126 | + IntPtr fetchedPtr = IntPtr.Zero; |
| 127 | + try |
| 128 | + { |
| 129 | + var para = nextUsesIntPtr |
| 130 | + ? new object[] { celt, rgelt, fetchedPtr = Marshal.AllocHGlobal(sizeof(uint)) } |
| 131 | + : new object[] { celt, rgelt, 0U }; |
| 132 | + if (fetchedPtr != IntPtr.Zero) |
| 133 | + Marshal.WriteInt32(fetchedPtr, 0); |
| 134 | + var hr = (HRESULT?)ComInvoke("Next", para); |
| 135 | + celtFetched = nextUsesIntPtr ? unchecked((uint)Marshal.ReadInt32(fetchedPtr)) : (uint)para[2]; |
| 136 | + return hr ?? HRESULT.S_OK; |
| 137 | + } |
| 138 | + finally |
| 139 | + { |
| 140 | + if (fetchedPtr != IntPtr.Zero) |
| 141 | + Marshal.FreeHGlobal(fetchedPtr); |
| 142 | + } |
119 | 143 | } |
120 | 144 |
|
121 | 145 | private object? ComInvoke(string meth, object[]? p = null) => typeof(T).GetMethod(meth)?.Invoke(obj, p); |
|
0 commit comments