|
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
namespace AssemblyLoader
{
public class Loader
{
static bool resolveAssembly;
static Dictionary<string, Assembly> loadedDependancies = new Dictionary<string, Assembly>();
static Dictionary<string, ZipAssemblyInfo> assembliesInZipResource = new Dictionary<string, ZipAssemblyInfo>();
static ResolveEventHandler handler = FindRequiredAssembly;
public static bool ResolveAssembly
{
get { return resolveAssembly; }
set
{
if (resolveAssembly == value) return;
resolveAssembly = value;
if (value == true)
{
AppDomain.CurrentDomain.AssemblyResolve += handler;
}
else
{
AppDomain.CurrentDomain.AssemblyResolve -= handler;
}
}
}
/// <summary>
/// AssemblyResolve Handler.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
private static Assembly FindRequiredAssembly(object sender, ResolveEventArgs args)
{
var requiredAssembly = new AssemblyName(args.Name);
var shortName = requiredAssembly.Name;
if (loadedDependancies.ContainsKey(shortName))
return loadedDependancies[shortName];
var assemblyFileName = shortName + ".dll";
if (assembliesInZipResource.ContainsKey(shortName) == false)
{
throw new FileNotFoundException(String.Format("Unable to find {0} in zip resource of assemblies", assemblyFileName));
}
var info = assembliesInZipResource[shortName];
using (var resourceStream = info.ContainingAssembly.GetManifestResourceStream(info.ManifestResourceName))
{
using (var zipStore = ZipStorer.Open(resourceStream, FileAccess.Read))
{
using (var extractedStream = new MemoryStream())
{
zipStore.ExtractFile(zipStore.ReadCentralDir()[0], extractedStream);
var assemblyBytes = extractedStream.GetBuffer();
//Load assembly in ReflectionOnly context to verify the name before loading for execution.
var foundAssembly = Assembly.ReflectionOnlyLoad(assemblyBytes);
//Verify that we have found correct assembly before loading it.
//This method do not care about match of version and PublicTokenKey
// Optionally string comparison can be used [foundAssembly.FullName == args.Name ]
if (!AssemblyName.ReferenceMatchesDefinition(foundAssembly.GetName(), requiredAssembly))
{
//if more than one resolve handler are present then we can return from here
// otherwise we can throw exception to indicate failure .
// return null;
throw new FileNotFoundException("File Name = " + requiredAssembly);
}
//Load assembly for execution.
foundAssembly = Assembly.Load(assemblyBytes);
loadedDependancies.Add(shortName, foundAssembly);
return foundAssembly;
}
}
}
}
public static Assembly LoadAssembly(string filePath)
{
if (!File.Exists(filePath)) throw new FileNotFoundException("File Name = " + filePath);
var assembly = Assembly.LoadFrom(filePath);
var fileNameInZip = string.Empty;
var fileNameWithExtension = string.Empty;
var fileNameWithoutExtension = string.Empty;
Debug.WriteLine(String.Format("Inspecting zip files in assembly {0}.", assembly.FullName));
foreach (var resourceName in assembly.GetManifestResourceNames())
{
if (resourceName.EndsWith(".zip"))
{
using (var stream = assembly.GetManifestResourceStream(resourceName))
{
using (var zipStore = ZipStorer.Open(stream, FileAccess.Read))
{
foreach (var zipFileEntry in zipStore.ReadCentralDir())
{
//Full path of file in zip (i.e. including directories)
fileNameInZip = zipFileEntry.FilenameInZip;
if (fileNameInZip.EndsWith(".dll"))
{
fileNameWithExtension = Path.GetFileName(fileNameInZip);
// This is the name of assembly if above dll file.
fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileNameWithExtension);
if (assembliesInZipResource.ContainsKey(fileNameWithoutExtension) == false)
{
var info = new ZipAssemblyInfo(fileNameWithExtension, assembly, resourceName, zipFileEntry);
assembliesInZipResource.Add(fileNameWithoutExtension, info);
}
else
{
var info = assembliesInZipResource[fileNameWithoutExtension];
Debug.WriteLine(String.Format("Assembly {0} is already found in zip. Info = {1}", fileNameWithoutExtension, info));
if (info.ZipFileEntry.Crc32 != zipFileEntry.Crc32)
{
var error = "Two files with same name but different Crc values are detected.";
var detailMsg = String.Format("Assembly {0} and {1} both contains assembly {3} in their embedded resource zip file and the crc32 value for them are not same.",
info.ContainingAssembly.GetName().Name, assembly.GetName().Name,
fileNameWithoutExtension);
Debug.Fail(error, detailMsg);
throw new InvalidOperationException(error);
}
}
//Here we are assuming that assembly name and file name will be same
//i.e. Assembly with Name A will be in A.dll
// So when we get A.dll in zip resource we are assuming that it contains assembly A.
//In Resolve Handler when we try to resolve Assembly A we will use the assembly name as key.
// if there is any possibility that the assembly name and dll file name can be different,
//then one can extract assembly and load it in reflection only context and then retrieve
// assembly name. However this seems very inefficient and unnecessary as no one usually changes the
//name of compiled assembly dlls.
}
}
}
}
}
}
return assembly;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.