| File: System\Resources\ResXSerializationBinder.cs | Web Access |
| Project: src\winforms\src\System.Windows.Forms\System.Windows.Forms.csproj (System.Windows.Forms) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.ComponentModel.Design; using System.Reflection.Metadata; using System.Runtime.Serialization; namespace System.Resources; /// <summary> /// This class implements a partial type resolver for the BinaryFormatter can provide custom type name binding to /// or from types. /// </summary> /// <remarks> /// <para> /// The key usage of this type is to attempt to redirect to / from .NET Framework type names. /// </para> /// </remarks> internal class ResXSerializationBinder : SerializationBinder { private readonly ITypeResolutionService? _typeResolver; private readonly Func<Type?, string>? _typeNameConverter; /// <param name="typeResolver"> /// The custom type resolution service used to bind names to a specific <see cref="Type"/>. Only /// <see cref="ITypeResolutionService.GetType(string)"/> is called by this binder. /// </param> internal ResXSerializationBinder(ITypeResolutionService? typeResolver) => _typeResolver = typeResolver; /// <param name="typeNameConverter"> /// The type name converter to use for binding a <see cref="Type"/> to a custom name. This is passed in through /// constructors on <see cref="ResXDataNode"/> such as <see cref="ResXDataNode(string, object?, Func{Type?, string}?)"/> /// </param> internal ResXSerializationBinder(Func<Type?, string>? typeNameConverter) => _typeNameConverter = typeNameConverter; public override Type? BindToType( string assemblyName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] string typeName) { if (_typeResolver is null || !TypeName.TryParse($"{typeName}, {assemblyName}".AsSpan(), out TypeName? parsed)) { // cs/deserialization/nullbindtotype return null; // CodeQL [SM04225] : This class is meant to redirect to .NET Framework type names. If this cannot be done, the default binder should be used. } Type? type = _typeResolver.GetType(parsed.AssemblyQualifiedName); if (type is not null) { return type; } if (parsed.AssemblyName is { } assemblyNameInfo) { // Try the name without the version. string fullyQualifiedNameWithoutVersion = $"{typeName}, {new AssemblyNameInfo( assemblyNameInfo.Name, version: null, assemblyNameInfo.CultureName, assemblyNameInfo.Flags, assemblyNameInfo.PublicKeyOrToken).FullName}"; type = _typeResolver.GetType(fullyQualifiedNameWithoutVersion); } // If that didn't work, try the simple name. type ??= _typeResolver.GetType(parsed.FullName); // Hand back what we found or null to let the default loader take over. // cs/deserialization/nullbindtotype return type; // CodeQL[SM04225] : This binder isn't intended as a security facility; it's allowable for us to return null. } public override void BindToName(Type serializedType, out string? assemblyName, out string? typeName) { // Normally, we don't change the type name when changing the target framework, only the assembly name. // Setting the out values to null indicates that we want default handling. if (_typeNameConverter is not null) { // Allow the specified type name converter to modify the type name. string? assemblyQualifiedTypeName = MultitargetUtil.GetAssemblyQualifiedName(serializedType, _typeNameConverter); if (!string.IsNullOrEmpty(assemblyQualifiedTypeName) && TypeName.TryParse(assemblyQualifiedTypeName.AsSpan(), out TypeName? parsed) && parsed.AssemblyName is { } assemblyInfo) { // Set the custom assembly name. assemblyName = assemblyInfo.FullName; // Customize the type name only if it changed. typeName = string.Equals(parsed.FullName, serializedType.FullName, StringComparison.Ordinal) ? null : parsed.FullName; return; } } base.BindToName(serializedType, out assemblyName, out typeName); } }