/// <summary> /// Loads the assembly references recursively. /// </summary> /// <param name="assemblies"> /// The assemblies. /// </param> /// <param name="assemblyLookup"> /// The assembly lookup. /// </param> private static void LoadReferencesRecursively(List <LoadedAssembly> assemblies, IDictionary <string, LoadedAssembly> assemblyLookup) { var extraAssemblies = new List <LoadedAssembly>(); foreach (var loadedAssembly in assemblies.ToArray()) { try { foreach (var reference in loadedAssembly.Assembly.GetReferencedAssemblies()) { if (assemblyLookup.Any(a => a.Key.Split(',')[0].Equals(reference.ToString().Split(',')[0], StringComparison.OrdinalIgnoreCase))) { continue; } try { var dlls = new[] { $"{reference.Name}.dll", Path.Combine(Path.GetDirectoryName(loadedAssembly.Path) ?? string.Empty, $"{reference.Name}.dll") }; var dll = dlls.FirstOrDefault(File.Exists); if (dll != null) { var pdb = Regex.Replace(dll, @"\.dll$", ".pdb", RegexOptions.IgnoreCase); var assembly = Assembly.Load(File.ReadAllBytes(dll), File.Exists(pdb) ? File.ReadAllBytes(pdb) : null, SecurityContextSource.CurrentAppDomain); var loaded = new LoadedAssembly(assembly, dll); extraAssemblies.Add(loaded); assemblyLookup[assembly.GetName().ToString()] = loaded; } } catch (FileNotFoundException) { } } } catch { //// Do nothing. } } if (extraAssemblies.Count > 0) { AppDomainIntellisenseSession.LoadReferencesRecursively(extraAssemblies, assemblyLookup); assemblies.AddRange(extraAssemblies); } }
/// <summary> /// Initializes a new instance of the <see cref="AppDomainIntellisenseSession"/> class. /// </summary> /// <param name="assembliesToLoad"> /// The assemblies. /// </param> /// <param name="fallbackConnectQl"> /// The fallback ConnectQl assembly when no reference is available in the project. /// </param> public AppDomainIntellisenseSession(IList <string> assembliesToLoad, string fallbackConnectQl) { var sw = Stopwatch.StartNew(); var referencedAssemblies = new List <Assembly>(); var loadedAssemblies = new List <LoadedAssembly> { new LoadedAssembly(this.GetType().Assembly) }; AppDomain.CurrentDomain.AssemblyResolve += AppDomainIntellisenseSession.CreateAssemblyResolver(loadedAssemblies); AppDomainIntellisenseSession.LoadAssemblies( assembliesToLoad.Where(AppDomainIntellisenseSession.IsIntellisenseAssembly), referencedAssemblies, loadedAssemblies); var connectQl = AppDomainIntellisenseSession.GetConnectQlAssembly(fallbackConnectQl, loadedAssemblies, referencedAssemblies); var assemblyLookup = loadedAssemblies.ToDictionary(a => a.Assembly.GetName().ToString()); var pluginLoaderType = connectQl.GetType("ConnectQl.Intellisense.AssemblyPluginResolver"); var pluginLoader = Activator.CreateInstance(pluginLoaderType, referencedAssemblies); var contextType = connectQl.GetType("ConnectQl.ConnectQlContext"); var contextInterfaceType = connectQl.GetType("ConnectQl.Intellisense.IConnectQlContext"); var createSession = connectQl.GetType("ConnectQl.Intellisense.ConnectQlExtensions").GetMethod("CreateIntellisenseSession"); var sessionType = connectQl.GetType("ConnectQl.Intellisense.IntellisenseSession"); this.context = Activator.CreateInstance(contextType, pluginLoader); this.session = createSession.Invoke(null, new[] { this.context }); this.executeToByteArrayAsync = contextInterfaceType?.GetMethod("ExecuteToByteArrayAsync"); this.getDocument = sessionType.GetMethod("GetDocumentAsByteArray"); this.removeDocument = sessionType.GetMethod("RemoveDocument"); this.updateDocument = sessionType.GetMethod("UpdateDocument"); this.updateDocumentSpan = sessionType.GetMethod("UpdateDocumentSpan"); sessionType.GetEvent("InternalDocumentUpdated", BindingFlags.Public | BindingFlags.Instance) ?.AddEventHandler(this.session, Delegate.CreateDelegate(typeof(EventHandler <byte[]>), this, nameof(this.HandleEvent))); Debug.WriteLine($"Intellisense load took {sw.ElapsedMilliseconds}ms."); AppDomainIntellisenseSession.LoadAssemblies( assembliesToLoad.Where(a => !AppDomainIntellisenseSession.IsIntellisenseAssembly(a)), referencedAssemblies, loadedAssemblies); AppDomainIntellisenseSession.LoadReferencesRecursively(loadedAssemblies, assemblyLookup); pluginLoaderType.GetProperty("IsLoading")?.SetValue(pluginLoader, false); pluginLoaderType.GetMethod("AddAssemblies")?.Invoke(pluginLoader, new object[] { referencedAssemblies }); Debug.WriteLine($"Appdomain load took {sw.ElapsedMilliseconds}ms."); }
/// <summary> /// Gets the ConnectQl assembly. /// </summary> /// <param name="fallbackConnectQl"> /// The assembly to fall back on, when it cannot be found in the project. /// </param> /// <param name="loadedAssemblies"> /// The assemblies that were loaded. /// </param> /// <param name="referencedAssemblies"> /// The assemblies that were referenced. /// </param> /// <returns> /// The ConnectQl assembly. /// </returns> private static Assembly GetConnectQlAssembly(string fallbackConnectQl, List <LoadedAssembly> loadedAssemblies, List <Assembly> referencedAssemblies) { var connectQl = AppDomainIntellisenseSession.GetConnectQlAssembly(loadedAssemblies, "ConnectQl"); if (connectQl == null) { var pdb = Regex.Replace(fallbackConnectQl, @"\.dll$", ".pdb", RegexOptions.IgnoreCase); var assembly = Assembly.Load(File.ReadAllBytes(fallbackConnectQl), File.Exists(pdb) ? File.ReadAllBytes(pdb) : null, SecurityContextSource.CurrentAppDomain); referencedAssemblies.Add(assembly); loadedAssemblies.Add(new LoadedAssembly(assembly, fallbackConnectQl)); connectQl = AppDomainIntellisenseSession.GetConnectQlAssembly(loadedAssemblies, "ConnectQl"); } return(connectQl); }