From f5fc478731ac76579c5257dac4677022cfa832e0 Mon Sep 17 00:00:00 2001 From: Mike-EEE Date: Thu, 6 Feb 2020 15:21:39 -0500 Subject: [PATCH] Adjusted assembly-loading to be a bit more robust for .NETFramework-based GAC assemblies. --- .../ReflectionModel/AssemblyLoader.cs | 39 ++++++++---- .../ReflectionModel/AssemblyPath.cs | 63 +++++++++++++++++++ ...dXmlSerializer.Tests.ReportedIssues.csproj | 12 ---- .../Issue366Tests.cs | 37 +++++++++++ 4 files changed, 126 insertions(+), 25 deletions(-) create mode 100644 src/ExtendedXmlSerializer/ReflectionModel/AssemblyPath.cs create mode 100644 test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue366Tests.cs diff --git a/src/ExtendedXmlSerializer/ReflectionModel/AssemblyLoader.cs b/src/ExtendedXmlSerializer/ReflectionModel/AssemblyLoader.cs index 187aecdb8..3e572e494 100644 --- a/src/ExtendedXmlSerializer/ReflectionModel/AssemblyLoader.cs +++ b/src/ExtendedXmlSerializer/ReflectionModel/AssemblyLoader.cs @@ -1,4 +1,5 @@ -using System.Collections.Immutable; +using System; +using System.Collections.Immutable; using System.IO; using System.Reflection; @@ -6,13 +7,18 @@ namespace ExtendedXmlSerializer.ReflectionModel { sealed class AssemblyLoader : IAssemblyLoader { - readonly ImmutableArray _loaded; public static AssemblyLoader Default { get; } = new AssemblyLoader(); - AssemblyLoader() : this(AssemblyProvider.Default.Get() - .ToImmutableArray()) {} + AssemblyLoader() : this(AssemblyPath.Default.Get, AssemblyProvider.Default.Get().ToImmutableArray()) {} - AssemblyLoader(ImmutableArray loaded) => _loaded = loaded; + AssemblyLoader(Func path, ImmutableArray loaded) + { + _path = path; + _loaded = loaded; + } + + readonly Func _path; + readonly ImmutableArray _loaded; public Assembly Get(string parameter) { @@ -22,18 +28,25 @@ public Assembly Get(string parameter) } catch (FileNotFoundException) { - var length = _loaded.Length; - for (var i = 0; i < length; i++) + try { - var assembly = _loaded[i]; - if (assembly.GetName() - .Name == parameter) + return Assembly.LoadFile(_path(parameter)); + } + catch + { + var length = _loaded.Length; + for (var i = 0; i < length; i++) { - return assembly; + var assembly = _loaded[i]; + if (assembly.GetName() + .Name == parameter) + { + return assembly; + } } - } - throw; + throw; + } } } } diff --git a/src/ExtendedXmlSerializer/ReflectionModel/AssemblyPath.cs b/src/ExtendedXmlSerializer/ReflectionModel/AssemblyPath.cs new file mode 100644 index 000000000..bd7cf8419 --- /dev/null +++ b/src/ExtendedXmlSerializer/ReflectionModel/AssemblyPath.cs @@ -0,0 +1,63 @@ +using ExtendedXmlSerializer.Core.Sources; +using System; +using System.Runtime.InteropServices; + +namespace ExtendedXmlSerializer.ReflectionModel +{ + /// + /// Attribution: https://stackoverflow.com/a/6131116/10340424 + /// + sealed class AssemblyPath : IParameterizedSource + { + public static AssemblyPath Default { get; } = new AssemblyPath(); + + AssemblyPath() {} + + public string Get(string parameter) + { + if (parameter == null) + { + throw new ArgumentNullException(nameof(parameter)); + } + + var finalName = parameter; + var aInfo = new AssemblyInfo {cchBuf = 1024}; // should be fine... + aInfo.currentAssemblyPath = new string('\0', aInfo.cchBuf); + + var hr = CreateAssemblyCache(out var ac, 0); + if (hr >= 0) + { + hr = ac.QueryAssemblyInfo(0, finalName, ref aInfo); + if (hr < 0) + { + return null; + } + } + + return aInfo.currentAssemblyPath; + } + + [DllImport("fusion.dll")] + static extern int CreateAssemblyCache(out IAssemblyCache ppAsmCache, int reserved); + + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("e707dcde-d1cd-11d2-bab9-00c04f8eceae")] + interface IAssemblyCache + { + void Reserved0(); + + [PreserveSig] + int QueryAssemblyInfo(int flags, [MarshalAs(UnmanagedType.LPWStr)] string assemblyName, + ref AssemblyInfo assemblyInfo); + } + + [StructLayout(LayoutKind.Sequential)] + struct AssemblyInfo + { + readonly int cbAssemblyInfo; + readonly int assemblyFlags; + readonly long assemblySizeInKB; + [MarshalAs(UnmanagedType.LPWStr)] public string currentAssemblyPath; + public int cchBuf; // size of path buf. + } + } +} \ No newline at end of file diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/ExtendedXmlSerializer.Tests.ReportedIssues.csproj b/test/ExtendedXmlSerializer.Tests.ReportedIssues/ExtendedXmlSerializer.Tests.ReportedIssues.csproj index 7fd2b9b09..e296cc12e 100644 --- a/test/ExtendedXmlSerializer.Tests.ReportedIssues/ExtendedXmlSerializer.Tests.ReportedIssues.csproj +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/ExtendedXmlSerializer.Tests.ReportedIssues.csproj @@ -29,18 +29,6 @@ - - - - True - - - - - - - - diff --git a/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue366Tests.cs b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue366Tests.cs new file mode 100644 index 000000000..f40470d3c --- /dev/null +++ b/test/ExtendedXmlSerializer.Tests.ReportedIssues/Issue366Tests.cs @@ -0,0 +1,37 @@ +using ExtendedXmlSerializer.Configuration; +using ExtendedXmlSerializer.Tests.ReportedIssues.Support; +using FluentAssertions; +using System.Drawing; +using Xunit; + +namespace ExtendedXmlSerializer.Tests.ReportedIssues +{ + public sealed class Issue366Tests + { + [Fact] + void Verify() + { + var instance = new Model(); + new ConfigurationContainer().Create() + .ForTesting() + .Cycle(instance) + .Should() + .BeEquivalentTo(instance); + } + + public class Model + { + public Model() + { + NestedModel = new NestedModel(); + } + + public NestedModel NestedModel { get; set; } + } + + public class NestedModel + { + public Size Size { get; set; } + } + } +} \ No newline at end of file