diff --git a/src/System.Private.Windows.Core/src/GlobalSuppressions.cs b/src/System.Private.Windows.Core/src/GlobalSuppressions.cs
new file mode 100644
index 00000000000..ca090fc82f0
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/GlobalSuppressions.cs
@@ -0,0 +1,7 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopDataObject.Composition.NativeToDesktopAdapter.ReadByteStreamFromHGLOBAL(Windows.Win32.Foundation.HGLOBAL,System.Boolean@)~System.IO.MemoryStream")]
+[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopDataObject.Composition.NativeToRuntimeAdapter.System#Runtime#InteropServices#ComTypes#IDataObject#EnumFormatEtc(System.Runtime.InteropServices.ComTypes.DATADIR)~System.Runtime.InteropServices.ComTypes.IEnumFORMATETC")]
+[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopClipboard.SetDataObject(System.Object,System.Boolean,System.Int32,System.Int32)")]
+[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Private.Windows.Core.OLE.DesktopClipboard.GetDataObject~System.Private.Windows.Core.OLE.IDataObjectDesktop")]
diff --git a/src/System.Private.Windows.Core/src/NativeMethods.txt b/src/System.Private.Windows.Core/src/NativeMethods.txt
index 5f8fab573cb..cc54bd51641 100644
--- a/src/System.Private.Windows.Core/src/NativeMethods.txt
+++ b/src/System.Private.Windows.Core/src/NativeMethods.txt
@@ -2,12 +2,15 @@ BeginPaint
BI_COMPRESSION
BitBlt
BOOL
+CFSTR_DROPDESCRIPTION
+CFSTR_INDRAGLOOP
CLIPBRD_E_BAD_DATA
CLR_*
CallWindowProc
CoCreateInstance
CombineRgn
CopyImage
+CP_ACP
CreateBitmap
CreateCompatibleBitmap
CreateCompatibleDC
@@ -50,7 +53,11 @@ DRAGDROP_S_CANCEL
DRAGDROP_S_DROP
DRAGDROP_S_USEDEFAULTCURSORS
DrawIconEx
+DragQueryFile
+DROPDESCRIPTION
+DROPFILES
DV_E_*
+DVASPECT
E_ABORT
E_ACCESSDENIED
E_FAIL
@@ -78,6 +85,7 @@ GetWindowText
GetWindowTextLength
GET_CLASS_LONG_INDEX
GetClientRect
+GetClipboardFormatName
GetClipRgn
GetDC
GetDCEx
@@ -119,7 +127,9 @@ HPROPSHEETPAGE
HRGN
HWND
HWND_*
+IDataObject
IDI_*
+IDragSourceHelper2
IEnumUnknown
IGlobalInterfaceTable
ImageLockMode
@@ -162,6 +172,10 @@ OLE_E_INVALIDRECT
OLE_E_NOCONNECTION
OLE_E_PROMPTSAVECANCELLED
OleCreatePictureIndirect
+OleDuplicateData
+OleFlushClipboard
+OleGetClipboard
+OleSetClipboard
PeekMessage
PostMessage
POINTS
@@ -171,7 +185,9 @@ PWSTR
RealizePalette
RECT
REGDB_E_CLASSNOTREG
+RegisterClipboardFormat
ReleaseDC
+ReleaseStgMedium
RestoreDC
RPC_E_CHANGED_MODE
RPC_E_DISCONNECTED
@@ -215,6 +231,7 @@ SystemParametersInfoForDpi
TYPE_E_BADMODULEKIND
UNICODE_STRING_MAX_CHARS
VIEW_E_DRAW
+WideCharToMultiByte
WIN32_ERROR
WINCODEC_ERR_*
WINDOW_LONG_PTR_INDEX
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopClipboard.cs b/src/System.Private.Windows.Core/src/OLE/DesktopClipboard.cs
new file mode 100644
index 00000000000..310887db865
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopClipboard.cs
@@ -0,0 +1,582 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Specialized;
+using System.Private.Windows.Core.Resources;
+using System.Reflection.Metadata;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Text.Json;
+using Windows.Win32;
+using Windows.Win32.System.Com;
+using Com = Windows.Win32.System.Com;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
+using ITypedDataObject = System.Private.Windows.Core.OLE.ITypedDataObjectDesktop;
+using TextDataFormat = System.Private.Windows.Core.OLE.DesktopTextDataFormat;
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Provides methods to place data on and retrieve data from the system clipboard.
+///
+internal abstract class DesktopClipboard
+{
+ ///
+ /// Provides the appropriate based on the application.
+ ///
+ /// the format to save under.
+ /// Pass to save under equivalent formats. Otherwise,
+ /// The object to save in the data object.
+ internal abstract DesktopDataObject CreateDataObject(string format, bool autoConvert, object data);
+
+ ///
+ internal abstract DesktopDataObject CreateDataObject(string format, object data);
+
+ ///
+ internal abstract DesktopDataObject CreateDataObject();
+
+ ///
+ /// Wraps the data before setting onto the clipboard
+ /// in the appropriate based on the application.
+ /// This is needed to be able to retrieve the same data out from the clipboard.
+ ///
+ internal abstract DesktopDataObject WrapForSetDataObject(object data);
+
+ ///
+ /// Ensures that the current thread is STA.
+ ///
+ /// If thread is not STA.
+ public abstract void EnsureStaThread();
+
+ ///
+ /// Wrap the native data object pointer in the appropriate based on the application.
+ ///
+ internal abstract unsafe DesktopDataObject WrapForGetDataObject(Com.IDataObject* dataObject);
+
+ ///
+ /// Gets the current data object and determines whether is implemented on it.
+ ///
+ /// The type of the object of interest.
+ /// The format the object of interest is saved under.
+ /// The data object which implements
+ ///
+ /// if the current data object implements and contains valid type and format.
+ ///
+ internal abstract bool GetTypedDataObject(string format, [NotNullWhen(true), MaybeNullWhen(false)] out ITypedDataObject typed);
+
+ ///
+ /// Places non-persistent data on the system .
+ ///
+ ///
+ public void SetDataObject(object data) => SetDataObject(data, copy: false);
+
+ ///
+ /// Overload that uses default values for retryTimes and retryDelay.
+ ///
+ ///
+ public void SetDataObject(object data, bool copy) =>
+ SetDataObject(data, copy, retryTimes: 10, retryDelay: 100);
+
+ ///
+ /// Places data on the system and uses copy to specify whether the data
+ /// should remain on the after the application exits.
+ ///
+ ///
+ ///
+ /// See remarks for for recommendations on how to implement custom .
+ ///
+ ///
+ public unsafe void SetDataObject(object data, bool copy, int retryTimes, int retryDelay)
+ {
+ EnsureStaThread();
+ ArgumentNullException.ThrowIfNull(data);
+ ArgumentOutOfRangeException.ThrowIfNegative(retryTimes);
+ ArgumentOutOfRangeException.ThrowIfNegative(retryDelay);
+
+ // Always wrap the data in our DataObject since we know how to retrieve our DataObject from the proxy OleGetClipboard returns.
+ DesktopDataObject wrappedData = WrapForSetDataObject(data);
+ using var dataObject = ComHelpers.GetComScope(wrappedData);
+
+ HRESULT hr;
+ int retry = retryTimes;
+ while ((hr = PInvokeCore.OleSetClipboard(dataObject)).Failed)
+ {
+ if (--retry < 0)
+ {
+ throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
+ }
+
+ Thread.Sleep(millisecondsTimeout: retryDelay);
+ }
+
+ if (copy)
+ {
+ retry = retryTimes;
+ while ((hr = PInvokeCore.OleFlushClipboard()).Failed)
+ {
+ if (--retry < 0)
+ {
+ throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
+ }
+
+ Thread.Sleep(millisecondsTimeout: retryDelay);
+ }
+ }
+ }
+
+ ///
+ /// Retrieves the data that is currently on the system .
+ ///
+ internal unsafe IDataObject? GetDataObject()
+ {
+ EnsureStaThread();
+ int retryTimes = 10;
+ using ComScope proxyDataObject = new(null);
+ HRESULT hr;
+ while ((hr = PInvokeCore.OleGetClipboard(proxyDataObject)).Failed)
+ {
+ if (--retryTimes < 0)
+ {
+ throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
+ }
+
+ Thread.Sleep(millisecondsTimeout: 100);
+ }
+
+ // OleGetClipboard always returns a proxy. The proxy forwards all IDataObject method calls to the real data object,
+ // without giving out the real data object. If the data placed on the clipboard is not one of our CCWs or the clipboard
+ // has been flushed, a wrapper around the proxy for us to use will be given. However, if the data placed on
+ // the clipboard is one of our own and the clipboard has not been flushed, we need to retrieve the real data object
+ // pointer in order to retrieve the original managed object via ComWrappers if an IDataObject was set on the clipboard.
+ // To do this, we must query for an interface that is not known to the proxy e.g. IComCallableWrapper.
+ // If we are able to query for IComCallableWrapper it means that the real data object is one of our CCWs and we've retrieved it successfully,
+ // otherwise it is not ours and we will use the wrapped proxy.
+ var realDataObject = proxyDataObject.TryQuery(out hr);
+
+ if (hr.Succeeded
+ && ComHelpers.TryUnwrapComWrapperCCW(realDataObject.AsUnknown, out DesktopDataObject? dataObject)
+ && !dataObject.IsOriginalNotIDataObject)
+ {
+ // An IDataObject was given to us to place on the clipboard. We want to unwrap and return it instead of a proxy.
+ return dataObject.TryUnwrapInnerIDataObject();
+ }
+
+ // Original data given wasn't an IDataObject, give the proxy value back.
+ return WrapForGetDataObject(proxyDataObject.Value);
+ }
+
+ ///
+ /// Removes all data from the Clipboard.
+ ///
+ public static unsafe void Clear()
+ {
+ HRESULT hr;
+ int retry = 10;
+ while ((hr = PInvokeCore.OleSetClipboard(null)).Failed)
+ {
+ if (--retry < 0)
+ {
+#pragma warning disable CA2201 // Do not raise reserved exception types
+ throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
+#pragma warning restore CA2201
+ }
+
+ Thread.Sleep(millisecondsTimeout: 100);
+ }
+ }
+
+ ///
+ /// Indicates whether there is data on the Clipboard in the format.
+ ///
+ public bool ContainsAudio() => ContainsData(DesktopDataFormats.WaveAudioConstant);
+
+ ///
+ /// Indicates whether there is data on the Clipboard that is in the specified format
+ /// or can be converted to that format.
+ ///
+ public bool ContainsData(string? format) =>
+ !string.IsNullOrWhiteSpace(format) && ContainsData(format, autoConvert: false);
+
+ private bool ContainsData(string format, bool autoConvert) =>
+ GetDataObject() is IDataObject dataObject && dataObject.GetDataPresent(format, autoConvert: autoConvert);
+
+ ///
+ /// Indicates whether there is data on the Clipboard that is in the format
+ /// or can be converted to that format.
+ ///
+ public bool ContainsFileDropList() => ContainsData(DesktopDataFormats.FileDropConstant, autoConvert: true);
+
+ ///
+ /// Indicates whether there is data on the Clipboard that is in the format
+ /// or can be converted to that format.
+ ///
+ public bool ContainsImage() => ContainsData(DesktopDataFormats.BitmapConstant, autoConvert: true);
+
+ ///
+ /// Indicates whether there is text data on the Clipboard in format.
+ ///
+ public bool ContainsText() => ContainsText(TextDataFormat.UnicodeText);
+
+ ///
+ /// Indicates whether there is text data on the Clipboard in the format indicated by the specified
+ /// value.
+ ///
+ internal bool ContainsText(TextDataFormat format)
+ {
+ return ContainsData(ConvertToDataFormats(format));
+ }
+
+ ///
+ /// Retrieves an audio stream from the .
+ ///
+ public Stream? GetAudioStream() => GetTypedDataIfAvailable(DesktopDataFormats.WaveAudioConstant);
+
+ ///
+ /// Retrieves data from the in the specified format.
+ ///
+ ///
+ /// The current thread is not in single-threaded apartment (STA) mode.
+ ///
+ ///
+ /// Public facing API is obsolete.
+ ///
+ public object? GetData(string format) =>
+ string.IsNullOrWhiteSpace(format) ? null : GetData(format, autoConvert: false);
+
+ private object? GetData(string format, bool autoConvert) =>
+ GetDataObject() is IDataObject dataObject ? dataObject.GetData(format, autoConvert) : null;
+
+ ///
+ /// Retrieves data from the in the specified format if that data is of type .
+ /// This is an alternative to that uses only when application
+ /// enabled the switch named "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization".
+ /// By default the NRBF deserializer attempts to deserialize the stream. It can be disabled in favor of
+ /// with switch named "Windows.ClipboardDragDrop.EnableNrbfSerialization".
+ ///
+ ///
+ ///
+ /// The format of the data to retrieve. See the class for a set of predefined data formats.
+ ///
+ ///
+ ///
+ ///
+ /// A that is used only when deserializing non-OLE formats. It returns the type if
+ /// is allowed or throws a if is not
+ /// expected. It should not return a . It should resolve type requested by the user as
+ /// , as well as types of its fields, unless they are primitive or known types.
+ ///
+ ///
+ /// The following types are resolved automatically:
+ ///
+ ///
+ ///
+ ///
+ /// NRBF primitive types
+ /// (bool, byte, char, decimal, double, short, int, long, sbyte, ushort, uint, ulong, float, string, TimeSpan, DateTime).
+ ///
+ ///
+ ///
+ ///
+ /// System.Drawing.Primitive.dll exchange types (PointF, RectangleF, Point, Rectangle, SizeF, Size, Color).
+ ///
+ ///
+ ///
+ ///
+ /// Types commonly used in WinForms applications (System.Drawing.Bitmap, System.Windows.Forms.ImageListStreamer,
+ /// System.NotSupportedException, only the message is re-hydrated, List{T} where T is an NRBF primitive type,
+ /// and arrays of NRBF primitive types).
+ ///
+ ///
+ ///
+ ///
+ /// parameter can be matched according to the user requirements, for example, only namespace-qualified
+ /// type names, or full type and assembly names, or full type names and short assembly names.
+ ///
+ ///
+ ///
+ ///
+ /// Out parameter that contains the retrieved data in the specified format, or if the data is
+ /// unavailable in the specified format, or is of a wrong .
+ ///
+ ///
+ ///
+ /// if the data of this format is present on the clipboard and the value is
+ /// of a matching type and that value can be successfully retrieved, or
+ /// if the format is not present or the value is of a wrong .
+ ///
+ ///
+ ///
+ /// Avoid loading assemblies named in the argument of the resolver function. Resolve only types
+ /// available at the compile time, for example do not call the method.
+ ///
+ ///
+ /// Some common types, are type-forwarded from .NET Framework assemblies using the
+ /// . serializes these types
+ /// using the forwarded from assembly information. The resolver function should take this into account and either
+ /// match only namespace qualified type names or read the
+ /// from the allowed type and match it to the property of .
+ ///
+ ///
+ /// Make sure to match short assembly names if other information, such as version, is not needed, for example, when your
+ /// application can read multiple versions of the type. For exact matching, including assembly version, resolver
+ /// function is required, however primitive and common types are always matched after assembly version is removed.
+ ///
+ ///
+ /// Arrays, generic types, and nullable value types have full element name, including its assembly name, in the
+ /// property. Resolver function should either remove or type-forward these assembly
+ /// names when matching.
+ ///
+ ///
+ ///
+ /// If application does not support and the object can't be deserialized otherwise, or
+ /// application supports but is an ,
+ /// or not a concrete type, or if does not resolve the actual payload type. Or
+ /// the on the does not implement
+ /// interface.
+ ///
+ ///
+ ///
+ ///
+ public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ Func resolver,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data)
+ {
+ data = default;
+ resolver.OrThrowIfNull();
+
+ return GetTypedDataObject(format, out ITypedDataObject? typed)
+ && typed.TryGetData(format, resolver, autoConvert: false, out data);
+ }
+
+ ///
+ public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data)
+ {
+ data = default;
+
+ return GetTypedDataObject(format, out ITypedDataObject? typed) && typed.TryGetData(format, out data);
+ }
+
+ ///
+ /// Retrieves a collection of file names from the .
+ ///
+ public StringCollection GetFileDropList()
+ {
+ StringCollection result = [];
+
+ if (GetTypedDataIfAvailable(DesktopDataFormats.FileDropConstant) is string[] strings)
+ {
+ result.AddRange(strings);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Retrieves text data from the in the format.
+ ///
+ public string GetText() => GetText(TextDataFormat.UnicodeText);
+
+ ///
+ /// Retrieves text data from the in the format indicated by the specified
+ /// value.
+ ///
+ internal string GetText(TextDataFormat format)
+ {
+ return GetTypedDataIfAvailable(ConvertToDataFormats(format)) is string text ? text : string.Empty;
+ }
+
+ internal T? GetTypedDataIfAvailable<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format)
+ {
+ IDataObject? data = GetDataObject();
+ if (data is ITypedDataObject typed)
+ {
+ return typed.TryGetData(format, autoConvert: true, out T? value) ? value : default;
+ }
+
+ if (data is IDataObject dataObject)
+ {
+ return dataObject.GetData(format, autoConvert: true) is T value ? value : default;
+ }
+
+ return default;
+ }
+
+ ///
+ /// Clears the and then adds data in the format.
+ ///
+ public void SetAudio(byte[] audioBytes) => SetAudio(new MemoryStream(audioBytes.OrThrowIfNull()));
+
+ ///
+ /// Clears the and then adds data in the format.
+ ///
+ public void SetAudio(Stream audioStream) =>
+ SetDataObject(CreateDataObject(DesktopDataFormats.WaveAudioConstant, audioStream.OrThrowIfNull()), copy: true);
+
+ ///
+ /// Clears the Clipboard and then adds data in the specified format.
+ ///
+ ///
+ ///
+ /// See remarks for for recommendations on how to implement custom .
+ ///
+ ///
+ public void SetData(string format, object data)
+ {
+ if (string.IsNullOrWhiteSpace(format.OrThrowIfNull()))
+ {
+ throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
+ }
+
+ // Note: We delegate argument checking to IDataObject.SetData, if it wants to do so.
+ SetDataObject(CreateDataObject(format, data), copy: true);
+ }
+
+ ///
+ /// Saves the data onto the clipboard in the specified format.
+ /// If the data is a non intrinsic type, the object will be serialized using JSON.
+ ///
+ ///
+ /// , empty string, or whitespace was passed as the format.
+ ///
+ ///
+ /// If is a non derived . This is for better error reporting as will serialize as empty.
+ /// If needs to be placed on the clipboard, use
+ /// to JSON serialize the data to be held in the , then set the
+ /// onto the clipboard via .
+ ///
+ ///
+ ///
+ /// If your data is an intrinsically handled type such as primitives, string, or Bitmap
+ /// and you are using a custom format or ,
+ /// it is recommended to use the APIs to avoid unnecessary overhead.
+ ///
+ ///
+ /// The default behavior of is used to serialize the data.
+ ///
+ ///
+ /// See
+ ///
+ /// and
+ /// for more details on default behavior.
+ ///
+ ///
+ /// If custom JSON serialization behavior is needed, manually JSON serialize the data and then use
+ /// to save the data onto the clipboard, or create a custom , attach the
+ /// , and then recall this method.
+ /// See for more details
+ /// on custom converters for JSON serialization.
+ ///
+ ///
+ [RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
+ public void SetDataAsJson(string format, T data)
+ {
+ data.OrThrowIfNull(nameof(data));
+ if (string.IsNullOrWhiteSpace(format.OrThrowIfNull()))
+ {
+ throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
+ }
+
+ DesktopDataObject dataObject = CreateDataObject();
+ dataObject.SetDataAsJson(format, data);
+ SetDataObject(dataObject, copy: true);
+ }
+
+ ///
+ /// Clears the Clipboard and then adds a collection of file names in the format.
+ ///
+ public void SetFileDropList(StringCollection filePaths)
+ {
+ if (filePaths.OrThrowIfNull().Count == 0)
+ {
+ throw new ArgumentException(SR.CollectionEmptyException);
+ }
+
+ // Validate the paths to make sure they don't contain invalid characters
+ string[] filePathsArray = new string[filePaths.Count];
+ filePaths.CopyTo(filePathsArray, 0);
+
+ foreach (string path in filePathsArray)
+ {
+ // These are the only error states for Path.GetFullPath
+ if (string.IsNullOrEmpty(path) || path.Contains('\0'))
+ {
+ throw new ArgumentException(string.Format(SR.Clipboard_InvalidPath, path, nameof(filePaths)));
+ }
+ }
+
+ SetDataObject(CreateDataObject(DesktopDataFormats.FileDropConstant, autoConvert: true, filePathsArray), copy: true);
+ }
+
+ ///
+ /// Clears the Clipboard and then adds text data in the format.
+ ///
+ public void SetText(string text) => SetText(text, TextDataFormat.UnicodeText);
+
+ ///
+ /// Clears the Clipboard and then adds text data in the format indicated by the specified
+ /// value.
+ ///
+ internal void SetText(string text, TextDataFormat format)
+ {
+ text.ThrowIfNullOrEmpty();
+ SetDataObject(CreateDataObject(ConvertToDataFormats(format), text), copy: true);
+ }
+
+ private static string ConvertToDataFormats(TextDataFormat format) => format switch
+ {
+ TextDataFormat.Text => DesktopDataFormats.TextConstant,
+ TextDataFormat.UnicodeText => DesktopDataFormats.UnicodeTextConstant,
+ TextDataFormat.Rtf => DesktopDataFormats.RtfConstant,
+ TextDataFormat.Html => DesktopDataFormats.HtmlConstant,
+ TextDataFormat.CommaSeparatedValue => DesktopDataFormats.CsvConstant,
+ _ => DesktopDataFormats.UnicodeTextConstant,
+ };
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.Format.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.Format.cs
new file mode 100644
index 00000000000..686fab41f51
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.Format.cs
@@ -0,0 +1,39 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Translates between WinForms text-based
+/// formats and Win32 32-bit signed integer-based clipboard
+/// formats. Provides methods to create new
+/// formats and add them to the Windows Registry.
+///
+internal static partial class DesktopDataFormats
+{
+ ///
+ /// Represents a format type.
+ ///
+ public class Format
+ {
+ ///
+ /// Initializes a new instance of the class and
+ /// specifies whether a Win32 handle is expected with this format.
+ ///
+ public Format(string name, int id)
+ {
+ Name = name;
+ Id = id;
+ }
+
+ ///
+ /// Specifies the name of this format.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Specifies the ID number for this format.
+ ///
+ public int Id { get; }
+ }
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.cs
new file mode 100644
index 00000000000..fe44a5d6651
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataFormats.cs
@@ -0,0 +1,193 @@
+// 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.Private.Windows.Core.Resources;
+using System.Runtime.InteropServices;
+using Windows.Win32;
+using Windows.Win32.System.Ole;
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Translates between WinForms text-based
+/// formats and Win32 32-bit signed integer-based clipboard
+/// formats. Provides methods to create new
+/// formats and add them to the Windows Registry.
+///
+internal static partial class DesktopDataFormats
+{
+ internal const string TextConstant = "Text";
+ internal const string UnicodeTextConstant = "UnicodeText";
+ internal const string DibConstant = "DeviceIndependentBitmap";
+ internal const string BitmapConstant = "Bitmap";
+ internal const string EmfConstant = "EnhancedMetafile";
+ internal const string WmfConstant = "MetaFilePict";
+ internal const string SymbolicLinkConstant = "SymbolicLink";
+ internal const string DifConstant = "DataInterchangeFormat";
+ internal const string TiffConstant = "TaggedImageFileFormat";
+ internal const string OemTextConstant = "OEMText";
+ internal const string PaletteConstant = "Palette";
+ internal const string PenDataConstant = "PenData";
+ internal const string RiffConstant = "RiffAudio";
+ internal const string WaveAudioConstant = "WaveAudio";
+ internal const string FileDropConstant = "FileDrop";
+ internal const string LocaleConstant = "Locale";
+ internal const string HtmlConstant = "HTML Format";
+ internal const string RtfConstant = "Rich Text Format";
+ internal const string CsvConstant = "Csv";
+ internal const string StringConstant = "System.String";
+ internal const string SerializableConstant = "WindowsForms10PersistentObject";
+
+ private static Format[]? s_formatList;
+
+ private static int s_formatCount;
+
+#if NET9_0_OR_GREATER
+ private static readonly Lock s_internalSyncObject = new();
+#else
+ private static readonly object s_internalSyncObject = new();
+#endif
+
+ ///
+ /// Gets a with the Windows Clipboard numeric ID and name for the specified format.
+ ///
+ public static Format GetFormat(string format)
+ {
+ ArgumentException.ThrowIfNullOrWhiteSpace(format);
+
+ lock (s_internalSyncObject)
+ {
+ EnsurePredefined();
+
+ // It is much faster to do a case sensitive search here.
+ // So do the case sensitive compare first, then the expensive one.
+ for (int n = 0; n < s_formatCount; n++)
+ {
+ if (s_formatList[n].Name.Equals(format))
+ {
+ return s_formatList[n];
+ }
+ }
+
+ for (int n = 0; n < s_formatCount; n++)
+ {
+ if (string.Equals(s_formatList[n].Name, format, StringComparison.OrdinalIgnoreCase))
+ {
+ return s_formatList[n];
+ }
+ }
+
+ // Need to add this format string
+ uint formatId = PInvokeCore.RegisterClipboardFormat(format);
+ if (formatId == 0)
+ {
+ throw new Win32Exception(Marshal.GetLastWin32Error(), SR.RegisterCFFailed);
+ }
+
+ EnsureFormatSpace(1);
+ s_formatList[s_formatCount] = new Format(format, (int)formatId);
+ return s_formatList[s_formatCount++];
+ }
+ }
+
+ ///
+ /// Gets a with the Windows Clipboard numeric ID and name for the specified ID.
+ ///
+ internal static unsafe Format GetFormat(ushort id)
+ {
+ lock (s_internalSyncObject)
+ {
+ EnsurePredefined();
+
+ for (int n = 0; n < s_formatCount; n++)
+ {
+ if (s_formatList[n].Id == id)
+ {
+ return s_formatList[n];
+ }
+ }
+
+ string? name = null;
+
+ // The max length of the name of clipboard formats is equal to the max length
+ // of a Win32 Atom of 255 chars. An additional null terminator character is added,
+ // giving a required capacity of 256 chars.
+ Span formatName = stackalloc char[256];
+ fixed (char* pFormatName = formatName)
+ {
+ int length = PInvokeCore.GetClipboardFormatName(id, pFormatName, 256);
+ if (length != 0)
+ {
+ name = formatName[..length].ToString();
+ }
+ }
+
+ // This can happen if windows adds a standard format that we don't know about,
+ // so we should play it safe.
+ name ??= $"Format{id}";
+
+ EnsureFormatSpace(1);
+ s_formatList[s_formatCount] = new Format(name, id);
+ return s_formatList[s_formatCount++];
+ }
+ }
+
+ ///
+ /// Ensures that we have enough room in our format list
+ ///
+ [MemberNotNull(nameof(s_formatList))]
+ private static void EnsureFormatSpace(int size)
+ {
+ if (s_formatList is null || s_formatList.Length <= s_formatCount + size)
+ {
+ int newSize = s_formatCount + 20;
+
+ Format[] newList = new Format[newSize];
+ for (int n = 0; n < s_formatCount; n++)
+ {
+ newList[n] = s_formatList![n];
+ }
+
+ s_formatList = newList;
+ }
+ }
+
+ ///
+ /// Ensures that the Win32 predefined formats are setup in our format list.
+ /// This is called anytime we need to search the list
+ ///
+ [MemberNotNull(nameof(s_formatList))]
+ private static void EnsurePredefined()
+ {
+ if (s_formatCount == 0)
+ {
+ s_formatList =
+ [
+ // Text name Win32 format ID
+ new(UnicodeTextConstant, (int)CLIPBOARD_FORMAT.CF_UNICODETEXT),
+ new(TextConstant, (int)CLIPBOARD_FORMAT.CF_TEXT),
+ new(BitmapConstant, (int)CLIPBOARD_FORMAT.CF_BITMAP),
+ new(WmfConstant, (int)CLIPBOARD_FORMAT.CF_METAFILEPICT),
+ new(EmfConstant, (int)CLIPBOARD_FORMAT.CF_ENHMETAFILE),
+ new(DifConstant, (int)CLIPBOARD_FORMAT.CF_DIF),
+ new(TiffConstant, (int)CLIPBOARD_FORMAT.CF_TIFF),
+ new(OemTextConstant, (int)CLIPBOARD_FORMAT.CF_OEMTEXT),
+ new(DibConstant, (int)CLIPBOARD_FORMAT.CF_DIB),
+ new(PaletteConstant, (int)CLIPBOARD_FORMAT.CF_PALETTE),
+ new(PenDataConstant, (int)CLIPBOARD_FORMAT.CF_PENDATA),
+ new(RiffConstant, (int)CLIPBOARD_FORMAT.CF_RIFF),
+ new(WaveAudioConstant, (int)CLIPBOARD_FORMAT.CF_WAVE),
+ new(SymbolicLinkConstant, (int)CLIPBOARD_FORMAT.CF_SYLK),
+ new(FileDropConstant, (int)CLIPBOARD_FORMAT.CF_HDROP),
+ new(LocaleConstant, (int)CLIPBOARD_FORMAT.CF_LOCALE)
+ ];
+
+ s_formatCount = s_formatList.Length;
+ }
+ else
+ {
+ s_formatList ??= [];
+ }
+ }
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.BinaryFormatUtilities.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.BinaryFormatUtilities.cs
new file mode 100644
index 00000000000..e00c7293791
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.BinaryFormatUtilities.cs
@@ -0,0 +1,133 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Private.Windows.Core.BinaryFormat;
+using System.Private.Windows.Core.Nrbf;
+using System.Private.Windows.Core.Resources;
+using System.Reflection.Metadata;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters;
+using System.Runtime.Serialization.Formatters.Binary;
+
+namespace System.Private.Windows.Core.OLE;
+
+internal abstract unsafe partial class DesktopDataObject
+{
+ internal abstract unsafe partial class Composition
+ {
+ internal abstract class BinaryFormatUtilities
+ {
+ ///
+ /// Gets the appropriate binary format writer for application.
+ ///
+ public abstract IBinaryFormatWriter GetBinaryFormatWriter();
+
+ ///
+ /// Reads the object of type T from the stream.
+ ///
+ /// The stream to read from.
+ ///
+ /// Indicates whether this was called from legacy method family
+ /// The object of type T if successfully read. Otherwise, .
+ internal abstract object? ReadObjectFromStream(
+ MemoryStream stream,
+ Func? resolver,
+ bool legacyMode);
+
+ ///
+ internal abstract object? ReadRestrictedObjectFromStream(
+ MemoryStream stream,
+ Func? resolver,
+ bool legacyMode);
+
+ internal void WriteObjectToStream(MemoryStream stream, object data, bool restrictSerialization)
+ {
+ long position = stream.Position;
+
+ try
+ {
+ // probably need an interface for this. Maybe an abstract class ?
+ if (GetBinaryFormatWriter().TryWriteCommonObject(stream, data))
+ {
+ return;
+ }
+ }
+ catch (Exception ex) when (!ex.IsCriticalException())
+ {
+ // Being extra cautious here, but the Try method above should never throw in normal circumstances.
+ Debug.Fail($"Unexpected exception writing binary formatted data. {ex.Message}");
+ }
+
+ if (restrictSerialization)
+ {
+ throw new SerializationException(string.Format(SR.UnexpectedTypeForClipboardFormat, data.GetType().FullName));
+ }
+
+ // This check is to help in trimming scenarios with a trim warning on a call to
+ // BinaryFormatter.Serialize(), which has a RequiresUnreferencedCode annotation.
+ // If the flag is false, the trimmer will not generate a warning, since BinaryFormatter.Serialize(),
+ // will not be called,
+ // If the flag is true, the trimmer will generate a warning for calling a method that has a
+ // RequiresUnreferencedCode annotation.
+ if (!EnableUnsafeBinaryFormatterInNativeObjectSerialization)
+ {
+ throw new NotSupportedException(SR.BinaryFormatterNotSupported);
+ }
+
+ if (!LocalAppContextSwitchesCore.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization)
+ {
+ throw new NotSupportedException(SR.BinaryFormatter_NotSupported_InClipboardOrDragDrop);
+ }
+
+ stream.Position = position;
+#pragma warning disable SYSLIB0011 // Type or member is obsolete
+#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
+ new BinaryFormatter().Serialize(stream, data);
+#pragma warning restore IL2026
+#pragma warning restore SYSLIB0011
+ }
+
+ [RequiresUnreferencedCode("Calls System.Private.Windows.Core.BinaryFormat.ITypeResolver.GetType(TypeName)")]
+ protected static bool TypeNameIsAssignableToType(TypeName typeName, Type type, ITypeResolver resolver)
+ {
+ try
+ {
+ return resolver.GetType(typeName)?.IsAssignableTo(type) == true;
+ }
+ catch (Exception ex) when (!ex.IsCriticalException())
+ {
+ // Clipboard contains a wrong type, we want the typed API to return false to the caller.
+ }
+
+ return false;
+ }
+
+ protected static object? ReadObjectWithBinaryFormatter(MemoryStream stream, SerializationBinder binder)
+ {
+ // This check is to help in trimming scenarios with a trim warning on a call to BinaryFormatter.Deserialize(),
+ // which has a RequiresUnreferencedCode annotation.
+ // If the flag is false, the trimmer will not generate a warning, since BinaryFormatter.Deserialize() will not be called,
+ // If the flag is true, the trimmer will generate a warning for calling a method that has a RequiresUnreferencedCode annotation.
+ if (!EnableUnsafeBinaryFormatterInNativeObjectSerialization)
+ {
+ throw new NotSupportedException(SR.BinaryFormatterNotSupported);
+ }
+
+#pragma warning disable SYSLIB0011, SYSLIB0050 // Type or member is obsolete
+#pragma warning disable CA2300 // Do not use insecure deserializer BinaryFormatter
+#pragma warning disable CA2302 // Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize
+ // cs/dangerous-binary-deserialization
+#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
+ return new BinaryFormatter()
+ {
+ Binder = binder,
+ AssemblyFormat = FormatterAssemblyStyle.Simple
+ }.Deserialize(stream); // CodeQL[SM03722] : BinaryFormatter is intended to be used as a fallback for unsupported types. Users must explicitly opt into this behavior.
+#pragma warning restore IL2026
+#pragma warning restore CA2300
+#pragma warning restore CA2302
+#pragma warning restore SYSLIB0050, SYSLIB0011
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.Binder.cs
similarity index 66%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.Binder.cs
index 97b40173b75..5e913c67b7b 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.Binder.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.Binder.cs
@@ -2,20 +2,20 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Private.Windows.Core.BinaryFormat;
+using System.Private.Windows.Core.Resources;
using System.Reflection.Metadata;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
-using Switches = System.Windows.Forms.Primitives.LocalAppContextSwitches;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public unsafe partial class DataObject
+internal abstract unsafe partial class DesktopDataObject
{
- internal unsafe partial class Composition
+ internal abstract unsafe partial class Composition
{
///
- /// A type resolver for use in the when processing binary formatted stream
- /// contained in our class using the typed consumption side APIs, such as
+ /// A type resolver for use in the when processing binary formatted stream
+ /// contained in our class using the typed consumption side APIs, such as
/// . This class recognizes primitive types, exchange types from
/// System.Drawing.Primitives, s or arrays of primitive types, and common WinForms types.
/// The user can provide a custom resolver for additional types. If the resolver function is not provided,
@@ -26,7 +26,7 @@ internal unsafe partial class Composition
/// This class is used in and NRBF deserialization.
///
///
- internal sealed class Binder : SerializationBinder, ITypeResolver
+ internal abstract class Binder : SerializationBinder, ITypeResolver
{
private readonly Func? _resolver;
private readonly bool _legacyMode;
@@ -91,18 +91,15 @@ internal sealed class Binder : SerializationBinder, ITypeResolver
typeof(decimal[]),
typeof(DateTime[]),
typeof(TimeSpan[]),
- // Common WinForms types.
- typeof(ImageListStreamer),
- typeof(Drawing.Bitmap),
// Exchange types, they are serialized with the .NET Framework assembly name.
// In .NET they are located in System.Drawing.Primitives.
typeof(Drawing.RectangleF),
- typeof(PointF),
+ typeof(Drawing.PointF),
typeof(Drawing.SizeF),
typeof(Drawing.Rectangle),
- typeof(Point),
+ typeof(Drawing.Point),
typeof(Drawing.Size),
- typeof(Color)
+ typeof(Drawing.Color)
];
private static Dictionary? s_knownTypes;
@@ -146,8 +143,26 @@ public Binder(Type type, Func? resolver, bool legacyMode)
}
}
+ ///
+ /// Returns of an array of additional types that are supported by the application aside from intrinsic types.
+ ///
+ public abstract Type[] AdditionalSupportedTypes();
+
+ private bool IsAdditionalSupportedType()
+ {
+ foreach (Type type in AdditionalSupportedTypes())
+ {
+ if (typeof(T) == type)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
[MemberNotNull(nameof(s_knownTypes))]
- private static void InitializeCommonTypes()
+ private void InitializeCommonTypes()
{
if (s_knownTypes is not null)
{
@@ -155,76 +170,83 @@ private static void InitializeCommonTypes()
}
s_knownTypes = new(TypeNameComparer.Default);
-
foreach (Type type in s_intrinsicTypes)
{
s_knownTypes.Add(type.ToTypeName(), type);
}
+
+ foreach (Type type in AdditionalSupportedTypes())
+ {
+ s_knownTypes.Add(type.ToTypeName(), type);
+ }
}
- public static bool IsKnownType() =>
+ public static bool IsKnownTypeCore() =>
typeof(T) == typeof(byte)
- || typeof(T) == typeof(sbyte)
- || typeof(T) == typeof(short)
- || typeof(T) == typeof(ushort)
- || typeof(T) == typeof(int)
- || typeof(T) == typeof(uint)
- || typeof(T) == typeof(long)
- || typeof(T) == typeof(ulong)
- || typeof(T) == typeof(double)
- || typeof(T) == typeof(float)
- || typeof(T) == typeof(char)
- || typeof(T) == typeof(bool)
- || typeof(T) == typeof(string)
- || typeof(T) == typeof(decimal)
- || typeof(T) == typeof(DateTime)
- || typeof(T) == typeof(TimeSpan)
- || typeof(T) == typeof(IntPtr)
- || typeof(T) == typeof(UIntPtr)
- || typeof(T) == typeof(NotSupportedException)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(List)
- || typeof(T) == typeof(byte[])
- || typeof(T) == typeof(sbyte[])
- || typeof(T) == typeof(short[])
- || typeof(T) == typeof(ushort[])
- || typeof(T) == typeof(int[])
- || typeof(T) == typeof(uint[])
- || typeof(T) == typeof(long[])
- || typeof(T) == typeof(ulong[])
- || typeof(T) == typeof(float[])
- || typeof(T) == typeof(double[])
- || typeof(T) == typeof(char[])
- || typeof(T) == typeof(bool[])
- || typeof(T) == typeof(string[])
- || typeof(T) == typeof(decimal[])
- || typeof(T) == typeof(DateTime[])
- || typeof(T) == typeof(TimeSpan[])
- || typeof(T) == typeof(ImageListStreamer)
- || typeof(T) == typeof(Drawing.Bitmap)
- || typeof(T) == typeof(Drawing.RectangleF)
- || typeof(T) == typeof(PointF)
- || typeof(T) == typeof(Drawing.SizeF)
- || typeof(T) == typeof(Drawing.Rectangle)
- || typeof(T) == typeof(Point)
- || typeof(T) == typeof(Drawing.Size)
- || typeof(T) == typeof(Color);
+ || typeof(T) == typeof(sbyte)
+ || typeof(T) == typeof(short)
+ || typeof(T) == typeof(ushort)
+ || typeof(T) == typeof(int)
+ || typeof(T) == typeof(uint)
+ || typeof(T) == typeof(long)
+ || typeof(T) == typeof(ulong)
+ || typeof(T) == typeof(double)
+ || typeof(T) == typeof(float)
+ || typeof(T) == typeof(char)
+ || typeof(T) == typeof(bool)
+ || typeof(T) == typeof(string)
+ || typeof(T) == typeof(decimal)
+ || typeof(T) == typeof(DateTime)
+ || typeof(T) == typeof(TimeSpan)
+ || typeof(T) == typeof(IntPtr)
+ || typeof(T) == typeof(UIntPtr)
+ || typeof(T) == typeof(NotSupportedException)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(List)
+ || typeof(T) == typeof(byte[])
+ || typeof(T) == typeof(sbyte[])
+ || typeof(T) == typeof(short[])
+ || typeof(T) == typeof(ushort[])
+ || typeof(T) == typeof(int[])
+ || typeof(T) == typeof(uint[])
+ || typeof(T) == typeof(long[])
+ || typeof(T) == typeof(ulong[])
+ || typeof(T) == typeof(float[])
+ || typeof(T) == typeof(double[])
+ || typeof(T) == typeof(char[])
+ || typeof(T) == typeof(bool[])
+ || typeof(T) == typeof(string[])
+ || typeof(T) == typeof(decimal[])
+ || typeof(T) == typeof(DateTime[])
+ || typeof(T) == typeof(TimeSpan[])
+ || typeof(T) == typeof(Drawing.RectangleF)
+ || typeof(T) == typeof(Drawing.PointF)
+ || typeof(T) == typeof(Drawing.SizeF)
+ || typeof(T) == typeof(Drawing.Rectangle)
+ || typeof(T) == typeof(Drawing.Point)
+ || typeof(T) == typeof(Drawing.Size)
+ || typeof(T) == typeof(Drawing.Color);
+
+ public bool IsKnownType() => IsKnownTypeCore() || IsAdditionalSupportedType();
+ [RequiresUnreferencedCode("Calls System.Private.Windows.Core.OLE.DataObjectInner.Composition.Binder.UseResolver(TypeName)")]
+#pragma warning disable IL2046 // 'RequiresUnreferencedCodeAttribute' annotations must match across all interface implementations or overrides.
public override Type? BindToType(string assemblyName, string typeName)
+#pragma warning restore IL2046
{
ArgumentException.ThrowIfNullOrWhiteSpace(assemblyName);
ArgumentException.ThrowIfNullOrWhiteSpace(typeName);
@@ -236,7 +258,7 @@ public static bool IsKnownType() =>
if (_legacyMode)
{
- return Switches.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization
+ return LocalAppContextSwitchesCore.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization
? null
: throw new NotSupportedException(string.Format(
SR.BinaryFormatter_NotSupported_InClipboardOrDragDrop_UseTypedAPI,
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.WinFormsToNativeAdapter.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.DesktopToNativeAdapter.cs
similarity index 83%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.WinFormsToNativeAdapter.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.DesktopToNativeAdapter.cs
index f0907158841..ee1919ea964 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.WinFormsToNativeAdapter.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.DesktopToNativeAdapter.cs
@@ -4,30 +4,49 @@
using System.Drawing;
using System.Runtime.Serialization;
using System.Text;
+using Windows.Win32;
using Windows.Win32.System.Com;
+using Windows.Win32.System.Memory;
+using Windows.Win32.UI.Shell;
using Com = Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public unsafe partial class DataObject
+internal abstract unsafe partial class DesktopDataObject
{
- internal unsafe partial class Composition
+ internal abstract unsafe partial class Composition
{
///
/// Maps to .
///
- private unsafe class WinFormsToNativeAdapter : Com.IDataObject.Interface, IManagedWrapper
+ protected abstract unsafe class DesktopToNativeAdapter : Com.IDataObject.Interface, IManagedWrapper
{
private const int DATA_S_SAMEFORMATETC = 0x00040130;
private readonly IDataObject _dataObject;
- public WinFormsToNativeAdapter(IDataObject dataObject)
+ public DesktopToNativeAdapter(IDataObject dataObject)
{
_dataObject = dataObject;
}
+ ///
+ /// Tries to get the compatible bitmap and save it in if is a bitmap.
+ ///
+ public abstract HRESULT TryGetCompatibleBitmap(string format, object data, STGMEDIUM* pMedium);
+
+ ///
+ /// Gets the appropriate for the application.
+ ///
+ public abstract BinaryFormatUtilities GetBinaryFormatUtilities();
+
+ ///
+ /// Returns whether is an image.
+ ///
+ public abstract bool IsDataImage(object data);
+
///
/// Returns true if the tymed is usable.
///
@@ -48,7 +67,7 @@ public HRESULT GetData(FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
if (DragDropHelper.IsInDragLoop(_dataObject))
{
- string formatName = DataFormats.GetFormat(pformatetcIn->cfFormat).Name;
+ string formatName = DesktopDataFormats.GetFormat(pformatetcIn->cfFormat).Name;
if (!_dataObject.GetDataPresent(formatName))
{
*pmedium = default;
@@ -110,7 +129,7 @@ public HRESULT GetDataHere(FORMATETC* pformatetc, STGMEDIUM* pmedium)
return HRESULT.DV_E_TYMED;
}
- string format = DataFormats.GetFormat(pformatetc->cfFormat).Name;
+ string format = DesktopDataFormats.GetFormat(pformatetc->cfFormat).Name;
if (!_dataObject.GetDataPresent(format))
{
@@ -146,48 +165,10 @@ public HRESULT GetDataHere(FORMATETC* pformatetc, STGMEDIUM* pmedium)
if (((TYMED)pformatetc->tymed).HasFlag(TYMED.TYMED_GDI))
{
- if (format.Equals(DataFormats.Bitmap) && data is Bitmap bitmap)
- {
- // Save bitmap
- pmedium->u.hBitmap = GetCompatibleBitmap(bitmap);
- }
-
- return HRESULT.S_OK;
+ return TryGetCompatibleBitmap(format, data, pmedium);
}
return HRESULT.DV_E_TYMED;
-
- static HBITMAP GetCompatibleBitmap(Bitmap bitmap)
- {
- using var screenDC = GetDcScope.ScreenDC;
-
- // 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.
- HBITMAP hbitmap = bitmap.GetHBITMAP();
-
- // Create a compatible DC to render the source bitmap.
- using CreateDcScope sourceDC = new(screenDC);
- using SelectObjectScope sourceBitmapSelection = new(sourceDC, hbitmap);
-
- // Create a compatible DC and a new compatible bitmap.
- using CreateDcScope destinationDC = new(screenDC);
- HBITMAP compatibleBitmap = PInvokeCore.CreateCompatibleBitmap(screenDC, bitmap.Size.Width, bitmap.Size.Height);
-
- // Select the new bitmap into a compatible DC and render the blt the original bitmap.
- using SelectObjectScope destinationBitmapSelection = new(destinationDC, compatibleBitmap);
- PInvokeCore.BitBlt(
- destinationDC,
- 0,
- 0,
- bitmap.Size.Width,
- bitmap.Size.Height,
- sourceDC,
- 0,
- 0,
- ROP_CODE.SRCCOPY);
-
- return compatibleBitmap;
- }
}
public HRESULT QueryGetData(FORMATETC* pformatetc)
@@ -212,7 +193,7 @@ public HRESULT QueryGetData(FORMATETC* pformatetc)
return HRESULT.S_FALSE;
}
- if (!_dataObject.GetDataPresent(DataFormats.GetFormat(pformatetc->cfFormat).Name))
+ if (!_dataObject.GetDataPresent(DesktopDataFormats.GetFormat(pformatetc->cfFormat).Name))
{
return HRESULT.DV_E_FORMATETC;
}
@@ -245,7 +226,7 @@ public HRESULT SetData(FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
if (DragDropHelper.IsInDragLoopFormat(*pformatetc) || DragDropHelper.IsInDragLoop(_dataObject))
{
- string formatName = DataFormats.GetFormat(pformatetc->cfFormat).Name;
+ string formatName = DesktopDataFormats.GetFormat(pformatetc->cfFormat).Name;
if (_dataObject.GetDataPresent(formatName) && _dataObject.GetData(formatName) is DragDropFormat dragDropFormat)
{
dragDropFormat.RefreshData(pformatetc->cfFormat, *pmedium, !fRelease);
@@ -306,36 +287,36 @@ public HRESULT EnumDAdvise(IEnumSTATDATA** ppenumAdvise)
{
_ when data is Stream dataStream
=> SaveStreamToHGLOBAL(ref medium.hGlobal, dataStream),
- DataFormats.TextConstant or DataFormats.RtfConstant or DataFormats.OemTextConstant
+ DesktopDataFormats.TextConstant or DesktopDataFormats.RtfConstant or DesktopDataFormats.OemTextConstant
=> SaveStringToHGLOBAL(medium.hGlobal, data.ToString()!, unicode: false),
- DataFormats.HtmlConstant
+ DesktopDataFormats.HtmlConstant
=> SaveHtmlToHGLOBAL(medium.hGlobal, data.ToString()!),
- DataFormats.UnicodeTextConstant
+ DesktopDataFormats.UnicodeTextConstant
=> SaveStringToHGLOBAL(medium.hGlobal, data.ToString()!, unicode: true),
- DataFormats.FileDropConstant
+ DesktopDataFormats.FileDropConstant
=> SaveFileListToHGLOBAL(medium.hGlobal, (string[])data),
CF_DEPRECATED_FILENAME
=> SaveStringToHGLOBAL(medium.hGlobal, ((string[])data)[0], unicode: false),
CF_DEPRECATED_FILENAMEW
=> SaveStringToHGLOBAL(medium.hGlobal, ((string[])data)[0], unicode: true),
- DataFormats.DibConstant when data is Image
+ DesktopDataFormats.DibConstant when IsDataImage(data)
// 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.
=> HRESULT.DV_E_TYMED,
#pragma warning disable SYSLIB0050 // Type or member is obsolete
- _ when format == DataFormats.SerializableConstant || data is ISerializable || data.GetType().IsSerializable
+ _ when format == DesktopDataFormats.SerializableConstant || data is ISerializable || data.GetType().IsSerializable
#pragma warning restore
=> SaveObjectToHGLOBAL(ref medium.hGlobal, data, RestrictDeserializationToSafeTypes(format)),
_ => HRESULT.E_UNEXPECTED
};
- private static HRESULT SaveObjectToHGLOBAL(ref HGLOBAL hglobal, object data, bool restrictSerialization)
+ private HRESULT SaveObjectToHGLOBAL(ref HGLOBAL hglobal, object data, bool restrictSerialization)
{
using MemoryStream stream = new();
stream.Write(s_serializedObjectID);
// Throws in case of serialization failure.
- BinaryFormatUtilities.WriteObjectToStream(stream, data, restrictSerialization);
+ GetBinaryFormatUtilities().WriteObjectToStream(stream, data, restrictSerialization);
return SaveStreamToHGLOBAL(ref hglobal, stream);
}
@@ -481,7 +462,7 @@ private HRESULT SaveStringToHGLOBAL(HGLOBAL hglobal, string value, bool unicode)
{
fixed (char* c = value)
{
- int pinvokeSize = PInvoke.WideCharToMultiByte(PInvoke.CP_ACP, 0, value, value.Length, null, 0, null, null);
+ int pinvokeSize = PInvokeCore.WideCharToMultiByte(PInvokeCore.CP_ACP, 0, value, value.Length, null, 0, null, null);
newHandle = PInvokeCore.GlobalReAlloc(hglobal, (uint)pinvokeSize + 1, (uint)GLOBAL_ALLOC_FLAGS.GMEM_MOVEABLE | (uint)GLOBAL_ALLOC_FLAGS.GMEM_ZEROINIT);
if (newHandle == 0)
{
@@ -494,7 +475,7 @@ private HRESULT SaveStringToHGLOBAL(HGLOBAL hglobal, string value, bool unicode)
return HRESULT.E_OUTOFMEMORY;
}
- PInvoke.WideCharToMultiByte(PInvoke.CP_ACP, 0, value, value.Length, buffer, pinvokeSize, null, null);
+ PInvokeCore.WideCharToMultiByte(PInvokeCore.CP_ACP, 0, value, value.Length, buffer, pinvokeSize, null, null);
// Null terminate
buffer[pinvokeSize] = 0;
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.NativeToDesktopAdapter.cs
similarity index 81%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.NativeToDesktopAdapter.cs
index b563cee7bc8..2d4b49f6488 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToWinFormsAdapter.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.NativeToDesktopAdapter.cs
@@ -1,27 +1,30 @@
// 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.Core.Resources;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text;
+using Windows.Win32;
+using Windows.Win32.System.Memory;
+using Windows.Win32.UI.Shell;
using Com = Windows.Win32.System.Com;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public unsafe partial class DataObject
+internal abstract unsafe partial class DesktopDataObject
{
- internal unsafe partial class Composition
+ internal abstract unsafe partial class Composition
{
///
/// Maps native pointer to .
///
- private unsafe class NativeToWinFormsAdapter : ITypedDataObject, Com.IDataObject.Interface
+ internal abstract unsafe class NativeToDesktopAdapter : ITypedDataObjectDesktop, Com.IDataObject.Interface
{
private readonly AgileComPointer _nativeDataObject;
- public NativeToWinFormsAdapter(Com.IDataObject* dataObject)
+ public NativeToDesktopAdapter(Com.IDataObject* dataObject)
{
#if DEBUG
_nativeDataObject = new(dataObject, takeOwnership: true, trackDisposal: false);
@@ -30,6 +33,22 @@ public NativeToWinFormsAdapter(Com.IDataObject* dataObject)
#endif
}
+ ///
+ /// Gets the appropriate for the application.
+ ///
+ public abstract BinaryFormatUtilities GetBinaryFormatUtilities();
+
+ ///
+ /// Tries to extract the bitmap out if it is present in the under the given format.
+ ///
+ /// if successful. Otherwise,
+ public abstract bool TryGetBitmap(Com.IDataObject* dataObject, string format, [NotNullWhen(true)] out object? bitmap);
+
+ ///
+ /// Throws if the format and type combination requires a resolver.
+ ///
+ protected abstract void ThrowIfFormatAndTypeRequireResolver(string format);
+
#region Com.IDataObject.Interface
HRESULT Com.IDataObject.Interface.DAdvise(Com.FORMATETC* pformatetc, uint advf, Com.IAdviseSink* pAdvSink, uint* pdwConnection)
@@ -91,7 +110,7 @@ HRESULT Com.IDataObject.Interface.SetData(Com.FORMATETC* pformatetc, Com.STGMEDI
///
/// Retrieves the specified format from the specified .
///
- private static bool TryGetDataFromHGLOBAL(
+ private bool TryGetDataFromHGLOBAL(
HGLOBAL hglobal,
string format,
Func? resolver,
@@ -106,11 +125,11 @@ private static bool TryGetDataFromHGLOBAL(
object? value = format switch
{
- DataFormats.TextConstant or DataFormats.RtfConstant or DataFormats.OemTextConstant =>
+ DesktopDataFormats.TextConstant or DesktopDataFormats.RtfConstant or DesktopDataFormats.OemTextConstant =>
ReadStringFromHGLOBAL(hglobal, unicode: false),
- DataFormats.HtmlConstant => ReadUtf8StringFromHGLOBAL(hglobal),
- DataFormats.UnicodeTextConstant => ReadStringFromHGLOBAL(hglobal, unicode: true),
- DataFormats.FileDropConstant => ReadFileListFromHDROP((HDROP)(nint)hglobal),
+ DesktopDataFormats.HtmlConstant => ReadUtf8StringFromHGLOBAL(hglobal),
+ DesktopDataFormats.UnicodeTextConstant => ReadStringFromHGLOBAL(hglobal, unicode: true),
+ DesktopDataFormats.FileDropConstant => ReadFileListFromHDROP((HDROP)(nint)hglobal),
CF_DEPRECATED_FILENAME => new string[] { ReadStringFromHGLOBAL(hglobal, unicode: false) },
CF_DEPRECATED_FILENAMEW => new string[] { ReadStringFromHGLOBAL(hglobal, unicode: true) },
_ => ReadObjectOrStreamFromHGLOBAL(hglobal, RestrictDeserializationToSafeTypes(format), resolver, legacyMode)
@@ -124,7 +143,7 @@ private static bool TryGetDataFromHGLOBAL(
return false;
- static object? ReadObjectOrStreamFromHGLOBAL(
+ object? ReadObjectOrStreamFromHGLOBAL(
HGLOBAL hglobal,
bool restrictDeserialization,
Func? resolver,
@@ -134,8 +153,8 @@ private static bool TryGetDataFromHGLOBAL(
return !isSerializedObject
? stream
: restrictDeserialization
- ? BinaryFormatUtilities.ReadRestrictedObjectFromStream(stream, resolver, legacyMode)
- : BinaryFormatUtilities.ReadObjectFromStream(stream, resolver, legacyMode);
+ ? GetBinaryFormatUtilities().ReadRestrictedObjectFromStream(stream, resolver, legacyMode)
+ : GetBinaryFormatUtilities().ReadObjectFromStream(stream, resolver, legacyMode);
}
}
@@ -203,7 +222,7 @@ private static unsafe string ReadUtf8StringFromHGLOBAL(HGLOBAL hglobal)
private static unsafe string[]? ReadFileListFromHDROP(HDROP hdrop)
{
- uint count = PInvoke.DragQueryFile(hdrop, iFile: 0xFFFFFFFF, lpszFile: null, cch: 0);
+ uint count = PInvokeCore.DragQueryFile(hdrop, iFile: 0xFFFFFFFF, lpszFile: null, cch: 0);
if (count == 0)
{
return null;
@@ -216,7 +235,7 @@ private static unsafe string ReadUtf8StringFromHGLOBAL(HGLOBAL hglobal)
{
for (uint i = 0; i < count; i++)
{
- uint charactersCopied = PInvoke.DragQueryFile(hdrop, i, buffer, (uint)fileName.Length);
+ uint charactersCopied = PInvokeCore.DragQueryFile(hdrop, i, buffer, (uint)fileName.Length);
if (charactersCopied == 0)
{
continue;
@@ -249,7 +268,7 @@ private static unsafe string ReadUtf8StringFromHGLOBAL(HGLOBAL hglobal)
///
///
/// is deserialization failed.
- private static bool TryGetObjectFromDataObject(
+ private bool TryGetObjectFromDataObject(
Com.IDataObject* dataObject,
string format,
Func? resolver,
@@ -264,10 +283,9 @@ private static bool TryGetObjectFromDataObject(
try
{
// Try to get the data as a bitmap first.
- if ((typeof(Bitmap) == typeof(T) || typeof(Image) == typeof(T))
- && TryGetBitmapData(dataObject, format, out Bitmap? bitmap))
+ if (TryGetBitmap(dataObject, format, out object? bitmap))
{
- data = (T)(object)bitmap;
+ data = (T)bitmap;
return true;
}
@@ -286,7 +304,7 @@ private static bool TryGetObjectFromDataObject(
return result;
}
- private static bool TryGetHGLOBALData(
+ private bool TryGetHGLOBALData(
Com.IDataObject* dataObject,
string format,
Func? resolver,
@@ -299,7 +317,7 @@ private static bool TryGetHGLOBALData(
Com.FORMATETC formatetc = new()
{
- cfFormat = (ushort)DataFormats.GetFormat(format).Id,
+ cfFormat = (ushort)DesktopDataFormats.GetFormat(format).Id,
dwAspect = (uint)Com.DVASPECT.DVASPECT_CONTENT,
lindex = -1,
tymed = (uint)Com.TYMED.TYMED_HGLOBAL
@@ -343,13 +361,13 @@ private static bool TryGetHGLOBALData(
}
finally
{
- PInvoke.ReleaseStgMedium(ref medium);
+ PInvokeCore.ReleaseStgMedium(ref medium);
}
return result;
}
- private static unsafe bool TryGetIStreamData(
+ private unsafe bool TryGetIStreamData(
Com.IDataObject* dataObject,
string format,
Func? resolver,
@@ -359,7 +377,7 @@ private static unsafe bool TryGetIStreamData(
data = default;
Com.FORMATETC formatEtc = new()
{
- cfFormat = (ushort)DataFormats.GetFormat(format).Id,
+ cfFormat = (ushort)DesktopDataFormats.GetFormat(format).Id,
dwAspect = (uint)Com.DVASPECT.DVASPECT_CONTENT,
lindex = -1,
tymed = (uint)Com.TYMED.TYMED_ISTREAM
@@ -405,81 +423,7 @@ private static unsafe bool TryGetIStreamData(
PInvokeCore.GlobalFree(hglobal);
}
- PInvoke.ReleaseStgMedium(ref medium);
- }
- }
-
- private static bool TryGetBitmapData(Com.IDataObject* dataObject, string format, [NotNullWhen(true)] out Bitmap? data)
- {
- data = default;
- if (format != DataFormats.BitmapConstant)
- {
- return false;
- }
-
- Com.FORMATETC formatEtc = new()
- {
- cfFormat = (ushort)DataFormats.GetFormat(format).Id,
- dwAspect = (uint)Com.DVASPECT.DVASPECT_CONTENT,
- lindex = -1,
- tymed = (uint)Com.TYMED.TYMED_GDI
- };
-
- Com.STGMEDIUM medium = default;
-
- if (dataObject->QueryGetData(formatEtc).Succeeded)
- {
- HRESULT hr = dataObject->GetData(formatEtc, out 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(hr == 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 ((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
- {
- PInvoke.ReleaseStgMedium(ref medium);
- }
-
- return false;
- }
-
- private static void ThrowIfFormatAndTypeRequireResolver(string format)
- {
- // Restricted format is either read directly from the HGLOBAL or serialization record is read manually.
- if (!IsRestrictedFormat(format)
- // This check is a convenience for simple usages if TryGetData APIs that don't take the resolver.
- && IsUnboundedType())
- {
- throw new NotSupportedException(string.Format(
- SR.ClipboardOrDragDrop_InvalidType,
- typeof(T).FullName));
- }
-
- static bool IsUnboundedType()
- {
- if (typeof(T) == typeof(object))
- {
- return true;
- }
-
- Type type = typeof(T);
- // Image is a special case because we are reading Bitmaps directly from the SerializationRecord.
- return type.IsInterface || (typeof(T) != typeof(Image) && type.IsAbstract);
+ PInvokeCore.ReleaseStgMedium(ref medium);
}
}
@@ -598,7 +542,7 @@ public string[] GetFormats(bool autoConvert)
while (enumFORMATETC.Value->Next(1, &formatEtc) == HRESULT.S_OK)
{
- string name = DataFormats.GetFormat(formatEtc.cfFormat).Name;
+ string name = DesktopDataFormats.GetFormat(formatEtc.cfFormat).Name;
if (autoConvert)
{
string[] mappedFormats = GetMappedFormats(name)!;
@@ -654,7 +598,7 @@ private bool GetDataPresentInner(string format)
{
Com.FORMATETC formatEtc = new()
{
- cfFormat = (ushort)(DataFormats.GetFormat(format).Id),
+ cfFormat = (ushort)(DesktopDataFormats.GetFormat(format).Id),
dwAspect = (uint)Com.DVASPECT.DVASPECT_CONTENT,
lindex = -1,
tymed = (uint)AllowedTymeds
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToRuntimeAdapter.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.NativeToRuntimeAdapter.cs
similarity index 80%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToRuntimeAdapter.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.NativeToRuntimeAdapter.cs
index 1493b7d0def..fa74c734429 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.NativeToRuntimeAdapter.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.NativeToRuntimeAdapter.cs
@@ -1,22 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.Private.Windows.Core.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
+using Windows.Win32;
using Com = Windows.Win32.System.Com;
-using ComTypes = System.Runtime.InteropServices.ComTypes;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public unsafe partial class DataObject
+internal abstract unsafe partial class DesktopDataObject
{
- internal unsafe partial class Composition
+ internal abstract unsafe partial class Composition
{
///
- /// Maps native pointer to .
+ /// Maps native pointer to .
///
- private class NativeToRuntimeAdapter : ComTypes.IDataObject
+ protected class NativeToRuntimeAdapter : IDataObject
{
private readonly AgileComPointer _nativeDataObject;
@@ -29,7 +30,7 @@ public NativeToRuntimeAdapter(Com.IDataObject* dataObject)
#endif
}
- int ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
+ int IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
{
using var nativeAdviseSink = ComHelpers.TryGetComScope(adviseSink);
fixed (Com.FORMATETC* nativeFormat = &Unsafe.As(ref pFormatetc))
@@ -40,13 +41,13 @@ int ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSin
}
}
- void ComTypes.IDataObject.DUnadvise(int connection)
+ void IDataObject.DUnadvise(int connection)
{
using var nativeDataObject = _nativeDataObject.GetInterface();
nativeDataObject.Value->DUnadvise((uint)connection).ThrowOnFailure();
}
- int ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA? enumAdvise)
+ int IDataObject.EnumDAdvise(out IEnumSTATDATA? enumAdvise)
{
using ComScope nativeStatData = new(null);
using var nativeDataObject = _nativeDataObject.GetInterface();
@@ -55,7 +56,7 @@ int ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA? enumAdvise)
return result;
}
- IEnumFORMATETC ComTypes.IDataObject.EnumFormatEtc(DATADIR direction)
+ IEnumFORMATETC IDataObject.EnumFormatEtc(DATADIR direction)
{
using ComScope nativeFormat = new(null);
using var nativeDataObject = _nativeDataObject.GetInterface();
@@ -67,7 +68,7 @@ IEnumFORMATETC ComTypes.IDataObject.EnumFormatEtc(DATADIR direction)
return (IEnumFORMATETC)ComHelpers.GetObjectForIUnknown(nativeFormat);
}
- int ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
+ int IDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
{
using var nativeDataObject = _nativeDataObject.GetInterface();
HRESULT result = nativeDataObject.Value->GetCanonicalFormatEtc(Unsafe.As(ref formatIn), out Com.FORMATETC nativeFormat);
@@ -75,7 +76,7 @@ int ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMA
return result;
}
- void ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
+ void IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
{
Com.FORMATETC nativeFormat = Unsafe.As(ref format);
Com.STGMEDIUM nativeMedium = default;
@@ -85,7 +86,7 @@ void ComTypes.IDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
nativeMedium.ReleaseUnknown();
}
- void ComTypes.IDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
+ void IDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
{
Com.FORMATETC nativeFormat = Unsafe.As(ref format);
Com.STGMEDIUM nativeMedium = (Com.STGMEDIUM)medium;
@@ -95,13 +96,13 @@ void ComTypes.IDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium
nativeMedium.ReleaseUnknown();
}
- int ComTypes.IDataObject.QueryGetData(ref FORMATETC format)
+ int IDataObject.QueryGetData(ref FORMATETC format)
{
using var nativeDataObject = _nativeDataObject.GetInterface();
return nativeDataObject.Value->QueryGetData(Unsafe.As(ref format));
}
- void ComTypes.IDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
+ void IDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
{
Com.STGMEDIUM nativeMedium = (Com.STGMEDIUM)medium;
Com.FORMATETC nativeFormat = Unsafe.As(ref formatIn);
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.RuntimeToNativeAdapter.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.RuntimeToNativeAdapter.cs
similarity index 90%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.RuntimeToNativeAdapter.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.RuntimeToNativeAdapter.cs
index a7b876145c2..13a2f6c5361 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.RuntimeToNativeAdapter.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.RuntimeToNativeAdapter.cs
@@ -2,23 +2,23 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices.ComTypes;
+using Windows.Win32;
using Com = Windows.Win32.System.Com;
-using ComTypes = System.Runtime.InteropServices.ComTypes;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public unsafe partial class DataObject
+internal abstract unsafe partial class DesktopDataObject
{
- internal unsafe partial class Composition
+ internal abstract unsafe partial class Composition
{
///
- /// Maps the runtime to the native .
+ /// Maps the runtime to the native .
///
- private class RuntimeToNativeAdapter : Com.IDataObject.Interface, ComTypes.IDataObject, Com.IManagedWrapper
+ protected class RuntimeToNativeAdapter : Com.IDataObject.Interface, IDataObject, Com.IManagedWrapper
{
- private readonly ComTypes.IDataObject _runtimeDataObject;
+ private readonly IDataObject _runtimeDataObject;
- public RuntimeToNativeAdapter(ComTypes.IDataObject dataObject) => _runtimeDataObject = dataObject;
+ public RuntimeToNativeAdapter(IDataObject dataObject) => _runtimeDataObject = dataObject;
#region ComTypes.IDataObject
public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection) => _runtimeDataObject.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.TypeNameComparer.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.TypeNameComparer.cs
similarity index 94%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.TypeNameComparer.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.TypeNameComparer.cs
index c1a8f1e3857..313b85c6356 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.TypeNameComparer.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.TypeNameComparer.cs
@@ -3,10 +3,11 @@
using System.Reflection.Metadata;
-namespace System.Windows.Forms;
-public unsafe partial class DataObject
+namespace System.Private.Windows.Core.OLE;
+
+internal abstract unsafe partial class DesktopDataObject
{
- internal unsafe partial class Composition
+ internal abstract unsafe partial class Composition
{
///
/// Match s by matching full namespace-qualified type names and full assembly names,
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.cs
similarity index 57%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.cs
index e04fb728c06..e9f1cad6b09 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.Composition.cs
@@ -5,16 +5,18 @@
using System.Runtime.InteropServices.ComTypes;
using Com = Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
+using ITypedDataObject = System.Private.Windows.Core.OLE.ITypedDataObjectDesktop;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public unsafe partial class DataObject
+internal abstract unsafe partial class DesktopDataObject
{
///
/// Contains the logic to move between , ,
/// and calls.
///
- internal unsafe partial class Composition : ITypedDataObject, Com.IDataObject.Interface, ComTypes.IDataObject
+ internal abstract unsafe partial class Composition : ITypedDataObject, Com.IDataObject.Interface, ComTypes.IDataObject
{
private const Com.TYMED AllowedTymeds = Com.TYMED.TYMED_HGLOBAL | Com.TYMED.TYMED_ISTREAM | Com.TYMED.TYMED_GDI;
@@ -30,51 +32,53 @@ internal unsafe partial class Composition : ITypedDataObject, Com.IDataObject.In
0xa6, 0x79, 0x56, 0x10, 0x6b, 0xb2, 0x88, 0xfb
];
- private readonly IDataObject _winFormsDataObject;
- private readonly Com.IDataObject.Interface _nativeDataObject;
- private readonly ComTypes.IDataObject _runtimeDataObject;
+ private IDataObject? _winFormsDataObject;
+ private Com.IDataObject.Interface? _nativeDataObject;
+ private 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.
+#if NET9_0_OR_GREATER
[FeatureSwitchDefinition("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization")]
+#endif
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)
+ ///
+ /// Populates the Composition appropriately based of the given param.
+ /// This or one of the other PopulateFromX methods will be called when is being created.
+ ///
+ ///
+ ///
+ /// Implementation must call
+ /// before method ends.
+ ///
+ ///
+ public abstract Composition PopulateFromRuntimeDataObject(ComTypes.IDataObject runtimeDataObject);
+
+ ///
+ public abstract Composition PopulateFromDesktopDataObject(IDataObject desktopDataObject);
+
+ ///
+ public abstract Composition PopulateFromNativeDataObject(Com.IDataObject* nativeDataObject);
+
+ [MemberNotNull(nameof(_winFormsDataObject))]
+ [MemberNotNull(nameof(_nativeDataObject))]
+ [MemberNotNull(nameof(_runtimeDataObject))]
+ public Composition Populate(IDataObject winFormsDataObject, Com.IDataObject.Interface nativeDataObject, ComTypes.IDataObject runtimeDataObject)
{
_winFormsDataObject = winFormsDataObject;
_nativeDataObject = nativeDataObject;
_runtimeDataObject = runtimeDataObject;
- if (winFormsDataObject is not NativeToWinFormsAdapter and not DataStore)
+ if (winFormsDataObject is not NativeToDesktopAdapter and not DataStore)
{
+ // This is our DataObjectAdapter.
OriginalIDataObject = winFormsDataObject;
}
- }
-
- public static Composition CreateFromWinFormsDataObject(IDataObject winFormsDataObject)
- {
- WinFormsToNativeAdapter winFormsToNative = new(winFormsDataObject);
- NativeToRuntimeAdapter nativeToRuntime = new(ComHelpers.GetComPointer(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(runtimeToNative));
- return new(nativeToWinForms, runtimeToNative, runtimeDataObject);
+ return this;
}
///
@@ -83,18 +87,18 @@ public static Composition CreateFromRuntimeDataObject(ComTypes.IDataObject runti
public IDataObject? OriginalIDataObject { get; private set; }
#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);
+ 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 ITypedDataObject
@@ -103,46 +107,46 @@ public static Composition CreateFromRuntimeDataObject(ComTypes.IDataObject runti
Func resolver,
bool autoConvert,
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- _winFormsDataObject.TryGetData(format, resolver, autoConvert, out data);
+ _winFormsDataObject!.TryGetData(format, resolver, autoConvert, out data);
public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
string format,
bool autoConvert,
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- _winFormsDataObject.TryGetData(format, autoConvert, out data);
+ _winFormsDataObject!.TryGetData(format, autoConvert, out data);
public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
string format,
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- _winFormsDataObject.TryGetData(format, out data);
+ _winFormsDataObject!.TryGetData(format, out data);
public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- _winFormsDataObject.TryGetData(typeof(T).FullName!, out data);
+ _winFormsDataObject!.TryGetData(typeof(T).FullName!, out data);
#endregion
#region Com.IDataObject.Interface
- public HRESULT DAdvise(Com.FORMATETC* pformatetc, uint advf, Com.IAdviseSink* pAdvSink, uint* pdwConnection) => _nativeDataObject.DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
- public HRESULT DUnadvise(uint dwConnection) => _nativeDataObject.DUnadvise(dwConnection);
- public HRESULT EnumDAdvise(Com.IEnumSTATDATA** ppenumAdvise) => _nativeDataObject.EnumDAdvise(ppenumAdvise);
- public HRESULT EnumFormatEtc(uint dwDirection, Com.IEnumFORMATETC** ppenumFormatEtc) => _nativeDataObject.EnumFormatEtc(dwDirection, ppenumFormatEtc);
- public HRESULT GetCanonicalFormatEtc(Com.FORMATETC* pformatectIn, Com.FORMATETC* pformatetcOut) => _nativeDataObject.GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
- public HRESULT GetData(Com.FORMATETC* pformatetcIn, Com.STGMEDIUM* pmedium) => _nativeDataObject.GetData(pformatetcIn, pmedium);
- public HRESULT GetDataHere(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium) => _nativeDataObject.GetDataHere(pformatetc, pmedium);
- public HRESULT QueryGetData(Com.FORMATETC* pformatetc) => _nativeDataObject.QueryGetData(pformatetc);
- public HRESULT SetData(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium, BOOL fRelease) => _nativeDataObject.SetData(pformatetc, pmedium, fRelease);
+ public HRESULT DAdvise(Com.FORMATETC* pformatetc, uint advf, Com.IAdviseSink* pAdvSink, uint* pdwConnection) => _nativeDataObject!.DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
+ public HRESULT DUnadvise(uint dwConnection) => _nativeDataObject!.DUnadvise(dwConnection);
+ public HRESULT EnumDAdvise(Com.IEnumSTATDATA** ppenumAdvise) => _nativeDataObject!.EnumDAdvise(ppenumAdvise);
+ public HRESULT EnumFormatEtc(uint dwDirection, Com.IEnumFORMATETC** ppenumFormatEtc) => _nativeDataObject!.EnumFormatEtc(dwDirection, ppenumFormatEtc);
+ public HRESULT GetCanonicalFormatEtc(Com.FORMATETC* pformatectIn, Com.FORMATETC* pformatetcOut) => _nativeDataObject!.GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
+ public HRESULT GetData(Com.FORMATETC* pformatetcIn, Com.STGMEDIUM* pmedium) => _nativeDataObject!.GetData(pformatetcIn, pmedium);
+ public HRESULT GetDataHere(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium) => _nativeDataObject!.GetDataHere(pformatetc, pmedium);
+ public HRESULT QueryGetData(Com.FORMATETC* pformatetc) => _nativeDataObject!.QueryGetData(pformatetc);
+ public HRESULT SetData(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium, BOOL fRelease) => _nativeDataObject!.SetData(pformatetc, pmedium, fRelease);
#endregion
#region ComTypes.IDataObject.Interface
- public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, 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 IEnumSTATDATA? enumAdvise) => _runtimeDataObject.EnumDAdvise(out enumAdvise);
- public IEnumFORMATETC EnumFormatEtc(DATADIR direction) => _runtimeDataObject.EnumFormatEtc(direction);
- public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) => _runtimeDataObject.GetCanonicalFormatEtc(ref formatIn, out formatOut);
- public void GetData(ref FORMATETC format, out STGMEDIUM medium) => _runtimeDataObject.GetData(ref format, out medium);
- public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) => _runtimeDataObject.GetDataHere(ref format, ref medium);
- public int QueryGetData(ref FORMATETC format) => _runtimeDataObject.QueryGetData(ref format);
- public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) => _runtimeDataObject.SetData(ref formatIn, ref medium, release);
+ public int DAdvise(ref FORMATETC pFormatetc, ADVF advf, 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 IEnumSTATDATA? enumAdvise) => _runtimeDataObject!.EnumDAdvise(out enumAdvise);
+ public IEnumFORMATETC EnumFormatEtc(DATADIR direction) => _runtimeDataObject!.EnumFormatEtc(direction);
+ public int GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut) => _runtimeDataObject!.GetCanonicalFormatEtc(ref formatIn, out formatOut);
+ public void GetData(ref FORMATETC format, out STGMEDIUM medium) => _runtimeDataObject!.GetData(ref format, out medium);
+ public void GetDataHere(ref FORMATETC format, ref STGMEDIUM medium) => _runtimeDataObject!.GetDataHere(ref format, ref medium);
+ public int QueryGetData(ref FORMATETC format) => _runtimeDataObject!.QueryGetData(ref format);
+ public void SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release) => _runtimeDataObject!.SetData(ref formatIn, ref medium, release);
#endregion
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.DataStoreEntry.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.DataStore.DataStoreEntry.cs
similarity index 70%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.DataStoreEntry.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.DataStore.DataStoreEntry.cs
index c845146a15c..4c27a00e94c 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.DataStoreEntry.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.DataStore.DataStoreEntry.cs
@@ -1,13 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public partial class DataObject
+internal abstract partial class DesktopDataObject
{
- private sealed partial class DataStore
+ internal abstract partial class DataStore
{
- private class DataStoreEntry
+ protected class DataStoreEntry
{
public object? Data { get; }
public bool AutoConvert { get; }
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.DataStore.cs
similarity index 83%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.DataStore.cs
index b651edfb082..075af489130 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.DataStore.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.DataStore.cs
@@ -2,18 +2,18 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Specialized;
-using System.Drawing;
-using System.Private.Windows;
using System.Reflection.Metadata;
using System.Runtime.Serialization;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public partial class DataObject
+internal abstract partial class DesktopDataObject
{
- private sealed partial class DataStore : ITypedDataObject
+ internal abstract partial class DataStore : ITypedDataObjectDesktop
{
- private readonly Dictionary _mappedData = new(BackCompatibleStringComparer.Default);
+ protected readonly Dictionary _mappedData = new(BackCompatibleStringComparer.Default);
+
+ public abstract void SetData(string format, bool autoConvert, object? data);
private bool TryGetDataInternal(
string format,
@@ -82,25 +82,6 @@ private bool TryGetDataInternal(
public object? GetData(Type format) => GetData(format.FullName!);
- public void SetData(string format, bool autoConvert, object? data)
- {
- if (string.IsNullOrWhiteSpace(format))
- {
- ArgumentNullException.ThrowIfNull(format);
- throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
- }
-
- // 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(DataFormats.Dib))
- {
- format = autoConvert ? DataFormats.Bitmap : throw new NotSupportedException(SR.DataObjectDibNotSupported);
- }
-
- _mappedData[format] = new DataStoreEntry(data, autoConvert);
- }
-
public void SetData(string format, object? data) => SetData(format, autoConvert: true, data);
public void SetData(Type format, object? data)
@@ -114,9 +95,9 @@ public void SetData(object? data)
ArgumentNullException.ThrowIfNull(data);
if (data is ISerializable
- && !_mappedData.ContainsKey(DataFormats.Serializable))
+ && !_mappedData.ContainsKey(DesktopDataFormats.SerializableConstant))
{
- SetData(DataFormats.Serializable, data);
+ SetData(DesktopDataFormats.SerializableConstant, data);
}
SetData(data.GetType(), data);
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.FormatEnumerator.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.FormatEnumerator.cs
similarity index 91%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.FormatEnumerator.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.FormatEnumerator.cs
index 3c33ad8a0e3..164ff3888ba 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.FormatEnumerator.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.FormatEnumerator.cs
@@ -2,12 +2,14 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.CompilerServices;
+using Windows.Win32;
using Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public partial class DataObject
+internal abstract partial class DesktopDataObject
{
///
/// Part of IComDataObject, used to interop with OLE.
@@ -43,11 +45,11 @@ public FormatEnumerator(IDataObject parent, string[]? formats)
string format = formats[i];
ComTypes.FORMATETC temp = new()
{
- cfFormat = (short)(ushort)DataFormats.GetFormat(format).Id,
+ cfFormat = (short)(ushort)DesktopDataFormats.GetFormat(format).Id,
dwAspect = ComTypes.DVASPECT.DVASPECT_CONTENT,
ptd = 0,
lindex = -1,
- tymed = format.Equals(DataFormats.Bitmap) ? ComTypes.TYMED.TYMED_GDI : ComTypes.TYMED.TYMED_HGLOBAL
+ tymed = format.Equals(DesktopDataFormats.BitmapConstant) ? ComTypes.TYMED.TYMED_GDI : ComTypes.TYMED.TYMED_HGLOBAL
};
_formats.Add(temp);
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.RestrictedTypeDeserializationException.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.RestrictedTypeDeserializationException.cs
similarity index 74%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.RestrictedTypeDeserializationException.cs
rename to src/System.Private.Windows.Core/src/OLE/DesktopDataObject.RestrictedTypeDeserializationException.cs
index e9d7c81389d..b568f1221bb 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.RestrictedTypeDeserializationException.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.RestrictedTypeDeserializationException.cs
@@ -1,15 +1,15 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
-public partial class DataObject
+internal abstract partial class DesktopDataObject
{
///
/// This exception is used to indicate that clipboard contains a serialized
/// managed object that contains unexpected types and that we should stop processing this data.
///
- private class RestrictedTypeDeserializationException : Exception
+ protected class RestrictedTypeDeserializationException : Exception
{
public RestrictedTypeDeserializationException(string? message) : base(message)
{
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.cs
new file mode 100644
index 00000000000..d1457298ef4
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObject.cs
@@ -0,0 +1,483 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Specialized;
+using System.Private.Windows.Core.Resources;
+using System.Reflection.Metadata;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text.Json;
+using Com = Windows.Win32.System.Com;
+using ComTypes = System.Runtime.InteropServices.ComTypes;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Implements a basic data transfer mechanism.
+///
+[ClassInterface(ClassInterfaceType.None)]
+internal abstract unsafe partial class DesktopDataObject :
+ ITypedDataObjectDesktop,
+ Com.IDataObject.Interface,
+ ComTypes.IDataObject,
+ Com.IManagedWrapper
+{
+ protected const string CF_DEPRECATED_FILENAME = "FileName";
+ protected const string CF_DEPRECATED_FILENAMEW = "FileNameW";
+ protected const string BitmapFullName = "System.Drawing.Bitmap";
+
+ private readonly Composition _innerData;
+
+ ///
+ /// Initializes a new instance of the class, with the raw
+ /// and the managed data object the raw pointer is associated with.
+ ///
+ ///
+ internal DesktopDataObject(Com.IDataObject* data, Composition composition) => _innerData = composition.PopulateFromNativeDataObject(data);
+
+ ///
+ /// Initializes a new instance of the class, which can store arbitrary data.
+ ///
+ ///
+ internal DesktopDataObject(Composition composition) => _innerData = composition.PopulateFromDesktopDataObject(CreateIDataObject());
+
+ ///
+ /// Initializes a new instance of the class, containing the specified data.
+ ///
+ /// A default that will be populated appropriately by this constructor.
+ ///
+ ///
+ /// If implements an interface,
+ /// we strongly recommend implementing interface to support the
+ /// `TryGetData{T}` API family that restricts deserialization to the requested and known types.
+ /// will throw
+ /// if is not implemented.
+ ///
+ ///
+ internal DesktopDataObject(object data, Composition composition)
+ {
+ if (data is DesktopDataObject dataObject)
+ {
+ _innerData = dataObject._innerData;
+ IsOriginalNotIDataObject = dataObject.IsOriginalNotIDataObject;
+ }
+ else if (data is IDataObject iDataObject)
+ {
+ // This is must be an adapter which knows how to move between internal and public IDataObject.
+ _innerData = composition.PopulateFromDesktopDataObject(iDataObject);
+ }
+ else if (data is ComTypes.IDataObject comDataObject)
+ {
+ _innerData = composition.PopulateFromRuntimeDataObject(comDataObject);
+ }
+ else
+ {
+ _innerData = composition.PopulateFromDesktopDataObject(CreateIDataObject());
+ SetData(data);
+ }
+ }
+
+ ///
+ /// Initializes a new instance of the class, containing the specified data and its
+ /// associated format.
+ ///
+ internal DesktopDataObject(string format, object data, Composition composition) : this(composition) => SetData(format, data);
+
+ internal DesktopDataObject(string format, bool autoConvert, object data, Composition composition) : this(composition) => SetData(format, autoConvert, data);
+
+ ///
+ /// Returns if is a known type to the application.
+ /// Otherwise,
+ ///
+ ///
+ ///
+ /// Be sure to call to capture intrinsic types.
+ ///
+ ///
+ public abstract bool IsKnownType();
+
+ ///
+ /// Ensures is not of type public DataObject for JSON serialization as
+ /// it cannot be serialized meaningfully.
+ ///
+ /// If is of type public DataObject.
+ public abstract void CheckDataObjectForJsonSet();
+
+ ///
+ /// Creates the appropriate default for the application.
+ ///
+ public abstract IDataObject CreateIDataObject();
+
+ ///
+ /// Verify if the specified format is valid and compatible with the specified type .
+ ///
+ internal abstract bool IsValidFormatAndType(string format);
+
+ ///
+ /// Flags that the original data was not a user passed .
+ ///
+ internal bool IsOriginalNotIDataObject { get; init; }
+
+ ///
+ /// Returns the inner data that the was created with if the original data implemented
+ /// . Otherwise, returns this.
+ /// This method should only be used if the was created for clipboard purposes.
+ ///
+ internal IDataObject TryUnwrapInnerIDataObject()
+ {
+ Debug.Assert(!IsOriginalNotIDataObject, "This method should only be used for clipboard purposes.");
+ return _innerData.OriginalIDataObject is { } original ? original : this;
+ }
+
+ ///
+ internal IDataObject? OriginalIDataObject => _innerData.OriginalIDataObject;
+
+ ///
+ [RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
+ public void SetDataAsJson(string format, T data) => SetData(format, TryJsonSerialize(format, data));
+
+ ///
+ [RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
+ public void SetDataAsJson(T data) => SetData(typeof(T), TryJsonSerialize(typeof(T).FullName!, data));
+
+ ///
+ /// Stores the data in the specified format.
+ /// If the data is a managed object and format allows for serialization of managed objects, the object will be serialized as JSON.
+ ///
+ /// The format associated with the data. See for predefined formats.
+ /// to allow the data to be converted to another format; otherwise, .
+ /// The data to store.
+ ///
+ /// If is a non derived . This is for better error reporting as will serialize as empty.
+ /// If needs to be set, JSON serialize the data held in using this method, then use
+ /// passing in .
+ ///
+ ///
+ ///
+ /// If your data is an intrinsically handled type such as primitives, string, or Bitmap
+ /// and you are using a custom format or
+ /// it is recommended to use the APIs to avoid unnecessary overhead.
+ ///
+ ///
+ /// The default behavior of is used to serialize the data.
+ ///
+ ///
+ /// See
+ ///
+ /// and
+ /// for more details on default behavior.
+ ///
+ ///
+ /// If custom JSON serialization behavior is needed, manually JSON serialize the data and then use ,
+ /// or create a custom , attach the
+ /// , and then recall this method.
+ /// See for more details
+ /// on custom converters for JSON serialization.
+ ///
+ ///
+ [RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
+ public void SetDataAsJson(string format, bool autoConvert, T data) => SetData(format, autoConvert, TryJsonSerialize(format, data));
+
+ ///
+ /// JSON serialize the data only if the format is not a restricted deserialization format and the data is not an intrinsic type.
+ ///
+ ///
+ /// The passed in as is if the format is restricted. Otherwise the JSON serialized .
+ ///
+ [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(TValue, JsonSerializerOptions)")]
+ private object TryJsonSerialize(string format, T data)
+ {
+ if (string.IsNullOrWhiteSpace(format.OrThrowIfNull()))
+ {
+ throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
+ }
+
+ data.OrThrowIfNull(nameof(data));
+ CheckDataObjectForJsonSet();
+
+ return IsRestrictedFormat(format) || IsKnownType()
+ ? data
+ : new JsonData() { JsonBytes = JsonSerializer.SerializeToUtf8Bytes(data) };
+ }
+
+ ///
+ /// Check if the is one of the restricted formats, which formats that
+ /// correspond to primitives or are pre-defined in the OS such as strings, bitmaps, and OLE types.
+ ///
+ internal static bool IsRestrictedFormat(string format) => RestrictDeserializationToSafeTypes(format)
+ || format is DesktopDataFormats.TextConstant
+ or DesktopDataFormats.UnicodeTextConstant
+ or DesktopDataFormats.RtfConstant
+ or DesktopDataFormats.HtmlConstant
+ or DesktopDataFormats.OemTextConstant
+ or DesktopDataFormats.FileDropConstant
+ or CF_DEPRECATED_FILENAME
+ or CF_DEPRECATED_FILENAMEW;
+
+ ///
+ /// We are restricting binary serialization and deserialization of formats that represent strings, bitmaps or OLE types.
+ ///
+ /// format name
+ /// - serialize only safe types, strings or bitmaps.
+ ///
+ ///
+ /// These formats are also restricted in WPF
+ /// https://github.com/dotnet/wpf/blob/db1ae73aae0e043326e2303b0820d361de04e751/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/dataobject.cs#L2801
+ ///
+ ///
+ private static bool RestrictDeserializationToSafeTypes(string format) =>
+ format is DesktopDataFormats.StringConstant
+ or BitmapFullName
+ or DesktopDataFormats.CsvConstant
+ or DesktopDataFormats.DibConstant
+ or DesktopDataFormats.DifConstant
+ or DesktopDataFormats.LocaleConstant
+ or DesktopDataFormats.PenDataConstant
+ or DesktopDataFormats.RiffConstant
+ or DesktopDataFormats.SymbolicLinkConstant
+ or DesktopDataFormats.TiffConstant
+ or DesktopDataFormats.WaveAudioConstant
+ or DesktopDataFormats.BitmapConstant
+ or DesktopDataFormats.EmfConstant
+ or DesktopDataFormats.PaletteConstant
+ or DesktopDataFormats.WmfConstant;
+
+ #region IDataObject
+ public object? GetData(string format, bool autoConvert)
+ {
+ return _innerData.GetData(format, autoConvert);
+ }
+
+ public object? GetData(string format) => GetData(format, autoConvert: true);
+
+ public object? GetData(Type format) => format is null ? null : GetData(format.FullName!);
+
+ public bool GetDataPresent(string format, bool autoConvert) => _innerData.GetDataPresent(format, autoConvert);
+
+ public bool GetDataPresent(string format) => GetDataPresent(format, autoConvert: true);
+
+ public bool GetDataPresent(Type format) => format is not null && GetDataPresent(format.FullName!);
+
+ public string[] GetFormats(bool autoConvert) => _innerData.GetFormats(autoConvert);
+
+ public string[] GetFormats() => GetFormats(autoConvert: true);
+
+ public void SetData(string format, bool autoConvert, object? data) =>
+ _innerData.SetData(format, autoConvert, data);
+
+ public void SetData(string format, object? data) => _innerData.SetData(format, data);
+
+ public void SetData(Type format, object? data) => _innerData.SetData(format, data);
+
+ public void SetData(object? data) => _innerData.SetData(data);
+ #endregion
+
+ #region ITypedDataObject
+ public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ Func resolver,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data)
+ {
+ data = default;
+ resolver.OrThrowIfNull();
+
+ return TryGetDataInternal(format, resolver, autoConvert, out data);
+ }
+
+ public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ TryGetDataInternal(format, resolver: null, autoConvert, out data);
+
+ public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ TryGetDataInternal(format, resolver: null, autoConvert: true, out data);
+
+ public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ TryGetDataInternal(typeof(T).FullName!, resolver: null, autoConvert: true, out data);
+ #endregion
+
+ ///
+ /// Override this method in the derived class to provide custom data retrieval logic using the typed APIs.
+ ///
+ ///
+ internal virtual bool TryGetDataCore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ Func? resolver,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ _innerData.TryGetData(format, resolver!, autoConvert, out data);
+
+ public virtual bool ContainsAudio() => GetDataPresent(DesktopDataFormats.WaveAudioConstant, autoConvert: false);
+
+ public virtual bool ContainsFileDropList() => GetDataPresent(DesktopDataFormats.FileDropConstant, autoConvert: true);
+
+ public virtual bool ContainsImage() => GetDataPresent(DesktopDataFormats.BitmapConstant, autoConvert: true);
+
+ public virtual bool ContainsText() => ContainsText(DesktopTextDataFormat.UnicodeText);
+
+ public virtual bool ContainsText(DesktopTextDataFormat format)
+ {
+ return GetDataPresent(ConvertToDataFormats(format), autoConvert: false);
+ }
+
+#pragma warning disable WFDEV005 // Type or member is obsolete
+ public virtual Stream? GetAudioStream() => GetData(DesktopDataFormats.WaveAudioConstant, autoConvert: false) as Stream;
+
+ public virtual StringCollection GetFileDropList()
+ {
+ StringCollection dropList = [];
+ if (GetData(DesktopDataFormats.FileDropConstant, autoConvert: true) is string[] strings)
+ {
+ dropList.AddRange(strings);
+ }
+
+ return dropList;
+ }
+
+ public virtual string GetText(DesktopTextDataFormat format)
+ {
+ return GetData(ConvertToDataFormats(format), autoConvert: false) is string text ? text : string.Empty;
+ }
+#pragma warning restore WFDEV005
+
+ public virtual string GetText() => GetText(DesktopTextDataFormat.UnicodeText);
+
+ public virtual void SetAudio(byte[] audioBytes) => SetAudio(new MemoryStream(audioBytes.OrThrowIfNull()));
+
+ public virtual void SetAudio(Stream audioStream) =>
+ SetData(DesktopDataFormats.WaveAudioConstant, autoConvert: false, audioStream.OrThrowIfNull());
+
+ public virtual void SetFileDropList(StringCollection filePaths)
+ {
+ string[] strings = new string[filePaths.OrThrowIfNull().Count];
+ filePaths.CopyTo(strings, 0);
+ SetData(DesktopDataFormats.FileDropConstant, true, strings);
+ }
+
+ public virtual void SetText(string textData) => SetText(textData, DesktopTextDataFormat.UnicodeText);
+
+ public virtual void SetText(string textData, DesktopTextDataFormat format)
+ {
+ textData.ThrowIfNullOrEmpty();
+ SetData(ConvertToDataFormats(format), false, textData);
+ }
+
+ private bool TryGetDataInternal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ Func? resolver,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data)
+ {
+ data = default;
+
+ if (!IsValidFormatAndType(format))
+ {
+ // Resolver implementation is specific to the overridden TryGetDataCore method,
+ // can't validate if a non-null resolver is required for unbounded types.
+ return false;
+ }
+
+ return TryGetDataCore(format, resolver, autoConvert, out data);
+ }
+
+ private static string ConvertToDataFormats(DesktopTextDataFormat format) => format switch
+ {
+ DesktopTextDataFormat.UnicodeText => DesktopDataFormats.UnicodeTextConstant,
+ DesktopTextDataFormat.Rtf => DesktopDataFormats.RtfConstant,
+ DesktopTextDataFormat.Html => DesktopDataFormats.HtmlConstant,
+ DesktopTextDataFormat.CommaSeparatedValue => DesktopDataFormats.CsvConstant,
+ _ => DesktopDataFormats.UnicodeTextConstant,
+ };
+
+ ///
+ /// Returns all the "synonyms" for the specified format.
+ ///
+ private static string[]? GetMappedFormats(string format) => format switch
+ {
+ null => null,
+ DesktopDataFormats.TextConstant or DesktopDataFormats.UnicodeTextConstant or DesktopDataFormats.StringConstant =>
+ [
+ DesktopDataFormats.StringConstant,
+ DesktopDataFormats.UnicodeTextConstant,
+ DesktopDataFormats.TextConstant
+ ],
+ DesktopDataFormats.FileDropConstant or CF_DEPRECATED_FILENAME or CF_DEPRECATED_FILENAMEW =>
+ [
+ DesktopDataFormats.FileDropConstant,
+ CF_DEPRECATED_FILENAMEW,
+ CF_DEPRECATED_FILENAME
+ ],
+ DesktopDataFormats.BitmapConstant or BitmapFullName =>
+ [
+ BitmapFullName,
+ DesktopDataFormats.BitmapConstant
+ ],
+ _ => [format]
+ };
+
+ #region ComTypes.IDataObject
+ int ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink pAdvSink, out int pdwConnection) =>
+ _innerData.DAdvise(ref pFormatetc, advf, pAdvSink, out pdwConnection);
+
+ void ComTypes.IDataObject.DUnadvise(int dwConnection) => _innerData.DUnadvise(dwConnection);
+
+ int ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA? enumAdvise) =>
+ _innerData.EnumDAdvise(out enumAdvise);
+
+ IEnumFORMATETC ComTypes.IDataObject.EnumFormatEtc(DATADIR dwDirection) =>
+ _innerData.EnumFormatEtc(dwDirection);
+
+ int ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC pformatetcIn, out FORMATETC pformatetcOut) =>
+ _innerData.GetCanonicalFormatEtc(ref pformatetcIn, out pformatetcOut);
+
+ void ComTypes.IDataObject.GetData(ref FORMATETC formatetc, out STGMEDIUM medium) =>
+ _innerData.GetData(ref formatetc, out medium);
+
+ void ComTypes.IDataObject.GetDataHere(ref FORMATETC formatetc, ref STGMEDIUM medium) =>
+ _innerData.GetDataHere(ref formatetc, ref medium);
+
+ int ComTypes.IDataObject.QueryGetData(ref FORMATETC formatetc) =>
+ _innerData.QueryGetData(ref formatetc);
+
+ void ComTypes.IDataObject.SetData(ref FORMATETC pFormatetcIn, ref STGMEDIUM pmedium, bool fRelease) =>
+ _innerData.SetData(ref pFormatetcIn, ref pmedium, fRelease);
+
+ #endregion
+
+ #region Com.IDataObject.Interface
+
+ HRESULT Com.IDataObject.Interface.DAdvise(Com.FORMATETC* pformatetc, uint advf, Com.IAdviseSink* pAdvSink, uint* pdwConnection) =>
+ _innerData.DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
+
+ HRESULT Com.IDataObject.Interface.DUnadvise(uint dwConnection) =>
+ _innerData.DUnadvise(dwConnection);
+
+ HRESULT Com.IDataObject.Interface.EnumDAdvise(Com.IEnumSTATDATA** ppenumAdvise) =>
+ _innerData.EnumDAdvise(ppenumAdvise);
+
+ HRESULT Com.IDataObject.Interface.EnumFormatEtc(uint dwDirection, Com.IEnumFORMATETC** ppenumFormatEtc) =>
+ _innerData.EnumFormatEtc(dwDirection, ppenumFormatEtc);
+
+ HRESULT Com.IDataObject.Interface.GetData(Com.FORMATETC* pformatetcIn, Com.STGMEDIUM* pmedium) =>
+ _innerData.GetData(pformatetcIn, pmedium);
+
+ HRESULT Com.IDataObject.Interface.GetDataHere(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium) =>
+ _innerData.GetDataHere(pformatetc, pmedium);
+
+ HRESULT Com.IDataObject.Interface.QueryGetData(Com.FORMATETC* pformatetc) =>
+ _innerData.QueryGetData(pformatetc);
+
+ HRESULT Com.IDataObject.Interface.GetCanonicalFormatEtc(Com.FORMATETC* pformatectIn, Com.FORMATETC* pformatetcOut) =>
+ _innerData.GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
+
+ HRESULT Com.IDataObject.Interface.SetData(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium, BOOL fRelease) =>
+ _innerData.SetData(pformatetc, pmedium, fRelease);
+
+ #endregion
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopDataObjectExtensions.cs b/src/System.Private.Windows.Core/src/OLE/DesktopDataObjectExtensions.cs
new file mode 100644
index 00000000000..a88afac5231
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopDataObjectExtensions.cs
@@ -0,0 +1,58 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using ITypedDataObject = System.Private.Windows.Core.OLE.ITypedDataObjectDesktop;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
+using System.Private.Windows.Core.Resources;
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Extension methods for data objects.
+///
+internal static class DesktopDataObjectExtensions
+{
+ private static ITypedDataObject GetTypedDataObjectOrThrow(IDataObject dataObject)
+ {
+ ArgumentNullException.ThrowIfNull(dataObject);
+
+ if (dataObject is not ITypedDataObject typed)
+ {
+ throw new NotSupportedException(string.Format(
+ SR.ITypeDataObject_Not_Implemented,
+ dataObject.GetType().FullName));
+ }
+
+ return typed;
+ }
+
+ ///
+ /// if the does not implement .
+ /// if the is
+ public static bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ this IDataObject dataObject,
+ string format,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ GetTypedDataObjectOrThrow(dataObject).TryGetData(format, out data);
+
+ ///
+ /// if the does not implement .
+ /// if the is
+ public static bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ this IDataObject dataObject,
+ string format,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ GetTypedDataObjectOrThrow(dataObject).TryGetData(format, autoConvert, out data);
+
+ ///
+ /// if the does not implement .
+ /// if the is
+ public static bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ this IDataObject dataObject,
+ string format,
+ Func resolver,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
+ GetTypedDataObjectOrThrow(dataObject).TryGetData(format, resolver, autoConvert, out data);
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/DesktopTextDataFormat.cs b/src/System.Private.Windows.Core/src/OLE/DesktopTextDataFormat.cs
new file mode 100644
index 00000000000..9b3fd25d12f
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DesktopTextDataFormat.cs
@@ -0,0 +1,17 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Specifies the formats that can be used with Clipboard.GetText and
+/// Clipboard.SetText methods
+///
+internal enum DesktopTextDataFormat
+{
+ Text,
+ UnicodeText,
+ Rtf,
+ Html,
+ CommaSeparatedValue
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropFormat.cs b/src/System.Private.Windows.Core/src/OLE/DragDropFormat.cs
similarity index 93%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropFormat.cs
rename to src/System.Private.Windows.Core/src/OLE/DragDropFormat.cs
index 13c3ae10d2d..94112d65272 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropFormat.cs
+++ b/src/System.Private.Windows.Core/src/OLE/DragDropFormat.cs
@@ -2,10 +2,12 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.InteropServices;
-using Windows.Win32.System.Ole;
+using Windows.Win32;
using Windows.Win32.System.Com;
+using Windows.Win32.System.Memory;
+using Windows.Win32.System.Ole;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core.OLE;
///
/// Represents a private format used for data transfer by the drag-and-drop helpers.
@@ -65,7 +67,7 @@ private static unsafe STGMEDIUM CopyData(ushort format, STGMEDIUM mediumSource)
case TYMED.TYMED_GDI:
case TYMED.TYMED_MFPICT:
- mediumDestination.hGlobal = (HGLOBAL)(nint)PInvoke.OleDuplicateData(
+ mediumDestination.hGlobal = (HGLOBAL)(nint)PInvokeCore.OleDuplicateData(
(HANDLE)(nint)mediumSource.hGlobal,
(CLIPBOARD_FORMAT)format,
// Note that GMEM_DDESHARE is ignored
@@ -104,7 +106,7 @@ private static unsafe STGMEDIUM CopyData(ushort format, STGMEDIUM mediumSource)
}
catch
{
- PInvoke.ReleaseStgMedium(ref mediumDestination);
+ PInvokeCore.ReleaseStgMedium(ref mediumDestination);
return default;
}
}
@@ -114,7 +116,7 @@ private static unsafe STGMEDIUM CopyData(ushort format, STGMEDIUM mediumSource)
///
private unsafe void ReleaseData()
{
- PInvoke.ReleaseStgMedium(ref _medium);
+ PInvokeCore.ReleaseStgMedium(ref _medium);
_medium.pUnkForRelease = null;
_medium.tymed = TYMED.TYMED_NULL;
_medium.hGlobal = HGLOBAL.Null;
diff --git a/src/System.Private.Windows.Core/src/OLE/DragDropHelper.cs b/src/System.Private.Windows.Core/src/OLE/DragDropHelper.cs
new file mode 100644
index 00000000000..f6309a1be65
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/DragDropHelper.cs
@@ -0,0 +1,92 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Windows.Win32;
+using Windows.Win32.System.Com;
+using DataFormats = System.Private.Windows.Core.OLE.DesktopDataFormats;
+using IDataObject = System.Private.Windows.Core.OLE.IDataObjectDesktop;
+using Windows.Win32.UI.Shell;
+
+namespace System.Private.Windows.Core.OLE;
+
+internal static class DragDropHelper
+{
+ ///
+ /// A format used internally by the drag image manager.
+ ///
+ internal const string DRAGCONTEXT = "DragContext";
+
+ ///
+ /// A format that contains the drag image bottom-up device-independent bitmap bits.
+ ///
+ internal const string DRAGIMAGEBITS = "DragImageBits";
+
+ ///
+ /// A format that contains the value passed to
+ /// and controls whether to allow text specified in to be displayed on the drag image.
+ ///
+ internal const string DRAGSOURCEHELPERFLAGS = "DragSourceHelperFlags";
+
+ ///
+ /// A format used to identify an object's drag image window so that it's visual information can be updated dynamically.
+ ///
+ internal const string DRAGWINDOW = "DragWindow";
+
+ ///
+ /// A format that is non-zero if the drop target supports drag images.
+ ///
+ internal const string ISSHOWINGLAYERED = "IsShowingLayered";
+
+ ///
+ /// A format that is non-zero if the drop target supports drop description text.
+ ///
+ internal const string ISSHOWINGTEXT = "IsShowingText";
+
+ ///
+ /// A format that is non-zero if the drag image is a layered window with a size of 96x96.
+ ///
+ internal const string USINGDEFAULTDRAGIMAGE = "UsingDefaultDragImage";
+
+ ///
+ /// Determines whether the data object is in a drag loop.
+ ///
+ ///
+ /// if is in a drag-and-drop loop; otherwise .
+ ///
+ public static unsafe bool IsInDragLoop(IDataObject dataObject)
+ {
+ ArgumentNullException.ThrowIfNull(dataObject);
+
+ if (dataObject.GetDataPresent(PInvokeCore.CFSTR_INDRAGLOOP)
+ && dataObject.GetData(PInvokeCore.CFSTR_INDRAGLOOP) is DragDropFormat dragDropFormat)
+ {
+ try
+ {
+ void* basePtr = PInvokeCore.GlobalLock(dragDropFormat.Medium.hGlobal);
+ return (basePtr is not null) && (*(BOOL*)basePtr == true);
+ }
+ finally
+ {
+ PInvokeCore.GlobalUnlock(dragDropFormat.Medium.hGlobal);
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Determines whether the specified format is a drag loop format.
+ ///
+ ///
+ /// if is a drag loop format; otherwise .
+ ///
+ public static bool IsInDragLoopFormat(FORMATETC format)
+ {
+ string formatName = DataFormats.GetFormat(format.cfFormat).Name;
+ return formatName.Equals(DRAGCONTEXT) || formatName.Equals(DRAGIMAGEBITS) || formatName.Equals(DRAGSOURCEHELPERFLAGS)
+ || formatName.Equals(DRAGWINDOW) || formatName.Equals(PInvokeCore.CFSTR_DROPDESCRIPTION) || formatName.Equals(PInvokeCore.CFSTR_INDRAGLOOP)
+ || formatName.Equals(ISSHOWINGLAYERED) || formatName.Equals(ISSHOWINGTEXT) || formatName.Equals(USINGDEFAULTDRAGIMAGE);
+ }
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/IDataObjectDesktop.cs b/src/System.Private.Windows.Core/src/OLE/IDataObjectDesktop.cs
new file mode 100644
index 00000000000..bc5e2bd3daa
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/IDataObjectDesktop.cs
@@ -0,0 +1,91 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Provides a format-independent mechanism for transferring data.
+///
+///
+///
+/// When implementing a , consider implementing
+/// interface instead. This interface will ensure that only data of a specified
+/// is exchanged. If is not implemented by a data object exchanged
+/// in the clipboard or drag and drop scenarios, the APIs that specify a ,
+/// such as , will throw a .
+///
+///
+internal interface IDataObjectDesktop
+{
+ ///
+ /// Retrieves the data associated with the specified data format, using
+ /// to determine whether to convert the data to the format.
+ ///
+ object? GetData(string format, bool autoConvert);
+
+ ///
+ /// Retrieves the data associated with the specified data format.
+ ///
+ object? GetData(string format);
+
+ ///
+ /// Retrieves the data associated with the specified class type format.
+ ///
+ object? GetData(Type format);
+
+ ///
+ /// Determines whether data stored in this instance is associated with the
+ /// specified format, using autoConvert to determine whether to convert the
+ /// data to the format.
+ ///
+ bool GetDataPresent(string format, bool autoConvert);
+
+ ///
+ /// Determines whether data stored in this instance is associated with, or
+ /// can be converted to, the specified format.
+ ///
+ bool GetDataPresent(string format);
+
+ ///
+ /// Determines whether data stored in this instance is associated with, or
+ /// can be converted to, the specified format.
+ ///
+ bool GetDataPresent(Type format);
+
+ ///
+ /// Gets a list of all formats that data stored in this instance is
+ /// associated with or can be converted to, using to determine
+ /// whether to retrieve all formats that the data can be converted to, or
+ /// only native data formats.
+ ///
+ string[] GetFormats(bool autoConvert);
+
+ ///
+ /// Gets a list of all formats that data stored in this instance is
+ /// associated with or can be converted to.
+ ///
+ string[] GetFormats();
+
+ ///
+ /// Stores the specified data and its associated format in this instance,
+ /// using autoConvert to specify whether the data can be converted to
+ /// another format.
+ ///
+ void SetData(string format, bool autoConvert, object? data);
+
+ ///
+ /// Stores the specified data and its associated format in this instance.
+ ///
+ void SetData(string format, object? data);
+
+ ///
+ /// Stores the specified data and its associated class type in this instance.
+ ///
+ void SetData(Type format, object? data);
+
+ ///
+ /// Stores the specified data in this instance, using the class of the
+ /// data for the format.
+ ///
+ void SetData(object? data);
+}
diff --git a/src/System.Private.Windows.Core/src/OLE/ITypedDataObjectDesktop.cs b/src/System.Private.Windows.Core/src/OLE/ITypedDataObjectDesktop.cs
new file mode 100644
index 00000000000..29afde3a519
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/OLE/ITypedDataObjectDesktop.cs
@@ -0,0 +1,86 @@
+// 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 System.Runtime.Serialization.Formatters.Binary;
+
+namespace System.Private.Windows.Core.OLE;
+
+///
+/// Provides a format-independent mechanism for reading data of a specified .
+///
+///
+///
+/// Implement this interface to use your data object with
+/// family of methods as well as in the drag and drop operations. This interface will ensure that only
+/// data of the specified is exchanged. Otherwise the APIs that specify a parameter
+/// will throw a . This is replacement of
+/// interface, implement this interface as well. Otherwise the APIs that specify a parameter
+/// will throw a .
+///
+///
+internal interface ITypedDataObjectDesktop : IDataObjectDesktop
+{
+ ///
+ /// Retrieves data associated with data format named after ,
+ /// if that data is of type .
+ ///
+ ///
+ bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data);
+
+ ///
+ /// Retrieves data associated with the specified format if that data is of type .
+ ///
+ ///
+ bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data);
+
+ ///
+ /// Retrieves data in a specified format if that data is of type ,
+ /// optionally converting the data to the specified format.
+ ///
+ ///
+ bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data);
+
+ ///
+ /// compatible overload that retrieves typed data associated with the specified data format.
+ ///
+ ///
+ /// A user-provided function that defines a closure of s that can be retrieved from
+ /// the exchange medium.
+ ///
+ ///
+ /// A string that specifies what format to retrieve the data as. See the class for
+ /// a set of predefined data formats.
+ ///
+ ///
+ /// to attempt to automatically convert the data to the specified format;
+ /// for no data format conversion.
+ ///
+ ///
+ /// A data object with the data in the specified format, or if the data is not available
+ /// in the specified format or is of a wrong type.
+ ///
+ ///
+ /// if the data of this format is present and the value is
+ /// of a matching type and that value can be successfully retrieved, or
+ /// if the format is not present or the value is not of the right type.
+ ///
+ ///
+ ///
+ /// Implement this method for backward compatibility with binary formatted data when binary formatters are enabled.
+ ///
+ ///
+ bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+#pragma warning disable CS3001 // Argument type is not CLS-compliant
+ Func resolver,
+#pragma warning restore CS3001
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data);
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/JsonData.cs b/src/System.Private.Windows.Core/src/OLE/JsonData.cs
similarity index 96%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/JsonData.cs
rename to src/System.Private.Windows.Core/src/OLE/JsonData.cs
index a4415349710..02b6d23238f 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/JsonData.cs
+++ b/src/System.Private.Windows.Core/src/OLE/JsonData.cs
@@ -3,7 +3,6 @@
using System.Runtime.CompilerServices;
using System.Text.Json;
-using System.Windows.Forms;
namespace System.Private.Windows;
@@ -117,7 +116,9 @@ public readonly object Deserialize()
object? result;
try
{
+#pragma warning disable IL2026 // Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code
result = JsonSerializer.Deserialize(JsonBytes);
+#pragma warning restore IL2026
}
catch (Exception ex)
{
diff --git a/src/System.Private.Windows.Core/src/Resources/SR.resx b/src/System.Private.Windows.Core/src/Resources/SR.resx
index d58708177e9..80d80327a46 100644
--- a/src/System.Private.Windows.Core/src/Resources/SR.resx
+++ b/src/System.Private.Windows.Core/src/Resources/SR.resx
@@ -125,4 +125,46 @@
Could not find type '{0}'.
+
+ Clipboard format registration did not succeed.
+
+
+ The specified Clipboard format is not compatible with '{0}' type.
+
+
+ BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information.
+
+
+ BinaryFormatter is not supported in clipboard or drag-and-drop operations.
+
+
+ BinaryFormatter is not supported in clipboard or drag-and-drop operations. Please enable it and use 'TryGetData<T>' APIs with a 'resolver' function that defines a set of allowed types to deserialize '{0}'.
+
+
+ Please use 'TryGetData<T>' APIs with a 'resolver' function that defines a set of allowed types to deserialize '{0}'.
+
+
+ Type {0} is not found. If this type is allowed, please ensure that the 'resolver' function provided in 'TryGetData<T>' APIs supports it.
+
+
+ External exception
+
+
+ Whitespace or empty string is not a valid value for parameter 'format'.
+
+
+ Data object '{0}' doesn't implement the 'ITypedDataObject' interface and can't be read using 'TryGetData<T>(string, out T)' methods.
+
+
+ Requested Clipboard operation did not succeed.
+
+
+ Cannot operate with an empty collection.
+
+
+ Path "{0}" in the argument "{1}" is not valid.
+
+
+ Failed to deserialize JSON data.
+
diff --git a/src/System.Windows.Forms/src/misc/CompatibleIComparer.cs b/src/System.Private.Windows.Core/src/System/CompatibleIComparer.cs
similarity index 100%
rename from src/System.Windows.Forms/src/misc/CompatibleIComparer.cs
rename to src/System.Private.Windows.Core/src/System/CompatibleIComparer.cs
diff --git a/src/System.Private.Windows.Core/src/System/LocalAppContextSwitches/LocalAppContextSwitchesCore.cs b/src/System.Private.Windows.Core/src/System/LocalAppContextSwitches/LocalAppContextSwitchesCore.cs
new file mode 100644
index 00000000000..07fb7658707
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/System/LocalAppContextSwitches/LocalAppContextSwitchesCore.cs
@@ -0,0 +1,102 @@
+// 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.CompilerServices;
+using System.Runtime.Serialization.Formatters.Binary;
+
+namespace System;
+
+// Borrowed from https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/System/LocalAppContextSwitches.Common.cs
+internal static class LocalAppContextSwitchesCore
+{
+ // Enabling switches in Core is different from Framework. See https://learn.microsoft.com/dotnet/core/runtime-config/
+ // for details on how to set switches.
+
+ // Switch names declared internal below are used in unit/integration tests. Refer to
+ // https://github.com/dotnet/winforms/blob/tree/main/docs/design/anchor-layout-changes-in-net80.md
+ // for more details on how to enable these switches in the application.
+ internal const string ClipboardDragDropEnableUnsafeBinaryFormatterSerializationSwitchName = "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization";
+ internal const string ClipboardDragDropEnableNrbfSerializationSwitchName = "Windows.ClipboardDragDrop.EnableNrbfSerialization";
+
+ private static int s_clipboardDragDropEnableUnsafeBinaryFormatterSerialization;
+ private static int s_clipboardDragDropEnableNrbfSerialization;
+
+ private static bool GetCachedSwitchValue(string switchName, ref int cachedSwitchValue)
+ {
+ // The cached switch value has 3 states: 0 - unknown, 1 - true, -1 - false
+ if (cachedSwitchValue < 0)
+ {
+ return false;
+ }
+
+ if (cachedSwitchValue > 0)
+ {
+ return true;
+ }
+
+ return GetSwitchValue(switchName, ref cachedSwitchValue);
+ }
+
+ private static bool GetSwitchValue(string switchName, ref int cachedSwitchValue)
+ {
+ bool hasSwitch = AppContext.TryGetSwitch(switchName, out bool isSwitchEnabled);
+ if (!hasSwitch)
+ {
+ isSwitchEnabled = GetSwitchDefaultValue(switchName);
+ }
+
+ AppContext.TryGetSwitch("TestSwitch.LocalAppContext.DisableCaching", out bool disableCaching);
+ if (!disableCaching)
+ {
+ cachedSwitchValue = isSwitchEnabled ? 1 /*true*/ : -1 /*false*/;
+ }
+ else if (!hasSwitch)
+ {
+ AppContext.SetSwitch(switchName, isSwitchEnabled);
+ }
+
+ return isSwitchEnabled;
+ }
+
+ private static bool GetSwitchDefaultValue(string switchName)
+ {
+ if (switchName == ClipboardDragDropEnableNrbfSerializationSwitchName)
+ {
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// If , then Clipboard and DataObject Get and Set methods will attempts to serialize or deserialize
+ /// binary formatted content using either or System.Windows.Forms.BinaryFormat.Deserializer.
+ /// To use , application should also opt in into the
+ /// "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization" option and reference the out-of-band
+ /// "System.Runtime.Serialization.Formatters" NuGet package and opt out from using the System.Windows.Forms.BinaryFormat.Deserializer
+ /// by setting "Windows.ClipboardDragDrop.EnableNrbfSerialization" to
+ ///
+ public static bool ClipboardDragDropEnableUnsafeBinaryFormatterSerialization
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetCachedSwitchValue(ClipboardDragDropEnableUnsafeBinaryFormatterSerializationSwitchName,
+ ref s_clipboardDragDropEnableUnsafeBinaryFormatterSerialization);
+ }
+
+ ///
+ /// If , then Clipboard Get methods will prefer System.Windows.Forms.BinaryFormat.Deserializer
+ /// to deserialize the payload, if needed. If , then is used
+ /// to get full compatibility with the downlevel versions of .NET.
+ ///
+ ///
+ ///
+ /// This switch has no effect if "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization"
+ /// is set to .
+ ///
+ ///
+ public static bool ClipboardDragDropEnableNrbfSerialization
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get => GetCachedSwitchValue(ClipboardDragDropEnableNrbfSerializationSwitchName, ref s_clipboardDragDropEnableNrbfSerialization);
+ }
+}
diff --git a/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/IBinaryFormatWriter.cs b/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/IBinaryFormatWriter.cs
new file mode 100644
index 00000000000..8d028596576
--- /dev/null
+++ b/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/IBinaryFormatWriter.cs
@@ -0,0 +1,20 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace System.Private.Windows.Core.Nrbf;
+
+///
+/// An interface identifying an object that handles serializing supported types.
+///
+internal interface IBinaryFormatWriter
+{
+ ///
+ /// Tries to binary format and write into if the type is supported.
+ ///
+ /// The stream to write the binary formatted data into
+ /// object to binary format
+ ///
+ /// if is supported and can be written to the stream. Otherwise, .
+ ///
+ bool TryWriteCommonObject(Stream stream, object value);
+}
diff --git a/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/SerializationRecordExtensions.cs b/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/SerializationRecordExtensions.cs
index 7f9ba95a70b..baf892c2b64 100644
--- a/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/SerializationRecordExtensions.cs
+++ b/src/System.Private.Windows.Core/src/System/Private/Windows/Core/Nrbf/SerializationRecordExtensions.cs
@@ -5,10 +5,14 @@
using System.ComponentModel;
using System.Drawing;
using System.Formats.Nrbf;
+using System.Private.Windows;
using System.Private.Windows.Core.BinaryFormat;
+using System.Private.Windows.Core.Resources;
using System.Reflection;
+using System.Reflection.Metadata;
using System.Runtime.ExceptionServices;
using System.Runtime.Serialization;
+using System.Text.Json;
namespace System.Windows.Forms.Nrbf;
@@ -592,4 +596,51 @@ public static bool TryGetDrawingPrimitivesObject(
private static bool IsPrimitiveArrayRecord(SerializationRecord serializationRecord) =>
serializationRecord.RecordType is SerializationRecordType.ArraySingleString or SerializationRecordType.ArraySinglePrimitive;
+
+ ///
+ /// Tries to deserialize this object if it was serialized as JSON.
+ ///
+ ///
+ /// if the data was serialized as JSON. Otherwise, .
+ ///
+ /// If the data was supposed to be our , but was serialized incorrectly./>
+ /// If an exception occurred while JSON deserializing.
+ [RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
+ public static bool TryGetObjectFromJson(this SerializationRecord record, ITypeResolver resolver, out object? @object)
+ {
+ @object = null;
+
+ if (record.TypeName.AssemblyName?.FullName != IJsonData.CustomAssemblyName)
+ {
+ // The data was not serialized as JSON.
+ return false;
+ }
+
+ if (record is not ClassRecord types
+ || types.GetRawValue("k__BackingField") is not SZArrayRecord byteData
+ || types.GetRawValue("k__BackingField") is not string innerTypeFullName
+ || !TypeName.TryParse(innerTypeFullName, out TypeName? serializedTypeName))
+ {
+ // This is supposed to be JsonData, but somehow the binary formatted data is corrupt.
+ throw new SerializationException();
+ }
+
+ Type serializedType = resolver.GetType(serializedTypeName);
+ if (!serializedType.IsAssignableTo(typeof(T)))
+ {
+ // Not the type the caller asked for so @object remains null.
+ return true;
+ }
+
+ try
+ {
+ @object = JsonSerializer.Deserialize(byteData.GetArray());
+ }
+ catch (Exception ex)
+ {
+ throw new NotSupportedException(SR.ClipboardOrDragDrop_JsonDeserializationFailed, ex);
+ }
+
+ return true;
+ }
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Internal/TypeExtensions.cs b/src/System.Private.Windows.Core/src/System/Private/Windows/Core/TypeExtensions.cs
similarity index 89%
rename from src/System.Windows.Forms/src/System/Windows/Forms/Internal/TypeExtensions.cs
rename to src/System.Private.Windows.Core/src/System/Private/Windows/Core/TypeExtensions.cs
index 1ffafb0d351..340cad7c5e6 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Internal/TypeExtensions.cs
+++ b/src/System.Private.Windows.Core/src/System/Private/Windows/Core/TypeExtensions.cs
@@ -5,7 +5,7 @@
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
-namespace System.Windows.Forms;
+namespace System.Private.Windows.Core;
///
/// Helper methods for comparing s and s.
@@ -49,7 +49,7 @@ public static bool TryGetForwardedFromName(this Type type, [NotNullWhen(true)] o
///
public static bool MatchExceptAssemblyVersion(this Type type, TypeName typeName)
{
- type = Formatter.NullableUnwrap(type);
+ type = NullableUnwrap(type);
return type.MatchLessAssemblyVersion(typeName);
}
@@ -101,12 +101,12 @@ private static bool MatchLessAssemblyVersion(this Type type, TypeName typeName)
if (typeName.IsArray)
{
- return MatchLessAssemblyVersion(type.GetElementType()!, typeName.GetElementType());
+ return type.GetElementType()!.MatchLessAssemblyVersion(typeName.GetElementType());
}
if (type.IsConstructedGenericType)
{
- if (!MatchLessAssemblyVersion(type.GetGenericTypeDefinition(), typeName.GetGenericTypeDefinition()))
+ if (!type.GetGenericTypeDefinition().MatchLessAssemblyVersion(typeName.GetGenericTypeDefinition()))
{
return false;
}
@@ -121,7 +121,7 @@ private static bool MatchLessAssemblyVersion(this Type type, TypeName typeName)
for (int i = 0; i < genericTypes.Length; i++)
{
- if (!MatchLessAssemblyVersion(genericTypes[i], genericNames[i]))
+ if (!genericTypes[i].MatchLessAssemblyVersion(genericNames[i]))
{
return false;
}
@@ -151,7 +151,7 @@ static bool AssemblyNamesLessVersionMatch(string? fullName, AssemblyNameInfo? na
// Match everything except for the versions.
return nameInfo.Name == nameInfo1.Name
- && ((nameInfo.CultureName ?? string.Empty) == nameInfo1.CultureName)
+ && (nameInfo.CultureName ?? string.Empty) == nameInfo1.CultureName
&& nameInfo.PublicKeyOrToken.AsSpan().SequenceEqual(nameInfo1.PublicKeyOrToken.AsSpan());
}
}
@@ -183,12 +183,12 @@ public static bool Matches(this TypeName x, TypeName y)
if (y.IsArray)
{
- return Matches(x.GetElementType(), y.GetElementType());
+ return x.GetElementType().Matches(y.GetElementType());
}
if (x.IsConstructedGenericType)
{
- if (!Matches(x.GetGenericTypeDefinition(), y.GetGenericTypeDefinition()))
+ if (!x.GetGenericTypeDefinition().Matches(y.GetGenericTypeDefinition()))
{
return false;
}
@@ -203,7 +203,7 @@ public static bool Matches(this TypeName x, TypeName y)
for (int i = 0; i < genericNamesX.Length; i++)
{
- if (!Matches(genericNamesX[i], genericNamesY[i]))
+ if (!genericNamesX[i].Matches(genericNamesY[i]))
{
return false;
}
@@ -234,6 +234,20 @@ static bool AssemblyNamesMatch(AssemblyNameInfo? name1, AssemblyNameInfo? name2)
}
}
+ ///
+ /// Extract the inner type from a nullable type.
+ ///
+ internal static Type NullableUnwrap(Type type)
+ {
+ if (type == typeof(string)) // ...performance optimization for the most common case
+ {
+ return typeof(string);
+ }
+
+ Type? underlyingType = Nullable.GetUnderlyingType(type);
+ return underlyingType ?? type;
+ }
+
///
/// Convert to . Take into account type forwarding in order
/// to create compatible with the type names serialized to the binary format.This
@@ -243,7 +257,7 @@ static bool AssemblyNamesMatch(AssemblyNameInfo? name1, AssemblyNameInfo? name2)
public static TypeName ToTypeName(this Type type)
{
// Unwrap type that is matched against the root record type.
- type = Formatter.NullableUnwrap(type);
+ type = NullableUnwrap(type);
if (!type.TryGetForwardedFromName(out string? assemblyName))
{
assemblyName = type.Assembly.FullName;
diff --git a/src/System.Windows.Forms.Primitives/src/Windows/Win32/System/Com/STGMEDIUM.cs b/src/System.Private.Windows.Core/src/Windows/Win32/System/Com/STGMEDIUM.cs
similarity index 100%
rename from src/System.Windows.Forms.Primitives/src/Windows/Win32/System/Com/STGMEDIUM.cs
rename to src/System.Private.Windows.Core/src/Windows/Win32/System/Com/STGMEDIUM.cs
diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ByteViewer.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ByteViewer.cs
index 6c523f8c4b9..abeb261d31a 100644
--- a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ByteViewer.cs
+++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/ByteViewer.cs
@@ -399,11 +399,11 @@ private unsafe void InitAnsi()
int bufferSize;
fixed (byte* pDataBuff = _dataBuf)
{
- bufferSize = PInvoke.MultiByteToWideChar(PInvoke.CP_ACP, 0, (PCSTR)pDataBuff, _dataBuf.Length, null, 0);
+ bufferSize = PInvoke.MultiByteToWideChar(PInvokeCore.CP_ACP, 0, (PCSTR)pDataBuff, _dataBuf.Length, null, 0);
charsBuffer.EnsureCapacity(bufferSize + 1);
fixed (char* pText = charsBuffer)
{
- size = PInvoke.MultiByteToWideChar(PInvoke.CP_ACP, 0, (PCSTR)pDataBuff, bufferSize, pText, bufferSize);
+ size = PInvoke.MultiByteToWideChar(PInvokeCore.CP_ACP, 0, (PCSTR)pDataBuff, bufferSize, pText, bufferSize);
}
}
diff --git a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt
index 5df70140251..6fb8a27e617 100644
--- a/src/System.Windows.Forms.Primitives/src/NativeMethods.txt
+++ b/src/System.Windows.Forms.Primitives/src/NativeMethods.txt
@@ -24,8 +24,6 @@ CDM_GETSPEC
CDRF_*
CFE_EFFECTS
CFM_MASK
-CFSTR_DROPDESCRIPTION
-CFSTR_INDRAGLOOP
CHILDID_SELF
ChildWindowFromPointEx
CHOOSECOLOR_FLAGS
@@ -48,7 +46,6 @@ CommandStateChangeConstants
CommDlgExtendedError
COPYDATASTRUCT
CoRegisterMessageFilter
-CP_ACP
CreateAcceleratorTableW
CreateActCtx
CreateBrushIndirect
@@ -84,7 +81,6 @@ DoDragDrop
DPI_AWARENESS_CONTEXT_*
DPI_HOSTING_BEHAVIOR
DragAcceptFiles
-DragQueryFile
DrawEdge
DrawFrameControl
DRAWITEMSTRUCT
@@ -95,8 +91,6 @@ DrawThemeBackground
DrawThemeEdge
DrawThemeParentBackground
DrawThemeText
-DROPDESCRIPTION
-DROPFILES
DROPIMAGETYPE
DSH_FLAGS
DTM_*
@@ -150,7 +144,6 @@ GetCaretBlinkTime
GetCaretPos
GetClassInfo
GetClassName
-GetClipboardFormatName
GetClipBox
GetClipCursor
GetClipRgn
@@ -276,7 +269,6 @@ IDataObject
IDC_*
IDispatchEx
IDropSource
-IDragSourceHelper2
IDropSourceNotify
IDropTarget
IDropTargetHelper
@@ -521,11 +513,7 @@ OLECONTF
OleCreateFontIndirect
OleCreatePropertyFrame
OleCreatePropertyFrameIndirect
-OleDuplicateData
-OleFlushClipboard
-OleGetClipboard
OleInitialize
-OleSetClipboard
OleUninitialize
OPEN_FILENAME_FLAGS
OPEN_FILENAME_FLAGS_EX
@@ -551,13 +539,11 @@ Rectangle
RedrawWindow
REGDB_E_CLASSNOTREG
RegisterClass
-RegisterClipboardFormat
RegisterDragDrop
RegisterWindowMessage
RegLoadMUIString
ReleaseCapture
ReleaseDC
-ReleaseStgMedium
RevokeDragDrop
RICH_EDIT_GET_CONTEXT_MENU_SEL_TYPE
RoundRect
@@ -724,7 +710,6 @@ WC_STATIC
WC_TABCONTROL
WC_TREEVIEW
WHEEL_DELTA
-WideCharToMultiByte
WindowFromPoint
WINDOWPOS
WINEVENT_INCONTEXT
diff --git a/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs b/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs
index b0b09d5dec3..04872ed891f 100644
--- a/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs
+++ b/src/System.Windows.Forms.Primitives/src/System/LocalAppContextSwitches/LocalAppContextSwitches.cs
@@ -2,7 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Runtime.CompilerServices;
-using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Versioning;
namespace System.Windows.Forms.Primitives;
@@ -26,8 +25,6 @@ internal static partial class LocalAppContextSwitches
internal const string NoClientNotificationsSwitchName = "Switch.System.Windows.Forms.AccessibleObject.NoClientNotifications";
internal const string EnableMsoComponentManagerSwitchName = "Switch.System.Windows.Forms.EnableMsoComponentManager";
internal const string TreeNodeCollectionAddRangeRespectsSortOrderSwitchName = "System.Windows.Forms.TreeNodeCollectionAddRangeRespectsSortOrder";
- internal const string ClipboardDragDropEnableUnsafeBinaryFormatterSerializationSwitchName = "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization";
- internal const string ClipboardDragDropEnableNrbfSerializationSwitchName = "Windows.ClipboardDragDrop.EnableNrbfSerialization";
private static int s_scaleTopLevelFormMinMaxSizeForDpi;
private static int s_anchorLayoutV2;
@@ -39,8 +36,6 @@ internal static partial class LocalAppContextSwitches
private static int s_noClientNotifications;
private static int s_enableMsoComponentManager;
private static int s_treeNodeCollectionAddRangeRespectsSortOrder;
- private static int s_clipboardDragDropEnableUnsafeBinaryFormatterSerialization;
- private static int s_clipboardDragDropEnableNrbfSerialization;
private static FrameworkName? s_targetFrameworkName;
@@ -111,11 +106,6 @@ private static bool GetSwitchDefaultValue(string switchName)
return true;
}
- if (switchName == ClipboardDragDropEnableNrbfSerializationSwitchName)
- {
- return true;
- }
-
if (TargetFrameworkName is not { } framework)
{
return false;
@@ -228,36 +218,4 @@ public static bool TreeNodeCollectionAddRangeRespectsSortOrder
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => GetCachedSwitchValue(TreeNodeCollectionAddRangeRespectsSortOrderSwitchName, ref s_treeNodeCollectionAddRangeRespectsSortOrder);
}
-
- ///
- /// If , then Clipboard and DataObject Get and Set methods will attempts to serialize or deserialize
- /// binary formatted content using either or System.Windows.Forms.BinaryFormat.Deserializer.
- /// To use , application should also opt in into the
- /// "System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization" option and reference the out-of-band
- /// "System.Runtime.Serialization.Formatters" NuGet package and opt out from using the System.Windows.Forms.BinaryFormat.Deserializer
- /// by setting "Windows.ClipboardDragDrop.EnableNrbfSerialization" to
- ///
- public static bool ClipboardDragDropEnableUnsafeBinaryFormatterSerialization
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => GetCachedSwitchValue(ClipboardDragDropEnableUnsafeBinaryFormatterSerializationSwitchName,
- ref s_clipboardDragDropEnableUnsafeBinaryFormatterSerialization);
- }
-
- ///
- /// If , then Clipboard Get methods will prefer System.Windows.Forms.BinaryFormat.Deserializer
- /// to deserialize the payload, if needed. If , then is used
- /// to get full compatibility with the downlevel versions of .NET.
- ///
- ///
- ///
- /// This switch has no effect if "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization"
- /// is set to .
- ///
- ///
- public static bool ClipboardDragDropEnableNrbfSerialization
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => GetCachedSwitchValue(ClipboardDragDropEnableNrbfSerializationSwitchName, ref s_clipboardDragDropEnableNrbfSerialization);
- }
}
diff --git a/src/System.Windows.Forms/src/GlobalSuppressions.cs b/src/System.Windows.Forms/src/GlobalSuppressions.cs
index 0e99c230d1d..0bc00c927d0 100644
--- a/src/System.Windows.Forms/src/GlobalSuppressions.cs
+++ b/src/System.Windows.Forms/src/GlobalSuppressions.cs
@@ -229,12 +229,8 @@
[assembly: SuppressMessage("CodeQuality", "IDE0051:Remove unused private members", Justification = "Designer", Scope = "member", Target = "~M:System.Windows.Forms.MdiClient.ShouldSerializeLocation~System.Boolean")]
// Ideally these should be different exceptions, but leaving them as shipped for compatibility
-[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.DataObject.Composition.NativeToWinFormsAdapter.ReadByteStreamFromHGLOBAL(Windows.Win32.Foundation.HGLOBAL,System.Boolean@)~System.IO.MemoryStream")]
-[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.DataObject.Composition.NativeToRuntimeAdapter.System#Runtime#InteropServices#ComTypes#IDataObject#EnumFormatEtc(System.Runtime.InteropServices.ComTypes.DATADIR)~System.Runtime.InteropServices.ComTypes.IEnumFORMATETC")]
-[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.Clipboard.SetDataObject(System.Object,System.Boolean,System.Int32,System.Int32)")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.ComponentModel.Com2Interop.Com2TypeInfoProcessor.ProcessFunctions(Windows.Win32.System.Com.ITypeInfo*,System.Collections.Generic.IDictionary{System.String,System.Windows.Forms.ComponentModel.Com2Interop.Com2TypeInfoProcessor.PropertyInfo},System.Int32,System.Int32,System.Boolean@)")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.Control.ActiveXImpl.ThrowHr(Windows.Win32.Foundation.HRESULT)")]
-[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.Clipboard.GetDataObject~System.Windows.Forms.IDataObject")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~F:System.Windows.Forms.AxHost.s_invalidArgumentException")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.ComponentModel.Com2Interop.Com2PropertyDescriptor.SetValue(System.Object,System.Object)")]
[assembly: SuppressMessage("Usage", "CA2201:Do not raise reserved exception types", Justification = "Compat", Scope = "member", Target = "~M:System.Windows.Forms.ImageList.ImageCollection.SetKeyName(System.Int32,System.String)")]
diff --git a/src/System.Windows.Forms/src/GlobalUsings.cs b/src/System.Windows.Forms/src/GlobalUsings.cs
index b07ec1ab7ee..5974f78b2a1 100644
--- a/src/System.Windows.Forms/src/GlobalUsings.cs
+++ b/src/System.Windows.Forms/src/GlobalUsings.cs
@@ -7,7 +7,6 @@
global using Windows.Win32;
global using Windows.Win32.Foundation;
global using Windows.Win32.Graphics.Gdi;
-global using Windows.Win32.System.Memory;
global using Windows.Win32.UI.Controls;
global using Windows.Win32.UI.HiDpi;
global using Windows.Win32.UI.Shell;
diff --git a/src/System.Windows.Forms/src/Resources/SR.resx b/src/System.Windows.Forms/src/Resources/SR.resx
index 9c57f908625..3467fcfcdcc 100644
--- a/src/System.Windows.Forms/src/Resources/SR.resx
+++ b/src/System.Windows.Forms/src/Resources/SR.resx
@@ -630,12 +630,6 @@
A circular control reference has been made. A control cannot be owned by or parented to itself.
-
- Path "{0}" in the argument "{1}" is not valid.
-
-
- Requested Clipboard operation did not succeed.
-
Due to security restrictions on clipboard, the specified clipboard format cannot be set.
@@ -651,9 +645,6 @@
Occurs whenever this collection's membership is about to change.
-
- Cannot operate with an empty collection.
-
Indicates the horizontal alignment of the text displayed in the column header.
@@ -2526,9 +2517,6 @@ To replace this default dialog please handle the DataError event.
Returns the count of visible rows in the grid.
-
- Whitespace or empty string is not a valid value for parameter 'format'.
-
Bitmap object does not directly support the device-independent bitmap format. Either specify auto-conversion or perform a SetData with the Bitmap data format.
@@ -2959,9 +2947,6 @@ If you click Continue, the application will ignore this error and attempt to con
{0}.
-
- External exception
-
Controls whether extensions are automatically added to file names.
@@ -5013,9 +4998,6 @@ Stack trace where the illegal operation occurred was:
Collection is read only.
-
- Clipboard format registration did not succeed.
-
Child list for field {0} cannot be created.
@@ -6643,9 +6625,6 @@ Stack trace where the illegal operation occurred was:
Clipboard contains an unexpected type.
-
- The specified Clipboard format is not compatible with '{0}' type.
-
A value that indicates whether to use the value of the Description property as the dialog title for Vista style dialogs. This property has no effect on old style dialogs.
@@ -6970,9 +6949,6 @@ Stack trace where the illegal operation occurred was:
Binding operations are not supported when application trimming is enabled.
-
- BinaryFormatter serialization and deserialization are disabled within this application. See https://aka.ms/binaryformatter for more information.
-
'{0}' is not supported in trimmed applications.
@@ -7015,30 +6991,12 @@ Stack trace where the illegal operation occurred was:
Failed to retrieve the Synchronization Context for showing the Form asynchronously.
-
- Data object '{0}' doesn't implement the 'ITypedDataObject' interface and can't be read using 'TryGetData<T>(string, out T)' methods.
-
-
- BinaryFormatter is not supported in clipboard or drag-and-drop operations.
-
-
- BinaryFormatter is not supported in clipboard or drag-and-drop operations. Please enable it and use 'TryGetData<T>' APIs with a 'resolver' function that defines a set of allowed types to deserialize '{0}'.
-
-
- Please use 'TryGetData<T>' APIs with a 'resolver' function that defines a set of allowed types to deserialize '{0}'.
-
-
- Type {0} is not found. If this type is allowed, please ensure that the 'resolver' function provided in 'TryGetData<T>' APIs supports it.
-
'{0}' is not a concrete type, and could result in an unbounded deserialization attempt. Please use a concrete type or alternatively define a 'resolver' function that supports types that you are expecting to retrieve from the clipboard or use in drag-and-drop operations.The specified type '{0}' is not compatible with the specified format '{1}'.
-
- Failed to deserialize JSON data.
-
An object of type 'DataObject' will serialize as empty. Use 'DataObject.SetDataAsJson' APIs on your object to JSON-serialize the data within your object, then use the '{0}' API with your object.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
index e78c0430cc8..7ad7753e05f 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.cs.xlf
@@ -287,21 +287,6 @@
Strukturovaný objekt DataBinding přijímá jako zdroj dat objekt IList nebo IListSource.
-
-
- Serializace a deserializace BinaryFormatter jsou v této aplikaci zakázané. Další informace najdete na https://aka.ms/binaryformatter.
-
-
-
-
- Binární formátovací modul není podporován ve schránce nebo operacích přetažení.
-
-
-
-
- Binární formátovací modul není podporován ve schránce nebo operacích přetažení. Povolte ho prosím a použijte rozhraní API TryGetData<T> s funkcí resolver, která definuje sadu povolených typů pro deserializaci '{0}'.
-
- Vyvolá se při změně kontextu vazby.
@@ -962,11 +947,6 @@
Byl vytvořen kruhový odkaz na ovládací prvek. Ovládací prvek nemůže být nadřazeným prvkem nebo vlastníkem sebe sama.
-
-
- Požadovaná operace se schránkou se nezdařila.
-
- Objekt typu DataObject bude serializován jako prázdný. Pomocí rozhraní API DataObject.SetDataAsJson u objektu můžete serializovat data v rámci objektu json a pak s objektem použít rozhraní API '{0}'.
@@ -982,31 +962,11 @@
'{0}' není konkrétní typ a může mít za následek nevázaný pokus o deserializaci. Použijte prosím konkrétní typ nebo alternativně definujte funkci resolver, která podporuje typy, které byste očekávali načíst ze schránky nebo použít při operacích přetažení myší.
-
-
- Nepovedlo se deserializovat data JSON.
-
-
-
-
- Typ {0} nebyl nalezen. Pokud je tento typ povolený, ujistěte se prosím, že ji podporuje funkce resolver poskytnutá v rozhraních API TryGetData<T>.
-
-
-
-
- Použijte prosím rozhraní API TryGetData<T> s funkcí překladače, která definuje sadu povolených typů pro deserializaci '{0}'.
-
- Z důvodu omezení zabezpečení schránky nelze zadaný formát schránky nastavit.
-
-
- Cesta {0} v argumentu {1} není platná.
-
- Zavřít
@@ -1017,11 +977,6 @@
Při zpracování funkce CreateHandle() nelze volat funkci {0}().
-
-
- Prázdnou kolekci nelze použít.
-
- Určuje vodorovné zarovnání textu zobrazeného v záhlaví sloupců.
@@ -4259,11 +4214,6 @@ Chcete-li nahradit toto výchozí dialogové okno, nastavte popisovač události
Objekt Bitmap přímo nepodporuje formát DIB. Zadejte automatický převod nebo proveďte příkaz SetData s datovým formátem Bitmap.
-
-
- Prázdný znak nebo řetězec není platnou hodnotou parametru format.
-
- Vlastnost DataMember {0} nebyla nalezena u vlastnosti DataSource.
@@ -5009,11 +4959,6 @@ Pokud kliknete na Pokračovat, aplikace bude tuto chybu ignorovat a pokusí se p
Rozbalené
-
-
- Externí výjimka
-
- Načte názvy všech vybraných souborů v dialogovém okně.
@@ -5815,11 +5760,6 @@ Chcete jej nahradit?
Určuje podporu pro provedenou inicializaci.
-
-
- '{0}' datového objektu neimplementuje rozhraní ITypedDataObject a nedá se číst pomocí metod TryGetData<T>(string, out T).
-
- Operace mezi vlákny není platná: Přístup k ovládacímu prvku {0} proběhl z jiného vlákna než z vlákna, v rámci kterého byl vytvořen.
@@ -8709,11 +8649,6 @@ Trasování zásobníku, kde došlo k neplatné operaci:
Kolekce je jen pro čtení.
-
-
- Registrace formátu schránky se nezdařila.
-
- Nelze vytvořit podřízený seznam pro pole {0}.
@@ -11139,11 +11074,6 @@ Trasování zásobníku, kde došlo k neplatné operaci:
Schránka obsahuje neočekávaný typ.
-
-
- Určený formát schránky není kompatibilní s typem {0}.
-
- Neznámý typ ATTRIBUTE.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
index 93c43175856..78c0b75480c 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.de.xlf
@@ -287,21 +287,6 @@
Das komplexe DataBinding akzeptiert als Datenquelle entweder IList oder IListSource.
-
-
- Die Serialisierung und Deserialisierung von BinaryFormatter sind in dieser Anwendung deaktiviert. Weitere Informationen finden Sie unter https://aka.ms/binaryformatter.
-
-
-
-
- BinaryFormatter wird in der Zwischenablage oder bei Drag & Drop-Vorgängen nicht unterstützt.
-
-
-
-
- BinaryFormatter wird in der Zwischenablage oder bei Drag & Drop-Vorgängen nicht unterstützt. Aktivieren Sie es, und verwenden Sie die APIs 'TryGetData<T>' mit einer 'Resolver'-Funktion, die eine Reihe zulässiger Typen definiert, um '{0}' zu deserialisieren.
-
- Tritt auf, wenn sich der Bindungskontext geändert hat.
@@ -962,11 +947,6 @@
Ein zirkulärer Steuerelementverweis ist entstanden. Ein Steuerelement kann nicht sich selbst besitzen oder übergeordnet sein.
-
-
- Der angeforderte Clipboard-Vorgang war nicht erfolgreich.
-
- Ein Objekt vom Typ "DataObject" wird als leer serialisiert. Verwenden Sie die DATAObject.SetDataAsJson-APIs für Ihr Objekt, um die Daten innerhalb Ihres Objekts JSON-serialisieren zu lassen, und verwenden Sie dann die '{0}'-API mit Ihrem Objekt.
@@ -982,31 +962,11 @@
'{0}' ist kein konkreter Typ und kann zu einem ungebundenen Deserialisierungsversuch führen. Verwenden Sie einen konkreten Typ, oder definieren Sie alternativ eine 'Resolver'-Funktion, die Typen unterstützt, die Sie aus der Zwischenablage abrufen oder in Drag & Drop-Vorgängen verwenden möchten.
-
-
- Fehler beim Deserialisieren von JSON-Daten.
-
-
-
-
- Der Typ {0} wurde nicht gefunden. Wenn dieser Typ zulässig ist, stellen Sie sicher, dass er von der in den APIs "TryGetData<T>" bereitgestellten Resolverfunktion unterstützt wird.
-
-
-
-
- Verwenden Sie die APIs "TryGetData<T>" mit einer Resolverfunktion, die eine Reihe zulässiger Typen zum Deserialisieren '{0}' definiert.
-
- Das angegebene Zwischenablageformat kann aufgrund von Sicherheitseinschränkungen für die Zwischenablage nicht festgelegt werden.
-
-
- Der Pfad {0} im Argument {1} ist ungültig.
-
- Schließen
@@ -1017,11 +977,6 @@
Der Wert {0}() kann nicht während der Ausführung von CreateHandle() aufgerufen werden.
-
-
- Der Vorgang kann nicht mit einer leeren Sammlung durchgeführt werden.
-
- Zeigt die horizontale Textausrichtung des Spaltenheaders an.
@@ -4259,11 +4214,6 @@ Behandeln Sie das DataError-Ereignis, um dieses Standarddialogfeld zu ersetzen.<
Das Bitmap-Objekt unterstützt das geräteunabhängige Bitmapformat nicht direkt. Legen Sie entweder die automatische Konvertierung fest, oder führen Sie SetData mit dem Bitmap-Datenformat durch.
-
-
- Leerraum oder leere Zeichenfolgen sind kein gültiger Wert für den format-Parameter.
-
- Die DataMember-Eigenschaft {0} kann in der DataSource nicht gefunden werden.
@@ -5009,11 +4959,6 @@ Wenn Sie auf "Weiter" klicken, ignoriert die Anwendung den Fehler und setzt den
Expanded
-
-
- Externe Ausnahme.
-
- Fragt die Dateinamen aller ausgewählter Dateien im Dialogfeld ab.
@@ -5815,11 +5760,6 @@ Möchten Sie sie ersetzen?
Gibt Unterstützung für transaktive Initialisierung an.
-
-
- Das Datenobjekt '{0}' implementiert die Schnittstelle "ITypedDataObject" nicht und kann nicht mithilfe der Methoden "TryGetData<T>(string, out T)" gelesen werden.
-
- Ungültiger threadübergreifender Vorgang: Der Zugriff auf das Steuerelement {0} erfolgte von einem anderen Thread als dem Thread, für den es erstellt wurde.
@@ -8709,11 +8649,6 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat:
Die Sammlung ist schreibgeschützt.
-
-
- Fehler beim Registrieren des Zwischenablagenformats.
-
- Die untergeordnete Liste für das Feld {0} kann nicht erstellt werden.
@@ -11139,11 +11074,6 @@ Stapelüberwachung, in der der unzulässige Vorgang auftrat:
Die Zwischenablage enthält einen unerwarteten Typ.
-
-
- Das angegebene Format der Zwischenablage ist mit dem Typ "{0}" nicht kompatibel.
-
- Unbekannter ATTRIBUTE-Typ.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
index 745931c0e65..0663e04bf4e 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.es.xlf
@@ -287,21 +287,6 @@
El DataBinding complejo acepta como origen de datos IList o IListSource.
-
-
- La serialización y deserialización de BinaryFormatter están deshabilitadas en esta aplicación. Consulte https://aka.ms/binaryformatter para obtener más información.
-
-
-
-
- BinaryFormatter no se admite en el Portapapeles ni en operaciones de arrastrar y colocar.
-
-
-
-
- BinaryFormatter no se admite en el Portapapeles ni en operaciones de arrastrar y colocar. Habilítelo y use las API "TryGetData<T>" con una función "resolver" que defina un conjunto de tipos permitidos para deserializar '{0}'.
-
- Se produce cuando el contexto de enlace ha cambiado.
@@ -962,11 +947,6 @@
Referencia circular del control. Un control no puede ser propietario o primario de sí mismo.
-
-
- Error en la operación solicitada del Portapapeles.
-
- Un objeto de tipo "DataObject" se serializará como vacío. Use las API "DataObject.SetDataAsJson" del objeto para serializar json los datos del objeto y, a continuación, use la API de '{0}' con el objeto.
@@ -982,31 +962,11 @@
'{0}' no es un tipo concreto y podría dar lugar a un intento de deserialización no enlazado. Use un tipo concreto o bien defina una función "resolver" que admita los tipos que espera recuperar del Portapapeles o use en operaciones de arrastrar y colocar.
-
-
- No se pudieron deserializar los datos JSON.
-
-
-
-
- No se encuentra el tipo {0}. Si se permite este tipo, asegúrese de que la función "resolver" proporcionada en las API "TryGetData<T>" la admite.
-
-
-
-
- Use las API "TryGetData<T>" con una función "resolver" que defina un conjunto de tipos permitidos para deserializar '{0}'.
-
- Por restricciones de seguridad en el portapapeles, el formato del portapapeles especificado no se puede establecer.
-
-
- La ruta de acceso "{0}" del argumento "{1}" no es válida.
-
- Cerrar
@@ -1017,11 +977,6 @@
No se puede llamar al valor {0}() durante CreateHandle().
-
-
- No se puede operar con una colección vacía.
-
- Indica la alineación horizontal del texto que se muestra en el encabezado de columna.
@@ -4259,11 +4214,6 @@ Para reemplazar este cuadro de diálogo predeterminado controle el evento DataEr
El objeto de mapa de bits no admite directamente el formato de mapa de bits del dispositivo independiente. Puede especificar la conversión automática o realizar la operación SetData con el formato de datos de mapa de bits.
-
-
- Un espacio o una cadena vacía no es un valor válido para el parámetro "format".
-
- La propiedad DataMember '{0}' no se encontró en DataSource.
@@ -5009,11 +4959,6 @@ Si hace clic en Continuar, la aplicación pasará por alto este error e intentar
Expandido
-
-
- Excepción externa
-
- Recupera los nombres de todos los archivos seleccionados en el cuadro de diálogo.
@@ -5815,11 +5760,6 @@ Do you want to replace it?
Especifica la compatibilidad para la inicialización por transacción.
-
-
- El objeto de datos '{0}' no implementa la interfaz 'ITypedDataObject' y no se puede leer con los métodos 'TryGetData<T>(string, out T)'.
-
- Operación no válida a través de subprocesos: Se tuvo acceso al control '{0}' desde un subproceso distinto a aquel en que lo creó.
@@ -8709,11 +8649,6 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue:
La colección es de solo lectura.
-
-
- No se pudo registrar el formato del portapapeles.
-
- No se puede crear una lista secundaria para el campo {0}.
@@ -11139,11 +11074,6 @@ El seguimiento de la pila donde tuvo lugar la operación no válida fue:
El Portapapeles contiene un tipo inesperado.
-
-
- El formato del Portapapeles especificado no es compatible con el tipo "{0}".
-
- Tipo ATTRIBUTE desconocido.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
index 3d0d5a48ef3..46b3b2221b5 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.fr.xlf
@@ -287,21 +287,6 @@
DataBinding complexe accepte IList ou IListSource comme source de données.
-
-
- La sérialisation et la désérialisation binaryFormatter sont désactivées dans cette application. Pour plus d’informations, consultez https://aka.ms/binaryformatter.
-
-
-
-
- BinaryFormatter n’est pas pris en charge dans les opérations de presse-papiers ou de glisser-déplacer.
-
-
-
-
- BinaryFormatter n’est pas pris en charge dans les opérations de presse-papiers ou de glisser-déplacer. Activez-la et utilisez les API 'TryGetData<T>' avec une fonction 'resolver' qui définit un ensemble de types autorisés pour désérialiser '{0}'.
-
- Se produit lorsque le contexte de liaison change.
@@ -962,11 +947,6 @@
Une référence de contrôle circulaire a été créée. Un contrôle ne peut pas être propriétaire de lui-même ou être son propre parent.
-
-
- Échec de l'opération du Presse-papiers demandée.
-
- Un objet de type 'DataObject' va sérialiser comme vide. Utilisez les API « DataObject.SetDataAsJson » sur votre objet pour sérialiser les données au sein de votre objet, puis utilisez l’API '{0}' avec votre objet.
@@ -982,31 +962,11 @@
'{0}' n’est pas un type concret et peut entraîner une tentative de désérialisation non liée. Utilisez un type concret ou définissez une fonction « resolver » qui prend en charge les types que vous voulez récupérer à partir du Presse-papiers ou utilisez-la dans les opérations de glisser-déplacer.
-
-
- Nous n’avons pas pu désérialiser les données JSON.
-
-
-
-
- Type {0} introuvable. Si ce type est autorisé, vérifiez que la fonction « resolver » fournie dans les API « TryGetData<T> » le prend en charge.
-
-
-
-
- Utilisez les API 'TryGetData<T>' avec une fonction 'resolver' qui définit un ensemble de types autorisés pour désérialiser '{0}'.
-
- En raison des restrictions de sécurité du Presse-papiers, il est impossible de définir le format de Presse-papiers spécifié.
-
-
- Le chemin "{0}" dans l'argument "{1}" n'est pas valide.
-
- Fermer
@@ -1017,11 +977,6 @@
Impossible d'appeler la valeur {0}() pendant un CreateHandle().
-
-
- Impossible d'agir avec une collection vide.
-
- Indique l'alignement horizontal du texte affiché dans l'en-tête de colonne.
@@ -4259,11 +4214,6 @@ Pour remplacer cette boîte de dialogue par défaut, traitez l'événement DataE
L'objet Bitmap ne prend pas directement en charge le format de bitmap indépendante du périphérique. Spécifiez une conversion automatique ou définissez un SetData avec le format de données Bitmap.
-
-
- Un espace ou une chaîne vide ne sont pas des valeurs valides pour le paramètre 'format'.
-
- La propriété DataMember '{0}' est introuvable dans le DataSource.
@@ -5009,11 +4959,6 @@ Si vous cliquez sur Continuer, l'application va ignorer cette erreur et tenter d
Développé
-
-
- Exception externe
-
- Extrait les noms de fichiers de tous les fichiers sélectionnés dans cette boîte de dialogue.
@@ -5815,11 +5760,6 @@ Voulez-vous le remplacer ?
Spécifie la prise en charge de l'initialisation traitée.
-
-
- L’objet de données '{0}' n’implémente pas l’interface 'ITypedDataObject' et ne peut pas être lu à l’aide des méthodes 'TryGetData<T>(string, out T)'.
-
- Opération inter-threads non valide : le contrôle '{0}' a fait l'objet d'un accès à partir d'un thread autre que celui sur lequel il a été créé.
@@ -8709,11 +8649,6 @@ Cette opération non conforme s'est produite sur la trace de la pile :
La collection est en lecture seule.
-
-
- Impossible d'inscrire le format du Presse-papiers.
-
- Impossible de créer une liste enfant pour le champ {0}.
@@ -11139,11 +11074,6 @@ Cette opération non conforme s'est produite sur la trace de la pile :
Le Presse-papiers contient un type inattendu.
-
-
- Le format de Presse-papiers spécifié n'est pas compatible avec le type '{0}'.
-
- Type ATTRIBUTE inconnu.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
index 6a9543ea978..497145accc6 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.it.xlf
@@ -287,21 +287,6 @@
L'origine dati di un'associazione dati complessa può essere solo IList o IListSource.
-
-
- La serializzazione e la deserializzazione di BinaryFormatter sono disabilitate in questa applicazione. Per altre informazioni, vedere https://aka.ms/binaryformatter.
-
-
-
-
- BinaryFormatter non è supportato negli Appunti o nelle operazioni di trascinamento della selezione.
-
-
-
-
- BinaryFormatter non è supportato negli Appunti o nelle operazioni di trascinamento della selezione. Abilitarlo e usare le API 'TryGetData<T>' con una funzione 'resolver' che definisce un set di tipi consentiti da deserializzare '{0}'.
-
- Si verifica quando il contesto di associazione viene modificato.
@@ -962,11 +947,6 @@
Riferimento circolare a un controllo. Un controllo non può essere l'elemento padre o il proprietario di se stesso.
-
-
- Operazione richiesta sugli Appunti non riuscita.
-
- Un oggetto di tipo 'DataObject' verrà serializzato come vuoto. Usare le API 'DataObject.SetDataAsJson' nell'oggetto per serializzare in formato JSON i dati all'interno dell'oggetto, quindi usare l'API '{0}' con l'oggetto.
@@ -982,31 +962,11 @@
'{0}' non è un tipo concreto e potrebbe causare un tentativo di deserializzazione senza limitazioni. Usare un tipo concreto o in alternativa definire una funzione 'resolver' che supporta tipi che si prevede di recuperare dagli Appunti o da usare nelle operazioni di trascinamento della selezione.
-
-
- Non è possibile deserializzare i dati JSON.
-
-
-
-
- Tipo {0} non trovato. Se questo tipo è consentito, assicurarsi che sia supportata dalla funzione 'resolver' fornita nelle API 'TryGetData<T>'.
-
-
-
-
- Usare le API 'TryGetData<T>' con una funzione 'resolver' che definisce un set di tipi consentiti per deserializzare '{0}'.
-
- A causa delle restrizioni di sicurezza sugli Appunti, il formato degli Appunti specificato non può essere impostato.
-
-
- Il percorso "{0}" nell'argomento "{1}" non è valido.
-
- Chiudi
@@ -1017,11 +977,6 @@
Impossibile chiamare il valore {0}() durante CreateHandle().
-
-
- Impossibile operare con una raccolta vuota.
-
- Indica l'allineamento orizzontale del testo visualizzato come intestazione di colonna.
@@ -4259,11 +4214,6 @@ Per sostituire questa finestra di dialogo predefinita, gestire l'evento DataErro
L'oggetto Bitmap non supporta direttamente il formato bitmap indipendente dal dispositivo. Specificare la conversione automatica o effettuare un'operazione SetData con il formato dati Bitmap.
-
-
- Una stringa composta da spazi vuoti o vuota non rappresenta un valore valido per il parametro 'format'.
-
- Impossibile trovare la proprietà DataMember '{0}' in DataSource.
@@ -5009,11 +4959,6 @@ Se si sceglie Continua, l'errore verrà ignorato e l'applicazione proverà a con
Espansa
-
-
- Eccezione esterna
-
- Recupera i nomi di tutti i file selezionati nella finestra di dialogo.
@@ -5815,11 +5760,6 @@ Sostituirlo?
Specifica il supporto per l'inizializzazione transazionale.
-
-
- L'oggetto dati '{0}' non implementa l'interfaccia 'ITypedDataObject' e non può essere letto con i metodi 'TryGetData<T>(string, out T)'.
-
- Operazione cross-thread non valida: è stato eseguito l'accesso al controllo '{0}' da un thread diverso da quello da cui è stata eseguita la creazione.
@@ -8709,11 +8649,6 @@ Traccia dello stack da cui si è verificata l'operazione non valida:
Raccolta in sola lettura.
-
-
- Registrazione del formato degli Appunti non riuscita.
-
- Impossibile creare un elenco di elementi figlio per il campo {0}.
@@ -11139,11 +11074,6 @@ Traccia dello stack da cui si è verificata l'operazione non valida:
Gli Appunti contengono un tipo imprevisto.
-
-
- Il formato specificato per gli Appunti non è compatibile con il tipo '{0}'.
-
- Tipo ATTRIBUTE sconosciuto.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
index 06c0eb577b3..d9c127bec39 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ja.xlf
@@ -287,21 +287,6 @@
Complex DataBinding は IList または IListSource のどちらかをデータソースとして受け入れます。
-
-
- BinaryFormatter のシリアル化と逆シリアル化は、このアプリケーションでは無効です。詳細については、https://aka.ms/binaryformatter を参照してください。
-
-
-
-
- BinaryFormatter はクリップボードまたはドラッグ アンド ドロップ操作ではサポートされていません。
-
-
-
-
- BinaryFormatter はクリップボードまたはドラッグ アンド ドロップ操作ではサポートされていません。これを有効にして、'{0}' を逆シリアル化するために許可される型のセットを定義する 'resolver' 関数を含む 'TryGetData<T>' API を使用してください。
-
- バインド コンテキストが変更されたときに発生します。
@@ -962,11 +947,6 @@
コントロールの循環参照が発生しました。コントロールはそれ自体を所有したり、その親になることはできません。
-
-
- 要求されたクリップボード操作に成功しませんでした。
-
- 型 'DataObject' のオブジェクトは空としてシリアル化されます。オブジェクトで 'DataObject.SetDataAsJson' API を使用してオブジェクト内のデータを JSON シリアル化してから、オブジェクトで '{0}' API を使用します。
@@ -982,31 +962,11 @@
'{0}' は具象型ではないため、バインドされていない逆シリアル化が試行される可能性があります。具象型を使用するか、クリップボードから取得する必要のある型をサポートする 'resolver' 関数を定義するか、ドラッグ アンド ドロップ操作で使用してください。
-
-
- トークン データを逆シリアル化できませんでした。
-
-
-
-
- 型 {0} が見つかりません。この型が許可されている場合は、'TryGetData<T>' API で指定された 'resolver' 関数でサポートされていることを確認してください。
-
-
-
-
- 'TryGetData<T>' API は、'{0}' を逆シリアル化するために許可される型のセットを定義する 'resolver' 関数と共に使用してください。
-
- クリップボードのセキュリティ上の制限が原因で、指定されたクリップボードの形式を設定できません。
-
-
- 引数 "{1}" のパス "{0}" が有効ではありません。
-
- 閉じる
@@ -1017,11 +977,6 @@
CreateHandle() の実行中は値 {0}() を呼び出せません。
-
-
- 空のコレクションを使用して操作できません。
-
- 列ヘッダーに表示されるテキストの水平方向の配置を示します。
@@ -4259,11 +4214,6 @@ To replace this default dialog please handle the DataError event.
ビットマップ オブジェクトは、デバイスに依存しないビットマップ フォーマットを直接サポートしません。 自動変換を指定するか、ビットマップ データ フォーマットで SetData を実行してください。
-
-
- スペース文字列または空の文字列は、パラメーター 'format' の有効な値ではありません。
-
- DataMember プロパティ '{0}' は DataSource に見つかりません。
@@ -5009,11 +4959,6 @@ If you click Continue, the application will ignore this error and attempt to con
展開済み
-
-
- 外部例外
-
- ダイアログ ボックスで選択されたすべてのファイルのファイル名を取得します。
@@ -5815,11 +5760,6 @@ Do you want to replace it?
処理された初期化のサポートを指定します。
-
-
- データ オブジェクト '{0}' は 'ITypedDataObject' インターフェイスを実装していないため、'TryGetData<T>(string, out T)' メソッドを使用して読み取ることができません。
-
- 有効ではないスレッド間の操作: コントロールが作成されたスレッド以外のスレッドからコントロール '{0}' がアクセスされました。
@@ -8709,11 +8649,6 @@ Stack trace where the illegal operation occurred was:
このコレクションは読み取り専用です。
-
-
- クリップボード形式を登録することができませんでした。
-
- フィールド {0} の子リストを作成できません。
@@ -11139,11 +11074,6 @@ Stack trace where the illegal operation occurred was:
クリップボードに予期しない型が含まれています。
-
-
- 指定されたクリップボード形式は '{0}' 型と互換性がありません。
-
- 不明な ATTRIBUTE タイプです。
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
index 26e2d018b82..9b4bc86907a 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ko.xlf
@@ -287,21 +287,6 @@
IList 또는 IListSource를 복합 DataBinding의 데이터 소스로 사용할 수 있습니다.
-
-
- 이 애플리케이션 내에서 BinaryFormatter 직렬화 및 역직렬화를 사용할 수 없습니다. 자세한 내용은 https://aka.ms/binaryformatter 페이지를 참조하세요.
-
-
-
-
- BinaryFormatter는 클립보드 또는 끌어서 놓기 작업에서 지원되지 않습니다.
-
-
-
-
- BinaryFormatter는 클립보드 또는 끌어서 놓기 작업에서 지원되지 않습니다. 사용하도록 설정하고 '{0}' deserialize할 수 있는 형식 집합을 정의하는 'resolver' 함수와 함께 'TryGetData<T>' API를 사용하세요.
-
- 바인딩 컨텍스트가 변경될 때 발생합니다.
@@ -962,11 +947,6 @@
순환 컨트롤 참조가 발생했습니다. 컨트롤은 자신에게 소유되거나 자신의 부모가 될 수 없습니다.
-
-
- 요청한 클립보드 작업을 수행하지 못했습니다.
-
- 'DataObject' 형식의 개체는 비어 있는 것으로 직렬화됩니다. 개체에서 'DataObject.SetDataAsJson' API를 사용하여 개체 내에서 데이터를 JSON으로 직렬화한 다음 개체와 함께 '{0}' API를 사용합니다.
@@ -982,31 +962,11 @@
'{0}' 구체적인 형식이 아니므로 바인딩되지 않은 역직렬화를 시도할 수 있습니다. 구체적인 형식을 사용하거나 클립보드에서 검색하거나 끌어서 놓기 작업에 사용할 형식을 지원하는 'resolver' 함수를 정의하십시오.
-
-
- JSON 데이터를 역직렬화하지 못했습니다.
-
-
-
-
- {0} 형식을 찾을 수 없습니다. 이 형식이 허용되는 경우 'TryGetData<T>' API에 제공된 'resolver' 함수가 이를 지원하는지 확인하세요.
-
-
-
-
- '{0}' 역직렬화할 수 있는 형식 집합을 정의하는 'resolver' 함수와 함께 'TryGetData<T>' API를 사용하세요.
-
- 클립보드의 보안 제약으로 인해 지정한 클립보드 형식을 설정할 수 없습니다.
-
-
- "{1}" 인수의 "{0}" 경로가 잘못되었습니다.
-
- 닫기
@@ -1017,11 +977,6 @@
CreateHandle() 중에는 {0}() 값을 호출할 수 없습니다.
-
-
- 빈 컬렉션으로 작업할 수 없습니다.
-
- 열 머리글에 표시되는 텍스트의 가로 맞춤을 나타냅니다.
@@ -4259,11 +4214,6 @@ To replace this default dialog please handle the DataError event.
Bitmap 개체는 디바이스 독립적 비트맵 형식을 직접 지원하지 않습니다. 자동 변환을 지정하거나 Bitmap 데이터 형식으로 SetData를 수행하십시오.
-
-
- 공백 또는 빈 문자열은 매개 변수 'format'의 유효한 값이 아닙니다.
-
- DataMember 속성 '{0}'을(를) DataSource에서 찾을 수 없습니다.
@@ -5009,11 +4959,6 @@ If you click Continue, the application will ignore this error and attempt to con
확장
-
-
- 외부 예외
-
- 대화 상자에서 선택한 모든 파일의 이름을 검색합니다.
@@ -5815,11 +5760,6 @@ Do you want to replace it?
트랜잭트 초기화를 지원합니다.
-
-
- 데이터 개체 '{0}' 'ITypedDataObject' 인터페이스를 구현하지 않으며 'TryGetData<T>(string, out T)' 메서드를 사용하여 읽을 수 없습니다.
-
- 크로스 스레드 작업이 잘못되었습니다. '{0}' 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다.
@@ -8709,11 +8649,6 @@ Stack trace where the illegal operation occurred was:
컬렉션이 읽기 전용입니다.
-
-
- 클립보드 형식을 등록하지 못했습니다.
-
- {0} 필드의 자식 목록을 만들 수 없습니다.
@@ -11139,11 +11074,6 @@ Stack trace where the illegal operation occurred was:
클립보드에 예기치 않은 형식이 있습니다.
-
-
- 지정한 클립보드 형식이 '{0}' 형식과 호환되지 않습니다.
-
- 알 수 없는 ATTRIBUTE 형식입니다.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
index 5ee0c6d4029..f7fea56361b 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pl.xlf
@@ -287,21 +287,6 @@
Złożony element DataBinding akceptuje jako źródło danych zarówno elementy IList, jak i IListSource.
-
-
- Serializacji BinaryFormatter i deserializacji są wyłączone w tej aplikacji. Aby uzyskać więcej informacji, zobacz https://aka.ms/binaryformatter.
-
-
-
-
- Element BinaryFormatter nie jest obsługiwany w operacjach przeciągania i upuszczania schowka ani przeciągania i upuszczania.
-
-
-
-
- Element BinaryFormatter nie jest obsługiwany w operacjach przeciągania i upuszczania schowka ani przeciągania i upuszczania. Włącz ją i użyj interfejsów API "TryGetData<T>" z funkcją "resolver", która definiuje zestaw dozwolonych typów do deserializacji '{0}'.
-
- Występuje, gdy kontekst powiązania zostanie zmieniony.
@@ -962,11 +947,6 @@
Utworzono cykliczne odwołanie do formantu. Formant nie może należeć do siebie lub być własnym elementem nadrzędnym.
-
-
- Żądana operacja schowka nie powiodła się.
-
- Obiekt typu "DataObject" będzie serializować jako pusty. Użyj interfejsów API "DataObject.SetDataAsJson" w obiekcie, aby serializować dane wewnątrz obiektu za pomocą kodu JSON, a następnie użyj interfejsu API '{0}' ze swoim obiektem.
@@ -982,31 +962,11 @@
'{0}' nie jest konkretnym typem i może spowodować niepowiązaną próbę deserializacji. Użyj konkretnego typu lub alternatywnie zdefiniuj funkcję "resolver", która obsługuje typy oczekiwane do pobrania ze schowka, lub użyj jej w operacjach przeciągania i upuszczania.
-
-
- Nie można zdeserializować danych JSON.
-
-
-
-
- Nie znaleziono {0} typu. Jeśli ten typ jest dozwolony, upewnij się, że funkcja "resolver" podana w interfejsach API "TryGetData<T>" obsługuje go.
-
-
-
-
- Użyj interfejsów API "TryGetData<T>" z funkcją "resolver", która definiuje zestaw dozwolonych typów do deserializacji '{0}'.
-
- Z powodu ograniczeń bezpieczeństwa w schowku nie można ustawić określonego formatu schowka.
-
-
- Ścieżka „{0}” w argumencie „{1}” jest nieprawidłowa.
-
- Zamknij
@@ -1017,11 +977,6 @@
Nie można wywołać wartości {0}() w trakcie wykonywania CreateHandle().
-
-
- Nie można operować pustą kolekcją.
-
- Wskazuje poziome wyrównanie tekstu wyświetlanego w nagłówku kolumny.
@@ -4259,11 +4214,6 @@ Aby zamienić to domyślne okno dialogowe, obsłuż zdarzenie DataError.Obiekt mapy bitowej nie obsługuje bezpośrednio formatu map bitowych niezależnych od urządzenia. Określ autokonwersję lub wykonaj operację SetData przy użyciu formatu danych Bitmap.
-
-
- Odstęp lub pusty ciąg jest nieprawidłową wartością dla parametru „format”.
-
- Właściwości DataMember '{0}' nie można odnaleźć w elemencie DataSource.
@@ -5009,11 +4959,6 @@ Jeśli klikniesz pozycję Kontynuuj, aplikacja zignoruje ten błąd i będzie pr
Rozwinięte
-
-
- Wyjątek zewnętrzny
-
- Pobiera nazwy wszystkich plików zaznaczonych w oknie dialogowym.
@@ -5815,11 +5760,6 @@ Czy chcesz go zamienić?
Określa pomoc techniczną dla inicjowania transakcyjnego.
-
-
- Obiekt danych '{0}' nie implementuje interfejsu "ITypedDataObject" i nie można go odczytać przy użyciu metod "TryGetData<T>(string, out T)".
-
- Nieprawidłowa operacja między wątkami: do formantu '{0}' uzyskiwany jest dostęp z wątku innego niż wątek, w którym został utworzony.
@@ -8709,11 +8649,6 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja:
Kolekcja jest tylko do odczytu.
-
-
- Nie można zarejestrować formatu schowka.
-
- Nie można utworzyć listy elementów podrzędnych pola {0}.
@@ -11139,11 +11074,6 @@ Stos śledzenia, w którym wystąpiła zabroniona operacja:
Schowek zawiera nieoczekiwany typ.
-
-
- Określony format schowka nie jest zgodny z typem „{0}”.
-
- Nieznany typ ATTRIBUTE.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf
index fe77bbb1ba7..fb021b0b9af 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.pt-BR.xlf
@@ -287,21 +287,6 @@
DataBinding complexa aceita IList ou IListSource como fonte de dados.
-
-
- A serialização e a desserialização de BinaryFormatter estão desabilitadas neste aplicativo. Consulte https://aka.ms/binaryformatter para obter mais informações.
-
-
-
-
- Não há suporte para BinaryFormatter em operações de área de transferência ou de arrastar e soltar.
-
-
-
-
- Não há suporte para BinaryFormatter em operações de área de transferência ou de arrastar e soltar. Habilite-o e use as APIs 'TryGetData<T>' com uma função 'resolver' que define um conjunto de tipos permitidos para desserializar '{0}'.
-
- Ocorre quando o contexto de associação foi alterado.
@@ -962,11 +947,6 @@
Foi criada uma referência de controle circular. Um controle não pode ser propriedade ou pai dele mesmo.
-
-
- A operação de Área de Transferência solicitada não foi bem-sucedida.
-
- Um objeto do tipo 'DataObject' será serializado como vazio. Use as APIs 'DataObject.SetDataAsJson' no objeto para serializar os dados em JSON no objeto e use a API de '{0}' com o objeto.
@@ -982,31 +962,11 @@
'{0}' não é um tipo concreto e pode resultar em uma tentativa de desserialização não associado. Use um tipo concreto ou, como alternativa, defina uma função 'resolver' que dê suporte aos tipos que você está esperando recuperar da área de transferência ou use em operações de arrastar e soltar.
-
-
- Falha ao desserializar dados JSON.
-
-
-
-
- Tipo {0} não encontrado. Se esse tipo for permitido, verifique se a função 'resolver' fornecida nas APIs 'TryGetData<T>' dá suporte a ela.
-
-
-
-
- Use apIs 'TryGetData<T>' com uma função 'resolver' que define um conjunto de tipos permitidos para desserializar '{0}'.
-
- Devido a restrições de segurança na área de transferência, não é possível definir o formato especificado de área de transferência.
-
-
- O caminho "{0}" no argumento "{1}" não é válido.
-
- Fechar
@@ -1017,11 +977,6 @@
O valor {0}() não pode ser chamado durante CreateHandle().
-
-
- Não é possível operar com uma coleção vazia.
-
- Indica o alinhamento horizontal do texto exibido no cabeçalho da coluna.
@@ -4259,11 +4214,6 @@ Para substituir a caixa de diálogo padrão, manipule o evento DataError.O objeto Bitmap não oferece suporte direto ao formato de bitmap independente de dispositivo. Especifique a conversão automática ou execute SetData com o formato de dados Bitmap.
-
-
- Um espaço em branco ou uma cadeia de caracteres vazia não é um valor válido para o parâmetro 'format'.
-
- Não foi possível encontrar a propriedade DataMember '{0}' na DataSource.
@@ -5009,11 +4959,6 @@ Se você clicar em Continuar, o aplicativo ignorará esse erro e tentará contin
Expandido
-
-
- Exceção externa
-
- Recupera os nomes de arquivo de todos os arquivos selecionados na caixa de diálogo.
@@ -5815,11 +5760,6 @@ Deseja substituí-lo?
Especifica o suporte para a inicialização transacionada.
-
-
- O objeto de '{0}' não implementa a interface 'ITypedDataObject' e não pode ser lido usando os métodos 'TryGetData<T>(string, out T)'.
-
- Operação entre threads inválida: controle '{0}' acessado de um thread que não é aquele no qual foi criado.
@@ -8709,11 +8649,6 @@ Rastreamento de pilha em que a operação ilegal ocorreu:
A coleção é somente leitura.
-
-
- O registro do formato de área de transferência não teve êxito.
-
- Não é possível criar uma lista filho para o campo {0}.
@@ -11139,11 +11074,6 @@ Rastreamento de pilha em que a operação ilegal ocorreu:
A área de transferência contém um tipo inesperado.
-
-
- O formato de Área de transferência especificado não é compatível com o tipo '{0}'.
-
- Tipo ATTRIBUTE desconhecido.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
index 3062b3003c8..173c03559d0 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.ru.xlf
@@ -287,21 +287,6 @@
Для составного DataBinding источником данных может служить IList или IListSource.
-
-
- Сериализация и десериализация BinaryFormatter отключены в этом приложении. Дополнительные сведения см. https://aka.ms/binaryformatter.
-
-
-
-
- BinaryFormatter не поддерживается в буфере обмена или операциях перетаскивания.
-
-
-
-
- BinaryFormatter не поддерживается в буфере обмена или операциях перетаскивания. Включите его и используйте API "TryGetData<T>" с функцией "resolver", которая определяет набор разрешенных типов для десериализации '{0}'.
-
- Происходит при изменении контекста привязки.
@@ -962,11 +947,6 @@
Создана циклическая ссылка на элемент управления. Элемент управления не может являться владельцем или родителем для самого себя.
-
-
- Сбой при выполнении запрошенной операции с буфером обмена.
-
- Объект типа "DataObject" будет сериализован как пустой. Используйте API-интерфейсы "DataObject.SetDataAsJson" на объекте для сериализации данных в объекте JSON, а затем используйте API '{0}' с объектом.
@@ -982,31 +962,11 @@
'{0}' не является конкретным типом и может привести к нео привязанной попытке десериализации. Используйте конкретный тип или определите функцию "resolver", которая поддерживает типы, которые вы ожидаете получить из буфера обмена, или используйте в операциях перетаскивания.
-
-
- Сбой десериализации данных JSON.
-
-
-
-
- Тип {0} не найден. Если этот тип разрешен, убедитесь, что функция "resolver", указанная в API "TryGetData<T>", поддерживает ее.
-
-
-
-
- Используйте API"TryGetData<T>" с функцией "resolver", которая определяет набор разрешенных типов для десериализации '{0}'.
-
- Установка указанного формата буфера обмена не допускается ограничениями по безопасности.
-
-
- Недопустимый путь "{0}" в аргументе "{1}".
-
- Закрыть
@@ -1017,11 +977,6 @@
Вызов значения {0}() во время выполнения CreateHandle() невозможен.
-
-
- Работа с пустой коллекцией невозможна.
-
- Указывает горизонтальное выравнивание текста, отображаемого в заголовке столбца.
@@ -4259,11 +4214,6 @@ To replace this default dialog please handle the DataError event.
Растровый объект непосредственно не поддерживает не зависящий от устройства растровый формат. Используйте автоматическое приведение либо выполните SetData с растровым форматом данных.
-
-
- Пробел и пустая строка не являются допустимыми значениями для параметра "format".
-
- Свойство '{0}' для DataMember не найдено в DataSource.
@@ -5010,11 +4960,6 @@ If you click Continue, the application will ignore this error and attempt to con
Развернуто
-
-
- Внешнее исключение
-
- Считывает имена всех файлов, выбранных в данном диалоговом окне.
@@ -5816,11 +5761,6 @@ Do you want to replace it?
Определяет поддержку для инициализации транзакции.
-
-
- Объекты '{0}' не реализуют интерфейс "ITypedDataObject" и не могут быть прочитаны с помощью методов "TryGetData<T>(string, out T)".
-
- Недопустимая операция в нескольких потоках: попытка доступа к элементу управления '{0}' не из того потока, в котором он был создан.
@@ -8710,11 +8650,6 @@ Stack trace where the illegal operation occurred was:
Коллекция предназначена только для чтения.
-
-
- Регистрация формата буфера обмена не удалась.
-
- Список потомков для поля {0} создать нельзя.
@@ -11140,11 +11075,6 @@ Stack trace where the illegal operation occurred was:
Буфер обмена содержит непредвиденный тип.
-
-
- Указанный формат буфера обмена не совместим с типом "{0}".
-
- Неизвестный тип ATTRIBUTE.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
index 370b79cc1c8..a1144eb8666 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.tr.xlf
@@ -287,21 +287,6 @@
Karmaşık DataBinding, veri kaynağı olarak ya IList ya da IListSource kabul eder.
-
-
- BinaryFormatter serileştirme ve seri durumdan çıkarma işlemleri bu uygulamada devre dışı. Daha fazla bilgi için https://aka.ms/binaryformatter sayfasına bakın.
-
-
-
-
- BinaryFormatter panoda veya sürükle ve bırak işlemlerinde desteklenmiyor.
-
-
-
-
- BinaryFormatter panoda veya sürükle ve bırak işlemlerinde desteklenmiyor. Lütfen etkinleştirin ve 'TryGetData<T>' API'lerini, seri durumdan çıkarma için izin verilen türler kümesi tanımlayan bir 'çözümleyici' işleviyle '{0}'.
-
- Bağlama bağlamı değiştiğinde gerçekleşir.
@@ -962,11 +947,6 @@
Döngüsel denetim başvurusu yapıldı. Denetim kendi kendisinin sahibi olamaz veya kendisi tarafından üstü olarak atanamaz.
-
-
- İstenen Pano işlemi başarılı olmadı.
-
- 'DataObject' türündeki bir nesne boş olarak seri hale getirilebilir. Nesnenizin içindeki verileri JSON ile serileştirmek için nesneniz üzerinde 'DataObject.SetDataAsJson' API'lerini kullanın ve ardından nesneniz '{0}' API'sini kullanın.
@@ -982,31 +962,11 @@
'{0}' somut bir tür değil ve bir unbounded deserialization girişimine neden olabilir. Lütfen somut bir tür kullanın veya alternatif olarak panodan almayı bekleyen türleri destekleyen bir 'çözümleyici' işlevi ya da sürükle ve bırak işlemleri içinde kullanın.
-
-
- JSON verileri seri durumdan çıkarılamadı.
-
-
-
-
- Tür {0} bulunamadı. Bu türe izin verilirse lütfen 'TryGetData<T>' API'lerinde sağlanan 'resolver' işlevinin bunu desteklediğinden emin olun.
-
-
-
-
- Lütfen 'TryGetData<T>' API'lerini, seri durumdan çıkarma için izin verilen türler kümesi tanımlayan bir 'çözümleyici' işleviyle '{0}'.
-
- Pano üzerindeki güvenlik kısıtlamaları nedeniyle, belirtilen pano biçimi ayarlanamıyor.
-
-
- "{1}" bağımsız değişkenindeki "{0}" yolu geçerli değil.
-
- Kapat
@@ -1017,11 +977,6 @@
CreateHandle() yapılırken {0}() değeri çağrılamaz.
-
-
- Boş koleksiyon ile çalışılamaz.
-
- Sütun üst bilgisinde görüntülenen metnin yatay hizalamasını gösterir.
@@ -4259,11 +4214,6 @@ Bu varsayılan iletişim kutusunu değiştirmek için, lütfen DataError olayın
Bit eşlem nesnesi cihaz bağımsız bit eşlem biçimini doğrudan desteklemiyor. Otomatik dönüştürmeyi belirtin veya Bit eşlem veri biçimiyle SetData gerçekleştirin.
-
-
- Boş dize veya boşluk, 'format' parametresi için geçerli bir değer değil.
-
- DataMember özelliği '{0}', DataSource'da bulunamıyor.
@@ -5009,11 +4959,6 @@ Devam'a tıkladığınızda uygulama bu hatayı yoksayar ve işlemi sürdürmeye
Genişletilmiş
-
-
- Dış özel durum
-
- İletişim kutusunda seçilen tüm dosyaların dosya adını alır.
@@ -5815,11 +5760,6 @@ Bunu değiştirmek istiyor musunuz?
İşlenen başlatma için desteği belirtir.
-
-
- Veri nesnesi '{0}' 'ITypedDataObject' arabirimini uygulamıyor ve 'TryGetData<T>(string, out T)' yöntemleri kullanılarak okunamıyor.
-
- Çapraz iş parçacığı işlemi geçerli değil: '{0}' denetimine oluşturulduğu iş parçacığı dışında başka bir iş parçacığından erişildi.
@@ -8709,11 +8649,6 @@ Geçersiz işlemin gerçekleştiği yığın izi:
Koleksiyon salt okunur.
-
-
- Pano biçimi kaydı yapılamadı.
-
- {0} alanı için al liste oluşturulamıyor.
@@ -11139,11 +11074,6 @@ Geçersiz işlemin gerçekleştiği yığın izi:
Pano beklenmeyen bir tür içeriyor.
-
-
- Belirtilen Pano biçimi '{0}' türüyle uyumlu değil.
-
- Bilinmeyen ATTRIBUTE türü.
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf
index ec24df4e44d..9564167e68d 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hans.xlf
@@ -287,21 +287,6 @@
复杂的 DataBinding 接受 IList 或 IListSource 作为数据源。
-
-
- 此应用程序中已禁用 BinaryFormatter 序列化和反序列化。有关详细信息,请参阅 https://aka.ms/binaryformatter。
-
-
-
-
- 剪贴板或拖放操作不支持 BinaryFormatter。
-
-
-
-
- 剪贴板或拖放操作不支持 BinaryFormatter。请启用它,并将 “TryGetData<T>” API 与定义一组允许类型的 “resolver” 函数一起使用,以反序列化 '{0}'。
-
- 当绑定上下文发生更改时发生。
@@ -962,11 +947,6 @@
进行了循环控件引用。控件不能属于自身或成为自身的父级。
-
-
- 所请求的剪贴板操作失败。
-
- 类型为 “DataObject” 的对象将序列化为空。使用对象上的 “DataObject.SetDataAsJson” API 对对象中的数据进行 JSON 序列化,然后将 '{0}' API 与对象一起使用。
@@ -982,31 +962,11 @@
'{0}' 不是具体类型,可能导致未绑定的反序列化尝试。请使用具体类型,或者定义支持希望从剪贴板检索的类型或用于拖放操作的“解析程序”函数。
-
-
- 无法反序列化 JSON 数据。
-
-
-
-
- 找不到类型 {0}。如果允许此类型,请确保 “TryGetData<T>” API 中提供的 “resolver” 函数支持它。
-
-
-
-
- 请将 “TryGetData<T>” API 与定义一组允许类型的 “resolver” 函数一起使用,以反序列化 '{0}'。
-
- 由于剪贴板有安全限制,因此无法设置指定的剪贴板格式。
-
-
- 参数“{1}”中的路径“{0}”无效。
-
- 关闭
@@ -1017,11 +977,6 @@
执行 CreateHandle() 时无法调用值 {0}()。
-
-
- 无法对空集合进行操作。
-
- 指示列标题中显示的文本的水平对齐方式。
@@ -4259,11 +4214,6 @@ To replace this default dialog please handle the DataError event.
Bitmap 对象不直接支持与设备无关的位图格式。 请指定自动转换,或使用位图数据格式执行 SetData。
-
-
- 空格或空字符串不是参数 "format" 的有效值。
-
- 在 DataSource 上未找到 DataMember 属性“{0}”。
@@ -5009,11 +4959,6 @@ If you click Continue, the application will ignore this error and attempt to con
已展开
-
-
- 外部异常
-
- 检索对话框中所有选定文件的文件名。
@@ -5815,11 +5760,6 @@ Do you want to replace it?
指定支持事务处理初始化。
-
-
- 数据对象 '{0}' 未实现 “ITypedDataObject” 接口,无法使用 “TryGetData<T>(string, out T)” 方法进行读取。
-
- 线程间操作无效: 从不是创建控件“{0}”的线程访问它。
@@ -8709,11 +8649,6 @@ Stack trace where the illegal operation occurred was:
集合为只读。
-
-
- 剪贴板格式注册失败。
-
- 无法创建字段 {0} 的子列表。
@@ -11139,11 +11074,6 @@ Stack trace where the illegal operation occurred was:
剪贴板包含意外的类型。
-
-
- 指定的剪贴板格式与“{0}”类型不兼容。
-
- 未知的 ATTRIBUTE 类型。
diff --git a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf
index 6aced6ff838..ff24b23c8d9 100644
--- a/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf
+++ b/src/System.Windows.Forms/src/Resources/xlf/SR.zh-Hant.xlf
@@ -287,21 +287,6 @@
複雜 DataBinding 接受以 IList 或 IListSource 做為資料來源。
-
-
- 此應用程式內已停用 BinaryFormatter 序列化和還原序列化。如需詳細資訊,請參閱 https://aka.ms/binaryformatter。
-
-
-
-
- 剪貼簿或拖放操作不支援 BinaryFormatter。
-
-
-
-
- 剪貼簿或拖放操作不支援 BinaryFormatter。請加以啟用,並使用 『TryGetData<T>』 API 搭配 『resolver』 函式,定義一組允許的類型,以還原串行化 '{0}'。
-
- 在繫結內容變更時發生。
@@ -962,11 +947,6 @@
已經造成循環控制項參考。控制項無法擁有自己本身或成為本身的父系。
-
-
- 要求的剪貼簿作業失敗。
-
- 類型 『DataObject』 的物件將串行化為空白。使用物件上的 'DataObject.SetDataAsJson' API 將對象內的數據 JSON 串行化,然後在物件中使用 '{0}' API。
@@ -982,31 +962,11 @@
'{0}' 不是實體類型,可能會導致未繫結的還原串行化嘗試。請使用實體類型,或是定義 『resolver』 函式,以支援您預期要從剪貼簿擷取的類型,或是用於拖放操作。
-
-
- 無法還原序列化 JSON 資料。
-
-
-
-
- 找不到類型 {0}。如果允許此類型,請確定 'TryGetData<T>' API 中提供的 'resolver' 函式支援。
-
-
-
-
- 請使用 『TryGetData<T>』 API 搭配 『resolver』 函式,定義一組允許的類型,以還原串行化 '{0}'。
-
- 基於剪貼簿的安全限制,無法設定指定的剪貼簿格式。
-
-
- 引數 "{1}" 中的路徑 "{0}" 無效。
-
- 關閉
@@ -1017,11 +977,6 @@
執行 CreateHandle() 時,無法呼叫值 {0}()。
-
-
- 無法使用空的集合作業。
-
- 表示資料行行首中所顯示文字的水平對齊方式。
@@ -4259,11 +4214,6 @@ To replace this default dialog please handle the DataError event.
無論是指定自動轉換或以 Bitmap 資料格式執行 SetData,Bitmap 物件無法直接支援與裝置無關的點陣圖格式。
-
-
- 空白或空字串不是參數 'format' 的有效值。
-
- DataSource 上找不到 DataMember 屬性 '{0}'。
@@ -5009,11 +4959,6 @@ If you click Continue, the application will ignore this error and attempt to con
展開的
-
-
- 外部例外狀況
-
- 擷取對話方塊中所有已選取檔案的檔名。
@@ -5815,11 +5760,6 @@ Do you want to replace it?
為交易性的初始設定指定支援。
-
-
- 數據物件 '{0}' 未實作 『ITypedDataObject』 介面,因此無法使用 『TryGetData<T>(string, out T)』 方法讀取。
-
- 跨執行緒作業無效: 存取控制項 '{0}' 時所使用的執行緒與建立控制項的執行緒不同。
@@ -8709,11 +8649,6 @@ Stack trace where the illegal operation occurred was:
集合是唯讀的。
-
-
- 剪貼簿格式註冊失敗。
-
- 無法對欄位 {0} 建立子清單。
@@ -11139,11 +11074,6 @@ Stack trace where the illegal operation occurred was:
剪貼簿包含非預期的類型。
-
-
- 指定的剪貼簿格式與 '{0}' 類型不相容。
-
- 未知的 ATTRIBUTE 類型。
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/ActiveX/Control.ActiveXImpl.cs b/src/System.Windows.Forms/src/System/Windows/Forms/ActiveX/Control.ActiveXImpl.cs
index 8e12b6debe8..e5daf36c476 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/ActiveX/Control.ActiveXImpl.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/ActiveX/Control.ActiveXImpl.cs
@@ -21,6 +21,8 @@
using System.Windows.Forms.Nrbf;
using RECTL = Windows.Win32.Foundation.RECTL;
using System.Windows.Forms.BinaryFormat;
+using System.Private.Windows.Core.OLE;
+using CoreSR = System.Private.Windows.Core.Resources.SR;
namespace System.Windows.Forms;
@@ -1139,9 +1141,9 @@ bool SetValue(PropertyDescriptor currentProperty, object data)
if (!success)
{
- if (!DataObject.Composition.EnableUnsafeBinaryFormatterInNativeObjectSerialization)
+ if (!DesktopDataObject.Composition.EnableUnsafeBinaryFormatterInNativeObjectSerialization)
{
- throw new NotSupportedException(SR.BinaryFormatterNotSupported);
+ throw new NotSupportedException(CoreSR.BinaryFormatterNotSupported);
}
stream.Position = 0;
@@ -1529,9 +1531,9 @@ internal void Save(IPropertyBag* propertyBag, BOOL clearDirty, BOOL saveAllPrope
{
stream.SetLength(0);
- if (!DataObject.Composition.EnableUnsafeBinaryFormatterInNativeObjectSerialization)
+ if (!DesktopDataObject.Composition.EnableUnsafeBinaryFormatterInNativeObjectSerialization)
{
- throw new NotSupportedException(SR.BinaryFormatterNotSupported);
+ throw new NotSupportedException(CoreSR.BinaryFormatterNotSupported);
}
#pragma warning disable SYSLIB0011 // Type or member is obsolete
@@ -1834,7 +1836,7 @@ internal unsafe HRESULT SetObjectRects(RECT* lprcPosRect, RECT* lprcClipRect)
/// Throws the given hresult. This is used by ActiveX sourcing.
///
[DoesNotReturn]
- internal static void ThrowHr(HRESULT hr) => throw new ExternalException(SR.ExternalException, (int)hr);
+ internal static void ThrowHr(HRESULT hr) => throw new ExternalException(CoreSR.ExternalException, (int)hr);
///
internal unsafe HRESULT TranslateAccelerator(MSG* lpmsg)
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormatWriter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormatWriter.cs
index ef6c79b14d2..92d5f5c4ea2 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormatWriter.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormatWriter.cs
@@ -5,13 +5,14 @@
using System.Private.Windows;
using System.Private.Windows.Core.BinaryFormat;
using System.Private.Windows.Core.BinaryFormat.Serializer;
+using System.Private.Windows.Core.Nrbf;
namespace System.Windows.Forms.BinaryFormat;
///
/// Writer that writes Windows Forms specific types in binary format without using the BinaryFormatter.
///
-internal static class WinFormsBinaryFormatWriter
+internal class WinFormsBinaryFormatWriter : IBinaryFormatWriter
{
private static readonly string[] s_dataMemberName = ["Data"];
@@ -100,7 +101,7 @@ static bool Write(Stream stream, object value)
///
/// Writes the given Framework, WinForms or System.Drawing.Primitives if supported.
///
- public static bool TryWriteCommonObject(Stream stream, object value)
+ public bool TryWriteCommonObject(Stream stream, object value)
{
return TryWriteObject(stream, value)
|| BinaryFormatWriter.TryWriteDrawingPrimitivesObject(stream, value);
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/RichTextBox/RichTextBox.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/RichTextBox/RichTextBox.cs
index c14818b4c25..d01e6e7920a 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Controls/RichTextBox/RichTextBox.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Controls/RichTextBox/RichTextBox.cs
@@ -3270,7 +3270,7 @@ internal unsafe void WmReflectNotify(ref Message m)
{
fixed (char* b = buffer)
{
- uint length = PInvoke.DragQueryFile(endropfiles, iFile: 0, b, cch: (uint)buffer.Length);
+ uint length = PInvokeCore.DragQueryFile(endropfiles, iFile: 0, b, cch: (uint)buffer.Length);
if (length != 0)
{
// Try to load the file as RTF.
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Internal/Formatter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Internal/Formatter.cs
index ae5f8e125a0..2c6e0d43c31 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Internal/Formatter.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Internal/Formatter.cs
@@ -4,6 +4,7 @@
using System.ComponentModel;
using System.Globalization;
using System.Reflection;
+using TypeExtensions = System.Private.Windows.Core.TypeExtensions;
namespace System.Windows.Forms;
@@ -54,7 +55,7 @@ internal static class Formatter
Type oldTargetType = targetType;
- targetType = NullableUnwrap(targetType);
+ targetType = TypeExtensions.NullableUnwrap(targetType);
sourceConverter = NullableUnwrap(sourceConverter);
targetConverter = NullableUnwrap(targetConverter);
@@ -248,8 +249,8 @@ internal static class Formatter
Type oldTargetType = targetType;
- sourceType = NullableUnwrap(sourceType);
- targetType = NullableUnwrap(targetType);
+ sourceType = TypeExtensions.NullableUnwrap(sourceType);
+ targetType = TypeExtensions.NullableUnwrap(targetType);
sourceConverter = NullableUnwrap(sourceConverter);
targetConverter = NullableUnwrap(targetConverter);
@@ -524,20 +525,6 @@ public static bool IsNullData(object? value, object? dataSourceNullValue)
}
}
- ///
- /// Extract the inner type from a nullable type.
- ///
- public static Type NullableUnwrap(Type type)
- {
- if (type == s_stringType) // ...performance optimization for the most common case
- {
- return s_stringType;
- }
-
- Type? underlyingType = Nullable.GetUnderlyingType(type);
- return underlyingType ?? type;
- }
-
///
/// Extract the inner type converter from a nullable type converter
///
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Nrbf/WinFormsSerializationRecordExtensions.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Nrbf/WinFormsSerializationRecordExtensions.cs
index 487f6f7bc39..352bd28457f 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/Nrbf/WinFormsSerializationRecordExtensions.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/Nrbf/WinFormsSerializationRecordExtensions.cs
@@ -3,11 +3,6 @@
using System.Drawing;
using System.Formats.Nrbf;
-using System.Private.Windows;
-using System.Private.Windows.Core.BinaryFormat;
-using System.Reflection.Metadata;
-using System.Runtime.Serialization;
-using System.Text.Json;
namespace System.Windows.Forms.Nrbf;
@@ -58,52 +53,6 @@ public static bool TryGetBitmap(this SerializationRecord record, out object? bit
return true;
}
- ///
- /// Tries to deserialize this object if it was serialized as JSON.
- ///
- ///
- /// if the data was serialized as JSON. Otherwise, .
- ///
- /// If the data was supposed to be our , but was serialized incorrectly./>
- /// If an exception occurred while JSON deserializing.
- public static bool TryGetObjectFromJson(this SerializationRecord record, ITypeResolver resolver, out object? @object)
- {
- @object = null;
-
- if (record.TypeName.AssemblyName?.FullName != IJsonData.CustomAssemblyName)
- {
- // The data was not serialized as JSON.
- return false;
- }
-
- if (record is not ClassRecord types
- || types.GetRawValue("k__BackingField") is not SZArrayRecord byteData
- || types.GetRawValue("k__BackingField") is not string innerTypeFullName
- || !TypeName.TryParse(innerTypeFullName, out TypeName? serializedTypeName))
- {
- // This is supposed to be JsonData, but somehow the binary formatted data is corrupt.
- throw new SerializationException();
- }
-
- Type serializedType = resolver.GetType(serializedTypeName);
- if (!serializedType.IsAssignableTo(typeof(T)))
- {
- // Not the type the caller asked for so @object remains null.
- return true;
- }
-
- try
- {
- @object = JsonSerializer.Deserialize(byteData.GetArray());
- }
- catch (Exception ex)
- {
- throw new NotSupportedException(SR.ClipboardOrDragDrop_JsonDeserializationFailed, ex);
- }
-
- return true;
- }
-
///
/// Try to get a supported object. This supports common types used in WinForms that do not have type converters.
///
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
index d2364923796..86b2a140b18 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/Clipboard.cs
@@ -5,11 +5,9 @@
using System.Drawing;
using System.Formats.Nrbf;
using System.Reflection.Metadata;
-using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text.Json;
-using Windows.Win32.System.Com;
-using Com = Windows.Win32.System.Com;
+using System.Private.Windows.Core.OLE;
namespace System.Windows.Forms;
@@ -18,18 +16,21 @@ namespace System.Windows.Forms;
///
public static class Clipboard
{
+ private static DesktopClipboard? s_internalClipboard;
+
+ private static DesktopClipboard InternalClipboard => s_internalClipboard ??= new WinFormsClipboard();
///
/// Places non-persistent data on the system .
///
///
- public static void SetDataObject(object data) => SetDataObject(data, copy: false);
+ public static void SetDataObject(object data) => InternalClipboard.SetDataObject(data);
///
/// Overload that uses default values for retryTimes and retryDelay.
///
///
public static void SetDataObject(object data, bool copy) =>
- SetDataObject(data, copy, retryTimes: 10, retryDelay: 100);
+ InternalClipboard.SetDataObject(data, copy);
///
/// Places data on the system and uses copy to specify whether the data
@@ -47,38 +48,17 @@ public static unsafe void SetDataObject(object data, bool copy, int retryTimes,
throw new ThreadStateException(SR.ThreadMustBeSTA);
}
- ArgumentNullException.ThrowIfNull(data);
- ArgumentOutOfRangeException.ThrowIfNegative(retryTimes);
- ArgumentOutOfRangeException.ThrowIfNegative(retryDelay);
-
- // Always wrap the data if not already a DataObject. Mark whether the data is an IDataObject so we unwrap it properly on retrieval.
- DataObject dataObject = data as DataObject ?? new DataObject(data) { IsOriginalNotIDataObject = data is not IDataObject };
- using var iDataObject = ComHelpers.GetComScope(dataObject);
-
- HRESULT hr;
- int retry = retryTimes;
- while ((hr = PInvoke.OleSetClipboard(iDataObject)).Failed)
+ if (data is DataObject dataObject)
{
- if (--retry < 0)
- {
- throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
- }
-
- Thread.Sleep(millisecondsTimeout: retryDelay);
+ InternalClipboard.SetDataObject(dataObject._innerDataObject, copy, retryTimes, retryDelay);
}
-
- if (copy)
+ else if (data is IDataObject iDataObject)
+ {
+ InternalClipboard.SetDataObject(new DataObjectAdapter(iDataObject), copy, retryTimes, retryDelay);
+ }
+ else
{
- retry = retryTimes;
- while ((hr = PInvoke.OleFlushClipboard()).Failed)
- {
- if (--retry < 0)
- {
- throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
- }
-
- Thread.Sleep(millisecondsTimeout: retryDelay);
- }
+ InternalClipboard.SetDataObject(data, copy, retryTimes, retryDelay);
}
}
@@ -94,39 +74,18 @@ public static unsafe void SetDataObject(object data, bool copy, int retryTimes,
return Application.MessageLoop ? throw new ThreadStateException(SR.ThreadMustBeSTA) : null;
}
- int retryTimes = 10;
- using ComScope proxyDataObject = new(null);
- HRESULT hr;
- while ((hr = PInvoke.OleGetClipboard(proxyDataObject)).Failed)
+ IDataObjectDesktop? iDataObjectInner = InternalClipboard.GetDataObject();
+ if (iDataObjectInner is DataObjectAdapter adapter)
{
- if (--retryTimes < 0)
- {
- throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
- }
-
- Thread.Sleep(millisecondsTimeout: 100);
+ return adapter.OriginalDataObject;
}
-
- // OleGetClipboard always returns a proxy. The proxy forwards all IDataObject method calls to the real data object,
- // without giving out the real data object. If the data placed on the clipboard is not one of our CCWs or the clipboard
- // has been flushed, a wrapper around the proxy for us to use will be given. However, if the data placed on
- // the clipboard is one of our own and the clipboard has not been flushed, we need to retrieve the real data object
- // pointer in order to retrieve the original managed object via ComWrappers if an IDataObject was set on the clipboard.
- // To do this, we must query for an interface that is not known to the proxy e.g. IComCallableWrapper.
- // If we are able to query for IComCallableWrapper it means that the real data object is one of our CCWs and we've retrieved it successfully,
- // otherwise it is not ours and we will use the wrapped proxy.
- var realDataObject = proxyDataObject.TryQuery(out hr);
-
- if (hr.Succeeded
- && ComHelpers.TryUnwrapComWrapperCCW(realDataObject.AsUnknown, out DataObject? dataObject)
- && !dataObject.IsOriginalNotIDataObject)
+ else if (iDataObjectInner is DesktopDataObject dataObjectInner)
{
- // An IDataObject was given to us to place on the clipboard. We want to unwrap and return it instead of a proxy.
- return dataObject.TryUnwrapInnerIDataObject();
+ return new DataObject(dataObjectInner);
}
- // Original data given wasn't an IDataObject, give the proxy value back.
- return new DataObject(proxyDataObject.Value);
+ // There should be no other cases..
+ return null;
}
///
@@ -139,52 +98,36 @@ public static unsafe void Clear()
throw new ThreadStateException(SR.ThreadMustBeSTA);
}
- HRESULT hr;
- int retry = 10;
- while ((hr = PInvoke.OleSetClipboard(null)).Failed)
- {
- if (--retry < 0)
- {
-#pragma warning disable CA2201 // Do not raise reserved exception types
- throw new ExternalException(SR.ClipboardOperationFailed, (int)hr);
-#pragma warning restore CA2201
- }
-
- Thread.Sleep(millisecondsTimeout: 100);
- }
+ DesktopClipboard.Clear();
}
///
/// Indicates whether there is data on the Clipboard in the format.
///
- public static bool ContainsAudio() => ContainsData(DataFormats.WaveAudioConstant);
+ public static bool ContainsAudio() => InternalClipboard.ContainsAudio();
///
/// Indicates whether there is data on the Clipboard that is in the specified format
/// or can be converted to that format.
///
- public static bool ContainsData(string? format) =>
- !string.IsNullOrWhiteSpace(format) && ContainsData(format, autoConvert: false);
-
- private static bool ContainsData(string format, bool autoConvert) =>
- GetDataObject() is IDataObject dataObject && dataObject.GetDataPresent(format, autoConvert: autoConvert);
+ public static bool ContainsData(string? format) => InternalClipboard.ContainsData(format);
///
/// Indicates whether there is data on the Clipboard that is in the format
/// or can be converted to that format.
///
- public static bool ContainsFileDropList() => ContainsData(DataFormats.FileDrop, autoConvert: true);
+ public static bool ContainsFileDropList() => InternalClipboard.ContainsFileDropList();
///
/// Indicates whether there is data on the Clipboard that is in the format
/// or can be converted to that format.
///
- public static bool ContainsImage() => ContainsData(DataFormats.Bitmap, autoConvert: true);
+ public static bool ContainsImage() => InternalClipboard.ContainsImage();
///
/// Indicates whether there is text data on the Clipboard in format.
///
- public static bool ContainsText() => ContainsText(TextDataFormat.UnicodeText);
+ public static bool ContainsText() => InternalClipboard.ContainsText();
///
/// Indicates whether there is text data on the Clipboard in the format indicated by the specified
@@ -193,13 +136,13 @@ private static bool ContainsData(string format, bool autoConvert) =>
public static bool ContainsText(TextDataFormat format)
{
SourceGenerated.EnumValidator.Validate(format, nameof(format));
- return ContainsData(ConvertToDataFormats(format));
+ return InternalClipboard.ContainsText((DesktopTextDataFormat)format);
}
///
/// Retrieves an audio stream from the .
///
- public static Stream? GetAudioStream() => GetTypedDataIfAvailable(DataFormats.WaveAudioConstant);
+ public static Stream? GetAudioStream() => InternalClipboard.GetAudioStream();
///
/// Retrieves data from the in the specified format.
@@ -213,10 +156,7 @@ public static bool ContainsText(TextDataFormat format)
DiagnosticId = Obsoletions.ClipboardGetDataDiagnosticId,
UrlFormat = Obsoletions.SharedUrlFormat)]
public static object? GetData(string format) =>
- string.IsNullOrWhiteSpace(format) ? null : GetData(format, autoConvert: false);
-
- private static object? GetData(string format, bool autoConvert) =>
- GetDataObject() is IDataObject dataObject ? dataObject.GetData(format, autoConvert) : null;
+ InternalClipboard.GetData(format);
///
/// Retrieves data from the in the specified format if that data is of type .
@@ -360,11 +300,7 @@ public static bool ContainsText(TextDataFormat format)
Func resolver,
[NotNullWhen(true), MaybeNullWhen(false)] out T data)
{
- data = default;
- resolver.OrThrowIfNull();
-
- return GetTypedDataObject(format, out ITypedDataObject? typed)
- && typed.TryGetData(format, resolver, autoConvert: false, out data);
+ return InternalClipboard.TryGetData(format, resolver, out data);
}
///
@@ -372,47 +308,13 @@ public static bool ContainsText(TextDataFormat format)
string format,
[NotNullWhen(true), MaybeNullWhen(false)] out T data)
{
- data = default;
-
- return GetTypedDataObject(format, out ITypedDataObject? typed) && typed.TryGetData(format, out data);
- }
-
- private static bool GetTypedDataObject(
- string format,
- [NotNullWhen(true), MaybeNullWhen(false)] out ITypedDataObject typed)
- {
- typed = default;
- if (!DataObject.IsValidFormatAndType(format)
- || GetDataObject() is not { } dataObject)
- {
- // Invalid format or no object on the clipboard at all.
- return false;
- }
-
- if (dataObject is not ITypedDataObject typedDataObject)
- {
- throw new NotSupportedException(
- string.Format(SR.ITypeDataObject_Not_Implemented, dataObject.GetType().FullName));
- }
-
- typed = typedDataObject;
- return true;
+ return InternalClipboard.TryGetData(format, out data);
}
///
/// Retrieves a collection of file names from the .
///
- public static StringCollection GetFileDropList()
- {
- StringCollection result = [];
-
- if (GetTypedDataIfAvailable(DataFormats.FileDropConstant) is string[] strings)
- {
- result.AddRange(strings);
- }
-
- return result;
- }
+ public static StringCollection GetFileDropList() => InternalClipboard.GetFileDropList();
///
/// Retrieves a from the .
@@ -420,12 +322,12 @@ public static StringCollection GetFileDropList()
///
/// s are re-hydrated from a by reading a byte array.
///
- public static Image? GetImage() => GetTypedDataIfAvailable(DataFormats.Bitmap);
+ public static Image? GetImage() => InternalClipboard.GetTypedDataIfAvailable(DataFormats.Bitmap);
///
/// Retrieves text data from the in the format.
///
- public static string GetText() => GetText(TextDataFormat.UnicodeText);
+ public static string GetText() => InternalClipboard.GetText();
///
/// Retrieves text data from the in the format indicated by the specified
@@ -435,35 +337,18 @@ public static string GetText(TextDataFormat format)
{
SourceGenerated.EnumValidator.Validate(format, nameof(format));
- return GetTypedDataIfAvailable(ConvertToDataFormats(format)) is string text ? text : string.Empty;
- }
-
- private static T? GetTypedDataIfAvailable<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(string format)
- {
- IDataObject? data = GetDataObject();
- if (data is ITypedDataObject typed)
- {
- return typed.TryGetData(format, autoConvert: true, out T? value) ? value : default;
- }
-
- if (data is IDataObject dataObject)
- {
- return dataObject.GetData(format, autoConvert: true) is T value ? value : default;
- }
-
- return default;
+ return InternalClipboard.GetText((DesktopTextDataFormat)format);
}
///
/// Clears the and then adds data in the format.
///
- public static void SetAudio(byte[] audioBytes) => SetAudio(new MemoryStream(audioBytes.OrThrowIfNull()));
+ public static void SetAudio(byte[] audioBytes) => InternalClipboard.SetAudio(audioBytes);
///
/// Clears the and then adds data in the format.
///
- public static void SetAudio(Stream audioStream) =>
- SetDataObject(new DataObject(DataFormats.WaveAudioConstant, audioStream.OrThrowIfNull()), copy: true);
+ public static void SetAudio(Stream audioStream) => InternalClipboard.SetAudio(audioStream);
///
/// Clears the Clipboard and then adds data in the specified format.
@@ -475,13 +360,7 @@ public static void SetAudio(Stream audioStream) =>
///
public static void SetData(string format, object data)
{
- if (string.IsNullOrWhiteSpace(format.OrThrowIfNull()))
- {
- throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
- }
-
- // Note: We delegate argument checking to IDataObject.SetData, if it wants to do so.
- SetDataObject(new DataObject(format, data), copy: true);
+ InternalClipboard.SetData(format, data);
}
///
@@ -523,58 +402,28 @@ public static void SetData(string format, object data)
[RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
public static void SetDataAsJson(string format, T data)
{
- data.OrThrowIfNull(nameof(data));
- if (string.IsNullOrWhiteSpace(format.OrThrowIfNull()))
- {
- throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
- }
-
if (typeof(T) == typeof(DataObject))
{
throw new InvalidOperationException(string.Format(SR.ClipboardOrDragDrop_CannotJsonSerializeDataObject, nameof(SetDataObject)));
}
- DataObject dataObject = new();
- dataObject.SetDataAsJson(format, data);
- SetDataObject(dataObject, copy: true);
+ InternalClipboard.SetDataAsJson(format, data);
}
///
/// Clears the Clipboard and then adds a collection of file names in the format.
///
- public static void SetFileDropList(StringCollection filePaths)
- {
- if (filePaths.OrThrowIfNull().Count == 0)
- {
- throw new ArgumentException(SR.CollectionEmptyException);
- }
-
- // Validate the paths to make sure they don't contain invalid characters
- string[] filePathsArray = new string[filePaths.Count];
- filePaths.CopyTo(filePathsArray, 0);
-
- foreach (string path in filePathsArray)
- {
- // These are the only error states for Path.GetFullPath
- if (string.IsNullOrEmpty(path) || path.Contains('\0'))
- {
- throw new ArgumentException(string.Format(SR.Clipboard_InvalidPath, path, nameof(filePaths)));
- }
- }
-
- SetDataObject(new DataObject(DataFormats.FileDropConstant, autoConvert: true, filePathsArray), copy: true);
- }
+ public static void SetFileDropList(StringCollection filePaths) => InternalClipboard.SetFileDropList(filePaths);
///
/// Clears the Clipboard and then adds an in the format.
///
- public static void SetImage(Image image) =>
- SetDataObject(new DataObject(DataFormats.BitmapConstant, autoConvert: true, image.OrThrowIfNull()), copy: true);
+ public static void SetImage(Image image) => SetDataObject(new DataObject(DataFormats.Bitmap, autoConvert: true, image.OrThrowIfNull()), copy: true);
///
/// Clears the Clipboard and then adds text data in the format.
///
- public static void SetText(string text) => SetText(text, TextDataFormat.UnicodeText);
+ public static void SetText(string text) => InternalClipboard.SetText(text);
///
/// Clears the Clipboard and then adds text data in the format indicated by the specified
@@ -582,18 +431,7 @@ public static void SetImage(Image image) =>
///
public static void SetText(string text, TextDataFormat format)
{
- text.ThrowIfNullOrEmpty();
SourceGenerated.EnumValidator.Validate(format, nameof(format));
- SetDataObject(new DataObject(ConvertToDataFormats(format), text), copy: true);
+ InternalClipboard.SetText(text, (DesktopTextDataFormat)format);
}
-
- private static string ConvertToDataFormats(TextDataFormat format) => format switch
- {
- TextDataFormat.Text => DataFormats.Text,
- TextDataFormat.UnicodeText => DataFormats.UnicodeText,
- TextDataFormat.Rtf => DataFormats.Rtf,
- TextDataFormat.Html => DataFormats.Html,
- TextDataFormat.CommaSeparatedValue => DataFormats.CommaSeparatedValue,
- _ => DataFormats.UnicodeText,
- };
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataFormats.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataFormats.cs
index fdb407af0ff..a5a1038d306 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataFormats.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataFormats.cs
@@ -1,9 +1,7 @@
// 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.Runtime.InteropServices;
-using Windows.Win32.System.Ole;
+using System.Private.Windows.Core.OLE;
namespace System.Windows.Forms;
@@ -15,136 +13,117 @@ namespace System.Windows.Forms;
///
public static partial class DataFormats
{
- internal const string TextConstant = "Text";
- internal const string UnicodeTextConstant = "UnicodeText";
- internal const string DibConstant = "DeviceIndependentBitmap";
- internal const string BitmapConstant = "Bitmap";
- internal const string EmfConstant = "EnhancedMetafile";
- internal const string WmfConstant = "MetaFilePict";
- internal const string SymbolicLinkConstant = "SymbolicLink";
- internal const string DifConstant = "DataInterchangeFormat";
- internal const string TiffConstant = "TaggedImageFileFormat";
- internal const string OemTextConstant = "OEMText";
- internal const string PaletteConstant = "Palette";
- internal const string PenDataConstant = "PenData";
- internal const string RiffConstant = "RiffAudio";
- internal const string WaveAudioConstant = "WaveAudio";
- internal const string FileDropConstant = "FileDrop";
- internal const string LocaleConstant = "Locale";
- internal const string HtmlConstant = "HTML Format";
- internal const string RtfConstant = "Rich Text Format";
- internal const string CsvConstant = "Csv";
- internal const string StringConstant = "System.String";
- internal const string SerializableConstant = "WindowsForms10PersistentObject";
-
+#pragma warning disable IDE1006 // Naming Styles
///
/// Specifies the standard ANSI text format.
///
- public static readonly string Text = TextConstant;
+ public static readonly string Text = DesktopDataFormats.TextConstant;
///
/// Specifies the standard Windows Unicode text format.
///
- public static readonly string UnicodeText = UnicodeTextConstant;
+ public static readonly string UnicodeText = DesktopDataFormats.UnicodeTextConstant;
///
/// Specifies the Windows Device Independent Bitmap (DIB) format.
///
- public static readonly string Dib = DibConstant;
+ public static readonly string Dib = DesktopDataFormats.DibConstant;
///
/// Specifies a Windows bitmap format.
///
- public static readonly string Bitmap = BitmapConstant;
+ public static readonly string Bitmap = DesktopDataFormats.BitmapConstant;
///
/// Specifies the Windows enhanced metafile format.
///
- public static readonly string EnhancedMetafile = EmfConstant;
+ public static readonly string EnhancedMetafile = DesktopDataFormats.EmfConstant;
///
/// Specifies the Windows metafile format, which WinForms does not directly use.
///
- public static readonly string MetafilePict = WmfConstant;
+ public static readonly string MetafilePict = DesktopDataFormats.WmfConstant;
///
/// Specifies the Windows symbolic link format, which WinForms does not directly use.
///
- public static readonly string SymbolicLink = SymbolicLinkConstant;
+ public static readonly string SymbolicLink = DesktopDataFormats.SymbolicLinkConstant;
///
/// Specifies the Windows data interchange format, which WinForms does not directly use.
///
- public static readonly string Dif = DifConstant;
+ public static readonly string Dif = DesktopDataFormats.DifConstant;
///
/// Specifies the Tagged Image File Format (TIFF), which WinForms does not directly use.
///
- public static readonly string Tiff = TiffConstant;
+ public static readonly string Tiff = DesktopDataFormats.TiffConstant;
///
/// Specifies the standard Windows original equipment manufacturer (OEM) text format.
///
- public static readonly string OemText = OemTextConstant;
+ public static readonly string OemText = DesktopDataFormats.OemTextConstant;
///
/// Specifies the Windows palette format.
///
- public static readonly string Palette = PaletteConstant;
+ public static readonly string Palette = DesktopDataFormats.PaletteConstant;
///
/// Specifies the Windows pen data format, which consists of pen strokes for handwriting
/// software; WinForms does not use this format.
///
- public static readonly string PenData = PenDataConstant;
+ public static readonly string PenData = DesktopDataFormats.PenDataConstant;
///
/// Specifies the Resource Interchange File Format (RIFF) audio format, which WinForms does not directly use.
///
- public static readonly string Riff = RiffConstant;
+ public static readonly string Riff = DesktopDataFormats.RiffConstant;
///
/// Specifies the wave audio format, which Win Forms does not directly use.
///
- public static readonly string WaveAudio = WaveAudioConstant;
+ public static readonly string WaveAudio = DesktopDataFormats.WaveAudioConstant;
///
/// Specifies the Windows file drop format, which WinForms does not directly use.
///
- public static readonly string FileDrop = FileDropConstant;
+ public static readonly string FileDrop = DesktopDataFormats.FileDropConstant;
///
/// Specifies the Windows culture format, which WinForms does not directly use.
///
- public static readonly string Locale = LocaleConstant;
+ public static readonly string Locale = DesktopDataFormats.LocaleConstant;
///
/// Specifies text consisting of HTML data.
///
- public static readonly string Html = HtmlConstant;
+ public static readonly string Html = DesktopDataFormats.HtmlConstant;
///
/// Specifies text consisting of Rich Text Format (RTF) data.
///
- public static readonly string Rtf = RtfConstant;
+ public static readonly string Rtf = DesktopDataFormats.RtfConstant;
///
/// Specifies a comma-separated value (CSV) format, which is a common interchange format
/// used by spreadsheets. This format is not used directly by WinForms.
///
- public static readonly string CommaSeparatedValue = CsvConstant;
+ public static readonly string CommaSeparatedValue = DesktopDataFormats.CsvConstant;
///
/// Specifies the WinForms string class format, which WinForms uses to store string objects.
///
- public static readonly string StringFormat = StringConstant;
+ public static readonly string StringFormat = DesktopDataFormats.StringConstant;
///
/// Specifies a format that encapsulates any type of WinForms object.
///
- public static readonly string Serializable = SerializableConstant;
+ public static readonly string Serializable = DesktopDataFormats.SerializableConstant;
+#pragma warning restore IDE1006 // Naming Styles
private static Format[]? s_formatList;
+
private static int s_formatCount;
private static readonly Lock s_internalSyncObject = new();
@@ -155,11 +134,9 @@ public static partial class DataFormats
public static Format GetFormat(string format)
{
ArgumentException.ThrowIfNullOrWhiteSpace(format);
-
lock (s_internalSyncObject)
{
- EnsurePredefined();
-
+ s_formatList ??= [];
// It is much faster to do a case sensitive search here.
// So do the case sensitive compare first, then the expensive one.
for (int n = 0; n < s_formatCount; n++)
@@ -178,15 +155,9 @@ public static Format GetFormat(string format)
}
}
- // Need to add this format string
- uint formatId = PInvoke.RegisterClipboardFormat(format);
- if (formatId == 0)
- {
- throw new Win32Exception(Marshal.GetLastWin32Error(), SR.RegisterCFFailed);
- }
-
+ DesktopDataFormats.Format innerFormat = DesktopDataFormats.GetFormat(format);
EnsureFormatSpace(1);
- s_formatList[s_formatCount] = new Format(format, (int)formatId);
+ s_formatList[s_formatCount] = new(innerFormat.Name, innerFormat.Id);
return s_formatList[s_formatCount++];
}
}
@@ -194,48 +165,26 @@ public static Format GetFormat(string format)
///
/// Gets a with the Windows Clipboard numeric ID and name for the specified ID.
///
- public static Format GetFormat(int id) =>
- // Win32 uses an unsigned 16 bit type as a format ID, thus stripping off the leading bits.
- // Registered format IDs are in the range 0xC000 through 0xFFFF, thus it's important
- // to represent format as an unsigned type.
- GetFormat((ushort)(id & 0xFFFF));
-
- ///
- internal static unsafe Format GetFormat(ushort id)
+ public static Format GetFormat(int id)
{
lock (s_internalSyncObject)
{
- EnsurePredefined();
-
+ s_formatList ??= [];
+ // Win32 uses an unsigned 16 bit type as a format ID, thus stripping off the leading bits.
+ // Registered format IDs are in the range 0xC000 through 0xFFFF, thus it's important
+ // to represent format as an unsigned type.
+ ushort strippedId = (ushort)(id & 0xFFFF);
for (int n = 0; n < s_formatCount; n++)
{
- if (s_formatList[n].Id == id)
+ if (s_formatList[n].Id == strippedId)
{
return s_formatList[n];
}
}
- string? name = null;
-
- // The max length of the name of clipboard formats is equal to the max length
- // of a Win32 Atom of 255 chars. An additional null terminator character is added,
- // giving a required capacity of 256 chars.
- Span formatName = stackalloc char[256];
- fixed (char* pFormatName = formatName)
- {
- int length = PInvoke.GetClipboardFormatName(id, pFormatName, 256);
- if (length != 0)
- {
- name = formatName[..length].ToString();
- }
- }
-
- // This can happen if windows adds a standard format that we don't know about,
- // so we should play it safe.
- name ??= $"Format{id}";
-
+ DesktopDataFormats.Format innerFormat = DesktopDataFormats.GetFormat(strippedId);
EnsureFormatSpace(1);
- s_formatList[s_formatCount] = new Format(name, id);
+ s_formatList[s_formatCount] = new(innerFormat.Name, innerFormat.Id);
return s_formatList[s_formatCount++];
}
}
@@ -259,42 +208,4 @@ private static void EnsureFormatSpace(int size)
s_formatList = newList;
}
}
-
- ///
- /// Ensures that the Win32 predefined formats are setup in our format list.
- /// This is called anytime we need to search the list
- ///
- [MemberNotNull(nameof(s_formatList))]
- private static void EnsurePredefined()
- {
- if (s_formatCount == 0)
- {
- s_formatList =
- [
- // Text name Win32 format ID
- new(UnicodeTextConstant, (int)CLIPBOARD_FORMAT.CF_UNICODETEXT),
- new(TextConstant, (int)CLIPBOARD_FORMAT.CF_TEXT),
- new(BitmapConstant, (int)CLIPBOARD_FORMAT.CF_BITMAP),
- new(WmfConstant, (int)CLIPBOARD_FORMAT.CF_METAFILEPICT),
- new(EmfConstant, (int)CLIPBOARD_FORMAT.CF_ENHMETAFILE),
- new(DifConstant, (int)CLIPBOARD_FORMAT.CF_DIF),
- new(TiffConstant, (int)CLIPBOARD_FORMAT.CF_TIFF),
- new(OemTextConstant, (int)CLIPBOARD_FORMAT.CF_OEMTEXT),
- new(DibConstant, (int)CLIPBOARD_FORMAT.CF_DIB),
- new(PaletteConstant, (int)CLIPBOARD_FORMAT.CF_PALETTE),
- new(PenDataConstant, (int)CLIPBOARD_FORMAT.CF_PENDATA),
- new(RiffConstant, (int)CLIPBOARD_FORMAT.CF_RIFF),
- new(WaveAudioConstant, (int)CLIPBOARD_FORMAT.CF_WAVE),
- new(SymbolicLinkConstant, (int)CLIPBOARD_FORMAT.CF_SYLK),
- new(FileDropConstant, (int)CLIPBOARD_FORMAT.CF_HDROP),
- new(LocaleConstant, (int)CLIPBOARD_FORMAT.CF_LOCALE)
- ];
-
- s_formatCount = s_formatList.Length;
- }
- else
- {
- s_formatList ??= [];
- }
- }
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
index 160dd95f12b..4b5c47186b4 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.cs
@@ -3,8 +3,9 @@
using System.Collections.Specialized;
using System.Drawing;
-using System.Reflection.Metadata;
using System.Private.Windows;
+using System.Private.Windows.Core.OLE;
+using System.Reflection.Metadata;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text.Json;
@@ -23,24 +24,20 @@ public unsafe partial class DataObject :
ComTypes.IDataObject,
Com.IManagedWrapper
{
- private const string CF_DEPRECATED_FILENAME = "FileName";
- private const string CF_DEPRECATED_FILENAMEW = "FileNameW";
- private const string BitmapFullName = "System.Drawing.Bitmap";
-
- private readonly Composition _innerData;
+ internal readonly WinFormsDataObject _innerDataObject;
///
/// Initializes a new instance of the class, with the raw
/// and the managed data object the raw pointer is associated with.
///
///
- internal DataObject(Com.IDataObject* data) => _innerData = Composition.CreateFromNativeDataObject(data);
+ internal DataObject(Com.IDataObject* data) => _innerDataObject = new(data, this);
///
/// Initializes a new instance of the class, which can store arbitrary data.
///
///
- public DataObject() => _innerData = Composition.CreateFromWinFormsDataObject(new DataStore());
+ public DataObject() => _innerDataObject = new(this);
///
/// Initializes a new instance of the class, containing the specified data.
@@ -56,22 +53,13 @@ public unsafe partial class DataObject :
///
public DataObject(object data)
{
- if (data is DataObject dataObject)
- {
- _innerData = dataObject._innerData;
- }
- else if (data is IDataObject iDataObject)
+ if (data is IDataObject iDataObject and not DesktopDataObject)
{
- _innerData = Composition.CreateFromWinFormsDataObject(iDataObject);
- }
- else if (data is ComTypes.IDataObject comDataObject)
- {
- _innerData = Composition.CreateFromRuntimeDataObject(comDataObject);
+ _innerDataObject = new(new DataObjectAdapter(iDataObject), this);
}
else
{
- _innerData = Composition.CreateFromWinFormsDataObject(new DataStore());
- SetData(data);
+ _innerDataObject = new(data, this);
}
}
@@ -79,36 +67,23 @@ public DataObject(object data)
/// Initializes a new instance of the class, containing the specified data and its
/// associated format.
///
- public DataObject(string format, object data) : this() => SetData(format, data);
-
- internal DataObject(string format, bool autoConvert, object data) : this() => SetData(format, autoConvert, data);
-
- ///
- /// Flags that the original data was not a user passed .
- ///
- internal bool IsOriginalNotIDataObject { get; init; }
-
- ///
- /// Returns the inner data that the was created with if the original data implemented
- /// . Otherwise, returns this.
- /// This method should only be used if the was created for clipboard purposes.
- ///
- internal IDataObject TryUnwrapInnerIDataObject()
+ public DataObject(string format, object data)
{
- Debug.Assert(!IsOriginalNotIDataObject, "This method should only be used for clipboard purposes.");
- return _innerData.OriginalIDataObject is { } original ? original : this;
+ _innerDataObject = new(format, data, this);
}
- ///
- internal IDataObject? OriginalIDataObject => _innerData.OriginalIDataObject;
+ internal DataObject(string format, bool autoConvert, object data)
+ {
+ _innerDataObject = new(format, autoConvert, data, this);
+ }
///
[RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
- public void SetDataAsJson(string format, T data) => SetData(format, TryJsonSerialize(format, data));
+ public void SetDataAsJson(string format, T data) => _innerDataObject.SetDataAsJson(format, data);
///
[RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
- public void SetDataAsJson(T data) => SetData(typeof(T), TryJsonSerialize(typeof(T).FullName!, data));
+ public void SetDataAsJson(T data) => _innerDataObject.SetDataAsJson(data);
///
/// Stores the data in the specified format.
@@ -146,74 +121,7 @@ internal IDataObject TryUnwrapInnerIDataObject()
///
///
[RequiresUnreferencedCode("Uses default System.Text.Json behavior which is not trim-compatible.")]
- public void SetDataAsJson(string format, bool autoConvert, T data) => SetData(format, autoConvert, TryJsonSerialize(format, data));
-
- ///
- /// JSON serialize the data only if the format is not a restricted deserialization format and the data is not an intrinsic type.
- ///
- ///
- /// The passed in as is if the format is restricted. Otherwise the JSON serialized .
- ///
- private static object TryJsonSerialize(string format, T data)
- {
- if (string.IsNullOrWhiteSpace(format.OrThrowIfNull()))
- {
- throw new ArgumentException(SR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
- }
-
- data.OrThrowIfNull(nameof(data));
-
- if (typeof(T) == typeof(DataObject))
- {
- throw new InvalidOperationException(string.Format(SR.ClipboardOrDragDrop_CannotJsonSerializeDataObject, nameof(SetData)));
- }
-
- return IsRestrictedFormat(format) || Composition.Binder.IsKnownType()
- ? data
- : new JsonData() { JsonBytes = JsonSerializer.SerializeToUtf8Bytes(data) };
- }
-
- ///
- /// Check if the is one of the restricted formats, which formats that
- /// correspond to primitives or are pre-defined in the OS such as strings, bitmaps, and OLE types.
- ///
- private static bool IsRestrictedFormat(string format) => RestrictDeserializationToSafeTypes(format)
- || format is DataFormats.TextConstant
- or DataFormats.UnicodeTextConstant
- or DataFormats.RtfConstant
- or DataFormats.HtmlConstant
- or DataFormats.OemTextConstant
- or DataFormats.FileDropConstant
- or CF_DEPRECATED_FILENAME
- or CF_DEPRECATED_FILENAMEW;
-
- ///
- /// We are restricting binary serialization and deserialization of formats that represent strings, bitmaps or OLE types.
- ///
- /// format name
- /// - serialize only safe types, strings or bitmaps.
- ///
- ///
- /// These formats are also restricted in WPF
- /// https://github.com/dotnet/wpf/blob/db1ae73aae0e043326e2303b0820d361de04e751/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/dataobject.cs#L2801
- ///
- ///
- 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;
+ public void SetDataAsJson(string format, bool autoConvert, T data) => _innerDataObject.SetDataAsJson(format, autoConvert, data);
#region IDataObject
[Obsolete(
@@ -223,7 +131,8 @@ or DataFormats.PaletteConstant
UrlFormat = Obsoletions.SharedUrlFormat)]
public virtual object? GetData(string format, bool autoConvert)
{
- object? result = _innerData.GetData(format, autoConvert);
+ object? result = _innerDataObject.GetData(format, autoConvert);
+
// Avoid exposing our internal JsonData
return result is IJsonData ? null : result;
}
@@ -233,33 +142,33 @@ or DataFormats.PaletteConstant
error: false,
DiagnosticId = Obsoletions.ClipboardGetDataDiagnosticId,
UrlFormat = Obsoletions.SharedUrlFormat)]
- public virtual object? GetData(string format) => GetData(format, autoConvert: true);
+ public virtual object? GetData(string format) => _innerDataObject.GetData(format);
[Obsolete(
Obsoletions.DataObjectGetDataMessage,
error: false,
DiagnosticId = Obsoletions.ClipboardGetDataDiagnosticId,
UrlFormat = Obsoletions.SharedUrlFormat)]
- public virtual object? GetData(Type format) => format is null ? null : GetData(format.FullName!);
+ public virtual object? GetData(Type format) => _innerDataObject.GetData(format);
- public virtual bool GetDataPresent(string format, bool autoConvert) => _innerData.GetDataPresent(format, autoConvert);
+ public virtual bool GetDataPresent(string format, bool autoConvert) => _innerDataObject.GetDataPresent(format, autoConvert);
- public virtual bool GetDataPresent(string format) => GetDataPresent(format, autoConvert: true);
+ public virtual bool GetDataPresent(string format) => _innerDataObject.GetDataPresent(format);
- public virtual bool GetDataPresent(Type format) => format is not null && GetDataPresent(format.FullName!);
+ public virtual bool GetDataPresent(Type format) => _innerDataObject.GetDataPresent(format);
- public virtual string[] GetFormats(bool autoConvert) => _innerData.GetFormats(autoConvert);
+ public virtual string[] GetFormats(bool autoConvert) => _innerDataObject.GetFormats(autoConvert);
- public virtual string[] GetFormats() => GetFormats(autoConvert: true);
+ public virtual string[] GetFormats() => _innerDataObject.GetFormats();
public virtual void SetData(string format, bool autoConvert, object? data) =>
- _innerData.SetData(format, autoConvert, data);
+ _innerDataObject.SetData(format, autoConvert, data);
- public virtual void SetData(string format, object? data) => _innerData.SetData(format, data);
+ public virtual void SetData(string format, object? data) => _innerDataObject.SetData(format, data);
- public virtual void SetData(Type format, object? data) => _innerData.SetData(format, data);
+ public virtual void SetData(Type format, object? data) => _innerDataObject.SetData(format, data);
- public virtual void SetData(object? data) => _innerData.SetData(data);
+ public virtual void SetData(object? data) => _innerDataObject.SetData(data);
#endregion
#region ITypedDataObject
@@ -270,26 +179,23 @@ public virtual void SetData(string format, bool autoConvert, object? data) =>
bool autoConvert,
[NotNullWhen(true), MaybeNullWhen(false)] out T data)
{
- data = default;
- resolver.OrThrowIfNull();
-
- return TryGetDataInternal(format, resolver, autoConvert, out data);
+ return _innerDataObject.TryGetData(format, resolver, autoConvert, out data);
}
public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
string format,
bool autoConvert,
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- TryGetDataInternal(format, resolver: null, autoConvert, out data);
+ _innerDataObject.TryGetData(format, autoConvert, out data);
public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
string format,
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- TryGetDataInternal(format, resolver: null, autoConvert: true, out data);
+ _innerDataObject.TryGetData(format, out data);
public bool TryGetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- TryGetDataInternal(typeof(T).FullName!, resolver: null, autoConvert: true, out data);
+ _innerDataObject.TryGetData(out data);
#endregion
///
@@ -302,223 +208,128 @@ public virtual void SetData(string format, bool autoConvert, object? data) =>
Func? resolver,
bool autoConvert,
[NotNullWhen(true), MaybeNullWhen(false)] out T data) =>
- _innerData.TryGetData(format, resolver!, autoConvert, out data);
+ _innerDataObject.TryGetDataCore(format, resolver, autoConvert, out data);
+
+ internal bool TryGetDataCoreInternal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
+ string format,
+ Func? resolver,
+ bool autoConvert,
+ [NotNullWhen(true), MaybeNullWhen(false)] out T data) => TryGetDataCore(format, resolver, autoConvert, out data);
- public virtual bool ContainsAudio() => GetDataPresent(DataFormats.WaveAudioConstant, autoConvert: false);
+ public virtual bool ContainsAudio() => _innerDataObject.ContainsAudio();
- public virtual bool ContainsFileDropList() => GetDataPresent(DataFormats.FileDropConstant, autoConvert: true);
+ public virtual bool ContainsFileDropList() => _innerDataObject.ContainsFileDropList();
- public virtual bool ContainsImage() => GetDataPresent(DataFormats.BitmapConstant, autoConvert: true);
+ public virtual bool ContainsImage() => _innerDataObject.ContainsImage();
- public virtual bool ContainsText() => ContainsText(TextDataFormat.UnicodeText);
+ public virtual bool ContainsText() => _innerDataObject.ContainsText();
public virtual bool ContainsText(TextDataFormat format)
{
// Valid values are 0x0 to 0x4
SourceGenerated.EnumValidator.Validate(format, nameof(format));
- return GetDataPresent(ConvertToDataFormats(format), autoConvert: false);
+ return _innerDataObject.ContainsText((DesktopTextDataFormat)format);
}
#pragma warning disable WFDEV005 // Type or member is obsolete
- public virtual Stream? GetAudioStream() => GetData(DataFormats.WaveAudio, autoConvert: false) as Stream;
+ public virtual Stream? GetAudioStream() => _innerDataObject.GetAudioStream();
public virtual StringCollection GetFileDropList()
{
- StringCollection dropList = [];
- if (GetData(DataFormats.FileDropConstant, autoConvert: true) is string[] strings)
- {
- dropList.AddRange(strings);
- }
-
- return dropList;
+ return _innerDataObject.GetFileDropList();
}
- public virtual Image? GetImage() => GetData(DataFormats.Bitmap, autoConvert: true) as Image;
+ public virtual Image? GetImage() => GetData(DesktopDataFormats.BitmapConstant, autoConvert: true) as Image;
public virtual string GetText(TextDataFormat format)
{
// Valid values are 0x0 to 0x4
SourceGenerated.EnumValidator.Validate(format, nameof(format));
- return GetData(ConvertToDataFormats(format), autoConvert: false) is string text ? text : string.Empty;
+ return _innerDataObject.GetText((DesktopTextDataFormat)format);
}
#pragma warning restore WFDEV005
- public virtual string GetText() => GetText(TextDataFormat.UnicodeText);
+ public virtual string GetText() => _innerDataObject.GetText();
- public virtual void SetAudio(byte[] audioBytes) => SetAudio(new MemoryStream(audioBytes.OrThrowIfNull()));
+ public virtual void SetAudio(byte[] audioBytes) => _innerDataObject.SetAudio(audioBytes);
public virtual void SetAudio(Stream audioStream) =>
- SetData(DataFormats.WaveAudioConstant, autoConvert: false, audioStream.OrThrowIfNull());
+ _innerDataObject.SetAudio(audioStream);
public virtual void SetFileDropList(StringCollection filePaths)
{
- string[] strings = new string[filePaths.OrThrowIfNull().Count];
- filePaths.CopyTo(strings, 0);
- SetData(DataFormats.FileDropConstant, true, strings);
+ _innerDataObject.SetFileDropList(filePaths);
}
- public virtual void SetImage(Image image) => SetData(DataFormats.BitmapConstant, true, image.OrThrowIfNull());
+ public virtual void SetImage(Image image) => SetData(DesktopDataFormats.BitmapConstant, true, image.OrThrowIfNull());
- public virtual void SetText(string textData) => SetText(textData, TextDataFormat.UnicodeText);
+ public virtual void SetText(string textData) => _innerDataObject.SetText(textData);
public virtual void SetText(string textData, TextDataFormat format)
{
- textData.ThrowIfNullOrEmpty();
-
// Valid values are 0x0 to 0x4
SourceGenerated.EnumValidator.Validate(format, nameof(format));
- SetData(ConvertToDataFormats(format), false, textData);
- }
-
- private bool TryGetDataInternal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>(
- string format,
- Func? resolver,
- bool autoConvert,
- [NotNullWhen(true), MaybeNullWhen(false)] out T data)
- {
- data = default;
-
- if (!IsValidFormatAndType(format))
- {
- // Resolver implementation is specific to the overridden TryGetDataCore method,
- // can't validate if a non-null resolver is required for unbounded types.
- return false;
- }
-
- return TryGetDataCore(format, resolver, autoConvert, out data);
+ _innerDataObject.SetText(textData, (DesktopTextDataFormat)format);
}
- ///
- /// Verify if the specified format is valid and compatible with the specified type .
- ///
- internal static bool IsValidFormatAndType(string format)
- {
- if (string.IsNullOrWhiteSpace(format))
- {
- return false;
- }
-
- if (IsValidPredefinedFormatTypeCombination(format))
- {
- return true;
- }
-
- throw new NotSupportedException(string.Format(
- SR.ClipboardOrDragDrop_InvalidFormatTypeCombination,
- typeof(T).FullName, format));
-
- static bool IsValidPredefinedFormatTypeCombination(string format) => format switch
- {
- DataFormats.TextConstant
- or DataFormats.UnicodeTextConstant
- or DataFormats.StringConstant
- or DataFormats.RtfConstant
- or DataFormats.HtmlConstant
- or DataFormats.OemTextConstant => typeof(string) == typeof(T),
-
- DataFormats.FileDropConstant
- or CF_DEPRECATED_FILENAME
- or CF_DEPRECATED_FILENAMEW => typeof(string[]) == typeof(T),
-
- DataFormats.BitmapConstant or BitmapFullName =>
- typeof(Bitmap) == typeof(T) || typeof(Image) == typeof(T),
- _ => true
- };
- }
-
- private static string ConvertToDataFormats(TextDataFormat format) => format switch
- {
- TextDataFormat.UnicodeText => DataFormats.UnicodeTextConstant,
- TextDataFormat.Rtf => DataFormats.RtfConstant,
- TextDataFormat.Html => DataFormats.HtmlConstant,
- TextDataFormat.CommaSeparatedValue => DataFormats.CsvConstant,
- _ => DataFormats.UnicodeTextConstant,
- };
-
- ///
- /// Returns all the "synonyms" for the specified format.
- ///
- private static string[]? GetMappedFormats(string format) => format switch
- {
- null => null,
- DataFormats.TextConstant or DataFormats.UnicodeTextConstant or DataFormats.StringConstant =>
- [
- DataFormats.StringConstant,
- DataFormats.UnicodeTextConstant,
- DataFormats.TextConstant
- ],
- DataFormats.FileDropConstant or CF_DEPRECATED_FILENAME or CF_DEPRECATED_FILENAMEW =>
- [
- DataFormats.FileDropConstant,
- CF_DEPRECATED_FILENAMEW,
- CF_DEPRECATED_FILENAME
- ],
- DataFormats.BitmapConstant or BitmapFullName =>
- [
- BitmapFullName,
- DataFormats.BitmapConstant
- ],
- _ => [format]
- };
-
#region ComTypes.IDataObject
int ComTypes.IDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink pAdvSink, out int pdwConnection) =>
- _innerData.DAdvise(ref pFormatetc, advf, pAdvSink, out pdwConnection);
+ ((ComTypes.IDataObject)_innerDataObject).DAdvise(ref pFormatetc, advf, pAdvSink, out pdwConnection);
- void ComTypes.IDataObject.DUnadvise(int dwConnection) => _innerData.DUnadvise(dwConnection);
+ void ComTypes.IDataObject.DUnadvise(int dwConnection) => ((ComTypes.IDataObject)_innerDataObject).DUnadvise(dwConnection);
int ComTypes.IDataObject.EnumDAdvise(out IEnumSTATDATA? enumAdvise) =>
- _innerData.EnumDAdvise(out enumAdvise);
+ ((ComTypes.IDataObject)_innerDataObject).EnumDAdvise(out enumAdvise);
IEnumFORMATETC ComTypes.IDataObject.EnumFormatEtc(DATADIR dwDirection) =>
- _innerData.EnumFormatEtc(dwDirection);
+ ((ComTypes.IDataObject)_innerDataObject).EnumFormatEtc(dwDirection);
int ComTypes.IDataObject.GetCanonicalFormatEtc(ref FORMATETC pformatetcIn, out FORMATETC pformatetcOut) =>
- _innerData.GetCanonicalFormatEtc(ref pformatetcIn, out pformatetcOut);
+ ((ComTypes.IDataObject)_innerDataObject).GetCanonicalFormatEtc(ref pformatetcIn, out pformatetcOut);
void ComTypes.IDataObject.GetData(ref FORMATETC formatetc, out STGMEDIUM medium) =>
- _innerData.GetData(ref formatetc, out medium);
+ ((ComTypes.IDataObject)_innerDataObject).GetData(ref formatetc, out medium);
void ComTypes.IDataObject.GetDataHere(ref FORMATETC formatetc, ref STGMEDIUM medium) =>
- _innerData.GetDataHere(ref formatetc, ref medium);
+ ((ComTypes.IDataObject)_innerDataObject).GetDataHere(ref formatetc, ref medium);
int ComTypes.IDataObject.QueryGetData(ref FORMATETC formatetc) =>
- _innerData.QueryGetData(ref formatetc);
+ ((ComTypes.IDataObject)_innerDataObject).QueryGetData(ref formatetc);
void ComTypes.IDataObject.SetData(ref FORMATETC pFormatetcIn, ref STGMEDIUM pmedium, bool fRelease) =>
- _innerData.SetData(ref pFormatetcIn, ref pmedium, fRelease);
+ ((ComTypes.IDataObject)_innerDataObject).SetData(ref pFormatetcIn, ref pmedium, fRelease);
#endregion
#region Com.IDataObject.Interface
HRESULT Com.IDataObject.Interface.DAdvise(Com.FORMATETC* pformatetc, uint advf, Com.IAdviseSink* pAdvSink, uint* pdwConnection) =>
- _innerData.DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
+ ((Com.IDataObject.Interface)_innerDataObject).DAdvise(pformatetc, advf, pAdvSink, pdwConnection);
HRESULT Com.IDataObject.Interface.DUnadvise(uint dwConnection) =>
- _innerData.DUnadvise(dwConnection);
+ ((Com.IDataObject.Interface)_innerDataObject).DUnadvise(dwConnection);
HRESULT Com.IDataObject.Interface.EnumDAdvise(Com.IEnumSTATDATA** ppenumAdvise) =>
- _innerData.EnumDAdvise(ppenumAdvise);
+ ((Com.IDataObject.Interface)_innerDataObject).EnumDAdvise(ppenumAdvise);
HRESULT Com.IDataObject.Interface.EnumFormatEtc(uint dwDirection, Com.IEnumFORMATETC** ppenumFormatEtc) =>
- _innerData.EnumFormatEtc(dwDirection, ppenumFormatEtc);
+ ((Com.IDataObject.Interface)_innerDataObject).EnumFormatEtc(dwDirection, ppenumFormatEtc);
HRESULT Com.IDataObject.Interface.GetData(Com.FORMATETC* pformatetcIn, Com.STGMEDIUM* pmedium) =>
- _innerData.GetData(pformatetcIn, pmedium);
+ ((Com.IDataObject.Interface)_innerDataObject).GetData(pformatetcIn, pmedium);
HRESULT Com.IDataObject.Interface.GetDataHere(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium) =>
- _innerData.GetDataHere(pformatetc, pmedium);
+ ((Com.IDataObject.Interface)_innerDataObject).GetDataHere(pformatetc, pmedium);
HRESULT Com.IDataObject.Interface.QueryGetData(Com.FORMATETC* pformatetc) =>
- _innerData.QueryGetData(pformatetc);
+ ((Com.IDataObject.Interface)_innerDataObject).QueryGetData(pformatetc);
HRESULT Com.IDataObject.Interface.GetCanonicalFormatEtc(Com.FORMATETC* pformatectIn, Com.FORMATETC* pformatetcOut) =>
- _innerData.GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
+ ((Com.IDataObject.Interface)_innerDataObject).GetCanonicalFormatEtc(pformatectIn, pformatetcOut);
HRESULT Com.IDataObject.Interface.SetData(Com.FORMATETC* pformatetc, Com.STGMEDIUM* pmedium, BOOL fRelease) =>
- _innerData.SetData(pformatetc, pmedium, fRelease);
+ ((Com.IDataObject.Interface)_innerDataObject).SetData(pformatetc, pmedium, fRelease);
#endregion
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectAdapter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectAdapter.cs
new file mode 100644
index 00000000000..6caf276b079
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectAdapter.cs
@@ -0,0 +1,38 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Private.Windows.Core.OLE;
+using System.Reflection.Metadata;
+
+namespace System.Windows.Forms;
+
+///
+/// Adapter class so that our internal OLE classes can understand public IDataObject interfaces.
+///
+internal class DataObjectAdapter : ITypedDataObjectDesktop
+{
+ public DataObjectAdapter(IDataObject original)
+ {
+ OriginalDataObject = original.OrThrowIfNull();
+ }
+
+ internal IDataObject OriginalDataObject { get; init; }
+
+ object? IDataObjectDesktop.GetData(string format, bool autoConvert) => OriginalDataObject.GetData(format, autoConvert);
+ object? IDataObjectDesktop.GetData(string format) => OriginalDataObject.GetData(format);
+ object? IDataObjectDesktop.GetData(Type format) => OriginalDataObject.GetData(format);
+ bool IDataObjectDesktop.GetDataPresent(string format, bool autoConvert) => OriginalDataObject.GetDataPresent(format, autoConvert);
+ bool IDataObjectDesktop.GetDataPresent(string format) => OriginalDataObject.GetDataPresent(format);
+ bool IDataObjectDesktop.GetDataPresent(Type format) => OriginalDataObject.GetDataPresent(format);
+ string[] IDataObjectDesktop.GetFormats(bool autoConvert) => OriginalDataObject.GetFormats(autoConvert);
+ string[] IDataObjectDesktop.GetFormats() => OriginalDataObject.GetFormats();
+ void IDataObjectDesktop.SetData(string format, bool autoConvert, object? data) => OriginalDataObject.SetData(format, autoConvert, data);
+ void IDataObjectDesktop.SetData(string format, object? data) => OriginalDataObject.SetData(format, data);
+ void IDataObjectDesktop.SetData(Type format, object? data) => OriginalDataObject.SetData(format, data);
+ void IDataObjectDesktop.SetData(object? data) => OriginalDataObject.SetData(data);
+
+ public bool TryGetData<[DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] T>([MaybeNullWhen(false), NotNullWhen(true)] out T data) => OriginalDataObject.TryGetData(out data);
+ public bool TryGetData<[DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] T>(string format, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => OriginalDataObject.TryGetData(format, out data);
+ public bool TryGetData<[DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] T>(string format, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => OriginalDataObject.TryGetData(format, autoConvert, out data);
+ public bool TryGetData<[DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] T>(string format, Func resolver, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data) => OriginalDataObject.TryGetData(format, resolver, autoConvert, out data);
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectExtensions.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectExtensions.cs
index 57b78ef4d23..b92503467cf 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectExtensions.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObjectExtensions.cs
@@ -1,11 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using CoreSR = System.Private.Windows.Core.Resources.SR;
+
namespace System.Windows.Forms;
-///
-/// Extension methods for data objects.
-///
public static class DataObjectExtensions
{
private static ITypedDataObject GetTypedDataObjectOrThrow(IDataObject dataObject)
@@ -15,7 +14,7 @@ private static ITypedDataObject GetTypedDataObjectOrThrow(IDataObject dataObject
if (dataObject is not ITypedDataObject typed)
{
throw new NotSupportedException(string.Format(
- SR.ITypeDataObject_Not_Implemented,
+ CoreSR.ITypeDataObject_Not_Implemented,
dataObject.GetType().FullName));
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropHelper.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropHelper.cs
index 3bb6e436816..38201fdffeb 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropHelper.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DragDropHelper.cs
@@ -10,6 +10,8 @@
using Com = Windows.Win32.System.Com;
using ComTypes = System.Runtime.InteropServices.ComTypes;
using IComDataObject = System.Runtime.InteropServices.ComTypes.IDataObject;
+using DragDropFormat = System.Private.Windows.Core.OLE.DragDropFormat;
+using CoreSR = System.Private.Windows.Core.Resources.SR;
namespace System.Windows.Forms;
@@ -19,32 +21,6 @@ namespace System.Windows.Forms;
///
internal static unsafe class DragDropHelper
{
- ///
- /// A format used internally by the drag image manager.
- ///
- internal const string DRAGCONTEXT = "DragContext";
-
- ///
- /// A format that contains the drag image bottom-up device-independent bitmap bits.
- ///
- internal const string DRAGIMAGEBITS = "DragImageBits";
-
- ///
- /// A format that contains the value passed to
- /// and controls whether to allow text specified in to be displayed on the drag image.
- ///
- internal const string DRAGSOURCEHELPERFLAGS = "DragSourceHelperFlags";
-
- ///
- /// A format used to identify an object's drag image window so that it's visual information can be updated dynamically.
- ///
- internal const string DRAGWINDOW = "DragWindow";
-
- ///
- /// A format that is non-zero if the drop target supports drag images.
- ///
- internal const string ISSHOWINGLAYERED = "IsShowingLayered";
-
///
/// A format that is non-zero if the drop target supports drop description text.
///
@@ -160,7 +136,7 @@ private static unsafe bool GetBooleanFormat(IComDataObject dataObject, string fo
{
ComTypes.FORMATETC formatEtc = new()
{
- cfFormat = (short)PInvoke.RegisterClipboardFormat(format),
+ cfFormat = (short)PInvokeCore.RegisterClipboardFormat(format),
dwAspect = ComTypes.DVASPECT.DVASPECT_CONTENT,
lindex = -1,
ptd = IntPtr.Zero,
@@ -181,7 +157,7 @@ private static unsafe bool GetBooleanFormat(IComDataObject dataObject, string fo
{
PInvokeCore.GlobalUnlock((HGLOBAL)medium.unionmember);
var comMedium = (STGMEDIUM)medium;
- PInvoke.ReleaseStgMedium(ref comMedium);
+ PInvokeCore.ReleaseStgMedium(ref comMedium);
}
}
@@ -191,28 +167,7 @@ private static unsafe bool GetBooleanFormat(IComDataObject dataObject, string fo
///
/// if is in a drag-and-drop loop; otherwise .
///
- public static unsafe bool IsInDragLoop(IDataObject dataObject)
- {
- ArgumentNullException.ThrowIfNull(dataObject);
-
- if (dataObject.GetDataPresent(PInvoke.CFSTR_INDRAGLOOP)
- && dataObject.GetData(PInvoke.CFSTR_INDRAGLOOP) is DragDropFormat dragDropFormat)
- {
- try
- {
- void* basePtr = PInvokeCore.GlobalLock(dragDropFormat.Medium.hGlobal);
- return (basePtr is not null) && (*(BOOL*)basePtr == true);
- }
- finally
- {
- PInvokeCore.GlobalUnlock(dragDropFormat.Medium.hGlobal);
- }
- }
- else
- {
- return false;
- }
- }
+ public static unsafe bool IsInDragLoop(IDataObject dataObject) => Private.Windows.Core.OLE.DragDropHelper.IsInDragLoop(dataObject is null ? null! : new DataObjectAdapter(dataObject)!);
///
/// Determines whether the data object is in a drag loop.
@@ -220,21 +175,7 @@ public static unsafe bool IsInDragLoop(IDataObject dataObject)
///
/// if is in a drag-and-drop loop; otherwise .
///
- public static bool IsInDragLoop(IComDataObject dataObject) => GetBooleanFormat(dataObject, PInvoke.CFSTR_INDRAGLOOP);
-
- ///
- /// Determines whether the specified format is a drag loop format.
- ///
- ///
- /// if is a drag loop format; otherwise .
- ///
- public static bool IsInDragLoopFormat(FORMATETC format)
- {
- string formatName = DataFormats.GetFormat(format.cfFormat).Name;
- return formatName.Equals(DRAGCONTEXT) || formatName.Equals(DRAGIMAGEBITS) || formatName.Equals(DRAGSOURCEHELPERFLAGS)
- || formatName.Equals(DRAGWINDOW) || formatName.Equals(PInvoke.CFSTR_DROPDESCRIPTION) || formatName.Equals(PInvoke.CFSTR_INDRAGLOOP)
- || formatName.Equals(ISSHOWINGLAYERED) || formatName.Equals(ISSHOWINGTEXT) || formatName.Equals(USINGDEFAULTDRAGIMAGE);
- }
+ public static bool IsInDragLoop(IComDataObject dataObject) => GetBooleanFormat(dataObject, PInvokeCore.CFSTR_INDRAGLOOP);
///
/// Releases formats used by the drag-and-drop helper.
@@ -265,7 +206,7 @@ private static unsafe void SetBooleanFormat(IComDataObject dataObject, string fo
ComTypes.FORMATETC formatEtc = new()
{
- cfFormat = (short)PInvoke.RegisterClipboardFormat(format),
+ cfFormat = (short)PInvokeCore.RegisterClipboardFormat(format),
dwAspect = ComTypes.DVASPECT.DVASPECT_CONTENT,
lindex = -1,
ptd = IntPtr.Zero,
@@ -281,7 +222,7 @@ private static unsafe void SetBooleanFormat(IComDataObject dataObject, string fo
if (medium.unionmember == IntPtr.Zero)
{
- throw new Win32Exception(Marshal.GetLastSystemError(), SR.ExternalException);
+ throw new Win32Exception(Marshal.GetLastSystemError(), CoreSR.ExternalException);
}
void* basePtr = PInvokeCore.GlobalLock((HGLOBAL)medium.unionmember);
@@ -289,7 +230,7 @@ private static unsafe void SetBooleanFormat(IComDataObject dataObject, string fo
{
PInvokeCore.GlobalFree((HGLOBAL)medium.unionmember);
medium.unionmember = IntPtr.Zero;
- throw new Win32Exception(Marshal.GetLastSystemError(), SR.ExternalException);
+ throw new Win32Exception(Marshal.GetLastSystemError(), CoreSR.ExternalException);
}
*(BOOL*)basePtr = value;
@@ -421,7 +362,7 @@ public static unsafe void SetDropDescription(
ComTypes.FORMATETC formatEtc = new()
{
- cfFormat = (short)PInvoke.RegisterClipboardFormat(PInvoke.CFSTR_DROPDESCRIPTION),
+ cfFormat = (short)PInvokeCore.RegisterClipboardFormat(PInvokeCore.CFSTR_DROPDESCRIPTION),
dwAspect = ComTypes.DVASPECT.DVASPECT_CONTENT,
lindex = -1,
ptd = IntPtr.Zero,
@@ -437,7 +378,7 @@ public static unsafe void SetDropDescription(
if (medium.unionmember == IntPtr.Zero)
{
- throw new Win32Exception(Marshal.GetLastSystemError(), SR.ExternalException);
+ throw new Win32Exception(Marshal.GetLastSystemError(), CoreSR.ExternalException);
}
void* basePtr = PInvokeCore.GlobalLock((HGLOBAL)medium.unionmember);
@@ -445,7 +386,7 @@ public static unsafe void SetDropDescription(
{
PInvokeCore.GlobalFree((HGLOBAL)medium.unionmember);
medium.unionmember = IntPtr.Zero;
- throw new Win32Exception(Marshal.GetLastSystemError(), SR.ExternalException);
+ throw new Win32Exception(Marshal.GetLastSystemError(), CoreSR.ExternalException);
}
DROPDESCRIPTION* pDropDescription = (DROPDESCRIPTION*)basePtr;
@@ -478,7 +419,7 @@ public static unsafe void SetDropDescription(
///
public static void SetInDragLoop(IComDataObject dataObject, bool inDragLoop)
{
- SetBooleanFormat(dataObject, PInvoke.CFSTR_INDRAGLOOP, inDragLoop);
+ SetBooleanFormat(dataObject, PInvokeCore.CFSTR_INDRAGLOOP, inDragLoop);
// If drag loop is over, release the drag and drop formats.
if (!inDragLoop)
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DropTarget.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DropTarget.cs
index 5cc5f3e7e80..fb4a1dc23f0 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DropTarget.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DropTarget.cs
@@ -50,8 +50,8 @@ private void ClearDropDescription()
if (ComWrappers.TryGetObject(unknown, out object? obj) && obj is IDataObject dataObject)
{
// If the original data object implemented IDataObject, we might've wrapped it. We need to give the original back out.
- return dataObject is DataObject winFormsDataObject && winFormsDataObject.OriginalIDataObject is { } originalDataObject
- ? originalDataObject
+ return dataObject is DataObject winFormsDataObject && winFormsDataObject._innerDataObject.OriginalIDataObject is DataObjectAdapter adapter
+ ? adapter.OriginalDataObject
: dataObject;
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsClipboard.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsClipboard.cs
new file mode 100644
index 00000000000..f85c1ea8a13
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsClipboard.cs
@@ -0,0 +1,47 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Private.Windows.Core.OLE;
+using Com = Windows.Win32.System.Com;
+using CoreSR = System.Private.Windows.Core.Resources.SR;
+
+namespace System.Windows.Forms;
+internal class WinFormsClipboard : DesktopClipboard
+{
+ public override void EnsureStaThread()
+ {
+ if (Application.OleRequired() != ApartmentState.STA)
+ {
+ throw new ThreadStateException(SR.ThreadMustBeSTA);
+ }
+ }
+
+ internal override DesktopDataObject CreateDataObject(string format, object data) => new WinFormsDataObject(format, data);
+ internal override DesktopDataObject CreateDataObject() => new WinFormsDataObject();
+ internal override DesktopDataObject CreateDataObject(string format, bool autoConvert, object data) => new WinFormsDataObject(format, autoConvert, data);
+
+ internal override bool GetTypedDataObject(string format, [MaybeNullWhen(false), NotNullWhen(true)] out ITypedDataObjectDesktop typed)
+ {
+ typed = default;
+ if (!WinFormsDataObject.IsValidFormatAndTypeCore(format)
+ || GetDataObject() is not { } dataObject)
+ {
+ // Invalid format or no object on the clipboard at all.
+ return false;
+ }
+
+ if (dataObject is not ITypedDataObjectDesktop typedDataObject)
+ {
+ throw new NotSupportedException(string.Format(CoreSR.ITypeDataObject_Not_Implemented, dataObject.GetType().FullName));
+ }
+
+ typed = typedDataObject;
+ return true;
+ }
+
+ internal override unsafe DesktopDataObject WrapForGetDataObject(Com.IDataObject* dataObject) =>
+ new WinFormsDataObject(dataObject);
+
+ internal override DesktopDataObject WrapForSetDataObject(object data) =>
+ data as DesktopDataObject ?? new WinFormsDataObject(data is IDataObject ido ? new DataObjectAdapter(ido) : data) { IsOriginalNotIDataObject = data is not IDataObject };
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.BinaryFormatUtilities.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.BinaryFormatUtilities.cs
similarity index 53%
rename from src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.BinaryFormatUtilities.cs
rename to src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.BinaryFormatUtilities.cs
index 05f7c33a935..4f7c7556f3d 100644
--- a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/DataObject.Composition.BinaryFormatUtilities.cs
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.BinaryFormatUtilities.cs
@@ -3,71 +3,26 @@
using System.Formats.Nrbf;
using System.Private.Windows.Core.BinaryFormat;
+using System.Private.Windows.Core.Nrbf;
+using System.Private.Windows.Core.OLE;
using System.Reflection.Metadata;
using System.Runtime.ExceptionServices;
using System.Runtime.Serialization;
-using System.Runtime.Serialization.Formatters;
-using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms.BinaryFormat;
using System.Windows.Forms.Nrbf;
-using System.Windows.Forms.Primitives;
+using CoreSR = System.Private.Windows.Core.Resources.SR;
namespace System.Windows.Forms;
-public unsafe partial class DataObject
+internal partial class WinFormsDataObject
{
internal unsafe partial class Composition
{
- internal static class BinaryFormatUtilities
+ internal class BinaryFormatUtilities : DesktopDataObject.Composition.BinaryFormatUtilities
{
- internal static void WriteObjectToStream(MemoryStream stream, object data, bool restrictSerialization)
- {
- long position = stream.Position;
-
- try
- {
- if (WinFormsBinaryFormatWriter.TryWriteCommonObject(stream, data))
- {
- return;
- }
- }
- catch (Exception ex) when (!ex.IsCriticalException())
- {
- // Being extra cautious here, but the Try method above should never throw in normal circumstances.
- Debug.Fail($"Unexpected exception writing binary formatted data. {ex.Message}");
- }
-
- if (restrictSerialization)
- {
- throw new SerializationException(string.Format(SR.UnexpectedTypeForClipboardFormat, data.GetType().FullName));
- }
-
- // This check is to help in trimming scenarios with a trim warning on a call to
- // BinaryFormatter.Serialize(), which has a RequiresUnreferencedCode annotation.
- // If the flag is false, the trimmer will not generate a warning, since BinaryFormatter.Serialize(),
- // will not be called,
- // If the flag is true, the trimmer will generate a warning for calling a method that has a
- // RequiresUnreferencedCode annotation.
- if (!EnableUnsafeBinaryFormatterInNativeObjectSerialization)
- {
- throw new NotSupportedException(SR.BinaryFormatterNotSupported);
- }
-
- if (!LocalAppContextSwitches.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization)
- {
- throw new NotSupportedException(SR.BinaryFormatter_NotSupported_InClipboardOrDragDrop);
- }
+ public override IBinaryFormatWriter GetBinaryFormatWriter() => new WinFormsBinaryFormatWriter();
- stream.Position = position;
-#pragma warning disable SYSLIB0011 // Type or member is obsolete
- new BinaryFormatter().Serialize(stream, data);
-#pragma warning restore SYSLIB0011
- }
-
- internal static object? ReadObjectFromStream(
- MemoryStream stream,
- Func? resolver,
- bool legacyMode)
+ internal override object? ReadObjectFromStream(MemoryStream stream, Func? resolver, bool legacyMode)
{
long startPosition = stream.Position;
SerializationRecord? record;
@@ -82,7 +37,7 @@ record = stream.Decode(out recordMap);
{
// Couldn't parse for some reason, let BinaryFormatter handle the legacy invocation.
// The typed APIs can't compare the specified type when the root record is not available.
- if (legacyMode && LocalAppContextSwitches.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization)
+ if (legacyMode && LocalAppContextSwitchesCore.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization)
{
stream.Position = startPosition;
return ReadObjectWithBinaryFormatter(stream, binder);
@@ -128,10 +83,10 @@ record = stream.Decode(out recordMap);
return value;
}
- if (!LocalAppContextSwitches.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization)
+ if (!LocalAppContextSwitchesCore.ClipboardDragDropEnableUnsafeBinaryFormatterSerialization)
{
throw new NotSupportedException(string.Format(
- SR.BinaryFormatter_NotSupported_InClipboardOrDragDrop_UseTypedAPI,
+ CoreSR.BinaryFormatter_NotSupported_InClipboardOrDragDrop_UseTypedAPI,
typeof(T).FullName));
}
@@ -141,7 +96,7 @@ record = stream.Decode(out recordMap);
// dependencies to indeterminate state)
// But it usually requires a resolver. Resolver is not available in the legacy mode,
// so we will fall back to BinaryFormatter in that case.
- if (LocalAppContextSwitches.ClipboardDragDropEnableNrbfSerialization)
+ if (LocalAppContextSwitchesCore.ClipboardDragDropEnableNrbfSerialization)
{
try
{
@@ -156,10 +111,7 @@ record = stream.Decode(out recordMap);
return ReadObjectWithBinaryFormatter(stream, binder);
}
- internal static object? ReadRestrictedObjectFromStream(
- MemoryStream stream,
- Func? resolver,
- bool legacyMode)
+ internal override object? ReadRestrictedObjectFromStream(MemoryStream stream, Func? resolver, bool legacyMode)
{
long startPosition = stream.Position;
SerializationRecord? record;
@@ -196,45 +148,6 @@ record = stream.Decode();
? value
: throw new RestrictedTypeDeserializationException(SR.UnexpectedClipboardType);
}
-
- private static bool TypeNameIsAssignableToType(TypeName typeName, Type type, ITypeResolver resolver)
- {
- try
- {
- return resolver.GetType(typeName)?.IsAssignableTo(type) == true;
- }
- catch (Exception ex) when (!ex.IsCriticalException())
- {
- // Clipboard contains a wrong type, we want the typed API to return false to the caller.
- }
-
- return false;
- }
-
- private static object? ReadObjectWithBinaryFormatter(MemoryStream stream, SerializationBinder binder)
- {
- // This check is to help in trimming scenarios with a trim warning on a call to BinaryFormatter.Deserialize(),
- // which has a RequiresUnreferencedCode annotation.
- // If the flag is false, the trimmer will not generate a warning, since BinaryFormatter.Deserialize() will not be called,
- // If the flag is true, the trimmer will generate a warning for calling a method that has a RequiresUnreferencedCode annotation.
- if (!EnableUnsafeBinaryFormatterInNativeObjectSerialization)
- {
- throw new NotSupportedException(SR.BinaryFormatterNotSupported);
- }
-
-#pragma warning disable SYSLIB0011, SYSLIB0050 // Type or member is obsolete
-#pragma warning disable CA2300 // Do not use insecure deserializer BinaryFormatter
-#pragma warning disable CA2302 // Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize
- // cs/dangerous-binary-deserialization
- return new BinaryFormatter()
- {
- Binder = binder,
- AssemblyFormat = FormatterAssemblyStyle.Simple
- }.Deserialize(stream); // CodeQL[SM03722] : BinaryFormatter is intended to be used as a fallback for unsupported types. Users must explicitly opt into this behavior.
-#pragma warning restore CA2300
-#pragma warning restore CA2302
-#pragma warning restore SYSLIB0050, SYSLIB0011
- }
}
}
}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.Binder.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.Binder.cs
new file mode 100644
index 00000000000..4afd5cc87f2
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.Binder.cs
@@ -0,0 +1,23 @@
+// 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.Core.OLE;
+using System.Reflection.Metadata;
+
+namespace System.Windows.Forms;
+
+internal partial class WinFormsDataObject
+{
+ internal unsafe partial class Composition
+ {
+ internal sealed class Binder : DesktopDataObject.Composition.Binder
+ {
+ public Binder(Type type, Func? resolver, bool legacyMode) : base(type, resolver, legacyMode)
+ {
+ }
+
+ public override Type[] AdditionalSupportedTypes() => [typeof(ImageListStreamer), typeof(Bitmap)];
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.DesktopToNativeAdapter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.DesktopToNativeAdapter.cs
new file mode 100644
index 00000000000..528b0c11038
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.DesktopToNativeAdapter.cs
@@ -0,0 +1,65 @@
+// 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.Core.OLE;
+using Windows.Win32.System.Com;
+
+namespace System.Windows.Forms;
+
+internal partial class WinFormsDataObject
+{
+ internal unsafe partial class Composition
+ {
+ private class DesktopToNativeAdapter : DesktopDataObject.Composition.DesktopToNativeAdapter
+ {
+ public DesktopToNativeAdapter(IDataObjectDesktop dataObject) : base(dataObject)
+ {
+ }
+
+ public override DesktopDataObject.Composition.BinaryFormatUtilities GetBinaryFormatUtilities() => BinaryFormatUtilitiesInstance;
+
+ public override bool IsDataImage(object data) => data is Image;
+
+ public override HRESULT TryGetCompatibleBitmap(string format, object data, STGMEDIUM* pMedium)
+ {
+ // could use assembly load to load the Bitmap assembly but seems inefficient...
+ if (format.Equals(DesktopDataFormats.BitmapConstant) && data is Bitmap bitmap)
+ {
+ using var screenDC = GetDcScope.ScreenDC;
+
+ // 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.
+ HBITMAP hbitmap = bitmap.GetHBITMAP();
+
+ // Create a compatible DC to render the source bitmap.
+ using CreateDcScope sourceDC = new(screenDC);
+ using SelectObjectScope sourceBitmapSelection = new(sourceDC, hbitmap);
+
+ // Create a compatible DC and a new compatible bitmap.
+ using CreateDcScope destinationDC = new(screenDC);
+ HBITMAP compatibleBitmap = PInvokeCore.CreateCompatibleBitmap(screenDC, bitmap.Size.Width, bitmap.Size.Height);
+
+ // Select the new bitmap into a compatible DC and render the blt the original bitmap.
+ using SelectObjectScope destinationBitmapSelection = new(destinationDC, compatibleBitmap);
+ PInvokeCore.BitBlt(
+ destinationDC,
+ 0,
+ 0,
+ bitmap.Size.Width,
+ bitmap.Size.Height,
+ sourceDC,
+ 0,
+ 0,
+ ROP_CODE.SRCCOPY);
+
+ // Save bitmap
+ pMedium->u.hBitmap = compatibleBitmap;
+ return HRESULT.S_OK;
+ }
+
+ return HRESULT.DV_E_TYMED;
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.NativeToDesktopAdapter.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.NativeToDesktopAdapter.cs
new file mode 100644
index 00000000000..76a4b9046e2
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.NativeToDesktopAdapter.cs
@@ -0,0 +1,98 @@
+// 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.Core.OLE;
+using Com = Windows.Win32.System.Com;
+
+namespace System.Windows.Forms;
+
+internal partial class WinFormsDataObject
+{
+ internal unsafe partial class Composition
+ {
+ private unsafe class NativeToDesktopAdapter : DesktopDataObject.Composition.NativeToDesktopAdapter
+ {
+ public NativeToDesktopAdapter(Com.IDataObject* dataObject) : base(dataObject) { }
+
+ public override DesktopDataObject.Composition.BinaryFormatUtilities GetBinaryFormatUtilities() => BinaryFormatUtilitiesInstance;
+
+ public override bool TryGetBitmap(Com.IDataObject* dataObject, string format, [NotNullWhen(true)] out object? bitmap)
+ {
+ bitmap = default;
+ if ((typeof(Bitmap) == typeof(T) || typeof(Image) == typeof(T)))
+ {
+ if (format != DesktopDataFormats.BitmapConstant)
+ {
+ return false;
+ }
+
+ Com.FORMATETC formatEtc = new()
+ {
+ cfFormat = (ushort)DesktopDataFormats.GetFormat(format).Id,
+ dwAspect = (uint)Com.DVASPECT.DVASPECT_CONTENT,
+ lindex = -1,
+ tymed = (uint)Com.TYMED.TYMED_GDI
+ };
+
+ Com.STGMEDIUM medium = default;
+
+ if (dataObject->QueryGetData(formatEtc).Succeeded)
+ {
+ HRESULT hr = dataObject->GetData(formatEtc, out 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(hr == 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 ((uint)medium.tymed == (uint)Com.TYMED.TYMED_GDI
+ && !medium.hGlobal.IsNull
+ && Image.FromHbitmap(medium.hGlobal) is Bitmap clipboardBitmap)
+ {
+ bitmap = (Bitmap)clipboardBitmap.Clone();
+ clipboardBitmap.Dispose();
+ return true;
+ }
+ }
+ finally
+ {
+ PInvokeCore.ReleaseStgMedium(ref medium);
+ }
+ }
+
+ return false;
+ }
+
+ protected override void ThrowIfFormatAndTypeRequireResolver(string format)
+ {
+ // Restricted format is either read directly from the HGLOBAL or serialization record is read manually.
+ if (!IsRestrictedFormat(format)
+ // This check is a convenience for simple usages if TryGetData APIs that don't take the resolver.
+ && IsUnboundedType())
+ {
+ throw new NotSupportedException(string.Format(
+ SR.ClipboardOrDragDrop_InvalidType,
+ typeof(T).FullName));
+ }
+
+ static bool IsUnboundedType()
+ {
+ if (typeof(T) == typeof(object))
+ {
+ return true;
+ }
+
+ Type type = typeof(T);
+ // Image is a special case because we are reading Bitmaps directly from the SerializationRecord.
+ return type.IsInterface || (typeof(T) != typeof(Image) && type.IsAbstract);
+ }
+ }
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.cs
new file mode 100644
index 00000000000..12efdbbc7e9
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.Composition.cs
@@ -0,0 +1,43 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Private.Windows.Core.OLE;
+using Com = Windows.Win32.System.Com;
+
+namespace System.Windows.Forms;
+
+internal partial class WinFormsDataObject
+{
+ internal unsafe partial class Composition : DesktopDataObject.Composition
+ {
+ private static BinaryFormatUtilities? s_formatUtilities;
+
+ public Composition() { }
+
+ private static BinaryFormatUtilities BinaryFormatUtilitiesInstance => s_formatUtilities ??= new BinaryFormatUtilities();
+
+ public override DesktopDataObject.Composition PopulateFromDesktopDataObject(IDataObjectDesktop desktopDataObject)
+ {
+ DesktopToNativeAdapter winFormsToNative = new(desktopDataObject);
+ NativeToRuntimeAdapter nativeToRuntime = new(ComHelpers.GetComPointer(winFormsToNative));
+ return Populate(desktopDataObject, winFormsToNative, nativeToRuntime);
+ }
+
+ public override unsafe DesktopDataObject.Composition PopulateFromNativeDataObject(Com.IDataObject* nativeDataObject)
+ {
+ // Add ref so each adapter can take ownership of the native data object.
+ nativeDataObject->AddRef();
+ nativeDataObject->AddRef();
+ NativeToDesktopAdapter nativeToWinForms = new(nativeDataObject);
+ NativeToRuntimeAdapter nativeToRuntime = new(nativeDataObject);
+ return Populate(nativeToWinForms, nativeToWinForms, nativeToRuntime);
+ }
+
+ public override DesktopDataObject.Composition PopulateFromRuntimeDataObject(Runtime.InteropServices.ComTypes.IDataObject runtimeDataObject)
+ {
+ RuntimeToNativeAdapter runtimeToNative = new(runtimeDataObject);
+ NativeToDesktopAdapter nativeToWinForms = new(ComHelpers.GetComPointer(runtimeToNative));
+ return Populate(nativeToWinForms, runtimeToNative, runtimeDataObject);
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.WinFormsDataStore.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.WinFormsDataStore.cs
new file mode 100644
index 00000000000..037ee654031
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.WinFormsDataStore.cs
@@ -0,0 +1,33 @@
+// 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.Core.OLE;
+using CoreSR = System.Private.Windows.Core.Resources.SR;
+
+namespace System.Windows.Forms;
+
+internal partial class WinFormsDataObject
+{
+ internal sealed class WinFormsDataStore : DataStore
+ {
+ public override void SetData(string format, bool autoConvert, object? data)
+ {
+ if (string.IsNullOrWhiteSpace(format))
+ {
+ ArgumentNullException.ThrowIfNull(format);
+ throw new ArgumentException(CoreSR.DataObjectWhitespaceEmptyFormatNotAllowed, nameof(format));
+ }
+
+ // 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(DesktopDataFormats.DibConstant))
+ {
+ format = autoConvert ? DesktopDataFormats.BitmapConstant : throw new NotSupportedException(SR.DataObjectDibNotSupported);
+ }
+
+ _mappedData[format] = new DataStoreEntry(data, autoConvert);
+ }
+ }
+}
diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.cs b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.cs
new file mode 100644
index 00000000000..5e8316392ec
--- /dev/null
+++ b/src/System.Windows.Forms/src/System/Windows/Forms/OLE/WinFormsDataObject.cs
@@ -0,0 +1,89 @@
+// 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.Core.OLE;
+using System.Reflection.Metadata;
+using Com = Windows.Win32.System.Com;
+
+namespace System.Windows.Forms;
+
+internal partial class WinFormsDataObject : DesktopDataObject
+{
+ private readonly DataObject? _dataObject;
+
+ private bool _haveCheckedOverride;
+
+ internal unsafe WinFormsDataObject(Com.IDataObject* data, DataObject? dataObject = null) : base(data, new Composition()) { _dataObject = dataObject; }
+
+ public WinFormsDataObject(DataObject? dataObject = null) : base(new Composition()) { _dataObject = dataObject; }
+
+ public WinFormsDataObject(object data, DataObject? dataObject = null) : base(data, new Composition()) { _dataObject = dataObject; }
+
+ public WinFormsDataObject(string format, object data, DataObject? dataObject = null) : base(format, data, new Composition()) { _dataObject = dataObject; }
+
+ internal WinFormsDataObject(string format, bool autoConvert, object data, DataObject? dataObject = null) : base(format, autoConvert, data, new Composition()) { _dataObject = dataObject; }
+
+ public override IDataObjectDesktop CreateIDataObject() => new WinFormsDataStore();
+
+ public static bool IsValidFormatAndTypeCore(string format)
+ {
+ if (string.IsNullOrWhiteSpace(format))
+ {
+ return false;
+ }
+
+ if (IsValidPredefinedFormatTypeCombination(format))
+ {
+ return true;
+ }
+
+ throw new NotSupportedException(string.Format(
+ SR.ClipboardOrDragDrop_InvalidFormatTypeCombination,
+ typeof(T).FullName, format));
+
+ static bool IsValidPredefinedFormatTypeCombination(string format) => format switch
+ {
+ DesktopDataFormats.TextConstant
+ or DesktopDataFormats.UnicodeTextConstant
+ or DesktopDataFormats.StringConstant
+ or DesktopDataFormats.RtfConstant
+ or DesktopDataFormats.HtmlConstant
+ or DesktopDataFormats.OemTextConstant => typeof(string) == typeof(T),
+
+ DesktopDataFormats.FileDropConstant
+ or CF_DEPRECATED_FILENAME
+ or CF_DEPRECATED_FILENAMEW => typeof(string[]) == typeof(T),
+
+ DesktopDataFormats.BitmapConstant or BitmapFullName => typeof(Bitmap) == typeof(T) || typeof(Image) == typeof(T),
+ _ => true
+ };
+ }
+
+ internal override bool IsValidFormatAndType(string format) => IsValidFormatAndTypeCore(format);
+
+ public override bool IsKnownType() => DesktopDataObject.Composition.Binder.IsKnownTypeCore() || typeof(T) == typeof(ImageListStreamer) || typeof(T) == typeof(Bitmap);
+
+ public override void CheckDataObjectForJsonSet()
+ {
+ if (typeof(T) == typeof(DesktopDataObject) || typeof(T) == typeof(DataObject))
+ {
+ throw new InvalidOperationException(string.Format(SR.ClipboardOrDragDrop_CannotJsonSerializeDataObject, nameof(SetData)));
+ }
+ }
+
+ internal override bool TryGetDataCore<[DynamicallyAccessedMembers((DynamicallyAccessedMemberTypes)(-1))] T>(string format, Func? resolver, bool autoConvert, [MaybeNullWhen(false), NotNullWhen(true)] out T data)
+ {
+ if (!_haveCheckedOverride && _dataObject is not null && _dataObject.GetType() != typeof(DataObject))
+ {
+ // TryGetDataCore could be overridden. Call the potential overridden version and flag that it's been called so that
+ // we don't end up in an infinite loop if it hasn't been overridden.
+ _haveCheckedOverride = true;
+ bool result = _dataObject.TryGetDataCoreInternal(format, resolver, autoConvert, out data);
+ _haveCheckedOverride = false;
+ return result;
+ }
+
+ return base.TryGetDataCore(format, resolver, autoConvert, out data);
+ }
+}
diff --git a/src/System.Windows.Forms/tests/TestUtilities/DataObjectTestHelpers.cs b/src/System.Windows.Forms/tests/TestUtilities/DataObjectTestHelpers.cs
index f84142e8c17..3f3e7da26ae 100644
--- a/src/System.Windows.Forms/tests/TestUtilities/DataObjectTestHelpers.cs
+++ b/src/System.Windows.Forms/tests/TestUtilities/DataObjectTestHelpers.cs
@@ -15,7 +15,7 @@ public static TheoryData StringFormat() =>
[
DataFormats.Text,
DataFormats.UnicodeText,
- DataFormats.StringConstant,
+ DataFormats.StringFormat,
DataFormats.Rtf,
DataFormats.Html,
DataFormats.OemText,
diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectTests.cs
index 18d459391e0..f60238129c8 100644
--- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectTests.cs
+++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormat/WinFormsBinaryFormattedObjectTests.cs
@@ -25,7 +25,7 @@ public void BinaryFormattedObject_NonJsonData_RemainsSerialized()
{
SimpleTestData testData = new() { X = 1, Y = 1 };
SerializationRecord format = testData.SerializeAndDecode();
- ITypeResolver resolver = new DataObject.Composition.Binder(typeof(SimpleTestData), resolver: null, legacyMode: false);
+ ITypeResolver resolver = new WinFormsDataObject.Composition.Binder(typeof(SimpleTestData), resolver: null, legacyMode: false);
format.TryGetObjectFromJson(resolver, out _).Should().BeFalse();
}
@@ -45,7 +45,7 @@ public void BinaryFormattedObject_JsonData_RoundTrip()
stream.Position = 0;
SerializationRecord binary = NrbfDecoder.Decode(stream);
binary.TypeName.AssemblyName!.FullName.Should().Be(IJsonData.CustomAssemblyName);
- ITypeResolver resolver = new DataObject.Composition.Binder(typeof(SimpleTestData), resolver: null, legacyMode: false);
+ ITypeResolver resolver = new WinFormsDataObject.Composition.Binder(typeof(SimpleTestData), resolver: null, legacyMode: false);
binary.TryGetObjectFromJson(resolver, out _).Should().BeTrue();
binary.TryGetObjectFromJson(resolver, out object? result).Should().BeTrue();
SimpleTestData deserialized = result.Should().BeOfType().Which;
diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormatUtilitiesTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormatUtilitiesTests.cs
index 48e3e8923b4..62ac739641b 100644
--- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormatUtilitiesTests.cs
+++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/BinaryFormatUtilitiesTests.cs
@@ -7,51 +7,56 @@
using System.Drawing;
using System.Reflection.Metadata;
using System.Runtime.Serialization;
-using Utilities = System.Windows.Forms.DataObject.Composition.BinaryFormatUtilities;
+using Utilities = System.Windows.Forms.WinFormsDataObject.Composition.BinaryFormatUtilities;
namespace System.Windows.Forms.Tests;
public partial class BinaryFormatUtilitiesTests : IDisposable
{
private readonly MemoryStream _stream;
+ private readonly Utilities _utilities;
- public BinaryFormatUtilitiesTests() => _stream = new();
+ public BinaryFormatUtilitiesTests()
+ {
+ _stream = new();
+ _utilities = new();
+ }
public void Dispose() => _stream.Dispose();
private void WriteObjectToStream(object value, bool restrictSerialization = false) =>
- Utilities.WriteObjectToStream(_stream, value, restrictSerialization);
+ _utilities.WriteObjectToStream(_stream, value, restrictSerialization);
private object? ReadObjectFromStream()
{
_stream.Position = 0;
- return Utilities.ReadObjectFromStream