/// <summary>Generates a <see cref="ToolStripMenuItem"/> from an assembly at <paramref name="fileName"/> containing an external tool.</summary> /// <returns> /// a <see cref="ToolStripMenuItem"/> with its <see cref="ToolStripItem.Tag"/> containing a <c>(string, string)</c>; /// the first is the assembly path (<paramref name="fileName"/>) and the second is the <see cref="Type.FullName"/> of the entry point form's type /// </returns> private static ToolStripMenuItem GenerateToolTipFromFileName(string fileName) { if (fileName == null) { throw new Exception(); } var item = new ToolStripMenuItem(Path.GetFileName(fileName)) { Enabled = false }; try { if (!OSTailoredCode.IsUnixHost) { MotWHack.RemoveMOTW(fileName); } var externalToolFile = Assembly.LoadFrom(fileName); var entryPoint = externalToolFile.GetTypes() .SingleOrDefault(t => typeof(IExternalToolForm).IsAssignableFrom(t) && t.GetCustomAttributes().OfType <ExternalToolAttribute>().Any()); #pragma warning disable CS0618 if (entryPoint == null) { throw new ExternalToolAttribute.MissingException(externalToolFile.GetCustomAttributes().OfType <BizHawkExternalToolAttribute>().Any()); } #pragma warning restore CS0618 var allAttrs = entryPoint.GetCustomAttributes().ToList(); var applicabilityAttrs = allAttrs.OfType <ExternalToolApplicabilityAttributeBase>().ToList(); if (applicabilityAttrs.Count > 1) { throw new ExternalToolApplicabilityAttributeBase.DuplicateException(); } var toolAttribute = allAttrs.OfType <ExternalToolAttribute>().First(); var embeddedIconAttr = allAttrs.OfType <ExternalToolEmbeddedIconAttribute>().FirstOrDefault(); if (embeddedIconAttr != null) { var rawIcon = externalToolFile.GetManifestResourceStream(embeddedIconAttr.ResourcePath); if (rawIcon != null) { item.Image = new Bitmap(rawIcon); } } item.Text = toolAttribute.Name; item.Tag = (externalToolFile.Location, entryPoint.FullName); // Tag set => no errors (show custom icon even when disabled) if (applicabilityAttrs.Count == 1) { var system = ClientApi.SystemIdConverter.Convert(Global.Emulator.SystemId); if (applicabilityAttrs[0].NotApplicableTo(system)) { item.ToolTipText = system == CoreSystem.Null ? "This tool doesn't work when no rom is loaded" : "This tool doesn't work with this system"; return(item); } if (applicabilityAttrs[0].NotApplicableTo(Global.Game.Hash, system)) { item.ToolTipText = "This tool doesn't work with this game"; return(item); } } item.Enabled = true; if (!string.IsNullOrWhiteSpace(toolAttribute.Description)) { item.ToolTipText = toolAttribute.Description; } return(item); } catch (Exception e) { #if DEBUG if (e is ReflectionTypeLoadException rtle) { foreach (var e1 in rtle.LoaderExceptions) { Debug.WriteLine(e1.Message); } } #endif item.ToolTipText = e switch { BadImageFormatException _ => "This assembly can't be loaded, probably because it's corrupt or targets an incompatible .NET runtime.", ExternalToolApplicabilityAttributeBase.DuplicateException _ => "The IExternalToolForm has conflicting applicability attributes.", ExternalToolAttribute.MissingException e1 => e1.OldAttributeFound ? "The assembly doesn't contain a class implementing IExternalToolForm and annotated with [ExternalTool].\nHowever, the assembly itself is annotated with [BizHawkExternalTool], which is now deprecated. Has the tool been updated since BizHawk 2.4?" : "The assembly doesn't contain a class implementing IExternalToolForm and annotated with [ExternalTool].", ReflectionTypeLoadException _ => "Something went wrong while trying to load the assembly.", _ => $"An exception of type {e.GetType().FullName} was thrown while trying to load the assembly and look for an IExternalToolForm:\n{e.Message}" }; } return(item); }
/// <summary> /// Generate a <see cref="ToolStripMenuItem"/> from an /// external tool dll. /// The assembly must have <see cref="BizHawkExternalToolAttribute"/> in its /// assembly attributes /// </summary> /// <param name="fileName">File that will be reflected</param> /// <returns>A new <see cref="ToolStripMenuItem"/>; assembly path can be found in the Tag property</returns> /// <remarks>For the moment, you could only load a dll that have a form (which implements <see cref="IExternalToolForm"/>)</remarks> private static ToolStripMenuItem GenerateToolTipFromFileName(string fileName) { ToolStripMenuItem item = null; try { if (!OSTailoredCode.IsUnixHost) { MotWHack.RemoveMOTW(fileName); } var externalToolFile = Assembly.LoadFrom(fileName); object[] attributes = externalToolFile.GetCustomAttributes(typeof(BizHawkExternalToolAttribute), false); if (attributes != null && attributes.Count() == 1) { BizHawkExternalToolAttribute attribute = (BizHawkExternalToolAttribute)attributes[0]; item = new ToolStripMenuItem(attribute.Name) { ToolTipText = attribute.Description }; if (attribute.IconResourceName != "") { Stream s = externalToolFile.GetManifestResourceStream($"{externalToolFile.GetName().Name}.{attribute.IconResourceName}"); if (s != null) { item.Image = new Bitmap(s); } } var customFormType = externalToolFile.GetTypes().FirstOrDefault(t => t != null && t.FullName == "BizHawk.Client.EmuHawk.CustomMainForm"); if (customFormType == null) { item.ToolTipText = "Does not have a CustomMainForm"; item.Enabled = false; } item.Tag = fileName; attributes = externalToolFile.GetCustomAttributes(typeof(BizHawkExternalToolUsageAttribute), false); if (attributes != null && attributes.Length == 1) { BizHawkExternalToolUsageAttribute attribute2 = (BizHawkExternalToolUsageAttribute)attributes[0]; if (Global.Emulator.SystemId == "NULL" && attribute2.ToolUsage != BizHawkExternalToolUsage.Global) { item.ToolTipText = "This tool doesn't work if nothing is loaded"; item.Enabled = false; } else if (attribute2.ToolUsage == BizHawkExternalToolUsage.EmulatorSpecific && Global.Emulator.SystemId != ClientApi.SystemIdConverter.ConvertBack(attribute2.System)) { item.ToolTipText = "This tool doesn't work for current system"; item.Enabled = false; } else if (attribute2.ToolUsage == BizHawkExternalToolUsage.GameSpecific && Global.Game.Hash != attribute2.GameHash) { item.ToolTipText = "This tool doesn't work for current game"; item.Enabled = false; } } } else { item = new ToolStripMenuItem(externalToolFile.GetName().Name) { ToolTipText = "BizHawkExternalTool attribute hasn't been found", Enabled = false }; } } catch (BadImageFormatException) { item = new ToolStripMenuItem(fileName); item.ToolTipText = "This is not an assembly"; item.Enabled = false; } #if DEBUG //I added special debug stuff to get additional information. Don't think it can be useful for released versions catch (ReflectionTypeLoadException ex) { foreach (Exception e in ex.LoaderExceptions) { Debug.WriteLine(e.Message); } item.ToolTipText = "Something goes wrong while trying to load"; item.Enabled = false; } #else catch (ReflectionTypeLoadException) { item.ToolTipText = "Something goes wrong while trying to load"; item.Enabled = false; } #endif return(item); }