File: PropertyInfoSet.cs
Web Access
Project: src\src\runtime\src\libraries\System.Data.OleDb\src\System.Data.OleDb.csproj (System.Data.OleDb)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Data.Common;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Data.OleDb
{
    internal sealed class OleDbPropertyInfo
    {
        public Guid _propertySet;
        public int _propertyID;
        public string? _description;
        public string? _lowercase;
        public Type? _type;

        public int _flags;
        public int _vtype;
        public object? _supportedValues;

        public object? _defaultValue;
    }

    internal sealed class PropertyInfoSet : SafeHandle
    {
        private readonly int setCount;
        private IntPtr descBuffer;

        internal PropertyInfoSet(UnsafeNativeMethods.IDBProperties idbProperties, PropertyIDSet propIDSet) : base(IntPtr.Zero, true)
        {
            OleDbHResult hr;
            int propIDSetCount = propIDSet.Count;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            { }
            finally
            {
                hr = idbProperties.GetPropertyInfo(propIDSetCount, propIDSet, out this.setCount, out base.handle, out this.descBuffer);
            }
            if ((0 <= hr) && (IntPtr.Zero != handle))
            {
                SafeNativeMethods.Wrapper.ClearErrorInfo();
            }
        }

        public override bool IsInvalid
        {
            get
            {
                return ((IntPtr.Zero == base.handle) && (IntPtr.Zero == this.descBuffer));
            }
        }

        internal Dictionary<string, OleDbPropertyInfo>? GetValues()
        {
            Dictionary<string, OleDbPropertyInfo>? propertyLookup = null;

            bool mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                DangerousAddRef(ref mustRelease);
                if (IntPtr.Zero != this.handle)
                {
                    propertyLookup = new Dictionary<string, OleDbPropertyInfo>(StringComparer.OrdinalIgnoreCase);

                    IntPtr setPtr = this.handle;
                    ItagDBPROPINFO propinfo = OleDbStructHelpers.CreateTagDbPropInfo();
                    tagDBPROPINFOSET propinfoset = new tagDBPROPINFOSET();

                    for (int i = 0; i < setCount; ++i, setPtr = ADP.IntPtrOffset(setPtr, ODB.SizeOf_tagDBPROPINFOSET))
                    {
                        Marshal.PtrToStructure(setPtr, propinfoset);

                        int infoCount = propinfoset.cPropertyInfos;
                        IntPtr infoPtr = propinfoset.rgPropertyInfos;
                        for (int k = 0; k < infoCount; ++k, infoPtr = ADP.IntPtrOffset(infoPtr, ODB.SizeOf_tagDBPROPINFO))
                        {
                            Marshal.PtrToStructure(infoPtr, propinfo);

                            OleDbPropertyInfo propertyInfo = new OleDbPropertyInfo();
                            propertyInfo._propertySet = propinfoset.guidPropertySet;
                            propertyInfo._propertyID = propinfo.dwPropertyID;
                            propertyInfo._flags = propinfo.dwFlags;
                            propertyInfo._vtype = propinfo.vtType;
                            propertyInfo._supportedValues = propinfo.vValue;
                            propertyInfo._description = propinfo.pwszDescription;
                            propertyInfo._lowercase = propinfo.pwszDescription!.ToLowerInvariant();
                            propertyInfo._type = PropertyInfoSet.FromVtType(propinfo.vtType);

                            propertyLookup[propertyInfo._lowercase] = propertyInfo;
                        }
                    }
                }
            }
            finally
            {
                if (mustRelease)
                {
                    DangerousRelease();
                }
            }
            return propertyLookup;
        }

        protected override bool ReleaseHandle()
        {
            // NOTE: The SafeHandle class guarantees this will be called exactly once and is non-interrutible.
            IntPtr ptr = base.handle;
            base.handle = IntPtr.Zero;
            if (IntPtr.Zero != ptr)
            {
                int count = this.setCount;
                for (int i = 0; i < count; ++i)
                {
                    int offset = (i * ODB.SizeOf_tagDBPROPINFOSET);
                    IntPtr infoPtr = Marshal.ReadIntPtr(ptr, offset);
                    if (IntPtr.Zero != infoPtr)
                    {
                        int infoCount = Marshal.ReadInt32(ptr, offset + IntPtr.Size);

                        for (int k = 0; k < infoCount; ++k)
                        {
                            IntPtr valuePtr = ADP.IntPtrOffset(infoPtr, (k * ODB.SizeOf_tagDBPROPINFO) + ODB.OffsetOf_tagDBPROPINFO_Value);
                            Interop.Ole32.PropVariantClear(valuePtr);
                        }
                        Interop.Ole32.CoTaskMemFree(infoPtr); // was allocated by provider
                    }
                }
                Interop.Ole32.CoTaskMemFree(ptr);
            }

            ptr = this.descBuffer;
            this.descBuffer = IntPtr.Zero;
            if (IntPtr.Zero != ptr)
            {
                Interop.Ole32.CoTaskMemFree(ptr);
            }
            return true;
        }

        internal static Type? FromVtType(int vartype) =>
            (VarEnum)vartype switch
            {
                VarEnum.VT_EMPTY => null,
                VarEnum.VT_NULL => typeof(System.DBNull),
                VarEnum.VT_I2 => typeof(short),
                VarEnum.VT_I4 => typeof(int),
                VarEnum.VT_R4 => typeof(float),
                VarEnum.VT_R8 => typeof(double),
                VarEnum.VT_CY => typeof(decimal),
                VarEnum.VT_DATE => typeof(System.DateTime),
                VarEnum.VT_BSTR => typeof(string),
                VarEnum.VT_DISPATCH => typeof(object),
                VarEnum.VT_ERROR => typeof(int),
                VarEnum.VT_BOOL => typeof(bool),
                VarEnum.VT_VARIANT => typeof(object),
                VarEnum.VT_UNKNOWN => typeof(object),
                VarEnum.VT_DECIMAL => typeof(decimal),
                VarEnum.VT_I1 => typeof(sbyte),
                VarEnum.VT_UI1 => typeof(byte),
                VarEnum.VT_UI2 => typeof(ushort),
                VarEnum.VT_UI4 => typeof(uint),
                VarEnum.VT_I8 => typeof(long),
                VarEnum.VT_UI8 => typeof(ulong),
                VarEnum.VT_INT => typeof(int),
                VarEnum.VT_UINT => typeof(uint),
                _ => typeof(object),
            };
    }
}