File: System\Private\Windows\Ole\Composition.cs
Web Access
Project: src\src\System.Private.Windows.Core\src\System.Private.Windows.Core.csproj (System.Private.Windows.Core)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Reflection.Metadata;
using Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
 
namespace System.Private.Windows.Ole;
 
/// <summary>
///  Contains the logic to move between <see cref="IDataObjectInternal"/>, <see cref="IDataObject.Interface"/>,
///  and <see cref="ComTypes.IDataObject"/> calls.
/// </summary>
internal sealed unsafe partial class Composition<TRuntime, TDataFormat>
    : IDataObjectInternal, IDataObject.Interface, ComTypes.IDataObject
    where TDataFormat : IDataFormat<TDataFormat>
    where TRuntime : IRuntime<TDataFormat>
{
    private const TYMED AllowedTymeds = TYMED.TYMED_HGLOBAL | TYMED.TYMED_ISTREAM | 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
    ];
 
    internal IDataObjectInternal ManagedDataObject { get; }
    private readonly IDataObject.Interface _nativeDataObject;
    private readonly ComTypes.IDataObject _runtimeDataObject;
 
    private Composition(IDataObjectInternal managedDataObject, IDataObject.Interface nativeDataObject, ComTypes.IDataObject runtimeDataObject)
    {
        ManagedDataObject = managedDataObject;
        _nativeDataObject = nativeDataObject;
        _runtimeDataObject = runtimeDataObject;
    }
 
    public static Composition<TRuntime, TDataFormat> CreateFromManagedDataObject(IDataObjectInternal managedDataObject)
    {
        ManagedToNativeAdapter winFormsToNative = new(managedDataObject);
        NativeToRuntimeAdapter nativeToRuntime = new(ComHelpers.GetComPointer<IDataObject>(winFormsToNative));
        return new(managedDataObject, winFormsToNative, nativeToRuntime);
    }
 
    public static Composition<TRuntime, TDataFormat> CreateFromNativeDataObject(IDataObject* nativeDataObject)
    {
        // Add ref so each adapter can take ownership of the native data object.
        nativeDataObject->AddRef();
        nativeDataObject->AddRef();
        NativeToManagedAdapter nativeToWinForms = new(nativeDataObject);
        NativeToRuntimeAdapter nativeToRuntime = new(nativeDataObject);
        return new(nativeToWinForms, nativeToWinForms, nativeToRuntime);
    }
 
    public static Composition<TRuntime, TDataFormat> CreateFromRuntimeDataObject(ComTypes.IDataObject runtimeDataObject)
    {
        RuntimeToNativeAdapter runtimeToNative = new(runtimeDataObject);
        NativeToManagedAdapter nativeToWinForms = new(ComHelpers.GetComPointer<IDataObject>(runtimeToNative));
        return new(nativeToWinForms, runtimeToNative, runtimeDataObject);
    }
 
    #region IDataObjectInternal
    public object? GetData(string format, bool autoConvert) => ManagedDataObject.GetData(format, autoConvert);
    public object? GetData(string format) => ManagedDataObject.GetData(format);
    public object? GetData(Type format) => ManagedDataObject.GetData(format);
    public bool GetDataPresent(string format, bool autoConvert) => ManagedDataObject.GetDataPresent(format, autoConvert);
    public bool GetDataPresent(string format) => ManagedDataObject.GetDataPresent(format);
    public bool GetDataPresent(Type format) => ManagedDataObject.GetDataPresent(format);
    public string[] GetFormats(bool autoConvert) => ManagedDataObject.GetFormats(autoConvert);
    public string[] GetFormats() => ManagedDataObject.GetFormats();
    public void SetData(string format, bool autoConvert, object? data) => ManagedDataObject.SetData(format, autoConvert, data);
    public void SetData(string format, object? data) => ManagedDataObject.SetData(format, data);
    public void SetData(Type format, object? data) => ManagedDataObject.SetData(format, data);
    public void SetData(object? data) => ManagedDataObject.SetData(data);
    #endregion
 
    #region ITypedDataObject
    public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
        string format,
        Func<TypeName, Type> resolver,
        bool autoConvert,
        [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
            ManagedDataObject.TryGetData(format, resolver, autoConvert, out data);
 
    public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
        string format,
        bool autoConvert,
        [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
            ManagedDataObject.TryGetData(format, autoConvert, out data);
 
    public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
        string format,
        [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
            ManagedDataObject.TryGetData(format, out data);
 
    public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
        [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
            ManagedDataObject.TryGetData(typeof(T).FullName.OrThrowIfNull(), out data);
    #endregion
 
    #region Com.IDataObject.Interface
    public HRESULT DAdvise(FORMATETC* pformatetc, uint advf, IAdviseSink* pAdvSink, uint* pdwConnection) => _nativeDataObject.DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
    public HRESULT DUnadvise(uint dwConnection) => _nativeDataObject.DUnadvise(dwConnection);
    public HRESULT EnumDAdvise(IEnumSTATDATA** ppenumAdvise) => _nativeDataObject.EnumDAdvise(ppenumAdvise);
    public HRESULT EnumFormatEtc(uint dwDirection, IEnumFORMATETC** ppenumFormatEtc) => _nativeDataObject.EnumFormatEtc(dwDirection, ppenumFormatEtc);
    public HRESULT GetCanonicalFormatEtc(FORMATETC* pformatectIn, FORMATETC* pformatetcOut) => _nativeDataObject.GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
    public HRESULT GetData(FORMATETC* pformatetcIn, STGMEDIUM* pmedium) => _nativeDataObject.GetData(pformatetcIn, pmedium);
    public HRESULT GetDataHere(FORMATETC* pformatetc, STGMEDIUM* pmedium) => _nativeDataObject.GetDataHere(pformatetc, pmedium);
    public HRESULT QueryGetData(FORMATETC* pformatetc) => _nativeDataObject.QueryGetData(pformatetc);
    public HRESULT SetData(FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease) => _nativeDataObject.SetData(pformatetc, pmedium, fRelease);
    #endregion
 
    #region ComTypes.IDataObject.Interface
    public int DAdvise(ref ComTypes.FORMATETC pFormatetc, ComTypes.ADVF advf, ComTypes.IAdviseSink adviseSink, out int connection) => _runtimeDataObject.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
    public void DUnadvise(int connection) => _runtimeDataObject.DUnadvise(connection);
    public int EnumDAdvise(out ComTypes.IEnumSTATDATA? enumAdvise) => _runtimeDataObject.EnumDAdvise(out enumAdvise);
    public ComTypes.IEnumFORMATETC EnumFormatEtc(ComTypes.DATADIR direction) => _runtimeDataObject.EnumFormatEtc(direction);
    public int GetCanonicalFormatEtc(ref ComTypes.FORMATETC formatIn, out ComTypes.FORMATETC formatOut) => _runtimeDataObject.GetCanonicalFormatEtc(ref formatIn, out formatOut);
    public void GetData(ref ComTypes.FORMATETC format, out ComTypes.STGMEDIUM medium) => _runtimeDataObject.GetData(ref format, out medium);
    public void GetDataHere(ref ComTypes.FORMATETC format, ref ComTypes.STGMEDIUM medium) => _runtimeDataObject.GetDataHere(ref format, ref medium);
    public int QueryGetData(ref ComTypes.FORMATETC format) => _runtimeDataObject.QueryGetData(ref format);
    public void SetData(ref ComTypes.FORMATETC formatIn, ref ComTypes.STGMEDIUM medium, bool release) => _runtimeDataObject.SetData(ref formatIn, ref medium, release);
    #endregion
}