首页 > 解决方案 > 使用 Assembly.Load 创建外部项目的新 WinForm

问题描述

我在 C# 中有 2 个 windowsForm 项目(项目 A 和 B),但我想通过代码在项目 B 中添加对项目 A 的引用并从项目 B 中调用项目 A。我使用了 Assembly.Load,它只在工作如果我删除 Main void 参数。

项目 A 的表单应作为项目 B 的 MdiParent 打开。

我尝试使用 Assembly.load 和 activator.createinstance 但是当我尝试传递方法参数时它不起作用。

使用参数 args返回错误(System.MissingMethodException: 'Constructor in type' CompareXMLTools.Main 'not found.')

#using  System.Reflection.Assembly

项目 A 程序.cs

namespace CompareXMLTools
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Main(args));

        }
    }
}

窗体

namespace CompareXMLTools
{
    public partial class Main : Form
    {
        public Main(string[] args)
        {
            InitializeComponent();
            ArgsPassed = args;
        }
    }
}

项目 B

namespace XMLValidator
{
    public partial class frmMain : Form
    {
        public frmMain(string[] args)
        {
            InitializeComponent();
            ArgsPassed = args;
        }
    }

    private void tsbrnCompareXML_Click(object sender, EventArgs e)
    {
        object dllUserControl = null;
        System.Reflection.Assembly assembly2 = AppDomain.CurrentDomain.Load(File.ReadAllBytes(@"D:\Projetos C#\XMLValidator\XMLValidator\CompareXMLTools.exe"));
        dllUserControl = assembly2.CreateInstance("CompareXMLTools.Main", true, System.Reflection.BindingFlags.Default, null, new string[] { }, System.Globalization.CultureInfo.CurrentCulture, null);

        ((frmMain)dllUserControl).MdiParent = this;
        ((frmMain)dllUserControl).Show();
    }
}

注意:项目 B 命令仅在我string [] args从主方法中删除 ** ** 字段时才有效。

我在调用项目 A 的新 WinForm 时需要传递参数,我该怎么做?

标签: c#winformsassembly.load

解决方案


我建议在 Form1 中添加一个无参数构造函数,这样您就可以在可能不知道它需要多少个参数(如果有)的外部程序集之后调用它,并添加处理null参数所需的逻辑。

namespace XMLValidator
{
    public partial class Main : Form
    {
        public Main() : this(null) { }

        public Main(string[] args) {
            InitializeComponent();
            [Something] = args;
        }
    }
}

在此之后,如果您不想/不能使用接口(对这些程序集之间的约定有一个共同的理解),您必须依靠您对要加载和调用的表单的了解Name

可以使用Activator.CreateInstance重载来传递参数,该重载接受 Type(您知道要加载的类型,一个 Form)和params object[].

public static object CreateInstance (Type type, params object[] args);

中的每个对象都params表示您指定的类的构造函数所期望的参数。无参数构造函数不需要任何参数,
因此您传入null. 否则,包含要调用的非空构造函数的参数,以初始化指定的类。args[0]
args[0]

object[] args = new object[1] { new string[] { "Args String", "Args Value", "Other args" } };

并致电:

Activator.CreateInstance([Type], args) as [Type];

我建议构建一个处理外部程序集初始化的中间类,自动提取一些有用的信息(NameSpace特定类型的资源 - Forms,项目资源等)。所以你只需要提供一个表单的名称来激活和显示它。

例如,从您的 MDIParent 表单中的菜单:

public partial class MDIParent : Form
{
    private ResourceBag formResources = null;

    public MDIParent()
    {
        InitializeComponent();
        formResources = new ResourceBag([Assembly Path]);
    }

    // [...]

    // From a ToolStrip MenuItem, load with arguments
    private void loadExternalFormToolStripMenuItem_Click(object sender, EventArgs e)
    {
        object[] args = new object[1] { new string[] { "Args String", "Args Value", "Other args" } };
        Form form1 = formResources.LoadForm("Form1", args);
        form1.MdiParent = this;
        form1.Show();
    }

    // From another ToolStrip MenuItem, load using the default Constructor
    private void loadExternalFormNoParamsToolStripMenuItem_Click(object sender, EventArgs e)
    {
        Form form1 = formResources.LoadForm("Form1");
        form1.MdiParent = this;
        form1.Show();
    }

}

ResourceBag助手类

也许添加一个重载public Form LoadForm(),传递一个不同NameSpace的,以防你想加载一个不属于默认的类对象NameSpace

using System.IO;
using System.Reflection;
using System.Windows.Forms;

internal class ResourceBag
{
    private string m_AssemblyName = string.Empty;
    private static Assembly asm = null;

    public ResourceBag() : this(null) { }

    public ResourceBag(string assemblyPath)
    {
        if (!string.IsNullOrEmpty(assemblyPath)) {
            this.AssemblyName = assemblyPath;
        }
    }

    public string NameSpace { get; set; }

    public string AssemblyName {
        get => m_AssemblyName;
        set {
            if (File.Exists(value)) {
                m_AssemblyName = value;
                asm = Assembly.LoadFrom(m_AssemblyName);
                this.NameSpace= asm.GetName().Name;
            }
            else {
                throw new ArgumentException("Invalid Assembly path");
            }
        }
    }

    public Form LoadForm(string formName, params object[] args)
    {
        if (asm == null) throw new BadImageFormatException("Resource Library not loaded");
        return Activator.CreateInstance(asm.GetType($"{NameSpace}.{formName}"), args) as Form;
    }
}

推荐阅读