<PackageReference Include="System.Drawing.Common" Version="10.0.0-preview.3.25173.2" />

SafeArrayScope<T>

struct SafeArrayScope<T>
Helper to scope lifetime of a SAFEARRAY created via SafeArrayCreate Destroys the SAFEARRAY (if any) when disposed. Note that this scope currently only works for a one dimensional SAFEARRAY.
using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Windows.Win32.Foundation; using Windows.Win32.System.Variant; namespace Windows.Win32.System.Com { [CompilerFeatureRequired("RefStructs")] internal readonly ref struct SafeArrayScope<[Nullable(2)] T> { private readonly IntPtr _value; public unsafe SAFEARRAY* Value => (SAFEARRAY*)(long)_value; [Nullable(2)] public unsafe T this[int i] { [NullableContext(2)] get { if (typeof(T) == typeof(string)) { using (BSTR bSTR = GetElement<BSTR>(i)) { return (T)(object)bSTR.ToString(); } } if (typeof(T) == typeof(int)) return (T)(object)GetElement<int>(i); if (typeof(T) == typeof(double)) return (T)(object)GetElement<double>(i); if (typeof(T) == typeof(IntPtr)) return (T)(object)GetElement<IntPtr>(i); if (typeof(T) == typeof(object)) { using (VARIANT vARIANT = GetElement<VARIANT>(i)) { return (T)vARIANT.ToObject(); } } return default(T); } [NullableContext(2)] set { if (Value->VarType == VARENUM.VT_VARIANT) { VARIANT vARIANT = VARIANT.FromObject(value); try { PutElement(i, &vARIANT); } finally { ((IDisposable)vARIANT).Dispose(); } } else { string text = value as string; if (text != null) { using (BSTR value2 = new BSTR(text)) PutElement(i, (char*)value2); } else if (((object)value) is int) { int num = value as int; PutElement(i, &num); } else if (((object)value) is double) { double num2 = value as double; PutElement(i, &num2); } else if (((object)value) is IntPtr) { IntPtr value3 = value as IntPtr; PutElement(i, (void*)(long)value3); } } } } public unsafe int Length => (int)Value->GetBounds(0).cElements; public bool IsNull => _value == (IntPtr)0; public bool IsEmpty => Length == 0; public unsafe SafeArrayScope(SAFEARRAY* value) { if (value == null) _value = (IntPtr)value; else { DefaultInterpolatedStringHandler defaultInterpolatedStringHandler; if (typeof(T) == typeof(string)) { if (value->VarType != VARENUM.VT_BSTR) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 2); defaultInterpolatedStringHandler.AppendLiteral("Wanted SafeArrayScope<"); defaultInterpolatedStringHandler.AppendFormatted<Type>(typeof(T)); defaultInterpolatedStringHandler.AppendLiteral("> but got SAFEARRAY with VarType="); defaultInterpolatedStringHandler.AppendFormatted<VARENUM>(value->VarType); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } } else if (typeof(T) == typeof(int)) { if (value->VarType != VARENUM.VT_I4) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 2); defaultInterpolatedStringHandler.AppendLiteral("Wanted SafeArrayScope<"); defaultInterpolatedStringHandler.AppendFormatted<Type>(typeof(T)); defaultInterpolatedStringHandler.AppendLiteral("> but got SAFEARRAY with VarType="); defaultInterpolatedStringHandler.AppendFormatted<VARENUM>(value->VarType); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } } else if (typeof(T) == typeof(double)) { if (value->VarType != VARENUM.VT_R8) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 2); defaultInterpolatedStringHandler.AppendLiteral("Wanted SafeArrayScope<"); defaultInterpolatedStringHandler.AppendFormatted<Type>(typeof(T)); defaultInterpolatedStringHandler.AppendLiteral("> but got SAFEARRAY with VarType="); defaultInterpolatedStringHandler.AppendFormatted<VARENUM>(value->VarType); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } } else if (typeof(T) == typeof(IntPtr)) { if (value->VarType != VARENUM.VT_UNKNOWN) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 2); defaultInterpolatedStringHandler.AppendLiteral("Wanted SafeArrayScope<"); defaultInterpolatedStringHandler.AppendFormatted<Type>(typeof(T)); defaultInterpolatedStringHandler.AppendLiteral("> but got SAFEARRAY with VarType="); defaultInterpolatedStringHandler.AppendFormatted<VARENUM>(value->VarType); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } } else { if (typeof(T).IsAssignableTo(typeof(IComIID))) throw new ArgumentException("Use ComSafeArrayScope instead"); if (!(typeof(T) == typeof(object))) throw new ArgumentException("Unknown type"); if (value->VarType != VARENUM.VT_VARIANT) { defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(55, 2); defaultInterpolatedStringHandler.AppendLiteral("Wanted SafeArrayScope<"); defaultInterpolatedStringHandler.AppendFormatted<Type>(typeof(T)); defaultInterpolatedStringHandler.AppendLiteral("> but got SAFEARRAY with VarType="); defaultInterpolatedStringHandler.AppendFormatted<VARENUM>(value->VarType); throw new ArgumentException(defaultInterpolatedStringHandler.ToStringAndClear()); } } _value = (IntPtr)value; } } public unsafe SafeArrayScope(uint size) { VARENUM vt; if (typeof(T) == typeof(string)) vt = VARENUM.VT_BSTR; else if (typeof(T) == typeof(int)) { vt = VARENUM.VT_I4; } else if (typeof(T) == typeof(double)) { vt = VARENUM.VT_R8; } else { if (typeof(T) == typeof(IntPtr) || typeof(T).IsAssignableTo(typeof(IComIID))) throw new ArgumentException("Use ComSafeArrayScope instead"); if (!(typeof(T) == typeof(object))) throw new ArgumentException("Unknown type"); vt = VARENUM.VT_VARIANT; } SAFEARRAYBOUND sAFEARRAYBOUND = new SAFEARRAYBOUND { cElements = size, lLbound = 0 }; _value = (IntPtr)PInvokeCore.SafeArrayCreate(vt, 1, &sAFEARRAYBOUND); if (_value == (IntPtr)0) throw new InvalidOperationException("Unable to create SAFEARRAY"); } [NullableContext(1)] public SafeArrayScope(T[] array) { this = new SafeArrayScope<T>((uint)array.Length); for (int i = 0; i < array.Length; i++) { this[i] = array[i]; } } private unsafe TReturn GetElement<[IsUnmanaged] TReturn>(int index) where TReturn : struct { int reference = index; TReturn result = default(TReturn); fixed (int* rgIndices = &new Span<int>(ref reference).GetPinnableReference()) { PInvokeCore.SafeArrayGetElement(this.Value, rgIndices, &result).ThrowOnFailure((IntPtr)0); } return result; } private unsafe void PutElement(int index, void* value) { int reference = index; fixed (int* rgIndices = &new Span<int>(ref reference).GetPinnableReference()) { PInvokeCore.SafeArrayPutElement((SAFEARRAY*)(long)_value, rgIndices, value).ThrowOnFailure((IntPtr)0); } } public unsafe void Dispose() { SAFEARRAY* value = (SAFEARRAY*)(long)_value; *(void**)ref this = null; if (value != null) PInvokeCore.SafeArrayDestroy(value).ThrowOnFailure((IntPtr)0); } public unsafe static explicit operator VARIANT([In] [IsReadOnly] [Nullable(new byte[] { 0, 1 })] ref SafeArrayScope<T> scope) { VARIANT result = default(VARIANT); result.vt = (VARENUM.VT_ARRAY | scope.Value->VarType); result.data = new VARIANT._Anonymous_e__Union._Anonymous_e__Struct._Anonymous_e__Union { parray = scope._value }; return result; } public unsafe static implicit operator SAFEARRAY*([In] [IsReadOnly] [Nullable(new byte[] { 0, 1 })] ref SafeArrayScope<T> scope) { return (SAFEARRAY*)(long)scope._value; } public static implicit operator IntPtr([In] [IsReadOnly] [Nullable(new byte[] { 0, 1 })] ref SafeArrayScope<T> scope) { return scope._value; } public unsafe static implicit operator SAFEARRAY**([In] [IsReadOnly] [Nullable(new byte[] { 0, 1 })] ref SafeArrayScope<T> scope) { return (SAFEARRAY**)Unsafe.AsPointer<IntPtr>(ref Unsafe.AsRef<IntPtr>(ref scope._value)); } public unsafe static implicit operator void**([In] [IsReadOnly] [Nullable(new byte[] { 0, 1 })] ref SafeArrayScope<T> scope) { return (void**)Unsafe.AsPointer<IntPtr>(ref Unsafe.AsRef<IntPtr>(ref scope._value)); } } }