Gets the package inspector all working with type checks and app domains
This commit is contained in:
@@ -11,41 +11,69 @@ using System.Web;
|
||||
|
||||
namespace Umbraco.Core.Packaging
|
||||
{
|
||||
internal class PackageBinaryInspector : MarshalByRefObject
|
||||
public class PackageBinaryInspector : MarshalByRefObject
|
||||
{
|
||||
public IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
|
||||
/// <summary>
|
||||
/// Entry point to call from your code
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="dllPath"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// Will perform the assembly scan in a separate app domain
|
||||
/// </remarks>
|
||||
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(string dllPath, out string[] errorReport)
|
||||
{
|
||||
var appDomain = GetTempAppDomain();
|
||||
var type = typeof(PackageBinaryInspector);
|
||||
try
|
||||
{
|
||||
var value = (PackageBinaryInspector)appDomain.CreateInstanceAndUnwrap(
|
||||
type.Assembly.FullName,
|
||||
type.FullName);
|
||||
var result = value.PerformScan<T>(dllPath, out errorReport);
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
AppDomain.Unload(appDomain);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the assembly scanning
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="dllPath"></param>
|
||||
/// <param name="errorReport"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// This method is executed in a separate app domain
|
||||
/// </remarks>
|
||||
internal IEnumerable<string> PerformScan<T>(string dllPath, out string[] errorReport)
|
||||
{
|
||||
if (Directory.Exists(dllPath) == false)
|
||||
{
|
||||
throw new DirectoryNotFoundException("Could not find directory " + dllPath);
|
||||
}
|
||||
|
||||
|
||||
var files = Directory.GetFiles(dllPath, "*.dll");
|
||||
var dllsWithReference = new List<string>();
|
||||
var errors = new List<string>();
|
||||
var assembliesWithErrors = new List<string>();
|
||||
|
||||
|
||||
//we need this handler to resolve assembly dependencies below
|
||||
AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += (s, e) =>
|
||||
{
|
||||
var a = Assembly.ReflectionOnlyLoad(e.Name);
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
{
|
||||
var a = Assembly.ReflectionOnlyLoadFrom(GetAssemblyPath(Assembly.ReflectionOnlyLoad(e.Name)));
|
||||
if (a == null) throw new TypeLoadException("Could not load assembly " + e.Name);
|
||||
return a;
|
||||
};
|
||||
|
||||
//First load each dll file into the context
|
||||
foreach (var f in files) Assembly.ReflectionOnlyLoadFrom(f);
|
||||
|
||||
////before we try to load these assemblies into context, ensure we haven't done that already
|
||||
//var alreadyLoaded = AppDomain.CurrentDomain.ReflectionOnlyGetAssemblies();
|
||||
////First load each dll file into the context
|
||||
//foreach (var f in files.Where(a => !IsAlreadyLoaded(a, alreadyLoaded)))
|
||||
//{
|
||||
// //NOTE: if you're loading an already loaded assembly from a new location
|
||||
// // you will get a FileLoadException here.
|
||||
// Assembly.ReflectionOnlyLoadFrom(f);
|
||||
//}
|
||||
|
||||
//Then load each referenced assembly into the context
|
||||
foreach (var f in files)
|
||||
{
|
||||
@@ -54,26 +82,31 @@ namespace Umbraco.Core.Packaging
|
||||
{
|
||||
try
|
||||
{
|
||||
Assembly.ReflectionOnlyLoad(assemblyName.FullName);
|
||||
Assembly.ReflectionOnlyLoadFrom(GetAssemblyPath(Assembly.ReflectionOnlyLoad(assemblyName.FullName)));
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
//if an exception occurs it means that a referenced assembly could not be found
|
||||
errors.Add(
|
||||
string.Concat("This package references the assembly '",
|
||||
assemblyName.Name,
|
||||
"' which was not found, this package may have problems running"));
|
||||
assemblyName.Name,
|
||||
"' which was not found, this package may have problems running"));
|
||||
assembliesWithErrors.Add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var contractType = GetLoadFromContractType<T>();
|
||||
|
||||
//now that we have all referenced types into the context we can look up stuff
|
||||
foreach (var f in files.Except(assembliesWithErrors))
|
||||
{
|
||||
//now we need to see if they contain any type 'T'
|
||||
var reflectedAssembly = Assembly.ReflectionOnlyLoadFrom(f);
|
||||
var found = reflectedAssembly.GetExportedTypes().Where(TypeHelper.IsTypeAssignableFrom<T>);
|
||||
|
||||
var found = reflectedAssembly.GetExportedTypes()
|
||||
.Where(contractType.IsAssignableFrom);
|
||||
|
||||
if (found.Any())
|
||||
{
|
||||
dllsWithReference.Add(reflectedAssembly.FullName);
|
||||
@@ -84,53 +117,62 @@ namespace Umbraco.Core.Packaging
|
||||
return dllsWithReference;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> ScanAssembliesForTypeReference<T>(string dllPath, out string[] errorReport)
|
||||
/// <summary>
|
||||
/// In order to compare types, the types must be in the same context, this method will return the type that
|
||||
/// we are checking against but from the LoadFrom context.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
private static Type GetLoadFromContractType<T>()
|
||||
{
|
||||
var appDomain = GetTempAppDomain();
|
||||
var type = typeof(PackageBinaryInspector);
|
||||
var value = (PackageBinaryInspector) appDomain.CreateInstanceAndUnwrap(
|
||||
type.Assembly.FullName,
|
||||
type.FullName);
|
||||
var result = value.PerformScan<T>(dllPath, out errorReport);
|
||||
AppDomain.Unload(appDomain);
|
||||
return result;
|
||||
var contractAssemblyLoadFrom = Assembly.ReflectionOnlyLoadFrom(
|
||||
GetAssemblyPath(Assembly.ReflectionOnlyLoad(typeof (T).Assembly.FullName)));
|
||||
|
||||
var contractType = contractAssemblyLoadFrom.GetExportedTypes()
|
||||
.FirstOrDefault(x => x.FullName == typeof(T).FullName && x.Assembly.FullName == typeof(T).Assembly.FullName);
|
||||
|
||||
if (contractType == null)
|
||||
{
|
||||
throw new InvalidOperationException("Could not find type " + typeof(T) + " in the LoadFrom assemblies");
|
||||
}
|
||||
return contractType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an app domain
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private static AppDomain GetTempAppDomain()
|
||||
{
|
||||
//copy the current app domain setup but don't shadow copy files
|
||||
var appName = "TempDomain" + Guid.NewGuid();
|
||||
var domainSetup = new AppDomainSetup
|
||||
{
|
||||
ApplicationName = appName,
|
||||
ApplicationName = appName,
|
||||
ShadowCopyFiles = "false",
|
||||
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
|
||||
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
|
||||
DynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase,
|
||||
LicenseFile = AppDomain.CurrentDomain.SetupInformation.LicenseFile,
|
||||
LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization,
|
||||
PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath,
|
||||
PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe
|
||||
ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
|
||||
ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile,
|
||||
DynamicBase = AppDomain.CurrentDomain.SetupInformation.DynamicBase,
|
||||
LicenseFile = AppDomain.CurrentDomain.SetupInformation.LicenseFile,
|
||||
LoaderOptimization = AppDomain.CurrentDomain.SetupInformation.LoaderOptimization,
|
||||
PrivateBinPath = AppDomain.CurrentDomain.SetupInformation.PrivateBinPath,
|
||||
PrivateBinPathProbe = AppDomain.CurrentDomain.SetupInformation.PrivateBinPathProbe
|
||||
};
|
||||
|
||||
//create new domain with full trust
|
||||
return AppDomain.CreateDomain(
|
||||
appName,
|
||||
AppDomain.CurrentDomain.Evidence,
|
||||
domainSetup,
|
||||
appName,
|
||||
AppDomain.CurrentDomain.Evidence,
|
||||
domainSetup,
|
||||
new PermissionSet(PermissionState.Unrestricted));
|
||||
}
|
||||
|
||||
//private static bool IsAlreadyLoaded(string assemblyFile, IEnumerable<Assembly> alreadyLoaded)
|
||||
//{
|
||||
// return alreadyLoaded.Any(assembly => GetAssemblyLocation(assembly) == assemblyFile);
|
||||
//}
|
||||
|
||||
//private static string GetAssemblyLocation(Assembly assembly)
|
||||
//{
|
||||
// var uri = new Uri(assembly.CodeBase);
|
||||
// return uri.LocalPath;
|
||||
//}
|
||||
private static string GetAssemblyPath(Assembly a)
|
||||
{
|
||||
var codeBase = a.CodeBase;
|
||||
var uri = new Uri(codeBase);
|
||||
return uri.LocalPath;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user