public Plugin GetPlugin(int resourceId) { HttpWebResponse error; string response = DownloadString(string.Format(PluginUrl, resourceId), out error); if (error?.StatusCode == HttpStatusCode.Forbidden) { return(null); // Plugin not found or not allowed access. } var htmlDoc = new HtmlDocument(); htmlDoc.LoadHtml(response); var rootNode = htmlDoc.DocumentNode; var result = new Plugin(); var resourceDesc = rootNode.SelectSingleNode("//*[@class='mainContent']/div[@class='resourceInfo']/div[@class='resourceDesc']"); result.Name = ParsePluginName(resourceDesc); result.Game = ParsePluginGame(resourceDesc); result.Description = resourceDesc.SelectSingleNode("p").InnerText; var historyTable = rootNode.SelectSingleNode("//*[@class='dataTable resourceHistory']"); foreach (var rowNode in historyTable.SelectNodes("tr").Skip(1)) // Skip header { Plugin.Version item = ParsePluginRow(rowNode); result.Versions.Add(item); } return(result); }
static void ExecuteAndUnload(string assemblyPath, out WeakReference alcWeakRef) { // Create the unloadable HostAssemblyLoadContext var alc = new HostAssemblyLoadContext(assemblyPath); // Create a weak reference to the AssemblyLoadContext that will allow us to detect // when the unload completes. alcWeakRef = new WeakReference(alc); // Load the plugin assembly into the HostAssemblyLoadContext. // NOTE: the assemblyPath must be an absolute path. Assembly a = alc.LoadFromAssemblyPath(assemblyPath); // Get the plugin interface by calling the PluginClass.GetInterface method via reflection. Type pluginType = a.GetType("Plugin.PluginClass"); MethodInfo getInterface = pluginType.GetMethod("GetInterface", BindingFlags.Static | BindingFlags.Public); Plugin.Interface plugin = (Plugin.Interface)getInterface.Invoke(null, null); // Now we can call methods of the plugin using the interface string result = plugin.GetMessage(); Plugin.Version version = plugin.GetVersion(); Console.WriteLine($"Response from the plugin: GetVersion(): {version}, GetMessage(): {result}"); // This initiates the unload of the HostAssemblyLoadContext. The actual unloading doesn't happen // right away, GC has to kick in later to collect all the stuff. alc.Unload(); }
private static Plugin.Version ParsePluginRow(HtmlNode rowNode) { Plugin.Version result = new Plugin.Version(); result.Value = rowNode.SelectSingleNode("td[1]").InnerText; HtmlNode dateTimeNode = rowNode.SelectSingleNode("td[2]/*[@class='DateTime']"); string dateString = dateTimeNode.GetAttributeValue("title", dateTimeNode.InnerText); result.ReleaseDate = DateTime.ParseExact(dateString, "MMM d', 'yyyy' at 'h:mm' 'tt", CultureInfo.InvariantCulture).ToUniversalTime(); string downloadsString = rowNode.SelectSingleNode("td[3]").InnerText; result.Downloads = int.Parse(downloadsString, NumberStyles.AllowThousands, CultureInfo.InvariantCulture); string downloadHref = rowNode.SelectSingleNode("td[4]/a").GetAttributeValue("href", null); string versionStr = HttpUtility.ParseQueryString(downloadHref.Split('?')[1])["version"]; result.Id = int.Parse(versionStr); return(result); }