| File: System\Windows\Forms\OLE\WinFormsOleServices.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\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.Drawing; using System.Private.Windows.Ole; using Windows.Win32.System.Com; using Windows.Win32.System.Ole; using Com = Windows.Win32.System.Com; namespace System.Windows.Forms.Ole; /// <summary> /// Provides Windows Forms specific OLE services. /// </summary> internal sealed class WinFormsOleServices : IOleServices { // Prevent instantiation private WinFormsOleServices() { } public static void EnsureThreadState() { // There were some cases historically where we would try not to throw to avoid user code crashing from // attempting to call OLE code in a finalizer. There isn't a bullet proof way to know whether or not // we're on the finalizer thread, this should be left to the finalizer implementers to handle (even if // that might be us on behalf of the user). // // In one other case we were checking for Control.CheckForIllegalCrossThreadCalls, but that is also not // a great idea, as it overloaded the meaning of the property. Try to keep this as simple and as consistent // as possible. if (Application.OleRequired() != ApartmentState.STA) { throw new ThreadStateException(SR.ThreadMustBeSTA); } } static unsafe HRESULT IOleServices.GetDataHere(string format, object data, FORMATETC* pformatetc, STGMEDIUM* pmedium) { if (format == DataFormatNames.Dib && data is Image) { // GDI+ does not properly handle saving to DIB images. Since the clipboard will take // an HBITMAP and publish a Dib, we don't need to support this. return HRESULT.DV_E_TYMED; } if (((TYMED)pformatetc->tymed).HasFlag(TYMED.TYMED_GDI)) { if (format.Equals(DataFormatNames.Bitmap) && data is Bitmap bitmap) { // GDI+ returns a DIBSECTION based HBITMAP. The clipboard only deals well with bitmaps created using // CreateCompatibleBitmap(). So, we convert the DIBSECTION into a compatible bitmap. pmedium->u.hBitmap = bitmap.GetHBITMAP().CreateCompatibleBitmap(bitmap.Width, bitmap.Height); } return HRESULT.S_OK; } return HRESULT.DV_E_TYMED; } static unsafe bool IOleServices.TryGetObjectFromDataObject<T>( Com.IDataObject* dataObject, string requestedFormat, [NotNullWhen(true)] out T data) { if (requestedFormat == DataFormatNames.Bitmap && (typeof(Bitmap) == typeof(T) || typeof(Image) == typeof(T)) && TryGetBitmapData(dataObject, out Bitmap? bitmap)) { data = (T)(object)bitmap!; return true; } data = default!; return false; static unsafe bool TryGetBitmapData(Com.IDataObject* dataObject, [NotNullWhen(true)] out Bitmap? data) { data = default; FORMATETC formatEtc = new() { cfFormat = (ushort)CLIPBOARD_FORMAT.CF_BITMAP, dwAspect = (uint)DVASPECT.DVASPECT_CONTENT, lindex = -1, tymed = (uint)TYMED.TYMED_GDI }; HRESULT result = dataObject->QueryGetData(formatEtc); if (result.Failed) { return false; } result = dataObject->GetData(formatEtc, out STGMEDIUM medium); // One of the ways this can happen is when we attempt to put binary formatted data onto the // clipboard, which will succeed as Windows ignores all errors when putting data on the clipboard. // The data state, however, is not good, and this error will be returned by Windows when asking to // get the data out. Debug.WriteLineIf(result == HRESULT.CLIPBRD_E_BAD_DATA, "CLIPBRD_E_BAD_DATA returned when trying to get clipboard data."); try { // GDI+ doesn't own this HBITMAP, but we can't delete it while the object is still around. So we // have to do the really expensive thing of cloning the image so we can release the HBITMAP. if (result.Succeeded && (uint)medium.tymed == (uint)TYMED.TYMED_GDI && !medium.hGlobal.IsNull && Image.FromHbitmap(medium.hGlobal) is Bitmap clipboardBitmap) { data = (Bitmap)clipboardBitmap.Clone(); clipboardBitmap.Dispose(); return true; } } finally { PInvokeCore.ReleaseStgMedium(ref medium); } return false; } } static bool IOleServices.AllowTypeWithoutResolver<T>() => // Image is a special case because we are reading Bitmaps directly from the SerializationRecord. typeof(T) == typeof(Image); static void IOleServices.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 (data is Bitmap && format.Equals(DataFormatNames.Dib)) { format = autoConvert ? DataFormatNames.Bitmap : throw new NotSupportedException(SR.DataObjectDibNotSupported); } } static bool IOleServices.IsValidTypeForFormat(Type type, string format) => format switch { DataFormatNames.Bitmap or DataFormatNames.BinaryFormatBitmap => type == typeof(Bitmap) || type == typeof(Image), // All else should fall through as valid. _ => true }; static IComVisibleDataObject IOleServices.CreateDataObject() => new DataObject(); static unsafe HRESULT IOleServices.OleGetClipboard(Com.IDataObject** dataObject) => PInvokeCore.OleGetClipboard(dataObject); static unsafe HRESULT IOleServices.OleSetClipboard(Com.IDataObject* dataObject) => PInvokeCore.OleSetClipboard(dataObject); static HRESULT IOleServices.OleFlushClipboard() => PInvokeCore.OleFlushClipboard(); }