Skip to content

Commit

Permalink
fixes a bug with unloadable declared types
Browse files Browse the repository at this point in the history
  • Loading branch information
replaysMike committed Jun 9, 2020
1 parent 1d17509 commit 3672a6b
Show file tree
Hide file tree
Showing 7 changed files with 215 additions and 73 deletions.
15 changes: 15 additions & 0 deletions AssemblyInfo/AssemblyInfo/AssemblyData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
{
public class AssemblyData
{
public AssemblyData() { }
public AssemblyData(string message)
{
Messages = message;
}

public string Name { get; set; }
public string ProductName { get; set; }
public string FileDescription { get; set; }
Expand Down Expand Up @@ -32,6 +38,15 @@ public class AssemblyData
public string Metadata { get; set; }
public string FullPath { get; set; }
public string Filename { get; set; }
public string Dependencies { get; set; }
public string DeclaredTypes { get; set; }
public string EmbeddedResources { get; set; }

/// <summary>
/// Warnings and Errors
/// </summary>
public string Messages { get; set; }

public override string ToString() => Name;
}
}
28 changes: 28 additions & 0 deletions AssemblyInfo/AssemblyInfo/AssemblyInfo.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
<Deterministic>true</Deterministic>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.1.1.0</ApplicationVersion>
<IsWebBootstrapper>false</IsWebBootstrapper>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>x64</PlatformTarget>
Expand Down Expand Up @@ -61,6 +76,7 @@
<HintPath>..\packages\PInvoke.Windows.Core.0.6.6\lib\net20\PInvoke.Windows.Core.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.ComponentModel.Composition" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
Expand Down Expand Up @@ -128,5 +144,17 @@
<ItemGroup>
<Content Include="AssemblyInfo_128.ico" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include=".NETFramework,Version=v4.8">
<Visible>False</Visible>
<ProductName>Microsoft .NET Framework 4.8 %28x86 and x64%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
224 changes: 157 additions & 67 deletions AssemblyInfo/AssemblyInfo/AssemblyInspector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@

namespace AssemblyInfo
{
[Serializable]
public class AssemblyInspector
{
/// <summary>
/// Max 1GB hash inspection length
/// </summary>
private const int MaxHashInspectionLength = 1 * 1024 * 1024 * 1024;

private List<Assembly> ResolvedAssemblies = new List<Assembly>();
private string _filename = string.Empty;

public AssemblyData Inspect(string filename)
{
_filename = filename;
if (File.Exists(filename))
return InspectAssembly(filename);
throw new FileNotFoundException(filename);
Expand All @@ -28,13 +33,13 @@ public AssemblyData Inspect(string filename)
private AssemblyData InspectAssembly(string filename)
{
var fileExtension = Path.GetExtension(filename).ToLower();
Assembly assembly;
Assembly assembly = null;
try
{
switch (fileExtension)
{
case ".msi":
return InspectMsi(filename);
return InspectMsi(filename, new AssemblyData());
case ".jpg":
case ".jpeg":
case ".gif":
Expand All @@ -51,27 +56,54 @@ private AssemblyData InspectAssembly(string filename)
case ".qt":
case ".mpeg":
case ".m4v":
return new MediaInspector().InspectMedia(InspectFile(filename), filename);
return new MediaInspector().InspectMedia(InspectFile(filename, new AssemblyData()), filename);
default:
AppDomain.CurrentDomain.AssemblyLoad += CurrentDomain_AssemblyLoad;
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
assembly = Assembly.LoadFrom(filename);
break;
}
}
}
catch (FileLoadException ex)
{
return InspectFile(filename);
return InspectFile(filename, new AssemblyData($"Could not load file! {ex.Message}"));
}
catch (BadImageFormatException ex)
{
return InspectFile(filename);
return InspectFile(filename, new AssemblyData($"Unknown assembly type. {ex.Message}"));
}

return InspectAssembly(assembly);
return InspectAssembly(assembly, new AssemblyData());
}

private void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
{
if (!ResolvedAssemblies.Contains(args.LoadedAssembly))
ResolvedAssemblies.Add(args.LoadedAssembly);
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
if (args.Name.Contains(".resources"))
return null;
if (ResolvedAssemblies.Any(x => x.FullName == args.Name))
return ResolvedAssemblies.FirstOrDefault(x => x.FullName.Equals(args.Name));
var tokens = args.Name.Split(",".ToCharArray());
var assemblyFile = Path.Combine(new string[] { Path.GetDirectoryName(_filename), tokens[0] + ".dll" });
System.Diagnostics.Debug.WriteLine("Resolving : " + args.Name);
Assembly assembly;
if (File.Exists(assemblyFile))
{
assembly = Assembly.LoadFile(assemblyFile);
ResolvedAssemblies.Add(assembly);
return assembly;
}
return null;
}

private AssemblyData InspectMsi(string filename)
private AssemblyData InspectMsi(string filename, AssemblyData assemblyData)
{
var data = InspectFile(filename);
var data = InspectFile(filename, assemblyData);
var inspector = new MsiInspector();
var allProperties = new Dictionary<string, List<Dictionary<int, string>>>();
var tables = inspector.GetAllProperties(filename, "_Tables");
Expand Down Expand Up @@ -113,36 +145,35 @@ private string GetProperty(List<Dictionary<int, string>> properties, string name
return properties.Select(x => x.Values).FirstOrDefault(x => x.Skip(1).FirstOrDefault() == name).Skip(2).FirstOrDefault();
}

private AssemblyData InspectFile(string filename)
private AssemblyData InspectFile(string filename, AssemblyData assemblyData)
{
var fileInfo = new FileInfo(filename);
var fileVersion = FileVersionInfo.GetVersionInfo(filename);
var data = new AssemblyData
{
Name = Path.GetFileName(filename),
Filename = Path.GetFileName(filename),
ProductName = fileVersion.ProductName,
FileDescription = fileVersion.FileDescription,
Description = fileVersion.Comments,
Company = fileVersion.CompanyName,
FileVersion = fileVersion.FileVersion,
ProductVersion = fileVersion.ProductVersion,
Copyright = fileVersion.LegalCopyright,
IsDebug = fileVersion.IsDebug,
IsPatched = fileVersion.IsPatched,
IsPreRelease = fileVersion.IsPreRelease,
Language = fileVersion.Language,
OriginalFilename = fileVersion.OriginalFilename,
FileSize = Util.BytesToString(fileInfo.Length),
FileLength = fileInfo.Length,
FullPath = filename
};

assemblyData.Name = Path.GetFileName(filename);
assemblyData.Filename = Path.GetFileName(filename);
assemblyData.ProductName = fileVersion.ProductName;
assemblyData.FileDescription = fileVersion.FileDescription;
assemblyData.Description = fileVersion.Comments;
assemblyData.Company = fileVersion.CompanyName;
assemblyData.FileVersion = fileVersion.FileVersion;
assemblyData.ProductVersion = fileVersion.ProductVersion;
assemblyData.Copyright = fileVersion.LegalCopyright;
assemblyData.IsDebug = fileVersion.IsDebug;
assemblyData.IsPatched = fileVersion.IsPatched;
assemblyData.IsPreRelease = fileVersion.IsPreRelease;
assemblyData.Language = fileVersion.Language;
assemblyData.OriginalFilename = fileVersion.OriginalFilename;
assemblyData.FileSize = Util.BytesToString(fileInfo.Length);
assemblyData.FileLength = fileInfo.Length;
assemblyData.FullPath = filename;

if (fileInfo.Length < MaxHashInspectionLength)
ComputeHash(data, filename);
return data;
ComputeHash(assemblyData, filename);
return assemblyData;
}

private AssemblyData InspectAssembly(Assembly assembly)
private AssemblyData InspectAssembly(Assembly assembly, AssemblyData assemblyData)
{
var fileInfo = new FileInfo(assembly.Location);
var assemblyName = assembly.GetName();
Expand All @@ -154,7 +185,7 @@ private AssemblyData InspectAssembly(Assembly assembly)
var assemblyInformationalVersion = assembly.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(AssemblyInformationalVersionAttribute));
var assemblyMetadatas = assembly.CustomAttributes.Where(x => x.AttributeType == typeof(AssemblyMetadataAttribute)).ToList();
var debuggable = assembly.CustomAttributes.FirstOrDefault(x => x.AttributeType == typeof(DebuggableAttribute));
var debuggableAttributeArguments = debuggable.ConstructorArguments.FirstOrDefault();
var debuggableAttributeArguments = debuggable?.ConstructorArguments.FirstOrDefault();

var frameworkString = targetFramework?.ConstructorArguments?.FirstOrDefault().Value.ToString();
var targetFrameworkName = frameworkString
Expand All @@ -169,40 +200,99 @@ private AssemblyData InspectAssembly(Assembly assembly)
var metadata = assemblyMetadatas?.SelectMany(x => x.ConstructorArguments.Select(y => y.Value.ToString()));
var metadataKeys = metadata?.Where((x, i) => i % 2 == 0).ToList();
var metadataValues = metadata?.Skip(1).Where((x, i) => i % 2 == 0).ToList();
var data = new AssemblyData
var declaredTypes = string.Empty;
try
{
Name = assembly.FullName,
Filename = Path.GetFileName(assembly.Location),
ProductName = fileVersion.ProductName,
FileDescription = fileVersion.FileDescription,
Description = fileVersion.Comments,
Company = fileVersion.CompanyName,
Version = version.ToString(),
FileVersion = fileVersion.FileVersion,
ProductVersion = fileVersion.ProductVersion,
IsClsCompliant = (bool?)clsCompliant?.ConstructorArguments?.FirstOrDefault().Value ?? false,
InformationalVersion = assemblyInformationalVersion?.ConstructorArguments?.FirstOrDefault().Value.ToString(),
Metadata = string.Join(",", metadataKeys.Select((x, i) => $"{x}={metadataValues[i].ToString()}")),
IsStronglyNamed = assemblyName.GetPublicKeyToken().Length > 0,
PublicKeyToken = string.Join("", assemblyName.GetPublicKeyToken().Select(b => b.ToString("x2"))),
Framework = targetFrameworkName,
FrameworkVersion = targetFrameworkVersion,
Copyright = fileVersion.LegalCopyright,
Build = assemblyConfiguration?.ConstructorArguments?.FirstOrDefault().Value.ToString(),
IsDebug = fileVersion.IsDebug,
DebuggableModes = debuggableAttributeArguments != null ? ((DebuggableAttribute.DebuggingModes)debuggableAttributeArguments.Value).ToString() : string.Empty,
IsPatched = fileVersion.IsPatched,
IsPreRelease = fileVersion.IsPreRelease,
Language = fileVersion.Language,
OriginalFilename = fileVersion.OriginalFilename,
FileSize = Util.BytesToString(fileInfo.Length),
FileLength = fileInfo.Length,
FullPath = assembly.Location
};

if (data.FileLength < MaxHashInspectionLength)
ComputeHash(data, assembly.Location);
return data;
declaredTypes = string.Join(Environment.NewLine, assembly.DefinedTypes.Select(x => BuildTypeName(x)));
}
catch (ReflectionTypeLoadException ex)
{
declaredTypes = string.Join(Environment.NewLine, ex.Types.Select(x => BuildTypeName(x)));
declaredTypes += $"{Environment.NewLine}LOAD FAILED TYPES{Environment.NewLine}";
foreach(var loaderException in ex.LoaderExceptions)
declaredTypes += $"{loaderException.GetBaseException().Message}{Environment.NewLine}";
}
var assemblyNames = assembly.GetReferencedAssemblies();
var loadedAssemblies = assemblyNames.Select(x =>
{
Assembly loadedAssembly = null;
try
{
loadedAssembly = Assembly.Load(x);
}
catch (Exception) { }
return new { Name = x.FullName, Assembly = loadedAssembly };
}).ToList();
var dependentAssemblies = ResolvedAssemblies.Distinct().Select(x => new { Name = x.FullName, Assembly = x }).ToList();
var dependencies = string.Join(Environment.NewLine, dependentAssemblies.Select(x => $"{x.Name}, {x.Assembly?.Location}"));
var embeddedResources = assembly.GetManifestResourceNames();

assemblyData.Name = assembly.FullName;
assemblyData.Filename = Path.GetFileName(assembly.Location);
assemblyData.ProductName = fileVersion.ProductName;
assemblyData.FileDescription = fileVersion.FileDescription;
assemblyData.Description = fileVersion.Comments;
assemblyData.Company = fileVersion.CompanyName;
assemblyData.Version = version.ToString();
assemblyData.FileVersion = fileVersion.FileVersion;
assemblyData.ProductVersion = fileVersion.ProductVersion;
assemblyData.IsClsCompliant = (bool?)clsCompliant?.ConstructorArguments?.FirstOrDefault().Value ?? false;
assemblyData.InformationalVersion = assemblyInformationalVersion?.ConstructorArguments?.FirstOrDefault().Value.ToString();
assemblyData.Metadata = string.Join(",", metadataKeys.Select((x, i) => $"{x}={metadataValues[i].ToString()}"));
assemblyData.IsStronglyNamed = assemblyName.GetPublicKeyToken().Length > 0;
assemblyData.PublicKeyToken = string.Join("", assemblyName.GetPublicKeyToken().Select(b => b.ToString("x2")));
assemblyData.Framework = targetFrameworkName;
assemblyData.FrameworkVersion = targetFrameworkVersion;
assemblyData.Copyright = fileVersion.LegalCopyright;
assemblyData.Build = assemblyConfiguration?.ConstructorArguments?.FirstOrDefault().Value.ToString();
assemblyData.IsDebug = fileVersion.IsDebug;
assemblyData.DebuggableModes = debuggableAttributeArguments.HasValue ? ((DebuggableAttribute.DebuggingModes)debuggableAttributeArguments.Value.Value).ToString() : string.Empty;
assemblyData.IsPatched = fileVersion.IsPatched;
assemblyData.IsPreRelease = fileVersion.IsPreRelease;
assemblyData.Language = fileVersion.Language;
assemblyData.OriginalFilename = fileVersion.OriginalFilename;
assemblyData.FileSize = Util.BytesToString(fileInfo.Length);
assemblyData.FileLength = fileInfo.Length;
assemblyData.FullPath = assembly.Location;
assemblyData.DeclaredTypes = declaredTypes;
assemblyData.Dependencies = dependencies;
assemblyData.EmbeddedResources = string.Join(Environment.NewLine, embeddedResources);

if (assemblyData.FileLength < MaxHashInspectionLength)
ComputeHash(assemblyData, assembly.Location);
return assemblyData;
}

private string BuildTypeName(Type type)
{
if (type == null)
return string.Empty;
if (type.IsGenericType)
{
var name = type.FullName;
var startPos = -1;
var len = 0;
var parameters = ((TypeInfo)type).GenericTypeParameters;
for (var i = 0; i < parameters.Length; i++)
{
var key = $"`{(parameters[i].GenericParameterPosition + 1)}";
var index = name.IndexOf(key);
if (startPos < 0 && index >= 0)
startPos = index;
if (index >= 0)
{
len += parameters[i].Name.Length;
name = name.Replace(key, parameters[i].Name);
}
}
if (len > 0)
{
name = name.Insert(startPos, "<");
name = name.Insert(startPos + len + 1, ">");
}
return $"{type.FullName} ({name})";
}
return type.FullName;
}

private void ComputeHash(AssemblyData data, string filename)
Expand Down
Loading

0 comments on commit 3672a6b

Please sign in to comment.