diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/ExecutableEditor.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/ExecutableEditor.cs.exclude similarity index 98% rename from Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/ExecutableEditor.cs rename to Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/ExecutableEditor.cs.exclude index 220b035d..b3a7e4bf 100644 --- a/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/ExecutableEditor.cs +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/ExecutableEditor.cs.exclude @@ -198,12 +198,7 @@ namespace UniversalEditor.Plugins.Executable.UserInterface.Editors.Executable txtAssemblyVersion.Text = executable.ManagedAssembly.GetName().Version.ToString(); // pnlManagedAssembly.Assembly = executable.ManagedAssembly; - // UpdateTypeList(); - tmManagedDisassemblyTypes.Rows.Add(new TreeModelRow(new TreeModelRowColumn[] - { - new TreeModelRowColumn(tmManagedDisassemblyTypes.Columns[0], "Test") - - })); + UpdateTypeList(); } } diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/NonLayout/ExecutableEditor.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/NonLayout/ExecutableEditor.cs new file mode 100644 index 00000000..b0063912 --- /dev/null +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/NonLayout/ExecutableEditor.cs @@ -0,0 +1,257 @@ +// +// ExecutableEditor.cs - provides an Editor for the ExecutableObjectModel +// +// Author: +// Michael Becker +// +// Copyright (c) 2019-2020 Mike Becker's Software +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using UniversalEditor.ObjectModels.Executable; +using UniversalEditor.UserInterface; +using MBS.Framework.UserInterface; +using MBS.Framework.UserInterface.Controls; +using MBS.Framework.UserInterface.Dialogs; +using MBS.Framework.UserInterface.Input.Mouse; +using MBS.Framework.UserInterface.Layouts; + +namespace UniversalEditor.Plugins.Executable.UserInterface.Editors.Executable +{ + /// + /// Provides an for the . + /// + public class ExecutableEditor : Editor + { + private static EditorReference _er = null; + public override EditorReference MakeReference() + { + if (_er == null) + { + _er = base.MakeReference(); + _er.SupportedObjectModels.Add(typeof(ExecutableObjectModel)); + } + return _er; + } + + protected override EditorSelection CreateSelectionInternal(object content) + { + throw new NotImplementedException(); + } + public override void UpdateSelections() + { + throw new NotImplementedException(); + } + + private ListView tvSections = null; + private DefaultTreeModel tmSections = null; + private TabContainer tbs = null; + + private Label lblAssemblyName = null; + private TextBox txtAssemblyName = null; + private Label lblAssemblyVersion = null; + private TextBox txtAssemblyVersion = null; + + private DefaultTreeModel tmOtherInformation = null; + private ManagedAssemblyPanel pnlManagedAssembly = null; + + public ExecutableEditor() + { + this.Layout = new BoxLayout(Orientation.Vertical); + + tmSections = new DefaultTreeModel(new Type[] { typeof(string), typeof(string), typeof(string), typeof(string) }); + tvSections = new ListView(); + tvSections.Model = tmSections; + + tvSections.Columns.Add(new ListViewColumnText(tmSections.Columns[0], "Name")); + tvSections.Columns.Add(new ListViewColumnText(tmSections.Columns[1], "Physical address")); + tvSections.Columns.Add(new ListViewColumnText(tmSections.Columns[2], "Virtual address")); + tvSections.Columns.Add(new ListViewColumnText(tmSections.Columns[3], "Size")); + tvSections.BeforeContextMenu += tvSections_BeforeContextMenu; + + tbs = new TabContainer(); + TabPage tabSections = new TabPage("Sections (0)"); + tabSections.Layout = new BoxLayout(Orientation.Vertical); + + tabSections.Controls.Add(tvSections, new BoxLayout.Constraints(true, true)); + tbs.TabPages.Add(tabSections); + + TabPage tabVersion = new TabPage("Version"); + tabVersion.Layout = new GridLayout(); + + Label lblFileVersion = new Label(); + lblFileVersion.Text = "File version:"; + lblFileVersion.HorizontalAlignment = HorizontalAlignment.Left; + tabVersion.Controls.Add(lblFileVersion, new GridLayout.Constraints(0, 0)); + + TextBox txtFileVersion = new TextBox(); + tabVersion.Controls.Add(txtFileVersion, new GridLayout.Constraints(0, 1, 1, 1, ExpandMode.Horizontal)); + + Label lblDescription = new Label(); + lblDescription.Text = "Description:"; + lblDescription.HorizontalAlignment = HorizontalAlignment.Left; + tabVersion.Controls.Add(lblDescription, new GridLayout.Constraints(1, 0)); + + TextBox txtDescription = new TextBox(); + tabVersion.Controls.Add(txtDescription, new GridLayout.Constraints(1, 1, 1, 1, ExpandMode.Horizontal)); + + Label lblCopyright = new Label(); + lblCopyright.Text = "Copyright:"; + lblCopyright.HorizontalAlignment = HorizontalAlignment.Left; + tabVersion.Controls.Add(lblCopyright, new GridLayout.Constraints(2, 0)); + + TextBox txtCopyright = new TextBox(); + tabVersion.Controls.Add(txtCopyright, new GridLayout.Constraints(2, 1, 1, 1, ExpandMode.Horizontal)); + + Label lblOtherInformationLabel = new Label(); + lblOtherInformationLabel.Text = "Other version information:"; + lblOtherInformationLabel.HorizontalAlignment = HorizontalAlignment.Left; + tabVersion.Controls.Add(lblOtherInformationLabel, new GridLayout.Constraints(3, 0, 1, 2)); + + tmOtherInformation = new DefaultTreeModel(new Type[] { typeof(string), typeof(string) }); + + ListView lvOtherInformation = new ListView(); + lvOtherInformation.Model = tmOtherInformation; + lvOtherInformation.Columns.Add(new ListViewColumnText(tmOtherInformation.Columns[0], "Name")); + lvOtherInformation.Columns.Add(new ListViewColumnText(tmOtherInformation.Columns[1], "Value")); + tabVersion.Controls.Add(lvOtherInformation, new GridLayout.Constraints(4, 0, 1, 2, ExpandMode.Both)); + + tbs.TabPages.Add(tabVersion); + + TabPage tabManagedAssembly = new TabPage("Managed Assembly"); + tabManagedAssembly.Layout = new BoxLayout(Orientation.Vertical); + + lblAssemblyName = new Label("Assembly name: "); + lblAssemblyName.HorizontalAlignment = HorizontalAlignment.Left; + + txtAssemblyName = new TextBox(); + + lblAssemblyVersion = new Label("Assembly version: "); + lblAssemblyVersion.HorizontalAlignment = HorizontalAlignment.Left; + + txtAssemblyVersion = new TextBox(); + + Container pnlMetadata = new Container(); + pnlMetadata.Layout = new GridLayout(); + + pnlMetadata.Controls.Add(lblAssemblyName, new GridLayout.Constraints(0, 0)); + pnlMetadata.Controls.Add(txtAssemblyName, new GridLayout.Constraints(0, 1, 1, 1, ExpandMode.Horizontal)); + pnlMetadata.Controls.Add(lblAssemblyVersion, new GridLayout.Constraints(1, 0)); + pnlMetadata.Controls.Add(txtAssemblyVersion, new GridLayout.Constraints(1, 1, 1, 1, ExpandMode.Horizontal)); + + tabManagedAssembly.Controls.Add(pnlMetadata, new BoxLayout.Constraints(false, true)); + + pnlManagedAssembly = new ManagedAssemblyPanel(); + tabManagedAssembly.Controls.Add(pnlManagedAssembly, new BoxLayout.Constraints(true, true)); + + tbs.TabPages.Add(tabManagedAssembly); + + Application.AttachCommandEventHandler("ExecutableEditor_ContextMenu_Sections_Selected_CopyTo", ContextMenu_CopyTo_Click); + + this.Controls.Add(tbs, new BoxLayout.Constraints(true, true)); + } + + private void ContextMenu_CopyTo_Click(object sender, EventArgs e) + { + FileDialog fd = new FileDialog(); + if (tvSections.SelectedRows.Count == 1) + { + fd.Mode = FileDialogMode.Save; + + ExecutableSection section = tvSections.SelectedRows[0].GetExtraData("section"); + fd.SelectedFileNames.Add(section.Name); + + if (fd.ShowDialog() == DialogResult.OK) + { + System.IO.File.WriteAllBytes(fd.SelectedFileNames[fd.SelectedFileNames.Count - 1], section.Data); + } + } + else if (tvSections.SelectedRows.Count > 1) + { + // select a folder + fd.Mode = FileDialogMode.SelectFolder; + + if (fd.ShowDialog() == DialogResult.OK) + { + foreach (TreeModelRow row in tvSections.SelectedRows) + { + ExecutableSection section = tvSections.SelectedRows[0].GetExtraData("section"); + System.IO.File.WriteAllBytes(fd.SelectedFileNames[fd.SelectedFileNames.Count - 1] + System.IO.Path.DirectorySeparatorChar.ToString() + section.Name, section.Data); + } + } + } + } + + void tvSections_BeforeContextMenu(object sender, EventArgs e) + { + bool selected = tvSections.SelectedRows.Count > 0; + if (e is MouseEventArgs) + { + MouseEventArgs ee = (e as MouseEventArgs); + ListViewHitTestInfo lvih = tvSections.HitTest(ee.X, ee.Y); + if (lvih.Row == null) + selected = false; + } + + if (selected) + { + tvSections.ContextMenuCommandID = "ExecutableEditor_ContextMenu_Sections_Selected"; + } + else + { + tvSections.ContextMenuCommandID = "ExecutableEditor_ContextMenu_Sections_Unselected"; + } + } + + + protected override void OnObjectModelChanged(EventArgs e) + { + base.OnObjectModelChanged(e); + + // tv.Nodes.Clear(); + // lvSections.Items.Clear(); + + tbs.TabPages[0].Text = "Sections (0)"; + tbs.TabPages[1].Visible = false; + + ExecutableObjectModel executable = (ObjectModel as ExecutableObjectModel); + if (executable == null) return; + + tbs.TabPages[0].Text = "Sections (" + executable.Sections.Count.ToString() + ")"; + + foreach (ExecutableSection section in executable.Sections) + { + tmSections.Rows.Add(new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmSections.Columns[0], section.Name), + new TreeModelRowColumn(tmSections.Columns[1], section.PhysicalAddress.ToString()), + new TreeModelRowColumn(tmSections.Columns[2], section.VirtualAddress.ToString()), + new TreeModelRowColumn(tmSections.Columns[3], section.VirtualSize.ToString()) + })); + tmSections.Rows[tmSections.Rows.Count - 1].SetExtraData("section", section); + } + + if (executable.ManagedAssembly != null) + { + tbs.TabPages[1].Visible = true; + + txtAssemblyName.Text = executable.ManagedAssembly.GetName().Name; + txtAssemblyVersion.Text = executable.ManagedAssembly.GetName().Version.ToString(); + + pnlManagedAssembly.Assembly = executable.ManagedAssembly; + } + } + } +} diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/NonLayout/ManagedAssemblyPanel.cs b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/NonLayout/ManagedAssemblyPanel.cs new file mode 100644 index 00000000..0154ff45 --- /dev/null +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/Editors/Executable/NonLayout/ManagedAssemblyPanel.cs @@ -0,0 +1,338 @@ +// +// ManagedAssemblyPanel.cs - provides a UWT Container with controls to edit managed assembly information for an ExecutableObjectModel +// +// Author: +// Michael Becker +// +// Copyright (c) 2019-2020 Mike Becker's Software +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using System; +using System.Reflection; +using System.Text; +using MBS.Framework.UserInterface; +using MBS.Framework.UserInterface.Controls; +using MBS.Framework.UserInterface.Dialogs; +using MBS.Framework.UserInterface.Layouts; + +namespace UniversalEditor.Plugins.Executable.UserInterface.Editors.Executable +{ + /// + /// Provides a UWT Container with controls to edit managed assembly information for an . + /// + public class ManagedAssemblyPanel : Container + { + private TextBox txtSearch = null; + private ListView tvTypes = null; + private DefaultTreeModel tmTypes = null; + private TextBox txtSource = null; + + private ComboBox cboLanguage = null; + + private Assembly _Assembly = null; + public Assembly Assembly + { + get { return _Assembly; } + set + { + _Assembly = value; + UpdateTypeList(); + } + } + + private void UpdateTypeList() + { + tmTypes.Rows.Clear(); + + Type[] types = null; + try + { + types = _Assembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + types = ex.Types; + } + + for (int i = 0; i < types.Length; i++) + { + if (types[i] == null) + continue; + + string[] nameParts = types[i].FullName.Split(new char[] { '.' }); + bool nestedClass = false; + while (nameParts[nameParts.Length - 1].Contains("+")) + { + // handle the case of nested classes + nestedClass = true; + Array.Resize(ref nameParts, nameParts.Length + 1); + string[] p = nameParts[nameParts.Length - 2].Split(new char[] { '+' }); + nameParts[nameParts.Length - 2] = p[0]; + nameParts[nameParts.Length - 1] = p[1]; + } + + TreeModelRow row = tmTypes.RecursiveCreateTreeModelRow(tmTypes.Columns[0], nameParts, new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[1], "Namespace") + }); + + if (nestedClass) + row.ParentRow.RowColumns[1].Value = "Class"; + + row.RowColumns.Add(new TreeModelRowColumn(tmTypes.Columns[1], "Class")); + + SetupTypeTreeModelRow(row, types[i]); + } + } + + private void SetupTypeTreeModelRow(TreeModelRow row, Type type) + { + row.SetExtraData("item", type); + + BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + + TreeModelRow rowBaseTypes = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], "Base Types") + }); + + if (type.BaseType != null) + { + TreeModelRow rowBaseType = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], type.BaseType.FullName) + }); + SetupTypeTreeModelRow(rowBaseType, type.BaseType); + rowBaseTypes.Rows.Add(rowBaseType); + } + + row.Rows.Add(rowBaseTypes); + + TreeModelRow rowDerivedTypes = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], "Derived Types") + }); + row.Rows.Add(rowDerivedTypes); + + FieldInfo[] fields = type.GetFields(bindingFlags); + for (int j = 0; j < fields.Length; j++) + { + TreeModelRow row2 = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], fields[j]), + new TreeModelRowColumn(tmTypes.Columns[1], "Field") + }); + row2.SetExtraData("item", fields[j]); + row.Rows.Add(row2); + } + + EventInfo[] events = type.GetEvents(bindingFlags); + for (int j = 0; j < events.Length; j++) + { + TreeModelRow row2 = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], events[j]), + new TreeModelRowColumn(tmTypes.Columns[1], "Event") + }); + row2.SetExtraData("item", events[j]); + row.Rows.Add(row2); + } + + PropertyInfo[] props = type.GetProperties(bindingFlags); + for (int j = 0; j < props.Length; j++) + { + TreeModelRow row2 = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], props[j]), + new TreeModelRowColumn(tmTypes.Columns[1], "Property") + }); + row2.SetExtraData("item", props[j]); + row.Rows.Add(row2); + } + + MethodInfo[] meths = type.GetMethods(bindingFlags); + for (int j = 0; j < meths.Length; j++) + { + if (meths[j].IsSpecialName && (meths[j].Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase) || meths[j].Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase))) + { + // we can be REASONABLY sure that this is a property setter / getter, + // and as we've already gotten all the properties, ignore it + continue; + } + + TreeModelRow row2 = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmTypes.Columns[0], GetMethodTitle(meths[j])), + new TreeModelRowColumn(tmTypes.Columns[1], "Method") + }); + row2.SetExtraData("item", meths[j]); + row.Rows.Add(row2); + } + } + + private void tsbILSave_Click(object sender, EventArgs e) + { + MemberInfo t = tvTypes.SelectedRows[0].GetExtraData("item"); + if (t == null) + return; + + FileDialog dlg = new FileDialog(); + dlg.Text = "Save Code File"; + + dlg.SelectedFileNames.Clear(); + + dlg.SelectedFileNames.Add(t.Name + Language.CodeFileExtension); + + if (dlg.ShowDialog() == DialogResult.OK) + { + + } + } + + public ManagedAssemblyPanel() + { + Layout = new BoxLayout(Orientation.Vertical); + + Container ctToolbarAndOthers = new Container(); + ctToolbarAndOthers.Layout = new BoxLayout(Orientation.Horizontal); + + Toolbar tb = new Toolbar(); + tb.Items.Add(new ToolbarItemButton("tsbILSave", StockType.Save, tsbILSave_Click)); + ctToolbarAndOthers.Controls.Add(tb, new BoxLayout.Constraints(false, true)); + + cboLanguage = new ComboBox(); + cboLanguage.Changed += cboLanguage_Changed; + Type[] codeProviders = MBS.Framework.Reflection.GetAvailableTypes(new Type[] { typeof(CodeProvider) }); + DefaultTreeModel tmLanguage = new DefaultTreeModel(new Type[] { typeof(string) }); + + /* + tmLanguage.Rows.Add(new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmLanguage.Columns[0], "Raw Bytes") + })); + */ + + for (int i = 0; i < codeProviders.Length; i++) + { + CodeProvider codeProvider = (codeProviders[i].Assembly.CreateInstance(codeProviders[i].FullName) as CodeProvider); + TreeModelRow row = new TreeModelRow(new TreeModelRowColumn[] + { + new TreeModelRowColumn(tmLanguage.Columns[0], codeProvider.Title) + }); + row.SetExtraData("provider", codeProvider); + tmLanguage.Rows.Add(row); + } + if (tmLanguage.Rows.Count > 0) + { + cboLanguage.SelectedItem = tmLanguage.Rows[0]; + } + cboLanguage.ReadOnly = true; + cboLanguage.Model = tmLanguage; + ctToolbarAndOthers.Controls.Add(cboLanguage, new BoxLayout.Constraints(false, false)); + + Controls.Add(ctToolbarAndOthers, new BoxLayout.Constraints(false, true)); + + SplitContainer scLeftRight = new SplitContainer(Orientation.Vertical); + scLeftRight.SplitterPosition = 250; + + txtSearch = new TextBox(); + + scLeftRight.Panel1.Layout = new BoxLayout(Orientation.Vertical); + scLeftRight.Panel1.Controls.Add(txtSearch, new BoxLayout.Constraints(false, true)); + + tvTypes = new ListView(); + tvTypes.SelectionChanged += tvTypes_SelectionChanged; + + tmTypes = new DefaultTreeModel(new Type[] { typeof(string), typeof(string) }); + tvTypes.Model = tmTypes; + + tvTypes.Columns.Add(new ListViewColumnText(tmTypes.Columns[0], "Name")); + tvTypes.Columns.Add(new ListViewColumnText(tmTypes.Columns[1], "Type")); + scLeftRight.Panel1.Controls.Add(tvTypes, new BoxLayout.Constraints(true, true)); + + scLeftRight.Panel2.Layout = new BoxLayout(Orientation.Vertical); + txtSource = new TextBox(); + txtSource.Multiline = true; + scLeftRight.Panel2.Controls.Add(txtSource, new BoxLayout.Constraints(true, true)); + + Controls.Add(scLeftRight, new BoxLayout.Constraints(true, true)); + } + + void cboLanguage_Changed(object sender, EventArgs e) + { + CodeProvider provider = cboLanguage.SelectedItem.GetExtraData("provider"); + Language = provider; + } + + + private CodeProvider _Language = CodeProvider.CSharp; + public CodeProvider Language + { + get { return _Language; } + set + { + _Language = value; + tvTypes_SelectionChanged(this, EventArgs.Empty); + } + } + + private string GetAccessModifiersSourceCode(PropertyInfo mi) + { + return Language.GetAccessModifiers(mi); + } + private string GetAccessModifiersSourceCode(MethodInfo mi) + { + return Language.GetAccessModifiers(mi); + } + private string GetAccessModifiersSourceCode(FieldInfo mi) + { + return Language.GetAccessModifiers(mi); + } + private string GetAccessModifiersSourceCode(Type mi) + { + return Language.GetAccessModifiers(mi); + } + + private string GetMethodTitle(MethodInfo mi) + { + StringBuilder sb = new StringBuilder(); + string typeName = Language.GetTypeName(mi.ReturnType); + sb.Append(mi.Name); + sb.Append('('); + sb.Append(')'); + sb.Append(" : "); + sb.Append(typeName); + return sb.ToString(); + } + + private void tvTypes_SelectionChanged(object sender, EventArgs e) + { + if (tvTypes.SelectedRows.Count == 0) + return; + + object item = tvTypes.SelectedRows[0].GetExtraData("item"); + if (item is Type) + { + Type typ = (item as Type); + txtSource.Text = Language.GetSourceCode(typ, 0); + } + else if (item is MethodInfo) + { + txtSource.Text = Language.GetSourceCode(item as MethodInfo, 0); + } + } + + } +} diff --git a/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/UniversalEditor.Plugins.Executable.UserInterface.csproj b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/UniversalEditor.Plugins.Executable.UserInterface.csproj index f96d8d7c..76eb7e81 100644 --- a/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/UniversalEditor.Plugins.Executable.UserInterface.csproj +++ b/Plugins.UserInterface/UniversalEditor.Plugins.Executable.UserInterface/UniversalEditor.Plugins.Executable.UserInterface.csproj @@ -31,16 +31,18 @@ - + + + @@ -68,5 +70,8 @@ MBS.Framework.UserInterface + + + \ No newline at end of file