From 2e5f020bcc6b1f4f7e5cbdea0d7fd3199edfd98c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 10:37:23 +0200 Subject: [PATCH 01/11] Move reflection helpers to ReflectionUtils file --- .../Resources/ResourceManagerWrapper.cs | 4 +-- .../Windows/Navigation/BaseUriHelper.cs | 2 +- .../MS/Internal/documents/DocumentsTrace.cs | 2 +- .../System/Windows/SystemResources.cs | 4 +-- .../src/Shared/MS/Internal/ReflectionUtils.cs | 30 +++++++++++++++++ .../Shared/MS/Internal/SafeSecurityHelper.cs | 32 ------------------- .../src/WindowsBase/WindowsBase.csproj | 1 + 7 files changed, 37 insertions(+), 38 deletions(-) create mode 100644 src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs index af85e23588f..f257a11b9b8 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs @@ -234,7 +234,7 @@ private ResourceSet ResourceSet { string manifestResourceName; - manifestResourceName = SafeSecurityHelper.GetAssemblyPartialName(_assembly) + UnLocalizableResourceNameSuffix; + manifestResourceName = ReflectionUtils.GetAssemblyPartialName(_assembly) + UnLocalizableResourceNameSuffix; ResourceManager manager = new ResourceManager(manifestResourceName, this._assembly); _resourceSet = manager.GetResourceSet(CultureInfo.InvariantCulture, true, false); @@ -256,7 +256,7 @@ private ResourceManager ResourceManager { string baseResourceName; // Our build system always generate a resource base name "$(AssemblyShortname).g" - baseResourceName = SafeSecurityHelper.GetAssemblyPartialName(_assembly) + LocalizableResourceNameSuffix; + baseResourceName = ReflectionUtils.GetAssemblyPartialName(_assembly) + LocalizableResourceNameSuffix; _resourceManager = new ResourceManager(baseResourceName, this._assembly); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs index 24647b3e6b3..50ed15e19e0 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs @@ -327,7 +327,7 @@ static internal bool IsComponentEntryAssembly(string component) if (assembly != null) { - return (string.Equals(SafeSecurityHelper.GetAssemblyPartialName(assembly), assemblyName, StringComparison.OrdinalIgnoreCase)); + return (string.Equals(ReflectionUtils.GetAssemblyPartialName(assembly), assemblyName, StringComparison.OrdinalIgnoreCase)); } else { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs index 1b977241a23..dfb17b216c3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs @@ -117,7 +117,7 @@ static DocumentsTrace() public DocumentsTrace(string switchName) { #if DEBUG - string name = SafeSecurityHelper.GetAssemblyPartialName( Assembly.GetCallingAssembly() ); + string name = ReflectionUtils.GetAssemblyPartialName(Assembly.GetCallingAssembly()); _switch = new BooleanSwitch(switchName, $"[{name}]"); #endif } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs index 62bd88447cb..4079dbbb112 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs @@ -571,7 +571,7 @@ internal ResourceDictionaries(Assembly assembly) } else { - _assemblyName = SafeSecurityHelper.GetAssemblyPartialName(assembly); + _assemblyName = ReflectionUtils.GetAssemblyPartialName(assembly); } } @@ -786,7 +786,7 @@ private void LoadExternalAssembly(bool classic, bool generic, out Assembly assem } assemblyName = sb.ToString(); - string fullName = SafeSecurityHelper.GetFullAssemblyNameFromPartialName(_assembly, assemblyName); + string fullName = ReflectionUtils.GetFullAssemblyNameFromPartialName(_assembly, assemblyName); assembly = null; try diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs new file mode 100644 index 00000000000..efcf7e118be --- /dev/null +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -0,0 +1,30 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; + +namespace MS.Internal +{ + internal static class ReflectionUtils + { + /// + /// Given an assembly, returns the partial name of the assembly. + /// + internal static string GetAssemblyPartialName(Assembly assembly) + { + AssemblyName name = new(assembly.FullName); + return name.Name ?? string.Empty; + } + + /// + /// Retrieves the full assembly name by combining the passed in + /// with everything else from . + /// + internal static string GetFullAssemblyNameFromPartialName(Assembly assembly, string partialName) + { + AssemblyName name = new(assembly.FullName) { Name = partialName }; + return name.FullName; + } + } +} diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/SafeSecurityHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/SafeSecurityHelper.cs index d2e70152c82..dc2e66b87ac 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/SafeSecurityHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/SafeSecurityHelper.cs @@ -58,39 +58,7 @@ internal static void TransformLocalRectToScreen(HandleRef hwnd, ref NativeMethod } #endif -#if PRESENTATION_CORE || PRESENTATIONFRAMEWORK ||REACHFRAMEWORK || DEBUG - -#if !WINDOWS_BASE && !SYSTEM_XAML - /// - /// Given an assembly, returns the partial name of the assembly. - /// - internal static string GetAssemblyPartialName(Assembly assembly) - { - AssemblyName name = new AssemblyName(assembly.FullName); - string partialName = name.Name; - return partialName ?? string.Empty; - } -#endif - -#endif - #if PRESENTATIONFRAMEWORK - - /// - /// Get the full assembly name by combining the partial name passed in - /// with everything else from proto assembly. - /// - internal static string GetFullAssemblyNameFromPartialName( - Assembly protoAssembly, - string partialName) - { - AssemblyName name = new AssemblyName(protoAssembly.FullName) - { - Name = partialName - }; - return name.FullName; - } - internal static Point ClientToScreen(UIElement relativeTo, Point point) { GeneralTransform transform; diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj index 9910284e9b2..ab69121ca5b 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/WindowsBase.csproj @@ -34,6 +34,7 @@ + From a1bb1723dba6d197e794f575188ab8d0e2641626 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 10:49:21 +0200 Subject: [PATCH 02/11] Use GetAssemblyPartialName in more places --- .../src/PresentationCore/MS/internal/FontCache/FontSource.cs | 4 ++-- .../System/Windows/InterOp/HwndSourceParameters.cs | 3 ++- .../System/Windows/Markup/Baml2006/Baml2006Reader.cs | 1 - .../System/Windows/Markup/Primitives/MarkupWriter.cs | 4 ++-- .../src/System.Xaml/System/Xaml/XamlSchemaContext.cs | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/FontSource.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/FontSource.cs index 795e3baa52e..4e19fc8aa08 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/FontSource.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/FontCache/FontSource.cs @@ -355,8 +355,8 @@ private Stream GetCompositeFontResourceStream() { string fontFilename = _fontUri.OriginalString.Substring(_fontUri.OriginalString.LastIndexOf('/') + 1).ToLowerInvariant(); - var fontResourceAssembly = Assembly.GetExecutingAssembly(); - ResourceManager rm = new ResourceManager($"{fontResourceAssembly.GetName().Name}.g", fontResourceAssembly); + Assembly fontResourceAssembly = Assembly.GetExecutingAssembly(); + ResourceManager rm = new($"{ReflectionUtils.GetAssemblyPartialName(fontResourceAssembly)}.g", fontResourceAssembly); return rm?.GetStream($"fonts/{fontFilename}"); } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs index fb2aa4183e6..a35f04f0673 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using MS.Win32; +using System.Windows.Media; using System.Windows.Input; namespace System.Windows.Interop @@ -418,7 +419,7 @@ internal static bool PlatformSupportsTransparentChildWindows /// Not intended to be tested outside test code internal static void SetPlatformSupportsTransparentChildWindowsForTestingOnly(bool value) { - if (string.Equals(System.Reflection.Assembly.GetEntryAssembly().GetName().Name, "drthwndsource", StringComparison.CurrentCultureIgnoreCase)) + if (string.Equals(ReflectionUtils.GetAssemblyPartialName(Assembly.GetEntryAssembly()), "drthwndsource", StringComparison.CurrentCultureIgnoreCase)) { _platformSupportsTransparentChildWindows = value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs index e2486208748..d17cdbf6373 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs @@ -2105,7 +2105,6 @@ private string Logic_GetFullXmlns(string uriInput) // Providing the assembly short name may lead to ambiguity between two versions of the same assembly, but we need to // keep it this way since it is exposed publicly via the Namespace property, Baml2006ReaderInternal provides the full Assembly name. - // We need to avoid Assembly.GetName() so we run in PartialTrust without asserting. internal virtual ReadOnlySpan GetAssemblyNameForNamespace(Assembly assembly) { string assemblyLongName = assembly.FullName; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs index 7d405f33707..b8d368261bc 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs @@ -1610,14 +1610,14 @@ public static string GetNamespaceUriFor(Type type) { if (type.Namespace == null) { - result = $"{clrUriPrefix};assembly={type.Assembly.GetName().Name}"; + result = $"{clrUriPrefix};assembly={ReflectionUtils.GetAssemblyPartialName(type.Assembly)}"; } else { Dictionary namespaceToUri = GetMappingsFor(type.Assembly); if (!namespaceToUri.TryGetValue(type.Namespace, out result)) { - result = $"{clrUriPrefix}{type.Namespace};assembly={type.Assembly.GetName().Name}"; + result = $"{clrUriPrefix}{type.Namespace};assembly={ReflectionUtils.GetAssemblyPartialName(type.Assembly)}"; } } } diff --git a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs index 341dbbd63f0..01fd2fec2ec 100644 --- a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs +++ b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs @@ -786,7 +786,6 @@ internal bool AreInternalsVisibleTo(Assembly fromAssembly, Assembly toAssembly) return false; } - // Not using Assembly.GetName() because it doesn't work in partial-trust AssemblyName toAssemblyName = new AssemblyName(toAssembly.FullName); foreach (AssemblyName friend in friends) { From 39ecb7f4437055cd571fb08983b536a786e88762 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 18:57:17 +0200 Subject: [PATCH 03/11] Few more places to use ReflectionUtils in --- .../src/PresentationBuildTasks/PresentationBuildTasks.csproj | 3 +++ .../System/Windows/Markup/XamlTypeMapper.cs | 2 +- .../src/Shared/System/Windows/Markup/ReflectionHelper.cs | 2 +- .../src/WindowsBase/System/Windows/SplashScreen.cs | 5 ++--- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj index 8627c32bddd..8dfa1c1a6ae 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj +++ b/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/PresentationBuildTasks.csproj @@ -85,6 +85,9 @@ Shared\MS\Internal\ResourceIDHelper.cs + + Shared\MS\Internal\ReflectionUtils.cs + Shared\MS\Internal\SecurityHelper.cs diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/XamlTypeMapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/XamlTypeMapper.cs index ad62adecc06..48988657ccf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/XamlTypeMapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/XamlTypeMapper.cs @@ -2481,7 +2481,7 @@ private static bool IsFriendAssembly(Assembly assembly) private static bool IsInternalAllowedOnType(Type type) { - bool isInternalAllowed = ReflectionHelper.LocalAssemblyName == type.Assembly.GetName().Name || + bool isInternalAllowed = ReflectionHelper.LocalAssemblyName == ReflectionUtils.GetAssemblyPartialName(type.Assembly) || IsFriendAssembly(type.Assembly); _hasInternals = _hasInternals || isInternalAllowed; return isInternalAllowed; diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs index 26e3d2bbdcc..a870be17172 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs @@ -504,7 +504,7 @@ internal static bool IsFriendAssembly(Assembly sourceAssembly) #if PBTCOMPILER internal static bool IsInternalAllowedOnType(Type type) { - return ((LocalAssemblyName == type.Assembly.GetName().Name) || IsFriendAssembly(type.Assembly)); + return LocalAssemblyName == ReflectionUtils.GetAssemblyPartialName(type.Assembly) || IsFriendAssembly(type.Assembly); } #endif diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs index 48f0eb8fdde..2a308ff9235 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs @@ -53,9 +53,8 @@ public SplashScreen(Assembly resourceAssembly, string resourceName) } _resourceName = resourceName.ToLowerInvariant(); - _hInstance = (HINSTANCE)Marshal.GetHINSTANCE(resourceAssembly.ManifestModule); - AssemblyName name = new(resourceAssembly.FullName); - _resourceManager = new ResourceManager($"{name.Name}.g", resourceAssembly); + _hInstance = Marshal.GetHINSTANCE(resourceAssembly.ManifestModule); + _resourceManager = new ResourceManager($"{ReflectionUtils.GetAssemblyPartialName(resourceAssembly)}.g", resourceAssembly); } public void Show(bool autoClose) From 55279e59912dd41d4f95e9165b72a2eaa1b9d41c Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 19:07:25 +0200 Subject: [PATCH 04/11] Add some more comments --- .../src/Shared/MS/Internal/ReflectionUtils.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs index efcf7e118be..e3c64dbc6bc 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -6,10 +6,13 @@ namespace MS.Internal { + /// + /// Provides utilities for working with reflection efficiently. + /// internal static class ReflectionUtils { /// - /// Given an assembly, returns the partial name of the assembly. + /// Given an , returns the partial/simple name of the assembly. /// internal static string GetAssemblyPartialName(Assembly assembly) { From 1bf006b23ba856ee57709ab464c8e9ec54e901c7 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 20:07:14 +0200 Subject: [PATCH 05/11] Introduce fast simple assembly name parsing (300ns -> 10ns) --- .../src/Shared/MS/Internal/ReflectionUtils.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs index e3c64dbc6bc..e5248dd6a7c 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Reflection; +using System; namespace MS.Internal { @@ -14,10 +15,37 @@ internal static class ReflectionUtils /// /// Given an , returns the partial/simple name of the assembly. /// +#if !NETFX + internal static ReadOnlySpan GetAssemblyPartialName(Assembly assembly) +#else internal static string GetAssemblyPartialName(Assembly assembly) +#endif { +#if !NETFX + // We know that the input is trusted (it will be properly escaped, with ", " between tokens etc.) + // So we can allow ourselves to do a little trick, where we just find the first separator + // You cannot load an assembly (or define) where name is empty, it needs to be at least 1 character + // But we will keep this for consistency of the previous function, maybe I've missed a class + ReadOnlySpan fullName = assembly.FullName; + if (fullName.IsEmpty) + return string.Empty; + + ReadOnlySpan nameSlice = fullName; + // Skip any escaped commas in the name + int escapedComma = fullName.LastIndexOf("\\,"); + if (escapedComma != -1) + nameSlice = nameSlice.Slice(escapedComma + 2); + + // Find the start of the next token (usually "Version") + int commaIndex = nameSlice.IndexOf(','); + if (commaIndex != -1) + fullName = fullName.Slice(0, fullName.Length - nameSlice.Length + commaIndex); + + return fullName; +#else AssemblyName name = new(assembly.FullName); return name.Name ?? string.Empty; +#endif } /// From 35a3fe65634f693e37495c0f60fc4ee0bcf427da Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 20:22:22 +0200 Subject: [PATCH 06/11] Remove faulty implementations that cannot parse assemblyName with escaped commas --- .../System/Windows/Markup/Baml2006/Baml2006Reader.cs | 3 +-- .../Windows/Markup/Baml2006/Baml2006ReaderInternal.cs | 4 ++-- .../src/System.Xaml/System.Xaml.csproj | 3 +++ .../src/System.Xaml/System/Xaml/MS/Impl/XmlNsInfo.cs | 5 +++-- .../src/System.Xaml/System/Xaml/XamlSchemaContext.cs | 11 +---------- 5 files changed, 10 insertions(+), 16 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs index d17cdbf6373..1c69babb557 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs @@ -2107,8 +2107,7 @@ private string Logic_GetFullXmlns(string uriInput) // keep it this way since it is exposed publicly via the Namespace property, Baml2006ReaderInternal provides the full Assembly name. internal virtual ReadOnlySpan GetAssemblyNameForNamespace(Assembly assembly) { - string assemblyLongName = assembly.FullName; - return assemblyLongName.AsSpan(0, assemblyLongName.IndexOf(',')); + return ReflectionUtils.GetAssemblyPartialName(assembly); } // (prefix, namespaceUri) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006ReaderInternal.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006ReaderInternal.cs index 3fcb932335a..b5b27fd8a01 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006ReaderInternal.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006ReaderInternal.cs @@ -34,9 +34,9 @@ internal Baml2006ReaderInternal( #endregion // Return the full assembly name, this includes the assembly version - internal override ReadOnlySpan GetAssemblyNameForNamespace(Assembly asm) + internal override ReadOnlySpan GetAssemblyNameForNamespace(Assembly assembly) { - return asm.FullName; + return assembly.FullName; } // When processing ResourceDictionary.Source we may find a Uri that references the diff --git a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System.Xaml.csproj b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System.Xaml.csproj index 3c58459914b..7bbbb42c222 100644 --- a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System.Xaml.csproj +++ b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System.Xaml.csproj @@ -24,6 +24,9 @@ Common\WPF\MS\Internal\Xaml\Parser\SpecialBracketCharacters.cs + + Common\WPF\MS\Internal\ReflectionUtils.cs + Common\WPF\MS\Internal\SafeSecurityHelper.cs diff --git a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/MS/Impl/XmlNsInfo.cs b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/MS/Impl/XmlNsInfo.cs index be558a9ee72..db1df2be05b 100644 --- a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/MS/Impl/XmlNsInfo.cs +++ b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/MS/Impl/XmlNsInfo.cs @@ -12,6 +12,8 @@ using System.Windows.Markup; using System.Xaml.Schema; +using MS.Internal; + namespace System.Xaml.MS.Impl { class XmlNsInfo @@ -223,8 +225,7 @@ ConcurrentDictionary> LoadClrToXmlNs() xmlNamespaceList.Add(nsDef.XmlNamespace); } - string assemblyName = _fullyQualifyAssemblyName ? - assembly.FullName : XamlSchemaContext.GetAssemblyShortName(assembly); + string assemblyName = _fullyQualifyAssemblyName ? assembly.FullName : ReflectionUtils.GetAssemblyPartialName(assembly).ToString(); foreach (KeyValuePair> clrToXmlNs in result) { // Sort namespaces in preference order diff --git a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs index 01fd2fec2ec..2f07df756de 100644 --- a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs +++ b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs @@ -1041,8 +1041,7 @@ private ReadOnlyCollection GetXmlNsMappings(Assembly assembly, string cl if (!assemblyMappings.TryGetValue(clrNs, out result)) { - string assemblyName = FullyQualifyAssemblyNamesInClrNamespaces ? - assembly.FullName : GetAssemblyShortName(assembly); + string assemblyName = FullyQualifyAssemblyNamesInClrNamespaces ? assembly.FullName : ReflectionUtils.GetAssemblyPartialName(assembly).ToString(); string xmlns = ClrNamespaceUriParser.GetUri(clrNs, assemblyName); List list = new List(); list.Add(xmlns); @@ -1201,14 +1200,6 @@ bool UpdateNamespaceByUriList(XmlNsInfo nsInfo) #region Helper Methods - // Given an assembly, return the assembly short name. We need to avoid Assembly.GetName() so we run in PartialTrust without asserting. - internal static string GetAssemblyShortName(Assembly assembly) - { - string assemblyLongName = assembly.FullName; - string assemblyShortName = assemblyLongName.Substring(0, assemblyLongName.IndexOf(',')); - return assemblyShortName; - } - internal static ConcurrentDictionary CreateDictionary() { return new ConcurrentDictionary(ConcurrencyLevel, DictionaryCapacity); From 68d80486fe10e1cab3bfaeb150be3a74855f5ec7 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 23:27:04 +0200 Subject: [PATCH 07/11] ReadOnlySpan adjustments, enable nullable context, unescape properly --- .../Resources/ResourceManagerWrapper.cs | 15 +++--- .../Windows/InterOp/HwndSourceParameters.cs | 2 +- .../Windows/Navigation/BaseUriHelper.cs | 2 +- .../MS/Internal/documents/DocumentsTrace.cs | 4 +- .../System/Windows/SystemResources.cs | 2 +- .../src/Shared/MS/Internal/ReflectionUtils.cs | 52 ++++++++++++++----- 6 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs index f257a11b9b8..834ddd5ead3 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/MS/internal/Resources/ResourceManagerWrapper.cs @@ -232,11 +232,10 @@ private ResourceSet ResourceSet { if (_resourceSet == null) { - string manifestResourceName; + //"$(AssemblyShortname).unlocalizable.g" + string manifestResourceName = $"{ReflectionUtils.GetAssemblyPartialName(_assembly)}{UnLocalizableResourceNameSuffix}"; + ResourceManager manager = new(manifestResourceName, _assembly); - manifestResourceName = ReflectionUtils.GetAssemblyPartialName(_assembly) + UnLocalizableResourceNameSuffix; - - ResourceManager manager = new ResourceManager(manifestResourceName, this._assembly); _resourceSet = manager.GetResourceSet(CultureInfo.InvariantCulture, true, false); } @@ -254,11 +253,9 @@ private ResourceManager ResourceManager { if (_resourceManager == null) { - string baseResourceName; // Our build system always generate a resource base name "$(AssemblyShortname).g" - - baseResourceName = ReflectionUtils.GetAssemblyPartialName(_assembly) + LocalizableResourceNameSuffix; - - _resourceManager = new ResourceManager(baseResourceName, this._assembly); + // Our build system always generate a resource base name "$(AssemblyShortname).g" + string baseResourceName = $"{ReflectionUtils.GetAssemblyPartialName(_assembly)}{LocalizableResourceNameSuffix}"; + _resourceManager = new ResourceManager(baseResourceName, _assembly); } return _resourceManager; diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs index a35f04f0673..dcf138563bf 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs @@ -419,7 +419,7 @@ internal static bool PlatformSupportsTransparentChildWindows /// Not intended to be tested outside test code internal static void SetPlatformSupportsTransparentChildWindowsForTestingOnly(bool value) { - if (string.Equals(ReflectionUtils.GetAssemblyPartialName(Assembly.GetEntryAssembly()), "drthwndsource", StringComparison.CurrentCultureIgnoreCase)) + if (ReflectionUtils.GetAssemblyPartialName(Assembly.GetEntryAssembly()).Equals("drthwndsource", StringComparison.CurrentCultureIgnoreCase)) { _platformSupportsTransparentChildWindows = value; } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs index 50ed15e19e0..a2fc6535482 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Navigation/BaseUriHelper.cs @@ -327,7 +327,7 @@ static internal bool IsComponentEntryAssembly(string component) if (assembly != null) { - return (string.Equals(ReflectionUtils.GetAssemblyPartialName(assembly), assemblyName, StringComparison.OrdinalIgnoreCase)); + return ReflectionUtils.GetAssemblyPartialName(assembly).Equals(assemblyName, StringComparison.OrdinalIgnoreCase); } else { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs index dfb17b216c3..6e540a56085 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/MS/Internal/documents/DocumentsTrace.cs @@ -117,8 +117,8 @@ static DocumentsTrace() public DocumentsTrace(string switchName) { #if DEBUG - string name = ReflectionUtils.GetAssemblyPartialName(Assembly.GetCallingAssembly()); - _switch = new BooleanSwitch(switchName, $"[{name}]"); + ReadOnlySpan shortAssemblyName = ReflectionUtils.GetAssemblyPartialName(Assembly.GetCallingAssembly()); + _switch = new BooleanSwitch(switchName, $"[{shortAssemblyName}]"); #endif } diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs index 4079dbbb112..2250e03c903 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/SystemResources.cs @@ -571,7 +571,7 @@ internal ResourceDictionaries(Assembly assembly) } else { - _assemblyName = ReflectionUtils.GetAssemblyPartialName(assembly); + _assemblyName = ReflectionUtils.GetAssemblyPartialName(assembly).ToString(); } } diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs index e5248dd6a7c..9ab42564f28 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -2,7 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + +using System.Runtime.CompilerServices; using System.Reflection; +using System.Text; using System; namespace MS.Internal @@ -12,6 +16,23 @@ namespace MS.Internal /// internal static class ReflectionUtils { + +#if !NETFX + /// + /// Retrieves the full assembly name by combining the passed in + /// with everything else from . + /// + internal static string GetFullAssemblyNameFromPartialName(Assembly assembly, string partialName) + { + ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); + string? fullName = assembly.FullName; + ArgumentNullException.ThrowIfNull(fullName, nameof(Assembly.FullName)); + + AssemblyName name = new(fullName) { Name = partialName }; + return name.FullName; + } +#endif + /// /// Given an , returns the partial/simple name of the assembly. /// @@ -22,40 +43,45 @@ internal static string GetAssemblyPartialName(Assembly assembly) #endif { #if !NETFX + ArgumentNullException.ThrowIfNull(assembly, nameof(assembly)); // We know that the input is trusted (it will be properly escaped, with ", " between tokens etc.) // So we can allow ourselves to do a little trick, where we just find the first separator // You cannot load an assembly (or define) where name is empty, it needs to be at least 1 character // But we will keep this for consistency of the previous function, maybe I've missed a class ReadOnlySpan fullName = assembly.FullName; if (fullName.IsEmpty) - return string.Empty; + return ReadOnlySpan.Empty; ReadOnlySpan nameSlice = fullName; - // Skip any escaped commas in the name + // Skip any escaped commas in the name if present int escapedComma = fullName.LastIndexOf("\\,"); if (escapedComma != -1) nameSlice = nameSlice.Slice(escapedComma + 2); - // Find the start of the next token (usually "Version") + // Find the real ending of the name section int commaIndex = nameSlice.IndexOf(','); if (commaIndex != -1) fullName = fullName.Slice(0, fullName.Length - nameSlice.Length + commaIndex); + // Check if we need to unescape, this is very rare case so we can just do it the dirty way + if (escapedComma != -1 || fullName.Contains('\\')) + UnescapeDirty(ref fullName); + + // Since having "," or "=" in the assembly name is very rare, we don't want to inline + [MethodImpl(MethodImplOptions.NoInlining)] + static void UnescapeDirty(ref ReadOnlySpan dirtyName) + { + StringBuilder sb = new(dirtyName.Length); + sb.Append(dirtyName); + sb.Replace("\\", null); + dirtyName = sb.ToString(); + } + return fullName; #else AssemblyName name = new(assembly.FullName); return name.Name ?? string.Empty; #endif } - - /// - /// Retrieves the full assembly name by combining the passed in - /// with everything else from . - /// - internal static string GetFullAssemblyNameFromPartialName(Assembly assembly, string partialName) - { - AssemblyName name = new(assembly.FullName) { Name = partialName }; - return name.FullName; - } } } From e89e672bde7f83a20d978e7582a01a431cc8e62a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Sat, 7 Sep 2024 23:47:17 +0200 Subject: [PATCH 08/11] Let's not forget this shall be Ordinal search --- .../src/Shared/MS/Internal/ReflectionUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs index 9ab42564f28..fa735a87ce0 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -54,7 +54,7 @@ internal static string GetAssemblyPartialName(Assembly assembly) ReadOnlySpan nameSlice = fullName; // Skip any escaped commas in the name if present - int escapedComma = fullName.LastIndexOf("\\,"); + int escapedComma = fullName.LastIndexOf("\\,", StringComparison.Ordinal); if (escapedComma != -1) nameSlice = nameSlice.Slice(escapedComma + 2); From 27d10167c0adcdf2f453dba5bff160a22a395dfa Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Thu, 17 Oct 2024 12:52:33 +0200 Subject: [PATCH 09/11] Fix missing using after merge conflict --- .../src/System.Xaml/System/Xaml/XamlSchemaContext.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs index 2f07df756de..cd3e66af26c 100644 --- a/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs +++ b/src/Microsoft.DotNet.Wpf/src/System.Xaml/System/Xaml/XamlSchemaContext.cs @@ -8,6 +8,7 @@ using System.Collections.ObjectModel; using System.Reflection; using System.Text; +using MS.Internal; using System.Threading; using System.Xaml.MS.Impl; using System.Xaml.Schema; From cf71a1f3bd4e55aa09afde9ac55d04688343139a Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 15 Jan 2025 20:02:52 +0100 Subject: [PATCH 10/11] Address post-merge using issues --- .../System/Windows/InterOp/HwndSourceParameters.cs | 6 +++--- .../System/Windows/Markup/Baml2006/Baml2006Reader.cs | 1 + .../System/Windows/Markup/Primitives/MarkupWriter.cs | 6 +++--- .../src/Shared/MS/Internal/ReflectionUtils.cs | 1 - .../src/WindowsBase/System/Windows/SplashScreen.cs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs index dcf138563bf..d944f4d3c73 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/InterOp/HwndSourceParameters.cs @@ -1,10 +1,10 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -using MS.Win32; -using System.Windows.Media; using System.Windows.Input; +using System.Reflection; +using MS.Internal; +using MS.Win32; namespace System.Windows.Interop { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs index 1c69babb557..f44ff2cf32e 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Baml2006/Baml2006Reader.cs @@ -12,6 +12,7 @@ using System.Globalization; using XamlReaderHelper = System.Windows.Markup.XamlReaderHelper; using System.Runtime.CompilerServices; +using MS.Internal; namespace System.Windows.Baml2006 { diff --git a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs index b8d368261bc..a9fbbee4e27 100644 --- a/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs +++ b/src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Markup/Primitives/MarkupWriter.cs @@ -1,17 +1,17 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. // // Contents: XAML writer // +using System.Xml.Serialization; using System.ComponentModel; -using System.Reflection; using System.Collections; +using System.Reflection; +using MS.Internal; using System.Text; using System.Xml; -using System.Xml.Serialization; namespace System.Windows.Markup.Primitives { diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs index fa735a87ce0..2b81fa40701 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -16,7 +16,6 @@ namespace MS.Internal /// internal static class ReflectionUtils { - #if !NETFX /// /// Retrieves the full assembly name by combining the passed in diff --git a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs index 2a308ff9235..cd5f3dc095f 100644 --- a/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs +++ b/src/Microsoft.DotNet.Wpf/src/WindowsBase/System/Windows/SplashScreen.cs @@ -53,7 +53,7 @@ public SplashScreen(Assembly resourceAssembly, string resourceName) } _resourceName = resourceName.ToLowerInvariant(); - _hInstance = Marshal.GetHINSTANCE(resourceAssembly.ManifestModule); + _hInstance = (HINSTANCE)Marshal.GetHINSTANCE(resourceAssembly.ManifestModule); _resourceManager = new ResourceManager($"{ReflectionUtils.GetAssemblyPartialName(resourceAssembly)}.g", resourceAssembly); } From 7e326da2c4c20738a04b91c88dfc0cddb5d7b196 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Fri, 17 Jan 2025 11:30:52 +0100 Subject: [PATCH 11/11] Fix fallback mechanism for escapes --- .../src/Shared/MS/Internal/ReflectionUtils.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs index 2b81fa40701..ef7fa223ce6 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/ReflectionUtils.cs @@ -5,8 +5,8 @@ #nullable enable using System.Runtime.CompilerServices; +using System.Reflection.Metadata; using System.Reflection; -using System.Text; using System; namespace MS.Internal @@ -67,13 +67,11 @@ internal static string GetAssemblyPartialName(Assembly assembly) UnescapeDirty(ref fullName); // Since having "," or "=" in the assembly name is very rare, we don't want to inline + // and we will fallback to the runtime implementation to handle such case for us [MethodImpl(MethodImplOptions.NoInlining)] static void UnescapeDirty(ref ReadOnlySpan dirtyName) { - StringBuilder sb = new(dirtyName.Length); - sb.Append(dirtyName); - sb.Replace("\\", null); - dirtyName = sb.ToString(); + dirtyName = !AssemblyNameInfo.TryParse(dirtyName, out AssemblyNameInfo? result) ? ReadOnlySpan.Empty : result.Name; } return fullName;