зеркало из https://github.com/dotnet/wpf.git
Add RetargetablePathAssemblyResolver
This commit is contained in:
Родитель
05ce539a03
Коммит
99d9347a9b
|
@ -98,6 +98,9 @@
|
|||
<Compile Include="$(WpfSharedDir)\System\Windows\Markup\ReflectionHelper.cs">
|
||||
<Link>Shared\System\Windows\Markup\ReflectionHelper.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(WpfSharedDir)\System\Windows\Markup\RetargetablePathAssemblyResolver.cs">
|
||||
<Link>Shared\System\Windows\Markup\RetargetablePathAssemblyResolver.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="$(WpfSharedDir)\MS\Internal\Xaml\Parser\SpecialBracketCharacters.cs">
|
||||
<Link>Shared\MS\Internal\Xaml\Parser\SpecialBracketCharacters.cs</Link>
|
||||
</Compile>
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace System.Xaml
|
|||
// System.Reflection.MetadataLoadContext Assembly cache
|
||||
_cachedMetadataLoadContextAssemblies = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
|
||||
_cachedMetadataLoadContextAssembliesByNameNoExtension = new Dictionary<string, Assembly>(StringComparer.OrdinalIgnoreCase);
|
||||
_metadataLoadContext = new MetadataLoadContext(new PathAssemblyResolver(assemblyPaths), MscorlibReflectionAssemblyName);
|
||||
_metadataLoadContext = new MetadataLoadContext(new RetargetablePathAssemblyResolver(assemblyPaths), MscorlibReflectionAssemblyName);
|
||||
_localAssemblyName = string.Empty;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
// 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.Collections.Generic;
|
||||
using System.IO;
|
||||
|
||||
namespace System.Reflection
|
||||
{
|
||||
/// <summary>
|
||||
/// An assembly resolver that uses paths to every assembly that may be loaded.
|
||||
/// The file name is expected to be the same as the assembly's simple name.
|
||||
/// Multiple assemblies can exist on disk with the same name but in different directories.
|
||||
/// A single instance of RetargetablePathAssemblyResolver can be used with multiple MetadataAssemblyResolver instances.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In order for an AssemblyName to match to a loaded assembly, AssemblyName.Name must be equal (casing ignored).
|
||||
/// - If AssemblyName.PublicKeyToken is specified, it must be equal.
|
||||
/// - If AssemblyName.PublicKeyToken is not specified, assemblies with no PublicKeyToken are selected over those with a PublicKeyToken.
|
||||
/// - If more than one assembly matches, the assembly with the highest Version is returned.
|
||||
/// - CultureName is ignored.
|
||||
/// </remarks>
|
||||
public class RetargetablePathAssemblyResolver : MetadataAssemblyResolver
|
||||
{
|
||||
private readonly Dictionary<string, List<string>> _fileToPaths = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="System.Reflection.RetargetablePathAssemblyResolver"/> class.
|
||||
/// </summary>
|
||||
/// <exception cref="System.ArgumentNullException">Thrown when assemblyPaths is null.</exception>
|
||||
/// <exception cref="System.ArgumentException">Thrown when a path is invalid.</exception>
|
||||
public RetargetablePathAssemblyResolver(IEnumerable<string> assemblyPaths)
|
||||
{
|
||||
if (assemblyPaths == null)
|
||||
throw new ArgumentNullException(nameof(assemblyPaths));
|
||||
|
||||
foreach (string path in assemblyPaths)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
throw new ArgumentException(path);
|
||||
|
||||
string file = Path.GetFileNameWithoutExtension(path);
|
||||
|
||||
if (file.Length == 0)
|
||||
throw new ArgumentException(path);
|
||||
|
||||
List<string> paths;
|
||||
if (!_fileToPaths.TryGetValue(file, out paths))
|
||||
{
|
||||
_fileToPaths.Add(file, paths = new List<string>());
|
||||
}
|
||||
paths.Add(path);
|
||||
}
|
||||
}
|
||||
|
||||
public override Assembly Resolve(MetadataLoadContext context, AssemblyName assemblyName)
|
||||
{
|
||||
Assembly candidateWithSamePkt = null;
|
||||
Assembly candidateIgnoringPkt = null;
|
||||
if (_fileToPaths.TryGetValue(assemblyName.Name, out List<string> paths))
|
||||
{
|
||||
ReadOnlySpan<byte> pktFromName = assemblyName.GetPublicKeyToken();
|
||||
bool isRetargetableAssembly = ((assemblyName.Flags & AssemblyNameFlags.Retargetable) != 0);
|
||||
|
||||
foreach (string path in paths)
|
||||
{
|
||||
Assembly assemblyFromPath = context.LoadFromAssemblyPath(path);
|
||||
AssemblyName assemblyNameFromPath = assemblyFromPath.GetName();
|
||||
if (assemblyName.Name.Equals(assemblyNameFromPath.Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
ReadOnlySpan<byte> pktFromAssembly = assemblyNameFromPath.GetPublicKeyToken();
|
||||
|
||||
// Find exact match on PublicKeyToken including treating no PublicKeyToken as its own entry.
|
||||
if (pktFromName.SequenceEqual(pktFromAssembly))
|
||||
{
|
||||
// Pick the highest version.
|
||||
if (candidateWithSamePkt == null || assemblyNameFromPath.Version > candidateWithSamePkt.GetName().Version)
|
||||
{
|
||||
candidateWithSamePkt = assemblyFromPath;
|
||||
}
|
||||
}
|
||||
// If assemblyName does not specify a PublicKeyToken, or assemblyName is marked 'Retargetable',
|
||||
// then still consider those with a PublicKeyToken, and take the highest version available.
|
||||
else if (candidateWithSamePkt == null && (pktFromName.IsEmpty || isRetargetableAssembly))
|
||||
{
|
||||
// Pick the highest version.
|
||||
if (candidateIgnoringPkt == null || assemblyNameFromPath.Version > candidateIgnoringPkt.GetName().Version)
|
||||
{
|
||||
candidateIgnoringPkt = assemblyFromPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return candidateWithSamePkt ?? candidateIgnoringPkt;
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче