From 541a8439391009c7a1ccf5352ead5d13872fa992 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 4 Jun 2024 14:57:23 +0200 Subject: [PATCH 01/11] replace arraylist with generic list, and improve general performance --- .../System/Windows/DataFormats.cs | 215 +++++++----------- 1 file changed, 82 insertions(+), 133 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 5ffca011d3c..1deb4631651 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -11,13 +11,13 @@ // using MS.Win32; -using System.Collections; +using System.Collections.Generic; using System.Globalization; using System.Runtime.InteropServices; using System.Security; using System.Text; using MS.Internal.PresentationCore; -using SecurityHelper=MS.Internal.SecurityHelper; +using SecurityHelper = MS.Internal.SecurityHelper; namespace System.Windows { @@ -41,10 +41,7 @@ public static class DataFormats /// Gets an object with the Windows Clipboard numeric /// ID and name for the specified ID. /// - public static DataFormat GetDataFormat(int id) - { - return InternalGetDataFormat(id); - } + public static DataFormat GetDataFormat(int id) => InternalGetDataFormat(id); /// /// Gets the data format with the Windows Clipboard numeric ID and name for the specified data format. @@ -57,57 +54,43 @@ public static DataFormat GetDataFormat(string format) ArgumentNullException.ThrowIfNull(format); if (format == string.Empty) - { throw new ArgumentException(SR.DataObject_EmptyFormatNotAllowed); - } // Ensures the predefined Win32 data formats into our format list. EnsurePredefined(); - // Lock the data format list to obtains the mutual-exclusion. + // Lock the data format list to obtain the mutual-exclusion. lock (_formatListlock) { - int formatId; - int index; - - // It is much faster to do a case sensitive search here. So do + // 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 < _formatList.Count; n++) + for (int i = 0; i < _formatList.Count; i++) { - DataFormat formatItem; - - formatItem = (DataFormat)_formatList[n]; + DataFormat formatItem = _formatList[i]; if (formatItem.Name.Equals(format)) - { return formatItem; - } } - for (int n = 0; n < _formatList.Count; n++) + for (int i = 0; i < _formatList.Count; i++) { - DataFormat formatItem; + DataFormat formatItem = _formatList[i]; - formatItem = (DataFormat)_formatList[n]; - - if (string.Equals(formatItem.Name, format, StringComparison.OrdinalIgnoreCase)) - { + if (formatItem.Name.Equals(format, StringComparison.OrdinalIgnoreCase)) return formatItem; - } } - // Reigster the this format string. - formatId = UnsafeNativeMethods.RegisterClipboardFormat(format); + // Register this format string + int formatId = UnsafeNativeMethods.RegisterClipboardFormat(format); if (formatId == 0) - { throw new System.ComponentModel.Win32Exception(); - } - index = _formatList.Add(new DataFormat(format, formatId)); + // Create a new format and store it + DataFormat newFormat = new(format, formatId); + _formatList.Add(newFormat); - return (DataFormat)_formatList[index]; + return newFormat; } } @@ -289,59 +272,31 @@ public static DataFormat GetDataFormat(string format) /// /// Convert TextDataFormat to Dataformats. /// - internal static string ConvertToDataFormats(TextDataFormat textDataformat) + internal static string ConvertToDataFormats(TextDataFormat textDataformat) => textDataformat switch { - string dataFormat = DataFormats.UnicodeText; - - switch (textDataformat) - { - case TextDataFormat.Text: - dataFormat = DataFormats.Text; - break; - - case TextDataFormat.UnicodeText: - dataFormat = DataFormats.UnicodeText; - break; - - case TextDataFormat.Rtf: - dataFormat = DataFormats.Rtf; - break; - - case TextDataFormat.Html: - dataFormat = DataFormats.Html; - break; - - case TextDataFormat.CommaSeparatedValue: - dataFormat = DataFormats.CommaSeparatedValue; - break; - - case TextDataFormat.Xaml: - dataFormat = DataFormats.Xaml; - break; - } - - return dataFormat; - } + TextDataFormat.Text => DataFormats.Text, + TextDataFormat.UnicodeText => DataFormats.UnicodeText, + TextDataFormat.Rtf => DataFormats.Rtf, + TextDataFormat.Html => DataFormats.Html, + TextDataFormat.CommaSeparatedValue => DataFormats.CommaSeparatedValue, + TextDataFormat.Xaml => DataFormats.Xaml, + _ => DataFormats.UnicodeText + }; /// /// Validate the text data format. /// - internal static bool IsValidTextDataFormat(TextDataFormat textDataFormat) + /// + internal static bool IsValidTextDataFormat(TextDataFormat textDataFormat) => textDataFormat switch { - if (textDataFormat == TextDataFormat.Text || - textDataFormat == TextDataFormat.UnicodeText || - textDataFormat == TextDataFormat.Rtf || - textDataFormat == TextDataFormat.Html || - textDataFormat == TextDataFormat.CommaSeparatedValue || - textDataFormat == TextDataFormat.Xaml) - { - return true; - } - else - { - return false; - } - } + TextDataFormat.Text => true, + TextDataFormat.UnicodeText => true, + TextDataFormat.Rtf => true, + TextDataFormat.Html => true, + TextDataFormat.CommaSeparatedValue => true, + TextDataFormat.Xaml => true, + _ => false + }; #endregion Internal Methods @@ -367,18 +322,15 @@ private static DataFormat InternalGetDataFormat(int id) { DataFormat formatItem; StringBuilder sb; - int index; - for (int n = 0; n < _formatList.Count; n++) + for (int i = 0; i < _formatList.Count; i++) { - formatItem = (DataFormat)_formatList[n]; + formatItem = _formatList[i]; // OLE FORMATETC defined CLIPFORMAT as the unsigned short, so we should ignore // high 2bytes to find the matched CLIPFORMAT ID. if ((formatItem.Id & 0x0000ffff) == (id & 0x0000ffff)) - { return formatItem; - } } sb = new StringBuilder(NativeMethods.MAX_PATH); @@ -387,13 +339,15 @@ private static DataFormat InternalGetDataFormat(int id) // so we should play it safe. if (UnsafeNativeMethods.GetClipboardFormatName(id, sb, sb.Capacity) == 0) { - sb.Length = 0; + sb.Length = 0; // Same as Clear() sb.Append("Format").Append(id); } - index = _formatList.Add(new DataFormat(sb.ToString(), id)); + // Create a new format and store it + formatItem = new(sb.ToString(), id); + _formatList.Add(formatItem); - return (DataFormat)_formatList[index]; + return formatItem; } } @@ -406,50 +360,45 @@ private static void EnsurePredefined() // Lock the data format list to obtains the mutual-exclusion. lock (_formatListlock) { - if (_formatList == null) + if (_formatList is not null) + return; + + // Create format list for the default formats. + _formatList = new List(19) { - // Create format list for the default formats. - _formatList = new ArrayList(19); - - _formatList.Add(new DataFormat(UnicodeText, NativeMethods.CF_UNICODETEXT)); - _formatList.Add(new DataFormat(Text, NativeMethods.CF_TEXT)); - _formatList.Add(new DataFormat(Bitmap, NativeMethods.CF_BITMAP)); - _formatList.Add(new DataFormat(MetafilePicture, NativeMethods.CF_METAFILEPICT)); - _formatList.Add(new DataFormat(EnhancedMetafile, NativeMethods.CF_ENHMETAFILE)); - _formatList.Add(new DataFormat(Dif, NativeMethods.CF_DIF)); - _formatList.Add(new DataFormat(Tiff, NativeMethods.CF_TIFF)); - _formatList.Add(new DataFormat(OemText, NativeMethods.CF_OEMTEXT)); - _formatList.Add(new DataFormat(Dib, NativeMethods.CF_DIB)); - _formatList.Add(new DataFormat(Palette, NativeMethods.CF_PALETTE)); - _formatList.Add(new DataFormat(PenData, NativeMethods.CF_PENDATA)); - _formatList.Add(new DataFormat(Riff, NativeMethods.CF_RIFF)); - _formatList.Add(new DataFormat(WaveAudio, NativeMethods.CF_WAVE)); - _formatList.Add(new DataFormat(SymbolicLink, NativeMethods.CF_SYLK)); - _formatList.Add(new DataFormat(FileDrop, NativeMethods.CF_HDROP)); - _formatList.Add(new DataFormat(Locale, NativeMethods.CF_LOCALE)); - int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(Xaml); - if (xamlFormatId != 0) - { - _formatList.Add(new DataFormat(Xaml,xamlFormatId)); - } - - // This is the format to store trust boundary information. Essentially this is accompalished by storing - // the permission set of the source application where the content comes from. During paste we compare this to - // the permissio set of the target application. - int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); - if (applicationTrustFormatId != 0) - { - _formatList.Add(new DataFormat(ApplicationTrust, applicationTrustFormatId)); - } - // RegisterClipboardFormat returns 0 on failure - int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(System.Windows.Ink.StrokeCollection.InkSerializedFormat); - - if (inkServicesFrameworkFormatId != 0) - { - _formatList.Add(new DataFormat(System.Windows.Ink.StrokeCollection.InkSerializedFormat, - inkServicesFrameworkFormatId)); - } -} + new(UnicodeText, NativeMethods.CF_UNICODETEXT), + new(Text, NativeMethods.CF_TEXT), + new(Bitmap, NativeMethods.CF_BITMAP), + new(MetafilePicture, NativeMethods.CF_METAFILEPICT), + new(EnhancedMetafile, NativeMethods.CF_ENHMETAFILE), + new(Dif, NativeMethods.CF_DIF), + new(Tiff, NativeMethods.CF_TIFF), + new(OemText, NativeMethods.CF_OEMTEXT), + new(Dib, NativeMethods.CF_DIB), + new(Palette, NativeMethods.CF_PALETTE), + new(PenData, NativeMethods.CF_PENDATA), + new(Riff, NativeMethods.CF_RIFF), + new(WaveAudio, NativeMethods.CF_WAVE), + new(SymbolicLink, NativeMethods.CF_SYLK), + new(FileDrop, NativeMethods.CF_HDROP), + new(Locale, NativeMethods.CF_LOCALE) + }; + + int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(Xaml); + if (xamlFormatId != 0) + _formatList.Add(new(Xaml, xamlFormatId)); + + // This is the format to store trust boundary information. Essentially this is accompalished by storing + // the permission set of the source application where the content comes from. During paste we compare this to + // the permissio set of the target application. + int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); + if (applicationTrustFormatId != 0) + _formatList.Add(new(ApplicationTrust, applicationTrustFormatId)); + + // RegisterClipboardFormat returns 0 on failure + int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(Ink.StrokeCollection.InkSerializedFormat); + if (inkServicesFrameworkFormatId != 0) + _formatList.Add(new(Ink.StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); } } @@ -464,10 +413,10 @@ private static void EnsurePredefined() #region Private Fields // The registered data format list. - private static ArrayList _formatList; + private static List _formatList; // This object is for locking the _formatList to access safe in the multi-thread. - private static readonly Object _formatListlock = new Object(); + private static readonly object _formatListlock = new(); #endregion Private Fields } From 973e1a23b281ca5f30d92647046bacf21b79f3c4 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 4 Jun 2024 16:39:36 +0200 Subject: [PATCH 02/11] remove case-sensitive search as string.Equals(...) is now vectorized in .net 8 --- .../PresentationCore/System/Windows/DataFormats.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 1deb4631651..dc8b67786d3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -62,16 +62,6 @@ public static DataFormat GetDataFormat(string format) // Lock the data format list to obtain the mutual-exclusion. lock (_formatListlock) { - // It is much faster to do a case sensitive search here. So do - // the case sensitive compare first, then the expensive one. - for (int i = 0; i < _formatList.Count; i++) - { - DataFormat formatItem = _formatList[i]; - - if (formatItem.Name.Equals(format)) - return formatItem; - } - for (int i = 0; i < _formatList.Count; i++) { DataFormat formatItem = _formatList[i]; @@ -317,7 +307,7 @@ private static DataFormat InternalGetDataFormat(int id) // Ensures the predefined Win32 data formats into our format list. EnsurePredefined(); - // Lock the data format list to obtains the mutual-exclusion. + // Lock the data format list to obtain the mutual-exclusion. lock (_formatListlock) { DataFormat formatItem; @@ -357,7 +347,7 @@ private static DataFormat InternalGetDataFormat(int id) /// private static void EnsurePredefined() { - // Lock the data format list to obtains the mutual-exclusion. + // Lock the data format list to obtain the mutual-exclusion. lock (_formatListlock) { if (_formatList is not null) From f532f0a0c9cd8ce9796ec4a24a858173ff967f6a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 4 Jun 2024 17:05:04 +0200 Subject: [PATCH 03/11] remove obsolete CAS remarks, unify public functions description --- .../src/PresentationCore/System/Windows/DataFormats.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index dc8b67786d3..5c5d9299bc8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -38,17 +38,13 @@ public static class DataFormats #region Public Methods /// - /// Gets an object with the Windows Clipboard numeric - /// ID and name for the specified ID. + /// Gets the data format with the Windows Clipboard numeric ID and name for the specified ID. /// public static DataFormat GetDataFormat(int id) => InternalGetDataFormat(id); /// /// Gets the data format with the Windows Clipboard numeric ID and name for the specified data format. /// - /// - /// Callers must have UnmanagedCode permission to call this API. - /// public static DataFormat GetDataFormat(string format) { ArgumentNullException.ThrowIfNull(format); @@ -380,7 +376,7 @@ private static void EnsurePredefined() // This is the format to store trust boundary information. Essentially this is accompalished by storing // the permission set of the source application where the content comes from. During paste we compare this to - // the permissio set of the target application. + // the permission set of the target application. int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); if (applicationTrustFormatId != 0) _formatList.Add(new(ApplicationTrust, applicationTrustFormatId)); From c151029595c877d04528d54b7a1ea942ce1374c5 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Tue, 4 Jun 2024 18:13:12 +0200 Subject: [PATCH 04/11] increase performance by half by using static constructor approach for initialization --- .../System/Windows/DataFormats.cs | 88 ++++++++----------- 1 file changed, 38 insertions(+), 50 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 5c5d9299bc8..6bd19ecee4e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -17,6 +17,7 @@ using System.Security; using System.Text; using MS.Internal.PresentationCore; +using System.Windows.Ink; using SecurityHelper = MS.Internal.SecurityHelper; namespace System.Windows @@ -52,9 +53,6 @@ public static DataFormat GetDataFormat(string format) if (format == string.Empty) throw new ArgumentException(SR.DataObject_EmptyFormatNotAllowed); - // Ensures the predefined Win32 data formats into our format list. - EnsurePredefined(); - // Lock the data format list to obtain the mutual-exclusion. lock (_formatListlock) { @@ -300,9 +298,6 @@ public static DataFormat GetDataFormat(string format) /// private static DataFormat InternalGetDataFormat(int id) { - // Ensures the predefined Win32 data formats into our format list. - EnsurePredefined(); - // Lock the data format list to obtain the mutual-exclusion. lock (_formatListlock) { @@ -341,51 +336,44 @@ private static DataFormat InternalGetDataFormat(int id) /// Ensures that the Win32 predefined formats are setup in our format list. This /// is called anytime we need to search the list /// - private static void EnsurePredefined() + static DataFormats() { - // Lock the data format list to obtain the mutual-exclusion. - lock (_formatListlock) + // Create format list for the default formats. + _formatList = new List(19) { - if (_formatList is not null) - return; - - // Create format list for the default formats. - _formatList = new List(19) - { - new(UnicodeText, NativeMethods.CF_UNICODETEXT), - new(Text, NativeMethods.CF_TEXT), - new(Bitmap, NativeMethods.CF_BITMAP), - new(MetafilePicture, NativeMethods.CF_METAFILEPICT), - new(EnhancedMetafile, NativeMethods.CF_ENHMETAFILE), - new(Dif, NativeMethods.CF_DIF), - new(Tiff, NativeMethods.CF_TIFF), - new(OemText, NativeMethods.CF_OEMTEXT), - new(Dib, NativeMethods.CF_DIB), - new(Palette, NativeMethods.CF_PALETTE), - new(PenData, NativeMethods.CF_PENDATA), - new(Riff, NativeMethods.CF_RIFF), - new(WaveAudio, NativeMethods.CF_WAVE), - new(SymbolicLink, NativeMethods.CF_SYLK), - new(FileDrop, NativeMethods.CF_HDROP), - new(Locale, NativeMethods.CF_LOCALE) - }; - - int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(Xaml); - if (xamlFormatId != 0) - _formatList.Add(new(Xaml, xamlFormatId)); - - // This is the format to store trust boundary information. Essentially this is accompalished by storing - // the permission set of the source application where the content comes from. During paste we compare this to - // the permission set of the target application. - int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); - if (applicationTrustFormatId != 0) - _formatList.Add(new(ApplicationTrust, applicationTrustFormatId)); - - // RegisterClipboardFormat returns 0 on failure - int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(Ink.StrokeCollection.InkSerializedFormat); - if (inkServicesFrameworkFormatId != 0) - _formatList.Add(new(Ink.StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); - } + new(DataFormats.UnicodeText, NativeMethods.CF_UNICODETEXT), + new(DataFormats.Text, NativeMethods.CF_TEXT), + new(DataFormats.Bitmap, NativeMethods.CF_BITMAP), + new(DataFormats.MetafilePicture, NativeMethods.CF_METAFILEPICT), + new(DataFormats.EnhancedMetafile, NativeMethods.CF_ENHMETAFILE), + new(DataFormats.Dif, NativeMethods.CF_DIF), + new(DataFormats.Tiff, NativeMethods.CF_TIFF), + new(DataFormats.OemText, NativeMethods.CF_OEMTEXT), + new(DataFormats.Dib, NativeMethods.CF_DIB), + new(DataFormats.Palette, NativeMethods.CF_PALETTE), + new(DataFormats.PenData, NativeMethods.CF_PENDATA), + new(DataFormats.Riff, NativeMethods.CF_RIFF), + new(DataFormats.WaveAudio, NativeMethods.CF_WAVE), + new(DataFormats.SymbolicLink, NativeMethods.CF_SYLK), + new(DataFormats.FileDrop, NativeMethods.CF_HDROP), + new(DataFormats.Locale, NativeMethods.CF_LOCALE) + }; + + int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.Xaml); + if (xamlFormatId != 0) + _formatList.Add(new(DataFormats.Xaml, xamlFormatId)); + + // This is the format to store trust boundary information. Essentially this is accompalished by storing + // the permission set of the source application where the content comes from. During paste we compare this to + // the permission set of the target application. + int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); + if (applicationTrustFormatId != 0) + _formatList.Add(new(DataFormats.ApplicationTrust, applicationTrustFormatId)); + + // RegisterClipboardFormat returns 0 on failure + int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(StrokeCollection.InkSerializedFormat); + if (inkServicesFrameworkFormatId != 0) + _formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); } #endregion Private Methods @@ -399,7 +387,7 @@ private static void EnsurePredefined() #region Private Fields // The registered data format list. - private static List _formatList; + private static readonly List _formatList; // This object is for locking the _formatList to access safe in the multi-thread. private static readonly object _formatListlock = new(); From e47426b32054f8f2f48e6ee8c396a16dc6912281 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 5 Jun 2024 22:13:46 +0200 Subject: [PATCH 05/11] fix beforefieldinit flag on main class, create private impl on state-dependent types --- .../System/Windows/DataFormats.cs | 261 ++++++++++-------- 1 file changed, 146 insertions(+), 115 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 6bd19ecee4e..517ec242485 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -41,42 +41,12 @@ public static class DataFormats /// /// Gets the data format with the Windows Clipboard numeric ID and name for the specified ID. /// - public static DataFormat GetDataFormat(int id) => InternalGetDataFormat(id); + public static DataFormat GetDataFormat(int id) => DataFormatsImpl.GetDataFormat(id); /// /// Gets the data format with the Windows Clipboard numeric ID and name for the specified data format. /// - public static DataFormat GetDataFormat(string format) - { - ArgumentNullException.ThrowIfNull(format); - - if (format == string.Empty) - throw new ArgumentException(SR.DataObject_EmptyFormatNotAllowed); - - // Lock the data format list to obtain the mutual-exclusion. - lock (_formatListlock) - { - for (int i = 0; i < _formatList.Count; i++) - { - DataFormat formatItem = _formatList[i]; - - if (formatItem.Name.Equals(format, StringComparison.OrdinalIgnoreCase)) - return formatItem; - } - - // Register this format string - int formatId = UnsafeNativeMethods.RegisterClipboardFormat(format); - - if (formatId == 0) - throw new System.ComponentModel.Win32Exception(); - - // Create a new format and store it - DataFormat newFormat = new(format, formatId); - _formatList.Add(newFormat); - - return newFormat; - } - } + public static DataFormat GetDataFormat(string format) => DataFormatsImpl.GetDataFormat(format); #endregion Public Methods @@ -286,113 +256,174 @@ public static DataFormat GetDataFormat(string format) //------------------------------------------------------ // - // Private Methods + // Data Formats Implementation // //------------------------------------------------------ - #region Private Methods + #region Data Formats Implementation Class /// - /// Allows a the new format name to be specified if the requested format is not - /// in the list + /// Static class containing the internal format list and associated lookup methods. /// - private static DataFormat InternalGetDataFormat(int id) + private static class DataFormatsImpl { - // Lock the data format list to obtain the mutual-exclusion. - lock (_formatListlock) + //------------------------------------------------------ + // + // Static constructor + // + //------------------------------------------------------ + + #region Static constructor + + /// + /// Ensures that the Win32 predefined formats are setup in our format list. + /// This is called anytime we need to search the list. + /// + static DataFormatsImpl() { - DataFormat formatItem; - StringBuilder sb; - - for (int i = 0; i < _formatList.Count; i++) + // Create format list for the default formats. + formatList = new List(19) { - formatItem = _formatList[i]; + new(DataFormats.UnicodeText, NativeMethods.CF_UNICODETEXT), + new(DataFormats.Text, NativeMethods.CF_TEXT), + new(DataFormats.Bitmap, NativeMethods.CF_BITMAP), + new(DataFormats.MetafilePicture, NativeMethods.CF_METAFILEPICT), + new(DataFormats.EnhancedMetafile, NativeMethods.CF_ENHMETAFILE), + new(DataFormats.Dif, NativeMethods.CF_DIF), + new(DataFormats.Tiff, NativeMethods.CF_TIFF), + new(DataFormats.OemText, NativeMethods.CF_OEMTEXT), + new(DataFormats.Dib, NativeMethods.CF_DIB), + new(DataFormats.Palette, NativeMethods.CF_PALETTE), + new(DataFormats.PenData, NativeMethods.CF_PENDATA), + new(DataFormats.Riff, NativeMethods.CF_RIFF), + new(DataFormats.WaveAudio, NativeMethods.CF_WAVE), + new(DataFormats.SymbolicLink, NativeMethods.CF_SYLK), + new(DataFormats.FileDrop, NativeMethods.CF_HDROP), + new(DataFormats.Locale, NativeMethods.CF_LOCALE) + }; + + int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.Xaml); + if (xamlFormatId != 0) + formatList.Add(new(DataFormats.Xaml, xamlFormatId)); + + // This is the format to store trust boundary information. Essentially this is accompalished by storing + // the permission set of the source application where the content comes from. During paste we compare this to + // the permission set of the target application. + int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); + if (applicationTrustFormatId != 0) + formatList.Add(new(DataFormats.ApplicationTrust, applicationTrustFormatId)); + + // RegisterClipboardFormat returns 0 on failure + int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(StrokeCollection.InkSerializedFormat); + if (inkServicesFrameworkFormatId != 0) + formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); + } - // OLE FORMATETC defined CLIPFORMAT as the unsigned short, so we should ignore - // high 2bytes to find the matched CLIPFORMAT ID. - if ((formatItem.Id & 0x0000ffff) == (id & 0x0000ffff)) - return formatItem; - } + #endregion Static Constructor + + //------------------------------------------------------ + // + // Private Methods + // + //------------------------------------------------------ - sb = new StringBuilder(NativeMethods.MAX_PATH); + #region Private Methods - // This can happen if windows adds a standard format that we don't know about, - // so we should play it safe. - if (UnsafeNativeMethods.GetClipboardFormatName(id, sb, sb.Capacity) == 0) + /// + /// Allows a new format name to be specified if the requested format is not in the list. + /// + public static DataFormat GetDataFormat(int id) + { + // Lock the data format list to obtain the mutual-exclusion. + lock (_formatListlock) { - sb.Length = 0; // Same as Clear() - sb.Append("Format").Append(id); + DataFormat formatItem; + StringBuilder sb; + + for (int i = 0; i < formatList.Count; i++) + { + formatItem = formatList[i]; + + // OLE FORMATETC defined CLIPFORMAT as the unsigned short, so we should ignore + // high 2bytes to find the matched CLIPFORMAT ID. + if ((formatItem.Id & 0x0000ffff) == (id & 0x0000ffff)) + return formatItem; + } + + sb = new StringBuilder(NativeMethods.MAX_PATH); + + // This can happen if windows adds a standard format that we don't know about, + // so we should play it safe. + if (UnsafeNativeMethods.GetClipboardFormatName(id, sb, sb.Capacity) == 0) + { + sb.Length = 0; // Same as Clear() + sb.Append("Format").Append(id); + } + + // Create a new format and store it + formatItem = new(sb.ToString(), id); + formatList.Add(formatItem); + + return formatItem; } + } + + /// + /// Retrieves a data format using its name or attempts to register a new one if it doesn't exist. + /// + public static DataFormat GetDataFormat(string format) + { + ArgumentNullException.ThrowIfNull(format); + + if (format == string.Empty) + throw new ArgumentException(SR.DataObject_EmptyFormatNotAllowed); + + // Lock the data format list to obtain the mutual-exclusion. + lock (_formatListlock) + { + for (int i = 0; i < formatList.Count; i++) + { + DataFormat formatItem = formatList[i]; + + if (formatItem.Name.Equals(format, StringComparison.OrdinalIgnoreCase)) + return formatItem; + } - // Create a new format and store it - formatItem = new(sb.ToString(), id); - _formatList.Add(formatItem); + // Register this format string + int formatId = UnsafeNativeMethods.RegisterClipboardFormat(format); - return formatItem; + if (formatId == 0) + throw new System.ComponentModel.Win32Exception(); + + // Create a new format and store it + DataFormat newFormat = new(format, formatId); + formatList.Add(newFormat); + + return newFormat; + } } - } - /// - /// Ensures that the Win32 predefined formats are setup in our format list. This - /// is called anytime we need to search the list - /// - static DataFormats() - { - // Create format list for the default formats. - _formatList = new List(19) - { - new(DataFormats.UnicodeText, NativeMethods.CF_UNICODETEXT), - new(DataFormats.Text, NativeMethods.CF_TEXT), - new(DataFormats.Bitmap, NativeMethods.CF_BITMAP), - new(DataFormats.MetafilePicture, NativeMethods.CF_METAFILEPICT), - new(DataFormats.EnhancedMetafile, NativeMethods.CF_ENHMETAFILE), - new(DataFormats.Dif, NativeMethods.CF_DIF), - new(DataFormats.Tiff, NativeMethods.CF_TIFF), - new(DataFormats.OemText, NativeMethods.CF_OEMTEXT), - new(DataFormats.Dib, NativeMethods.CF_DIB), - new(DataFormats.Palette, NativeMethods.CF_PALETTE), - new(DataFormats.PenData, NativeMethods.CF_PENDATA), - new(DataFormats.Riff, NativeMethods.CF_RIFF), - new(DataFormats.WaveAudio, NativeMethods.CF_WAVE), - new(DataFormats.SymbolicLink, NativeMethods.CF_SYLK), - new(DataFormats.FileDrop, NativeMethods.CF_HDROP), - new(DataFormats.Locale, NativeMethods.CF_LOCALE) - }; - - int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.Xaml); - if (xamlFormatId != 0) - _formatList.Add(new(DataFormats.Xaml, xamlFormatId)); - - // This is the format to store trust boundary information. Essentially this is accompalished by storing - // the permission set of the source application where the content comes from. During paste we compare this to - // the permission set of the target application. - int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); - if (applicationTrustFormatId != 0) - _formatList.Add(new(DataFormats.ApplicationTrust, applicationTrustFormatId)); - - // RegisterClipboardFormat returns 0 on failure - int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(StrokeCollection.InkSerializedFormat); - if (inkServicesFrameworkFormatId != 0) - _formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); - } + #endregion Private Methods - #endregion Private Methods + //------------------------------------------------------ + // + // Private Fields + // + //------------------------------------------------------ - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ + #region Private Fields - #region Private Fields + // The registered data format list. + private static readonly List formatList; + // This object is for locking the _formatList to access safe in the multi-thread. + private static readonly object _formatListlock = new(); - // The registered data format list. - private static readonly List _formatList; + #endregion Private Fields + + } - // This object is for locking the _formatList to access safe in the multi-thread. - private static readonly object _formatListlock = new(); + #endregion Data Formats Implementation Class - #endregion Private Fields } #endregion DataFormats class From a75b13e8dc8e01558fb55cf98acf61fa74a9c487 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 16 Oct 2024 22:47:05 +0200 Subject: [PATCH 06/11] Remove redundant usings --- .../PresentationCore/System/Windows/DataFormats.cs | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 517ec242485..dd2fb8677c3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -2,23 +2,15 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -// -// -// Description: Manage the data formats. // // See spec at http://avalon/uis/Data%20Transfer%20clipboard%20dragdrop/Avalon%20Data%20Transfer%20Object.htm // -// -using MS.Win32; -using System.Collections.Generic; -using System.Globalization; -using System.Runtime.InteropServices; -using System.Security; -using System.Text; using MS.Internal.PresentationCore; +using System.Collections.Generic; using System.Windows.Ink; -using SecurityHelper = MS.Internal.SecurityHelper; +using System.Text; +using MS.Win32; namespace System.Windows { From aded61848a77bc9ed8829bfe83dc015770a0ca81 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 16 Oct 2024 22:50:35 +0200 Subject: [PATCH 07/11] Remove all the comments that aren't needed --- .../System/Windows/DataFormats.cs | 92 ++----------------- 1 file changed, 7 insertions(+), 85 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index dd2fb8677c3..1c968431c32 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -14,22 +14,12 @@ namespace System.Windows { - #region DataFormats class - /// /// Translates between Windows Design text-based formats and /// 32-bit signed integer-based clipboard formats. /// public static class DataFormats { - //------------------------------------------------------ - // - // Public Methods - // - //------------------------------------------------------ - - #region Public Methods - /// /// Gets the data format with the Windows Clipboard numeric ID and name for the specified ID. /// @@ -40,16 +30,6 @@ public static class DataFormats /// public static DataFormat GetDataFormat(string format) => DataFormatsImpl.GetDataFormat(format); - #endregion Public Methods - - //------------------------------------------------------ - // - // Public Fields - // - //------------------------------------------------------ - - #region Public Fields - /// /// Specifies the standard ANSI text format. This field is read-only. /// @@ -185,15 +165,7 @@ public static class DataFormats /// Specifies a data format as Xaml Package. This field is read-only. /// public static readonly string XamlPackage = "XamlPackage"; - #endregion Public Fields - //------------------------------------------------------ - // - // Internal Fields - // - //------------------------------------------------------ - - #region Internal Fields /// /// Specifies a data format as ApplicationTrust which is used to block /// paste from partial trust to full trust applications. The intent of this @@ -205,16 +177,6 @@ public static class DataFormats internal const string FileName = "FileName"; internal const string FileNameW = "FileNameW"; - #endregion Internal Fields - - //------------------------------------------------------ - // - // Internal Methods - // - //------------------------------------------------------ - - #region Internal Methods - /// /// Convert TextDataFormat to Dataformats. /// @@ -244,29 +206,11 @@ public static class DataFormats _ => false }; - #endregion Internal Methods - - //------------------------------------------------------ - // - // Data Formats Implementation - // - //------------------------------------------------------ - - #region Data Formats Implementation Class - /// /// Static class containing the internal format list and associated lookup methods. /// private static class DataFormatsImpl { - //------------------------------------------------------ - // - // Static constructor - // - //------------------------------------------------------ - - #region Static constructor - /// /// Ensures that the Win32 predefined formats are setup in our format list. /// This is called anytime we need to search the list. @@ -311,16 +255,6 @@ static DataFormatsImpl() formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); } - #endregion Static Constructor - - //------------------------------------------------------ - // - // Private Methods - // - //------------------------------------------------------ - - #region Private Methods - /// /// Allows a new format name to be specified if the requested format is not in the list. /// @@ -395,28 +329,16 @@ public static DataFormat GetDataFormat(string format) } } - #endregion Private Methods - - //------------------------------------------------------ - // - // Private Fields - // - //------------------------------------------------------ - - #region Private Fields - - // The registered data format list. + /// + /// List of all registered that we're aware of. + /// private static readonly List formatList; - // This object is for locking the _formatList to access safe in the multi-thread. - private static readonly object _formatListlock = new(); - #endregion Private Fields + /// + /// Lock specially used for access to field. + /// + private static readonly object _formatListlock = new(); } - - #endregion Data Formats Implementation Class - } - - #endregion DataFormats class } From 0960c69e024b58e83feaef32c5e4af050981c071 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 16 Oct 2024 22:53:32 +0200 Subject: [PATCH 08/11] Suppress IDE1006 over the public fields as we cannot change them --- .../src/PresentationCore/System/Windows/DataFormats.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 1c968431c32..3066d8b7871 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -30,6 +30,8 @@ public static class DataFormats /// public static DataFormat GetDataFormat(string format) => DataFormatsImpl.GetDataFormat(format); +#pragma warning disable IDE1006 // Naming rule violation (Static fields without s_* prefix) + /// /// Specifies the standard ANSI text format. This field is read-only. /// @@ -155,7 +157,6 @@ public static class DataFormats /// public static readonly string Serializable = "PersistentObject"; - /// /// Specifies a data format as Xaml. This field is read-only. /// @@ -166,6 +167,8 @@ public static class DataFormats /// public static readonly string XamlPackage = "XamlPackage"; +#pragma warning restore IDE1006 // Naming rule violation (Static fields without s_* prefix) + /// /// Specifies a data format as ApplicationTrust which is used to block /// paste from partial trust to full trust applications. The intent of this From e4c16375aeafa24988b05232139dbd75b3384f91 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 16 Oct 2024 22:54:17 +0200 Subject: [PATCH 09/11] Change s_formatListlock from object to Lock, proper name for s_formatList --- .../System/Windows/DataFormats.cs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index 3066d8b7871..b9670810b48 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -9,6 +9,7 @@ using MS.Internal.PresentationCore; using System.Collections.Generic; using System.Windows.Ink; +using System.Threading; using System.Text; using MS.Win32; @@ -221,7 +222,7 @@ private static class DataFormatsImpl static DataFormatsImpl() { // Create format list for the default formats. - formatList = new List(19) + s_formatList = new List(19) { new(DataFormats.UnicodeText, NativeMethods.CF_UNICODETEXT), new(DataFormats.Text, NativeMethods.CF_TEXT), @@ -243,19 +244,19 @@ static DataFormatsImpl() int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.Xaml); if (xamlFormatId != 0) - formatList.Add(new(DataFormats.Xaml, xamlFormatId)); + s_formatList.Add(new(DataFormats.Xaml, xamlFormatId)); // This is the format to store trust boundary information. Essentially this is accompalished by storing // the permission set of the source application where the content comes from. During paste we compare this to // the permission set of the target application. int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); if (applicationTrustFormatId != 0) - formatList.Add(new(DataFormats.ApplicationTrust, applicationTrustFormatId)); + s_formatList.Add(new(DataFormats.ApplicationTrust, applicationTrustFormatId)); // RegisterClipboardFormat returns 0 on failure int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(StrokeCollection.InkSerializedFormat); if (inkServicesFrameworkFormatId != 0) - formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); + s_formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); } /// @@ -264,14 +265,14 @@ static DataFormatsImpl() public static DataFormat GetDataFormat(int id) { // Lock the data format list to obtain the mutual-exclusion. - lock (_formatListlock) + lock (s_formatListlock) { DataFormat formatItem; StringBuilder sb; - for (int i = 0; i < formatList.Count; i++) + for (int i = 0; i < s_formatList.Count; i++) { - formatItem = formatList[i]; + formatItem = s_formatList[i]; // OLE FORMATETC defined CLIPFORMAT as the unsigned short, so we should ignore // high 2bytes to find the matched CLIPFORMAT ID. @@ -291,7 +292,7 @@ public static DataFormat GetDataFormat(int id) // Create a new format and store it formatItem = new(sb.ToString(), id); - formatList.Add(formatItem); + s_formatList.Add(formatItem); return formatItem; } @@ -308,11 +309,11 @@ public static DataFormat GetDataFormat(string format) throw new ArgumentException(SR.DataObject_EmptyFormatNotAllowed); // Lock the data format list to obtain the mutual-exclusion. - lock (_formatListlock) + lock (s_formatListlock) { - for (int i = 0; i < formatList.Count; i++) + for (int i = 0; i < s_formatList.Count; i++) { - DataFormat formatItem = formatList[i]; + DataFormat formatItem = s_formatList[i]; if (formatItem.Name.Equals(format, StringComparison.OrdinalIgnoreCase)) return formatItem; @@ -326,7 +327,7 @@ public static DataFormat GetDataFormat(string format) // Create a new format and store it DataFormat newFormat = new(format, formatId); - formatList.Add(newFormat); + s_formatList.Add(newFormat); return newFormat; } @@ -335,12 +336,12 @@ public static DataFormat GetDataFormat(string format) /// /// List of all registered that we're aware of. /// - private static readonly List formatList; + private static readonly List s_formatList; /// - /// Lock specially used for access to field. + /// Lock specially used for access to field. /// - private static readonly object _formatListlock = new(); + private static readonly Lock s_formatListlock = new(); } } From 2960b6021b9a07d3ea10a55481454aeec9c6f064 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 16 Oct 2024 23:00:29 +0200 Subject: [PATCH 10/11] Adjust the coding style a bit --- .../System/Windows/DataFormats.cs | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index b9670810b48..a19f0bafc17 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -8,6 +8,7 @@ using MS.Internal.PresentationCore; using System.Collections.Generic; +using System.ComponentModel; using System.Windows.Ink; using System.Threading; using System.Text; @@ -244,19 +245,19 @@ static DataFormatsImpl() int xamlFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.Xaml); if (xamlFormatId != 0) - s_formatList.Add(new(DataFormats.Xaml, xamlFormatId)); + s_formatList.Add(new DataFormat(DataFormats.Xaml, xamlFormatId)); // This is the format to store trust boundary information. Essentially this is accompalished by storing // the permission set of the source application where the content comes from. During paste we compare this to // the permission set of the target application. int applicationTrustFormatId = UnsafeNativeMethods.RegisterClipboardFormat(DataFormats.ApplicationTrust); if (applicationTrustFormatId != 0) - s_formatList.Add(new(DataFormats.ApplicationTrust, applicationTrustFormatId)); + s_formatList.Add(new DataFormat(DataFormats.ApplicationTrust, applicationTrustFormatId)); // RegisterClipboardFormat returns 0 on failure int inkServicesFrameworkFormatId = UnsafeNativeMethods.RegisterClipboardFormat(StrokeCollection.InkSerializedFormat); if (inkServicesFrameworkFormatId != 0) - s_formatList.Add(new(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); + s_formatList.Add(new DataFormat(StrokeCollection.InkSerializedFormat, inkServicesFrameworkFormatId)); } /// @@ -268,8 +269,6 @@ public static DataFormat GetDataFormat(int id) lock (s_formatListlock) { DataFormat formatItem; - StringBuilder sb; - for (int i = 0; i < s_formatList.Count; i++) { formatItem = s_formatList[i]; @@ -280,18 +279,16 @@ public static DataFormat GetDataFormat(int id) return formatItem; } - sb = new StringBuilder(NativeMethods.MAX_PATH); - - // This can happen if windows adds a standard format that we don't know about, - // so we should play it safe. - if (UnsafeNativeMethods.GetClipboardFormatName(id, sb, sb.Capacity) == 0) + // This can happen if windows adds a standard format that we don't know about, so we should play it safe. + StringBuilder stringBuilder = new(NativeMethods.MAX_PATH); + if (UnsafeNativeMethods.GetClipboardFormatName(id, stringBuilder, stringBuilder.Capacity) == 0) { - sb.Length = 0; // Same as Clear() - sb.Append("Format").Append(id); + stringBuilder.Length = 0; // Same as Clear() + stringBuilder.Append("Format").Append(id); } // Create a new format and store it - formatItem = new(sb.ToString(), id); + formatItem = new DataFormat(stringBuilder.ToString(), id); s_formatList.Add(formatItem); return formatItem; @@ -323,7 +320,7 @@ public static DataFormat GetDataFormat(string format) int formatId = UnsafeNativeMethods.RegisterClipboardFormat(format); if (formatId == 0) - throw new System.ComponentModel.Win32Exception(); + throw new Win32Exception(); // Create a new format and store it DataFormat newFormat = new(format, formatId); From 9d6f39019b41851814ded93a926751f3d6a768ee Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 16 Oct 2024 23:50:58 +0200 Subject: [PATCH 11/11] Use Span over StringBuilder --- .../System/Windows/DataFormats.cs | 24 +++++++++++++------ .../Shared/MS/Win32/UnsafeNativeMethodsCLR.cs | 6 ++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs index a19f0bafc17..fc58b3d5e11 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/DataFormats.cs @@ -263,7 +263,7 @@ static DataFormatsImpl() /// /// Allows a new format name to be specified if the requested format is not in the list. /// - public static DataFormat GetDataFormat(int id) + internal static unsafe DataFormat GetDataFormat(int id) { // Lock the data format list to obtain the mutual-exclusion. lock (s_formatListlock) @@ -279,16 +279,26 @@ public static DataFormat GetDataFormat(int id) return formatItem; } + const string Format = "Format"; + // This can happen if windows adds a standard format that we don't know about, so we should play it safe. - StringBuilder stringBuilder = new(NativeMethods.MAX_PATH); - if (UnsafeNativeMethods.GetClipboardFormatName(id, stringBuilder, stringBuilder.Capacity) == 0) + // Maximum length can be up to 255: https://learn.microsoft.com/en-us/windows/win32/dataxchg/about-atom-tables + Span formatName = stackalloc char[256]; + int atomLength = 0; + fixed (char* ptrFormatName = formatName) { - stringBuilder.Length = 0; // Same as Clear() - stringBuilder.Append("Format").Append(id); + // If the return value is zero, the ID was not found, hence we will just create a placeholder name "Format{id}" + if ((atomLength = UnsafeNativeMethods.GetClipboardFormatName(id, ptrFormatName, formatName.Length)) == 0) + { + Format.CopyTo(formatName); + id.TryFormat(formatName.Slice(Format.Length), out int charsWritten); + + atomLength = Format.Length + charsWritten; + } } // Create a new format and store it - formatItem = new DataFormat(stringBuilder.ToString(), id); + formatItem = new DataFormat(new string(formatName.Slice(0, atomLength)), id); s_formatList.Add(formatItem); return formatItem; @@ -298,7 +308,7 @@ public static DataFormat GetDataFormat(int id) /// /// Retrieves a data format using its name or attempts to register a new one if it doesn't exist. /// - public static DataFormat GetDataFormat(string format) + internal static DataFormat GetDataFormat(string format) { ArgumentNullException.ThrowIfNull(format); diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs index 3f7a7a66582..e1483f9717a 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs @@ -153,10 +153,10 @@ public static bool DeleteObjectNoThrow(HandleRef hObject) [DllImport(ExternDll.Gdi32, EntryPoint="SelectObject", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)] public static extern IntPtr CriticalSelectObject(HandleRef hdc, IntPtr obj); - [DllImport(ExternDll.User32, CharSet = System.Runtime.InteropServices.CharSet.Auto, BestFitMapping = false, SetLastError = true)] - public static extern int GetClipboardFormatName(int format, StringBuilder lpString, int cchMax); + [DllImport(ExternDll.User32, CharSet = CharSet.Auto, BestFitMapping = false, SetLastError = true)] + public static unsafe extern int GetClipboardFormatName(int format, char* lpString, int cchMax); - [DllImport(ExternDll.User32, SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto, BestFitMapping = false)] + [DllImport(ExternDll.User32, SetLastError = true, CharSet = CharSet.Auto, BestFitMapping = false)] public static extern int RegisterClipboardFormat(string format); [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]