diff --git a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs index 6eed83b7af..7de91582ed 100644 --- a/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs +++ b/src/Umbraco.Core/Packaging/PackageBinaryInspector.cs @@ -11,41 +11,69 @@ using System.Web; namespace Umbraco.Core.Packaging { - internal class PackageBinaryInspector : MarshalByRefObject + public class PackageBinaryInspector : MarshalByRefObject { - public IEnumerable PerformScan(string dllPath, out string[] errorReport) + /// + /// Entry point to call from your code + /// + /// + /// + /// + /// + /// + /// Will perform the assembly scan in a separate app domain + /// + public static IEnumerable ScanAssembliesForTypeReference(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(dllPath, out errorReport); + return result; + } + finally + { + AppDomain.Unload(appDomain); + } + } + + /// + /// Performs the assembly scanning + /// + /// + /// + /// + /// + /// + /// This method is executed in a separate app domain + /// + internal IEnumerable PerformScan(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(); var errors = new List(); var assembliesWithErrors = new List(); - + //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(); + //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); + + 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 ScanAssembliesForTypeReference(string dllPath, out string[] errorReport) + /// + /// 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. + /// + /// + /// + private static Type GetLoadFromContractType() { - var appDomain = GetTempAppDomain(); - var type = typeof(PackageBinaryInspector); - var value = (PackageBinaryInspector) appDomain.CreateInstanceAndUnwrap( - type.Assembly.FullName, - type.FullName); - var result = value.PerformScan(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; } + /// + /// Create an app domain + /// + /// 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 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; + } } }