internal class DataTransferToOleDataObjectWrapper()

in src/Windows/Avalonia.Win32/DataTransferToOleDataObjectWrapper.cs [20:189]


internal class DataTransferToOleDataObjectWrapper(IDataTransfer dataTransfer)
    : CallbackBase, Win32Com.IDataObject
{
    private class FormatEnumerator : CallbackBase, Win32Com.IEnumFORMATETC
    {
        private readonly FORMATETC[] _formats;
        private uint _current;

        private FormatEnumerator(FORMATETC[] formats, uint current)
        {
            _formats = formats;
            _current = current;
        }

        public FormatEnumerator(IReadOnlyList<DataFormat> dataFormats)
        {
            _formats = dataFormats.Select(OleDataObjectHelper.ToFormatEtc).ToArray();
            _current = 0;
        }

        public unsafe uint Next(uint celt, FORMATETC* rgelt, uint* results)
        {
            if (rgelt == null)
                return (uint)HRESULT.E_INVALIDARG;

            uint i = 0;
            while (i < celt && _current < _formats.Length)
            {
                rgelt[i] = _formats[_current];
                _current++;
                i++;
            }

            if (i != celt)
                return (uint)HRESULT.S_FALSE;

            // "results" parameter can be NULL if celt is 1.
            if (celt != 1 || results != null)
                *results = i;
            return (uint)HRESULT.S_OK;
        }

        public uint Skip(uint celt)
        {
            _current += Math.Min(celt, int.MaxValue - _current);
            if (_current >= _formats.Length)
                return (uint)HRESULT.S_FALSE;
            return (uint)HRESULT.S_OK;
        }

        public void Reset()
        {
            _current = 0;
        }

        public Win32Com.IEnumFORMATETC Clone()
        {
            return new FormatEnumerator(_formats, _current);
        }
    }

    public IDataTransfer? DataTransfer { get; private set; } = dataTransfer;

    public bool IsDisposed
        => DataTransfer is null;

    public event Action? OnDestroyed;

    unsafe int Win32Com.IDataObject.DAdvise(FORMATETC* pFormatetc, int advf, void* adviseSink)
        => (int)HRESULT.S_OK;

    void Win32Com.IDataObject.DUnadvise(int connection)
        => throw new COMException(nameof(OLE_E_ADVISENOTSUPPORTED), unchecked((int)OLE_E_ADVISENOTSUPPORTED));

    unsafe void* Win32Com.IDataObject.EnumDAdvise()
        => null;

    Win32Com.IEnumFORMATETC Win32Com.IDataObject.EnumFormatEtc(int direction)
    {
        if (DataTransfer is null)
            throw new COMException(nameof(COR_E_OBJECTDISPOSED), unchecked((int)HRESULT.E_NOTIMPL));

        if ((DATADIR)direction == DATADIR.DATADIR_GET)
            return new FormatEnumerator(DataTransfer.Formats);

        throw new COMException(nameof(HRESULT.E_NOTIMPL), unchecked((int)HRESULT.E_NOTIMPL));
    }

    unsafe FORMATETC Win32Com.IDataObject.GetCanonicalFormatEtc(FORMATETC* formatIn)
        => throw new COMException(nameof(HRESULT.E_NOTIMPL), unchecked((int)HRESULT.E_NOTIMPL));

    unsafe uint Win32Com.IDataObject.GetData(FORMATETC* format, STGMEDIUM* medium)
    {
        if (!ValidateFormat(format, out var result, out var dataFormat))
            return result;

        *medium = default;
        medium->tymed = TYMED.TYMED_HGLOBAL;
        return OleDataObjectHelper.WriteDataToHGlobal(DataTransfer, dataFormat, ref medium->unionmember);
    }

    unsafe uint Win32Com.IDataObject.GetDataHere(FORMATETC* format, STGMEDIUM* medium)
    {
        if (!ValidateFormat(format, out var result, out var dataFormat))
            return result;

        if (medium->unionmember == IntPtr.Zero)
            return STG_E_MEDIUMFULL;

        return OleDataObjectHelper.WriteDataToHGlobal(DataTransfer, dataFormat, ref medium->unionmember);
    }

    unsafe uint Win32Com.IDataObject.QueryGetData(FORMATETC* format)
    {
        if (!ValidateFormat(format, out var result, out _))
            return result;

        return (uint)HRESULT.S_OK;
    }

    [MemberNotNullWhen(true, nameof(DataTransfer))]
    private unsafe bool ValidateFormat(FORMATETC* format, out uint result, [NotNullWhen(true)] out DataFormat? dataFormat)
    {
        dataFormat = null;

        if (!format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
        {
            result = DV_E_TYMED;
            dataFormat = null;
            return false;
        }

        if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
        {
            result = DV_E_DVASPECT;
            return false;
        }

        if (DataTransfer is null)
        {
            result = COR_E_OBJECTDISPOSED;
            return false;
        }

        dataFormat = ClipboardFormatRegistry.GetFormatById(format->cfFormat);
        if (!DataTransfer.Contains(dataFormat))
        {
            result = DV_E_FORMATETC;
            return false;
        }

        result = (uint)HRESULT.S_OK;
        return true;
    }

    unsafe uint Win32Com.IDataObject.SetData(FORMATETC* pformatetc, STGMEDIUM* pmedium, int fRelease)
        => (uint)HRESULT.E_NOTIMPL;

    protected override void Destroyed()
    {
        OnDestroyed?.Invoke();
        ReleaseDataTransfer();
    }

    public void ReleaseDataTransfer()
    {
        DataTransfer?.Dispose();
        DataTransfer = null;
    }
}