File: System\Windows\Forms\OLE\DataObject.Composition.cs
Web Access
Project: src\src\System.Windows.Forms\src\System.Windows.Forms.csproj (System.Windows.Forms)
// 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.InteropServices.ComTypes;
using Com = Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
 
namespace System.Windows.Forms;
 
public unsafe partial class DataObject
{
    /// <summary>
    ///  Contains the logic to move between <see cref="IDataObject"/>, <see cref="Com.IDataObject.Interface"/>,
    ///  and <see cref="ComTypes.IDataObject"/> calls.
    /// </summary>
    internal unsafe partial class Composition : IDataObject, Com.IDataObject.Interface, ComTypes.IDataObject
    {
        private const Com.TYMED AllowedTymeds = Com.TYMED.TYMED_HGLOBAL | Com.TYMED.TYMED_ISTREAM | Com.TYMED.TYMED_GDI;
 
        // We use this to identify that a stream is actually a serialized object. On read, we don't know if the contents
        // of a stream were saved "raw" or if the stream is really pointing to a serialized object. If we saved an object,
        // we prefix it with this guid.
        private static readonly byte[] s_serializedObjectID =
        [
            // FD9EA796-3B13-4370-A679-56106BB288FB
            0x96, 0xa7, 0x9e, 0xfd,
            0x13, 0x3b,
            0x70, 0x43,
            0xa6, 0x79, 0x56, 0x10, 0x6b, 0xb2, 0x88, 0xfb
        ];
 
        private readonly IDataObject _winFormsDataObject;
        private readonly Com.IDataObject.Interface _nativeDataObject;
        private readonly ComTypes.IDataObject _runtimeDataObject;
 
        // Feature switch, when set to false, BinaryFormatter is not supported in trimmed applications.
        // This field, using the default BinaryFormatter switch, is used to control trim warnings related
        // to using BinaryFormatter in WinForms trimming. The trimmer will generate a warning when set
        // to true and will not generate a warning when set to false.
        [FeatureSwitchDefinition("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization")]
        internal static bool EnableUnsafeBinaryFormatterInNativeObjectSerialization =>
            !AppContext.TryGetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", out bool isEnabled) || isEnabled;
 
        private Composition(IDataObject winFormsDataObject, Com.IDataObject.Interface nativeDataObject, ComTypes.IDataObject runtimeDataObject)
        {
            _winFormsDataObject = winFormsDataObject;
            _nativeDataObject = nativeDataObject;
            _runtimeDataObject = runtimeDataObject;
            if (winFormsDataObject is not NativeToWinFormsAdapter and not DataStore)
            {
                OriginalIDataObject = winFormsDataObject;
            }
        }
 
        public static Composition CreateFromWinFormsDataObject(IDataObject winFormsDataObject)
        {
            WinFormsToNativeAdapter winFormsToNative = new(winFormsDataObject);
            NativeToRuntimeAdapter nativeToRuntime = new(ComHelpers.GetComPointer<Com.IDataObject>(winFormsToNative));
            return new(winFormsDataObject, winFormsToNative, nativeToRuntime);
        }
 
        public static Composition CreateFromNativeDataObject(Com.IDataObject* nativeDataObject)
        {
            // Add ref so each adapter can take ownership of the native data object.
            nativeDataObject->AddRef();
            nativeDataObject->AddRef();
            NativeToWinFormsAdapter nativeToWinForms = new(nativeDataObject);
            NativeToRuntimeAdapter nativeToRuntime = new(nativeDataObject);
            return new(nativeToWinForms, nativeToWinForms, nativeToRuntime);
        }
 
        public static Composition CreateFromRuntimeDataObject(ComTypes.IDataObject runtimeDataObject)
        {
            RuntimeToNativeAdapter runtimeToNative = new(runtimeDataObject);
            NativeToWinFormsAdapter nativeToWinForms = new(ComHelpers.GetComPointer<Com.IDataObject>(runtimeToNative));
            return new(nativeToWinForms, runtimeToNative, runtimeDataObject);
        }
 
        /// <summary>
        ///  The <see cref="IDataObject"/> the user passed to us, so that it can be passed back out.
        /// </summary>
        public IDataObject? OriginalIDataObject { get; private set; }
 
        /// <summary>
        ///  We are restricting binary serialization and deserialization of formats that represent strings, bitmaps or OLE types.
        /// </summary>
        /// <param name="format">format name</param>
        /// <returns><see langword="true" /> - serialize only safe types, strings or bitmaps.</returns>
        private static bool RestrictDeserializationToSafeTypes(string format) =>
            format is DataFormats.StringConstant
                or BitmapFullName
                or DataFormats.CsvConstant
                or DataFormats.DibConstant
                or DataFormats.DifConstant
                or DataFormats.LocaleConstant
                or DataFormats.PenDataConstant
                or DataFormats.RiffConstant
                or DataFormats.SymbolicLinkConstant
                or DataFormats.TiffConstant
                or DataFormats.WaveAudioConstant
                or DataFormats.BitmapConstant
                or DataFormats.EmfConstant
                or DataFormats.PaletteConstant
                or DataFormats.WmfConstant;
 
        #region IDataObject
        public object? GetData(string format, bool autoConvert) => _winFormsDataObject.GetData(format, autoConvert);
        public object? GetData(string format) => _winFormsDataObject.GetData(format);
        public object? GetData(Type format) => _winFormsDataObject.GetData(format);
        public bool GetDataPresent(string format, bool autoConvert) => _winFormsDataObject.GetDataPresent(format, autoConvert);
        public bool GetDataPresent(string format) => _winFormsDataObject.GetDataPresent(format);
        public bool GetDataPresent(Type format) => _winFormsDataObject.GetDataPresent(format);
        public string[] GetFormats(bool autoConvert) => _winFormsDataObject.GetFormats(autoConvert);
        public string[] GetFormats() => _winFormsDataObject.GetFormats();
        public void SetData(string format, bool autoConvert, object? data) => _winFormsDataObject.SetData(format, autoConvert, data);
        public void SetData(string format, object? data) => _winFormsDataObject.SetData(format, data);
        public void SetData(Type format, object? data) => _winFormsDataObject.SetData(format, data);
        public void SetData(object? data) => _winFormsDataObject.SetData(data);
        #endregion
 
        #region Com.IDataObject.Interface
        HRESULT Com.IDataObject.Interface.DAdvise(Com.FORMATETC* pformatetc, uint advf, Com.IAdviseSink* pAdvSink, uint* pdwConnection) => _nativeDataObject.DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
        HRESULT Com.IDataObject.Interface.DUnadvise(uint dwConnection) => _nativeDataObject.DUnadvise(dwConnection);
        HRESULT Com.IDataObject.Interface.EnumDAdvise(Com.IEnumSTATDATA** ppenumAdvise) => _nativeDataObject.EnumDAdvise(ppenumAdvise);
        HRESULT Com.IDataObject.Interface.EnumFormatEtc(uint dwDirection, Com.IEnumFORMATETC** ppenumFormatEtc) => _nativeDataObject.EnumFormatEtc(dwDirection, ppenumFormatEtc);
        HRESULT Com.IDataObject.Interface.GetCanonicalFormatEtc(Com.FORMATETC* pformatectIn, Com.FORMATETC* pformatetcOut) => _nativeDataObject.GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
        HRESULT Com.IDataObject.Interface.GetData(Com.FORMATETC* pformatetcIn, Com.STGMEDIUM* pmedium) => _nativeDataObject.GetData(pformatetcIn, pmedium);
        HRESULT Com.IDataObject.Interface.GetDataHere(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium) => _nativeDataObject.GetDataHere(pformatetc, pmedium);
        HRESULT Com.IDataObject.Interface.QueryGetData(Com.FORMATETC* pformatetc) => _nativeDataObject.QueryGetData(pformatetc);
        HRESULT Com.IDataObject.Interface.SetData(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium, BOOL fRelease) => _nativeDataObject.SetData(pformatetc, pmedium, fRelease);
        #endregion
 
        #region ComTypes.IDataObject
        int ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) => _runtimeDataObject.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
        void ComTypes.IDataObject.DUnadvise(int connection) => _runtimeDataObject.DUnadvise(connection);
        int ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA? enumAdvise) => _runtimeDataObject.EnumDAdvise(out enumAdvise);
        IEnumFORMATETC ComTypes.IDataObject.EnumFormatEtc(DATADIR direction) => _runtimeDataObject.EnumFormatEtc(direction);
        int ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) => _runtimeDataObject.GetCanonicalFormatEtc(ref formatIn, out formatOut);
        void ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium) => _runtimeDataObject.GetData(ref format, out medium);
        void ComTypes.IDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) => _runtimeDataObject.GetDataHere(ref format, ref medium);
        int ComTypes.IDataObject.QueryGetData(ref FORMATETC format) => _runtimeDataObject.QueryGetData(ref format);
        void ComTypes.IDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) => _runtimeDataObject.SetData(ref formatIn, ref medium, release);
        #endregion
    }
}