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 {
if (typeof(T) == typeof(string)) {
if (value->VarType != VARENUM.VT_BSTR) {
DefaultInterpolatedStringHandler 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 defaultInterpolatedStringHandler2 = new DefaultInterpolatedStringHandler(55, 2);
defaultInterpolatedStringHandler2.AppendLiteral("Wanted SafeArrayScope<");
defaultInterpolatedStringHandler2.AppendFormatted<Type>(typeof(T));
defaultInterpolatedStringHandler2.AppendLiteral("> but got SAFEARRAY with VarType=");
defaultInterpolatedStringHandler2.AppendFormatted<VARENUM>(value->VarType);
throw new ArgumentException(defaultInterpolatedStringHandler2.ToStringAndClear());
}
} else if (typeof(T) == typeof(double)) {
if (value->VarType != VARENUM.VT_R8) {
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler3 = new DefaultInterpolatedStringHandler(55, 2);
defaultInterpolatedStringHandler3.AppendLiteral("Wanted SafeArrayScope<");
defaultInterpolatedStringHandler3.AppendFormatted<Type>(typeof(T));
defaultInterpolatedStringHandler3.AppendLiteral("> but got SAFEARRAY with VarType=");
defaultInterpolatedStringHandler3.AppendFormatted<VARENUM>(value->VarType);
throw new ArgumentException(defaultInterpolatedStringHandler3.ToStringAndClear());
}
} else if (typeof(T) == typeof(IntPtr)) {
if (value->VarType != VARENUM.VT_UNKNOWN) {
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler4 = new DefaultInterpolatedStringHandler(55, 2);
defaultInterpolatedStringHandler4.AppendLiteral("Wanted SafeArrayScope<");
defaultInterpolatedStringHandler4.AppendFormatted<Type>(typeof(T));
defaultInterpolatedStringHandler4.AppendLiteral("> but got SAFEARRAY with VarType=");
defaultInterpolatedStringHandler4.AppendFormatted<VARENUM>(value->VarType);
throw new ArgumentException(defaultInterpolatedStringHandler4.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 defaultInterpolatedStringHandler5 = new DefaultInterpolatedStringHandler(55, 2);
defaultInterpolatedStringHandler5.AppendLiteral("Wanted SafeArrayScope<");
defaultInterpolatedStringHandler5.AppendFormatted<Type>(typeof(T));
defaultInterpolatedStringHandler5.AppendLiteral("> but got SAFEARRAY with VarType=");
defaultInterpolatedStringHandler5.AppendFormatted<VARENUM>(value->VarType);
throw new ArgumentException(defaultInterpolatedStringHandler5.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));
}
}
}