// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace System.Data.ProviderBase
{
    // We wrap the interface as a native IUnknown IntPtr so that every
    // thread that creates a connection will fake the correct context when
    // in transactions, otherwise everything is marshalled.  We do this
    // for two reasons: first for the connection pooler, this is a significant
    // performance gain, second for the OLE DB provider, it doesn't marshal.

    internal class WrappedIUnknown : SafeHandle
    {
        public WrappedIUnknown() : base(IntPtr.Zero, true)
        {
        }

        internal WrappedIUnknown(object? unknown) : this()
        {
            if (null != unknown)
            {
                RuntimeHelpers.PrepareConstrainedRegions();
                try
                { }
                finally
                {
                    base.handle = Marshal.GetIUnknownForObject(unknown);
                }
            }
        }

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

        internal object ComWrapper()
        {
            // NOTE: Method, instead of property, to avoid being evaluated at
            // runtime in the debugger.
            object? value = null;
            bool mustRelease = false;
            RuntimeHelpers.PrepareConstrainedRegions();
            try
            {
                DangerousAddRef(ref mustRelease);

                IntPtr handle = DangerousGetHandle();
                value = Marshal.GetObjectForIUnknown(handle);
            }
            finally
            {
                if (mustRelease)
                {
                    DangerousRelease();
                }
            }
            return value;
        }

        protected override bool ReleaseHandle()
        {
            // NOTE: The SafeHandle class guarantees this will be called exactly once.
            IntPtr ptr = base.handle;
            base.handle = IntPtr.Zero;
            if (IntPtr.Zero != ptr)
            {
                Marshal.Release(ptr);
            }
            return true;
        }
    }
}
