File: System\Windows\Ole\WpfOleServices.cs
Web Access
Project: src\src\Microsoft.DotNet.Wpf\src\PresentationCore\PresentationCore.csproj (PresentationCore)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Private.Windows.Ole;
using System.Windows.Interop;
using System.Windows.Media.Imaging;
using MS.Internal;
using Windows.Win32;
using Windows.Win32.Graphics.Gdi;
using Windows.Win32.System.Com;
using Windows.Win32.System.Ole;
using Com = Windows.Win32.System.Com;
using HRESULT = Windows.Win32.Foundation.HRESULT;
 
namespace System.Windows.Ole;
 
internal sealed unsafe class WpfOleServices : IOleServices
{
    // Prevent instantiation
    private WpfOleServices() { }
 
    public static void EnsureThreadState() => OleServicesContext.EnsureThreadState();
 
    public static HRESULT GetDataHere(string format, object data, FORMATETC* pformatetc, STGMEDIUM* pmedium)
    {
        TYMED mediumType = (TYMED)pformatetc->tymed;
 
        // Handle bitmaps.
        if (mediumType.HasFlag(TYMED.TYMED_GDI)
            && format.Equals(DataFormatNames.Bitmap)
            && (SystemDrawingHelper.IsBitmap(data) || data is BitmapSource))
        {
            pmedium->u.hBitmap = GetCompatibleBitmap(data);
            return HRESULT.S_OK;
        }
 
        // Handle enhanced metafiles.
        if (mediumType.HasFlag(TYMED.TYMED_ENHMF) && format.Equals(DataFormatNames.Emf))
        {
            if (SystemDrawingHelper.IsMetafile(data))
            {
                pmedium->u.hEnhMetaFile = SystemDrawingHelper.GetHandleFromMetafile(data);
            }
            else if (data is MemoryStream memoryStream && memoryStream.GetBuffer() is { } buffer && buffer.Length != 0)
            {
                HENHMETAFILE hemf = PInvoke.SetEnhMetaFileBits(buffer);
 
                if (hemf.IsNull)
                {
                    throw new Win32Exception();
                }
            }
 
            return HRESULT.S_OK;
        }
 
        return HRESULT.DV_E_TYMED;
 
        static HBITMAP GetCompatibleBitmap(object data)
        {
            HBITMAP hbitmap = SystemDrawingHelper.GetHBitmap(data, out int width, out int height);
 
            return hbitmap.IsNull ? HBITMAP.Null : hbitmap.CreateCompatibleBitmap(width, height);
        }
    }
 
    public static bool TryGetObjectFromDataObject<T>(
        Com.IDataObject* dataObject,
        string format,
        [NotNullWhen(true)] out T data)
    {
        data = default!;
 
        TYMED mediumType;
        ushort formatId;
 
        if (format == DataFormatNames.Bitmap)
        {
            mediumType = TYMED.TYMED_GDI;
            formatId = (ushort)CLIPBOARD_FORMAT.CF_BITMAP;
        }
        else if (format == DataFormatNames.Emf)
        {
            mediumType = TYMED.TYMED_ENHMF;
            formatId = (ushort)CLIPBOARD_FORMAT.CF_ENHMETAFILE;
        }
        else
        {
            return false;
        }
 
        FORMATETC formatEtc = new()
        {
            cfFormat = formatId,
            dwAspect = (uint)DVASPECT.DVASPECT_CONTENT,
            lindex = -1,
            tymed = (uint)mediumType
        };
 
        HRESULT result = dataObject->QueryGetData(formatEtc);
 
        if (result.Failed)
        {
            return false;
        }
 
        result = dataObject->GetData(formatEtc, out STGMEDIUM medium);
 
        try
        {
            if (result.Failed)
            {
                return false;
            }
 
            if (mediumType == TYMED.TYMED_GDI)
            {
                // Get the bitmap from the handle of bitmap.
                object bitmap = Imaging.CreateBitmapSourceFromHBitmap(
                    (HBITMAP)(nint)medium.hGlobal,
                    0,
                    Int32Rect.Empty,
                    sizeOptions: null);
 
                if (bitmap is T t)
                {
                    data = t;
                    return true;
                }
            }
            else
            {
                // Get the metafile object form the enhanced metafile handle.
                object metafile = SystemDrawingHelper.GetMetafileFromHemf((HENHMETAFILE)(nint)medium.hGlobal);
                if (metafile is T t)
                {
                    data = t;
                    return true;
                }
            }
        }
        finally
        {
            PInvokeCore.ReleaseStgMedium(ref medium);
        }
 
        return false;
    }
 
    public static bool AllowTypeWithoutResolver<T>()
    {
        // Image is a special case because we are reading bitmaps directly from the SerializationRecord.
        return typeof(T).FullName.Equals("System.Drawing.Image");
    }
 
    public static bool IsValidTypeForFormat(Type type, string format) => format switch
    {
        DataFormatNames.Bitmap or DataFormatNames.BinaryFormatBitmap =>
            type == typeof(BitmapSource) || type.FullName is "System.Drawing.Bitmap" or "System.Drawing.Image",
        DataFormatNames.Emf or DataFormatNames.BinaryFormatMetafile =>
            type.FullName is "System.Drawing.Imaging.Metafile" or "System.Drawing.Image",
 
        // All else should fall through as valid.
        _ => true
    };
 
    public static void ValidateDataStoreData(ref string format, bool autoConvert, object data)
    {
        // We do not have proper support for Dibs, so if the user explicitly asked
        // for Dib and provided a Bitmap object we can't convert.  Instead, publish as an HBITMAP
        // and let the system provide the conversion for us.
        if (format == DataFormats.Dib && autoConvert && (SystemDrawingHelper.IsBitmap(data) || data is BitmapSource))
        {
            format = DataFormats.Bitmap;
        }
    }
 
    public static IComVisibleDataObject CreateDataObject() => new DataObject();
 
    static HRESULT IOleServices.OleGetClipboard(Com.IDataObject** dataObject) =>
        PInvokeCore.OleGetClipboard(dataObject);
 
    static HRESULT IOleServices.OleSetClipboard(Com.IDataObject* dataObject) =>
        PInvokeCore.OleSetClipboard(dataObject);
 
    static HRESULT IOleServices.OleFlushClipboard() =>
        PInvokeCore.OleFlushClipboard();
}