using ANX.Framework.Content.Pipeline; using ANX.Framework.Content.Pipeline.Helpers; using ANX.Framework.Content.Pipeline.Tasks; using ANX.Framework.Design; using ANX.Framework.Graphics; using ANX.Framework.VisualStudio; using ANX.Framework.VisualStudio.Nodes; using ContentBuilder; using Microsoft.VisualStudio.Project; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Drawing.Design; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.ServiceModel; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; namespace ANX.Framework.Build { public sealed class BuildAppDomain : IDisposable { public class RemoteProxy : MarshalByRefObject { private List processorNames = new List(); private ProcessorManager processorManager = null; private bool processorNamesInvalid = true; private ImporterManager importerManager = null; private bool importerManagerInvalid = true; private List importerNames = new List(); public override object InitializeLifetimeService() { return null; } public void LoadProjectAssemblies(IEnumerable assemblies, IEnumerable searchPaths, IDictionary redirectAssemblies, Action errorCallback) { if (assemblies == null) throw new ArgumentNullException("assemblies"); if (searchPaths == null) throw new ArgumentNullException("searchPaths"); if (redirectAssemblies == null) throw new ArgumentNullException("redirectAssemblies"); foreach (string assembly in assemblies) { try { Assembly assemblyInstance = null; if (File.Exists(assembly)) { string assemblyName = Path.GetFileNameWithoutExtension(assembly); if (IsAssemblyAlreadyLoaded(assemblyName)) continue; assemblyInstance = LoadFrom(assembly, redirectAssemblies); } else { if (IsAssemblyAlreadyLoaded(assembly)) continue; Uri assemblyUri; if (Uri.TryCreate(assembly.Split(',').First() + ".dll", UriKind.RelativeOrAbsolute, out assemblyUri)) { foreach (Uri path in searchPaths) { Uri temp = new Uri(path, assemblyUri); if (File.Exists(temp.LocalPath)) { assemblyInstance = LoadFrom(temp.LocalPath, redirectAssemblies); break; } } if (assemblyInstance == null) assemblyInstance = Assembly.Load(assembly); } else { assemblyInstance = Assembly.Load(assembly); } } ClassPreloader.Preload(assemblyInstance); } catch (Exception exc) { if (errorCallback != null) errorCallback(assembly, exc); else throw; } } this.processorManager = null; this.processorNamesInvalid = true; this.importerManager = null; this.importerManagerInvalid = true; } private bool IsAssemblyAlreadyLoaded(string assemblyName) { bool isFullName = assemblyName.Contains(','); var loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies(); if (isFullName) { foreach (var loadedAssembly in loadedAssemblies) if (loadedAssembly.FullName == assemblyName) { return true; } } else { foreach (var loadedAssembly in loadedAssemblies) if (loadedAssembly.GetName().Name == assemblyName) { return true; } } return false; } private Assembly LoadFrom(string assembly, IDictionary redirectAssemblies) { string assemblyName = assembly; if (Path.IsPathRooted(assembly)) assemblyName = Path.GetFileNameWithoutExtension(assembly); else if (assembly.Contains(',')) assemblyName = assembly.Split(',').First(); Uri redirectedPath; if (redirectAssemblies.TryGetValue(assemblyName, out redirectedPath)) { return Assembly.LoadFrom(redirectedPath.LocalPath); } else { return Assembly.LoadFrom(assembly); } } public string GetAssemblyRuntimeVersion(string assemblyPath) { if (File.Exists(assemblyPath)) { return Assembly.ReflectionOnlyLoadFrom(assemblyPath).ImageRuntimeVersion; } return null; } public IEnumerable GetReferencedAssemblies(string assembly) { if (File.Exists(assembly)) { return Assembly.ReflectionOnlyLoadFrom(assembly).GetReferencedAssemblies(); } else { return Assembly.ReflectionOnlyLoad(assembly).GetReferencedAssemblies(); } } public string GetExistingFilesFilter() { ImporterManager importerManager = new ImporterManager(); SortedDictionary> supportedFileExtensions = new SortedDictionary>(); List allExtensions = new List(); foreach (var valuePair in importerManager.AvailableImporters) { var attribute = valuePair.Value.GetCustomAttribute(true); IEnumerable fileExtensions = attribute.FileExtensions; string category = attribute.Category; if (fileExtensions.Count() == 0) continue; if (string.IsNullOrWhiteSpace(category)) { category = "Other Files"; } category = category.Trim(); List extensions; if (!supportedFileExtensions.TryGetValue(category, out extensions)) { extensions = new List(); supportedFileExtensions.Add(category, extensions); } foreach (var fileExtension in fileExtensions) { if (!string.IsNullOrWhiteSpace(fileExtension)) { string usableExtension = "*" + fileExtension; if (!extensions.Contains(usableExtension)) { extensions.Add(usableExtension); if (!allExtensions.Contains(usableExtension)) { allExtensions.Add(usableExtension); } } } } } string allExtensionsJoined = string.Join(";", allExtensions); string filter = "Content Project Files|" + allExtensionsJoined; foreach (var valuePair in supportedFileExtensions) { string extensionsJoined = string.Join(";", valuePair.Value); filter += "|" + valuePair.Key + "|" + extensionsJoined; } return filter; } public IEnumerable GetProcessorNames() { if (processorNamesInvalid) { processorNames.Clear(); if (processorManager == null) { processorManager = new ProcessorManager(); } foreach (var value in processorManager.AvailableProcessors) { processorNames.Add(value.Key); } processorNamesInvalid = false; } return processorNames; } public IEnumerable GetImporterNames() { if (importerManagerInvalid) { importerNames.Clear(); if (importerManager == null) { importerManager = new ImporterManager(); } foreach (var value in importerManager.AvailableImporters) { importerNames.Add(value.Key); } importerManagerInvalid = false; } return importerNames; } public string GetImporterName(string displayName) { if (importerManager == null) { importerManager = new ImporterManager(); } return importerManager.GetImporterName(displayName); } public string GetImporterDisplayName(string importer) { if (importerManager == null) { importerManager = new ImporterManager(); } return importerManager.GetImporterDisplayName(importer); } public string GetProcessorName(string displayName) { if (processorManager == null) { processorManager = new ProcessorManager(); } return processorManager.GetProcessorName(displayName); } public string GetProcessorDisplayName(string processor) { if (processorManager == null) { processorManager = new ProcessorManager(); } return processorManager.GetProcessorDisplayName(processor); } public string GetProcessorTypeName(string processor) { if (processorManager == null) { processorManager = new ProcessorManager(); } return processorManager.GetInstance(processor).GetType().AssemblyQualifiedName; } public string GuessImporterByFileExtension(string fileName) { if (importerManager == null) { importerManager = new ImporterManager(); } return importerManager.GuessImporterByFileExtension(fileName); } public string GetDefaultProcessorForImporter(string importer) { if (importerManager == null) importerManager = new ImporterManager(); return importerManager.GetDefaultProcessor(importer); } public ICollection GetProcessorParameters(string processor) { if (processorManager == null) processorManager = new ProcessorManager(); List result = new List(); if (!string.IsNullOrEmpty(processor)) { ANX.Framework.Content.Pipeline.ProcessorParameterCollection processorParams = processorManager.GetProcessorParameters(processor); foreach (var parameter in processorParams) { string defaultValueText = null; string valueText = null; object processorInstance = processorManager.GetInstance(processor); Type processorType = processorInstance.GetType(); Type propertyType = TypeHelper.GetType(parameter.PropertyType); PropertyInfo property = processorType.GetProperty(parameter.PropertyName, propertyType); TypeConverter converter = property.GetConverter(); if (converter != null) { defaultValueText = (string)converter.ConvertTo(parameter.DefaultValue, typeof(string)); valueText = (string)converter.ConvertTo(property.GetValue(processorInstance), typeof(string)); } result.Add(new ANX.Framework.VisualStudio.ProcessorParameter(parameter.PropertyName, parameter.DisplayName, parameter.PropertyType, valueText, defaultValueText, parameter.Description)); } } return result; } public string GetConverterTypeName(string containerType, string propertyName) { Type type = TypeHelper.GetType(containerType); PropertyInfo property = type.GetProperty(propertyName); TypeConverter converter = property.GetConverter(); if (converter != null) return converter.GetType().AssemblyQualifiedName; return null; } private object CreateInstance(Type targetType, Type typeParam) { var parameterTypes = new Type[] { typeof(Type) }; ConstructorInfo constructor = targetType.GetConstructor(parameterTypes); if (constructor != null) { return TypeDescriptor.CreateInstance(null, targetType, parameterTypes, new object[] { typeParam }); } return TypeDescriptor.CreateInstance(null, targetType, null, null); } public IEnumerable GetAssemblyLocations() { List locations = new List(); foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (assembly.CodeBase == null) { locations.Add(assembly.Location); } else { locations.Add(new Uri(assembly.CodeBase).LocalPath); } } return locations; } /// /// Returns a proxy for an editor. /// The returned value should be wrapped by /// /// The wanted base type for the editor. Currently only supports . /// /// /// Must be a proxy to the class. /// public IProxy GetEditor(Type editorBaseType, string typeName, string propertyName, IProxy converterProxy) { if (editorBaseType != typeof(UITypeEditor)) { return null; } Type type = TypeHelper.GetType(typeName); PropertyInfo property = type.GetProperty(propertyName); UITypeEditor editor; var attributes = property.GetCustomAttributes(true); foreach (var attribute in attributes) { if (attribute.EditorBaseTypeName == editorBaseType.AssemblyQualifiedName) { Type editorType = TypeHelper.GetType(attribute.EditorTypeName); if (editorType != null && typeof(UITypeEditor).IsAssignableFrom(editorType)) { editor = (UITypeEditor)this.CreateInstance(editorType, property.PropertyType); if (editor != null) { return UITypeEditorWrapper.CreateProxy(editor, converterProxy); } else { return null; } } } } editor = (UITypeEditor)TypeDescriptor.GetEditor(property.PropertyType, editorBaseType); if (editor != null) { return UITypeEditorWrapper.CreateProxy(editor, converterProxy); } else { return null; } } public string[] GetPlatformDisplayNames() { return Utilities.GetTargetPlatformDisplayNames(); } public string[] GetGraphicsProfilesNames() { return Enum.GetNames(typeof(GraphicsProfile)); } public bool IsUpDoDate(string projectHome, IEnumerable buildItems, Configuration activeConfiguration, ErrorLoggingHelper loggingHelper) { BuildContentTask task = new BuildContentTask(); task.TargetPlatform = activeConfiguration.Platform; task.TargetProfile = activeConfiguration.Profile; var buildCache = new AnxBuildCache(task.ImporterManager, task.ProcessorManager, task.ContentCompiler); buildCache.ProjectHome = new Uri(projectHome, UriKind.Absolute); task.BuildCache = buildCache; string intermediateDirectory = Path.Combine(projectHome, "obj", CreateSafeFileName(activeConfiguration.Platform.ToDisplayName()), CreateSafeFileName(activeConfiguration.Name)) + Path.DirectorySeparatorChar; var buildCacheUri = new Uri(new Uri(intermediateDirectory, UriKind.Absolute), new Uri("build.cache", UriKind.Relative)); try { buildCache.LoadCache(buildCacheUri); } catch { return false; } foreach (var buildItem in buildItems) { if (!buildCache.IsValid(buildItem, new Uri(BuildHelper.GetOutputFileName(activeConfiguration.OutputDirectory, projectHome, buildItem), UriKind.Relative))) return false; } return true; } private string CreateSafeFileName(string text) { foreach (var unsafeChar in Path.GetInvalidFileNameChars()) text = text.Replace(unsafeChar, '_'); return text; } } //pretty much the same as if one would use lock(appDomain) but as a public api. public sealed class BuildDomainAquire : IDisposable { BuildAppDomain appDomain; internal BuildDomainAquire(BuildAppDomain appDomain) { Monitor.Enter(appDomain); this.appDomain = appDomain; } public RemoteProxy Proxy { get { return this.appDomain.proxyInstance; } } public void Unload() { this.appDomain.Unload(); } public void Initialize(string identifier) { appDomain.Initialize(identifier); } public T CreateInstanceAndUnwrap() { return appDomain.CreateInstanceAndUnwrap(); } public List SearchPaths { get { return appDomain.SearchPaths; } } public Dictionary Redirects { get { return appDomain.Redirects; } } public String MakeRelativeToSearchPaths(String path) { if (path == null) throw new ArgumentNullException("path"); Uri uri = new Uri(path); if (!uri.IsAbsoluteUri) throw new ArgumentException("uri must be absolute."); return uri.MakeRelativeUri(SearchPaths).OriginalString; } public Uri MakeRelativeToSearchPaths(Uri uri) { if (uri == null) throw new ArgumentNullException("uri"); if (!uri.IsAbsoluteUri) throw new ArgumentException("uri must be absolute."); return uri.MakeRelativeUri(SearchPaths); } public String MakeAbsoluteFromSearchPaths(String path) { if (path == null) throw new ArgumentNullException("path"); Uri uri; if (!Uri.TryCreate(path, UriKind.RelativeOrAbsolute, out uri)) return path; if (uri.IsAbsoluteUri) { return path; } foreach (Uri searchPath in SearchPaths) { Uri tempPath = new Uri(searchPath, uri); if (File.Exists(tempPath.LocalPath)) return tempPath.OriginalString; } return uri.OriginalString; } public Uri MakeAbsoluteFromSearchPaths(Uri uri) { if (uri == null) throw new ArgumentNullException("uri"); if (uri.IsAbsoluteUri) { return uri; } foreach (Uri path in SearchPaths) { Uri tempPath = new Uri(path, uri); if (File.Exists(tempPath.LocalPath)) return tempPath; } return uri; } public Uri[] ShadowCopyDirectories { get { return appDomain.ShadowCopyDirectories; } set { appDomain.ShadowCopyDirectories = value; } } public void AddShadowCopyDirectory(Uri path) { appDomain.AddShadowCopyDirectory(path); } public void RemoveShadowCopyDirectory(Uri path) { appDomain.RemoveShadowCopyDirectory(path); } public void Release() { Monitor.Exit(appDomain); } void IDisposable.Dispose() { this.Release(); } } object buildLock = new object(); bool disposed = false; AppDomain appDomain; RemoteProxy proxyInstance; List searchPaths = new List(); //string shadowCopyPath; Uri[] sourceShadowCopyDirectories = new Uri[0]; Dictionary redirects = new Dictionary(); ServiceHost loggerServiceHost; ContentProjectNode projectNode; Process buildProcess; public BuildAppDomain(ContentProjectNode projectNode) { this.SearchPaths.Add(new Uri(Path.GetDirectoryName(typeof(BuildAppDomain.RemoteProxy).Assembly.Location), UriKind.Absolute)); this.Redirects.Add(typeof(Color).Assembly.GetName().Name, new Uri(typeof(Color).Assembly.CodeBase, UriKind.Absolute)); //ANX.Framework this.Redirects.Add(typeof(IContentProcessor).Assembly.GetName().Name, new Uri(typeof(IContentProcessor).Assembly.CodeBase, UriKind.Absolute)); //ANX.Framework.Content.Pipeline this.projectNode = projectNode; } public BuildDomainAquire Aquire() { if (disposed) throw new ObjectDisposedException("BuildAppDomain"); return new BuildDomainAquire(this); } public bool IsDisposed { get { return disposed; } } private List SearchPaths { get { return searchPaths; } } private Dictionary Redirects { get { return redirects; } } public bool IsBuildRunning { get { return this.buildProcess != null && !this.buildProcess.HasExited; } } private void AddShadowCopyDirectory(Uri path) { if (path == null) throw new ArgumentNullException("path"); if (!path.IsAbsoluteUri) throw new ArgumentException("path must be absolute."); var newDirs = new Uri[sourceShadowCopyDirectories.Length + 1]; Array.Copy(sourceShadowCopyDirectories, newDirs, sourceShadowCopyDirectories.Length); newDirs[newDirs.Length - 1] = path; ShadowCopyDirectories = newDirs; } private void RemoveShadowCopyDirectory(Uri path) { if (path == null) throw new ArgumentNullException("path"); if (!path.IsAbsoluteUri) throw new ArgumentException("path must be absolute."); int index = Array.IndexOf(sourceShadowCopyDirectories, path); if (index == -1) return; var newDirs = new Uri[sourceShadowCopyDirectories.Length - 1]; Array.Copy(sourceShadowCopyDirectories, newDirs, index); if (newDirs.Length - index > 0) Array.Copy(sourceShadowCopyDirectories, index + 1, newDirs, index, newDirs.Length - index); ShadowCopyDirectories = newDirs; } //Disable obsolete code warning //Settings the shadow copy path for an appDomain is declared obsolete, it says I should use the shadow copy path in the appDomainSetup. //Doing that would require to recreate the appDomain whenever a new reference is added and to batch the adding of references. //Doing it this way was just the easier way. #pragma warning disable 618 private Uri[] ShadowCopyDirectories { get { return sourceShadowCopyDirectories.ToArray(); } set { if (value == null) { sourceShadowCopyDirectories = new Uri[0]; appDomain.SetShadowCopyPath(""); } else { if (!value.All((x) => x != null && x.IsAbsoluteUri)) { throw new ArgumentException("All Uri's must be not null and absolute."); } sourceShadowCopyDirectories = value; appDomain.SetShadowCopyPath(string.Join(";", sourceShadowCopyDirectories.Where((x) => x != null).Select((x) => x.LocalPath))); } } } //Not behind the lock because we only use that has been given in the parameters and we're not using any data from foreign assemblies. //Another reason is, when we start debugging, some project properties get refreshed and we need access to the lock for that. public void BuildContent(Configuration activeConfiguration, IEnumerable files, ErrorLoggingHelper loggingHelper, bool debug, string target) { if (activeConfiguration == null) throw new ArgumentNullException("activeConfiguration"); if (loggingHelper == null) throw new ArgumentNullException("loggingHelper"); string projectHome = this.projectNode.ProjectHome; List arguments = new List(); VisualStudioContentBuildLogger logger = new VisualStudioContentBuildLogger(loggingHelper); arguments.Add(this.projectNode.AbsoluteProjectFilePath); arguments.Add("-c:" + activeConfiguration.Name); arguments.Add("-t:" + activeConfiguration.Platform); arguments.Add("-cd:" + projectHome); if (target == MsBuildTarget.Rebuild) arguments.Add("-ForceRebuild"); else if (target == MsBuildTarget.Clean) arguments.Add("-CleanCache"); if (files != null && files.Count() > 0) { arguments.Add("-DontAddProjectBuildItems"); foreach (string file in files) { if (file.StartsWith(projectHome)) arguments.Add(file.Substring(projectHome.Length)); else arguments.Add(file); } } StartContentBuilder(arguments, debug, logger); } private void StartContentBuilder(IList arguments, bool debug, IContentBuildLogger logger, int? millisecondsTimeOut = null) { if (buildProcess != null) { if (buildProcess.HasExited) this.EndBuild(); else throw new InvalidOperationException("There is still a build process running."); } lock (buildLock) { string workingDir = Path.GetDirectoryName(this.GetType().Assembly.Location); string exePath = Path.Combine(workingDir, "ContentBuilder.exe"); Uri loggerUri = new Uri("net.pipe://localhost/VisualStudio/" + Process.GetCurrentProcess().Id + "/"); if (loggerServiceHost != null) loggerServiceHost.Close(); loggerServiceHost = new ServiceHost(logger, loggerUri); try { loggerServiceHost.AddServiceEndpoint(typeof(IContentBuildLogger), new NetNamedPipeBinding(), "ContentBuildLogger"); loggerServiceHost.Open(); arguments.Add("-l:" + new Uri(loggerUri, new Uri("ContentBuildLogger", UriKind.RelativeOrAbsolute)).OriginalString); if (debug) arguments.Add("-Debug"); //Prepare the arguments to be used for process start. var args = arguments.Select((x) => "\"" + Regex.Replace(x, "(\\\\*)(\\\\$|\")", "$1$1\\$2") + "\""); if (debug) { //May have to change the debugger interface for newer versions of visual studio. var debugger = this.projectNode.Site.GetService(typeof(SVsShellDebugger)) as IVsDebugger4; var targetInfo = new VsDebugTargetInfo4() { bstrArg = string.Join(" ", args), bstrCurDir = workingDir, bstrExe = exePath, guidLaunchDebugEngine = Microsoft.VisualStudio.VSConstants.DebugEnginesGuids.ManagedOnly_guid, project = this.projectNode, dlo = (uint)DEBUG_LAUNCH_OPERATION.DLO_CreateProcess, }; VsDebugTargetProcessInfo[] result = new VsDebugTargetProcessInfo[1]; debugger.LaunchDebugTargets4(1, new VsDebugTargetInfo4[] { targetInfo }, result); buildProcess = Process.GetProcessById((int)result[0].dwProcessId); } else { buildProcess = Process.Start(new ProcessStartInfo(exePath, string.Join(" ", args)) { CreateNoWindow = true, UseShellExecute = false, WorkingDirectory = workingDir, }); } if (millisecondsTimeOut.HasValue) buildProcess.WaitForExit(millisecondsTimeOut.Value); else buildProcess.WaitForExit(); } catch { if (loggerServiceHost != null) loggerServiceHost.Abort(); throw; } } } public void EndBuild() { lock (buildLock) { if (buildProcess != null) { buildProcess.Close(); buildProcess = null; } if (loggerServiceHost != null) { loggerServiceHost.Close(); loggerServiceHost = null; } } } public void PrepareBuild(bool cleanBuild) { if (cleanBuild) return; lock (buildLock) { ErrorLoggingHelper loggingHelper = new ErrorLoggingHelper(this.projectNode, this.projectNode.ErrorListProvider, null); //Test if ANX.Framework or ANX.Framework.Content.Pipeline are referenced, because we must use our own version. foreach (var reference in this.projectNode.GetReferenceContainer().EnumReferences()) { if (Redirects.ContainsKey(reference.Caption)) { loggingHelper.LogMessage("", null, reference.Url, ErrorLoggingHelper.MessageImportance.Info, PackageResources.GetString(PackageResources.AnxFrameworkAssembliesRedirected), reference.Caption); } } } } #pragma warning restore 618 private void Unload() { EndBuild(); if (appDomain != null) { proxyInstance = null; AppDomain.Unload(appDomain); appDomain = null; } } private void Initialize(string identifier) { if (appDomain != null) { throw new InvalidOperationException("The AppDomain is already initialized."); } AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationBase = Path.GetDirectoryName(typeof(BuildAppDomain.RemoteProxy).Assembly.Location); //if (SearchPath != null) // setup.ApplicationBase = SearchPath.OriginalString; setup.ApplicationName = "ANX Project " + identifier; setup.ShadowCopyFiles = "true"; //Shadow copying so that files like dll's of referenced projects can still be updated. //I can't shadow copy everything, otherwise I wouldn't be able to use the remoteProxy in the other appDomain because if the location of the loaded assembly differs from our own, I can't cast the proxy //in our appDomain. setup.ShadowCopyDirectories = string.Join(";", ShadowCopyDirectories.Select((x) => x.LocalPath)); //setup.CachePath = Path.Combine(Path.GetTempPath(), "ANX Project ShadowCopies"); //I might need to setup a custom shadow copy path, because the default path is restricted by space. But if I do that, I would also have to take care of the cleanup. //this.shadowCopyPath = Path.Combine(setup.CachePath, setup.ApplicationName); AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; appDomain = AppDomain.CreateDomain("ANX Project " + identifier, null, setup); appDomain.AssemblyResolve += BuildDomainAssemblyResolver.appDomain_AssemblyResolve; //appDomain.TypeResolve += BuildDomainAssemblyResolver.appDomain_AssemblyResolve; //appDomain.Load(typeof(ContentProject).Assembly.FullName); //ANX.Framework.Build proxyInstance = (RemoteProxy)appDomain.CreateInstanceAndUnwrap(typeof(BuildAppDomain.RemoteProxy).Assembly.FullName, typeof(BuildAppDomain.RemoteProxy).FullName); } [Serializable] static class BuildDomainAssemblyResolver { //Help placing project assemblies from the loadFrom context into the load context, only that way we can find the contained types later by using Type.GetType(string). public static Assembly appDomain_AssemblyResolve(object sender, ResolveEventArgs args) { foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (assembly.FullName == args.Name) return assembly; } return null; } } //Help placing our own assemblies into the load context of the newly created appDomain Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { var assembly = AppDomain.CurrentDomain.GetAssemblies().Where((x) => x.FullName == args.Name).FirstOrDefault(); if (assembly != null) return assembly; return null; } private T CreateInstanceAndUnwrap() { return (T)appDomain.CreateInstanceAndUnwrap(typeof(T).Assembly.FullName, typeof(T).FullName); } public void Dispose() { Unload(); disposed = true; /*if (Directory.Exists(shadowCopyPath)) { //If the directory is still used by another process, the delete should silently fail. try { Directory.Delete(shadowCopyPath, true); } catch { } }*/ } } }